From 3907d05678afba860d414aba7c3b1f4fed42c742 Mon Sep 17 00:00:00 2001 From: yyyyaaa Date: Mon, 17 Aug 2026 12:39:03 +0700 Subject: [PATCH 1/2] test: remove low-value coverage --- TEST_SUITE_AUDIT.md | 125 + apps/blocks/e2e/docs.smoke.spec.ts | 150 +- apps/blocks/e2e/mobile.interaction.spec.ts | 284 +- apps/blocks/src/app/sitemap.test.ts | 42 - .../constructive-console-kit.test.tsx | 28 - .../store/console-kit-store.test.ts | 100 - .../billing-showcase-preview.test.tsx | 150 +- .../billing-showcase-resources.test.ts | 36 - .../src/components/docs/ui-demos.test.tsx | 48 - .../components/site/registry-shell.test.tsx | 75 - .../src/components/site/site-topbar.test.tsx | 91 +- apps/blocks/src/feature-packs/catalog.test.ts | 73 - apps/blocks/src/lib/ai-docs.test.ts | 22 - apps/blocks/src/lib/base-primitives.test.ts | 72 - apps/blocks/src/lib/billing-blocks.test.ts | 35 - apps/blocks/src/lib/feature-packs.test.ts | 47 - .../src/__tests__/keybinding.test.ts | 304 +- .../src/__tests__/multi-step-builder.test.ts | 136 +- .../src/__tests__/registry.test.ts | 188 +- .../data/src/__tests__/complex-fields.test.ts | 547 -- .../data/src/__tests__/data.types.test.ts | 60 +- .../src/__tests__/mutation-input.node.test.ts | 25 - .../__snapshots__/builder.test.ts.snap | 507 -- .../meta-object.node.test.ts.snap | 6568 ----------------- .../__snapshots__/meta-object.test.ts.snap | 6568 ----------------- .../__tests__/meta-object.node.test.ts | 4 - .../src/__tests__/query-generator.test.ts | 8 - .../schema-builder-layout-motion.test.tsx | 392 - .../__tests__/schema-builder-render.test.tsx | 147 +- .../schema-builder-selectors.test.tsx | 90 +- .../tables/create-table-card.test.tsx | 257 - .../__tests__/create-sheets-cell.test.ts | 46 + .../__tests__/kind-parity.test-d.ts | 13 - .../src/cell-model/create-sheets-cell.ts | 4 +- .../src/cell-model/factories/boolean.ts | 10 +- .../src/cell-model/factories/datetime.ts | 2 +- .../src/cell-model/factories/geometry.ts | 5 +- .../sheets/src/cell-model/factories/number.ts | 9 +- .../src/cell-model/factories/relation.ts | 6 +- .../sheets/src/cell-model/factories/text.ts | 5 +- .../sheets/src/cell-model/factories/uri.ts | 5 +- packages/sheets/src/cell-model/sheets-cell.ts | 9 +- .../__tests__/draft-action-view.test.tsx | 31 - .../views/__tests__/geometry-view.test.tsx | 126 - .../views/__tests__/image-view.test.tsx | 151 - .../views/__tests__/loading-view.test.tsx | 78 - .../views/__tests__/relation-view.test.tsx | 107 - .../__tests__/registry-sheetscell.test.ts | 69 - .../__tests__/default-commands.test.ts | 200 - .../src/commands/__tests__/keymap.test.ts | 18 +- .../src/commands/__tests__/registry.test.ts | 12 +- .../__tests__/sheets-dom.parity.test.tsx | 175 - .../__tests__/sheets-dom.smoke.test.tsx | 139 - .../editors/__tests__/edit-intent.test.ts | 221 +- .../grid/__golden__/cell-display.golden.json | 496 -- .../cell-display.sheets.parity.test.ts | 31 - .../src/grid/__golden__/coverage.meta.test.ts | 110 - .../src/grid/__golden__/display-cases.ts | 13 +- .../src/grid/__golden__/harness.self.test.ts | 119 - .../src/grid/__golden__/parity.harness.ts | 298 - .../__golden__/post-processing.golden.json | 293 - .../__golden__/post-processing.golden.test.ts | 212 - .../src/grid/__golden__/resolver.golden.json | 2243 ------ .../grid/__golden__/resolver.golden.test.ts | 227 - .../src/grid/__golden__/selection.golden.json | 1054 --- .../grid/__golden__/selection.golden.test.ts | 389 - .../grid/__golden__/sheets-dom.golden.json | 3038 -------- .../grid/__tests__/cell-type-resolver.test.ts | 46 + .../grid/__tests__/registry-fidelity.test.ts | 141 - .../__tests__/range-set.golden.test.ts | 108 - .../src/selection/__tests__/range-set.test.ts | 36 + packages/sheets/src/selection/range-set.ts | 10 +- .../stories/__tests__/stories-render.test.tsx | 192 +- .../sheets/src/stories/_support/fixtures.ts | 8 +- .../grid-viewport-active-cell.test.tsx | 7 +- .../__tests__/grid-viewport-header.test.tsx | 18 - .../grid-viewport-rowmarker.test.tsx | 40 - .../__tests__/mock-execute-roundtrip.test.ts | 137 - packages/ui/test/app-shell.test.tsx | 104 +- packages/ui/test/button-as-child.test.tsx | 39 - packages/ui/test/field-empty.test.tsx | 78 - packages/ui/test/select-transition.test.tsx | 51 - packages/ui/test/stack-animation.test.tsx | 153 - 83 files changed, 526 insertions(+), 27785 deletions(-) create mode 100644 TEST_SUITE_AUDIT.md delete mode 100644 apps/blocks/src/app/sitemap.test.ts delete mode 100644 apps/blocks/src/components/docs/ui-demos.test.tsx delete mode 100644 apps/blocks/src/components/site/registry-shell.test.tsx delete mode 100644 apps/blocks/src/lib/ai-docs.test.ts delete mode 100644 apps/blocks/src/lib/base-primitives.test.ts delete mode 100644 apps/blocks/src/lib/billing-blocks.test.ts delete mode 100644 apps/blocks/src/lib/feature-packs.test.ts delete mode 100644 packages/data/src/__tests__/complex-fields.test.ts delete mode 100644 packages/data/src/__tests__/query-builder/__tests__/__snapshots__/builder.test.ts.snap delete mode 100644 packages/data/src/__tests__/query-builder/__tests__/__snapshots__/meta-object.node.test.ts.snap delete mode 100644 packages/data/src/__tests__/query-builder/__tests__/__snapshots__/meta-object.test.ts.snap delete mode 100644 packages/schema-builder/src/__tests__/schema-builder-layout-motion.test.tsx delete mode 100644 packages/schema-builder/src/schema/schema-builder-policies/components/tables/create-table-card.test.tsx create mode 100644 packages/sheets/src/cell-model/__tests__/create-sheets-cell.test.ts delete mode 100644 packages/sheets/src/cell-model/__tests__/kind-parity.test-d.ts delete mode 100644 packages/sheets/src/cell-model/views/__tests__/geometry-view.test.tsx delete mode 100644 packages/sheets/src/cell-model/views/__tests__/image-view.test.tsx delete mode 100644 packages/sheets/src/cell-model/views/__tests__/loading-view.test.tsx delete mode 100644 packages/sheets/src/cell-model/views/__tests__/relation-view.test.tsx delete mode 100644 packages/sheets/src/cell-types/__tests__/registry-sheetscell.test.ts delete mode 100644 packages/sheets/src/commands/__tests__/default-commands.test.ts delete mode 100644 packages/sheets/src/grid-dom/__tests__/sheets-dom.parity.test.tsx delete mode 100644 packages/sheets/src/grid-dom/__tests__/sheets-dom.smoke.test.tsx delete mode 100644 packages/sheets/src/grid/__golden__/cell-display.golden.json delete mode 100644 packages/sheets/src/grid/__golden__/cell-display.sheets.parity.test.ts delete mode 100644 packages/sheets/src/grid/__golden__/coverage.meta.test.ts delete mode 100644 packages/sheets/src/grid/__golden__/harness.self.test.ts delete mode 100644 packages/sheets/src/grid/__golden__/parity.harness.ts delete mode 100644 packages/sheets/src/grid/__golden__/post-processing.golden.json delete mode 100644 packages/sheets/src/grid/__golden__/post-processing.golden.test.ts delete mode 100644 packages/sheets/src/grid/__golden__/resolver.golden.json delete mode 100644 packages/sheets/src/grid/__golden__/resolver.golden.test.ts delete mode 100644 packages/sheets/src/grid/__golden__/selection.golden.json delete mode 100644 packages/sheets/src/grid/__golden__/selection.golden.test.ts delete mode 100644 packages/sheets/src/grid/__golden__/sheets-dom.golden.json create mode 100644 packages/sheets/src/grid/__tests__/cell-type-resolver.test.ts delete mode 100644 packages/sheets/src/grid/__tests__/registry-fidelity.test.ts delete mode 100644 packages/sheets/src/selection/__tests__/range-set.golden.test.ts create mode 100644 packages/sheets/src/selection/__tests__/range-set.test.ts delete mode 100644 packages/sheets/src/testing/__tests__/mock-execute-roundtrip.test.ts delete mode 100644 packages/ui/test/field-empty.test.tsx delete mode 100644 packages/ui/test/select-transition.test.tsx delete mode 100644 packages/ui/test/stack-animation.test.tsx diff --git a/TEST_SUITE_AUDIT.md b/TEST_SUITE_AUDIT.md new file mode 100644 index 0000000..3498a39 --- /dev/null +++ b/TEST_SUITE_AUDIT.md @@ -0,0 +1,125 @@ +# Test suite audit + +This audit removes tests that restated static structure, catalog contents, mock behavior, or visual implementation. It keeps runtime validation, security and tenancy rules, state transitions, user workflows, serialization, persistence-facing adapters, and difficult failure behavior. + +## Result + +- The normal test suite moved from 185 to 157 test files and from 1,491 to 1,157 cases. +- 31 test files were deleted, three focused contract files were added, and 27 existing files were rewritten or consolidated. +- 334 Vitest/Node cases and six Playwright cases were removed, for 340 fewer cases in the audited files. +- Nine obsolete snapshot, golden-data, and parity-harness artifacts were deleted. + +The counts include the unchanged registry and repository script tests. Playwright is counted separately because `pnpm test` does not run it. + +## Application and documentation tests + +| File | Decision | Existing assertion and value | Protection and retained coverage | +| --- | --- | --- | --- | +| `apps/blocks/e2e/docs.smoke.spec.ts` | Rewrite, 4 → 1 | Enumerated every catalog page, heading, and example count. That duplicated static catalogs and coupled the test to editorial inventory. | The pages build and generated-content checks protect valid pages. The retained browser test protects the runtime 404 contract for removed and unknown routes. | +| `apps/blocks/e2e/mobile.interaction.spec.ts` | Consolidate, 5 → 2 | Checked breakpoints, CSS overflow, touch-target dimensions, and viewport geometry alongside real interactions. The geometry checks were visual-policy tests. | Retained tests complete mobile navigation and verify modal Escape dismissal plus focus restoration. Layout and target sizing remain visual-review concerns. | +| `apps/blocks/src/app/sitemap.test.ts` | Delete | Compared the sitemap with exact catalog counts and URLs. The test changed whenever documentation inventory changed. | `build:pages`, sitemap compilation, and the browser route tests protect generation and routing. No separate runtime contract was lost. | +| `apps/blocks/src/components/docs/ui-demos.test.tsx` | Delete | Matched the primitive catalog and mounted every demo without asserting a workflow. | Type checking resolves every component, `gen:check` resolves and extracts demo source, and the pages build mounts the documentation graph. | +| `apps/blocks/src/components/site/registry-shell.test.tsx` | Delete | Mounted each shell variant and asserted landmarks and documentation chrome existed. | The application build catches composition failures; focused Console Kit and documentation interaction tests protect behavior. | +| `apps/blocks/src/lib/ai-docs.test.ts` | Delete | Pinned aggregate registry metadata and import strings. | Registry generation and package builds validate these static references. | +| `apps/blocks/src/lib/base-primitives.test.ts` | Delete | Froze the exact primitive catalog, exports, registry entries, and example import paths. | TypeScript, registry generation, and package/registry builds protect resolvability. Exact editorial inventory is intentionally not a runtime test. | +| `apps/blocks/src/lib/billing-blocks.test.ts` | Delete | Froze eight block names, guidance fields, and lookup presence. | The typed catalog and pages/registry builds protect structure and resolution. Billing resource behavior remains tested below. | +| `apps/blocks/src/lib/feature-packs.test.ts` | Delete | Froze seven documentation records, summaries, registry names, and editorial fields. | Typed catalogs, registry manifests, and page generation protect static consistency. Dependency semantics remain in `catalog.test.ts`. | +| `apps/blocks/src/blocks/console-kit/constructive/constructive-console-kit.test.tsx` | Consolidate, 15 → 14 | Included a barrel/existence assertion for the selected feature pack. | Removed only that static assertion. Adapter normalization, policy enforcement, callbacks, errors, and runtime behavior remain covered. | +| `apps/blocks/src/blocks/console-kit/store/console-kit-store.test.ts` | Consolidate, 9 → 7 | Inspected initial internal object shape and revision increments. | Store transition tests retain tenant isolation, resource state, invalidation, and observable update behavior. TypeScript protects the state shape. | +| `apps/blocks/src/components/billing-showcase/billing-showcase-preview.test.tsx` | Consolidate, 8 → 4 | Mounted fixtures, checked labels/loading copy, and asserted scale/layout details in addition to actions. | Retained delegated actions, partial-failure behavior, iframe control state, and full-screen keyboard focus. | +| `apps/blocks/src/components/billing-showcase/billing-showcase-resources.test.ts` | Consolidate, 4 → 2 | Rechecked fixture completeness and all-success aggregation. | Retained runtime value validation and partial-resource failure semantics, which TypeScript cannot protect. | +| `apps/blocks/src/components/site/site-topbar.test.tsx` | Consolidate, 11 → 3 | Checked static buttons, labels, hrefs, and presentational variants. | Retained clipboard behavior, feature-pack install mapping, and suppression on unsupported paths. | +| `apps/blocks/src/feature-packs/catalog.test.ts` | Consolidate, 9 → 5 | Froze exact catalog order, defaults, and sidecar fields. | Retained dependency ordering and malformed-pack rejection. Generated registry checks protect static manifest alignment. | + +## Command palette tests + +| File | Decision | Existing assertion and value | Protection and retained coverage | +| --- | --- | --- | --- | +| `packages/command-palette/src/__tests__/keybinding.test.ts` | Rewrite, 32 → 5 | Enumerated many equivalent key strings and exact formatting branches. | Five table-driven contracts retain platform modifiers, editable-target rejection, modifier matching, and user-visible formatting. | +| `packages/command-palette/src/__tests__/multi-step-builder.test.ts` | Consolidate, 10 → 3 | Checked fluent-builder method presence and every intermediate property. | Retained ordered step output and runtime rejection of invalid step configurations. TypeScript protects the fluent API surface. | +| `packages/command-palette/src/__tests__/registry.test.ts` | Consolidate, 17 → 3 | Proved CRUD methods delegated to the current Map/Set implementation and counted notifications. | Retained dynamic replacement/removal and subscriber-visible changes. Registration existence is covered by bootstrap and execution tests. | + +## Data tests + +| File | Decision | Existing assertion and value | Protection and retained coverage | +| --- | --- | --- | --- | +| `packages/data/src/__tests__/complex-fields.test.ts` | Delete | Mocked every `custom-ast` import, then asserted that the mocks returned their own fixtures. It tested the mock instead of the adapter. | Query-builder integration tests still protect owned GraphQL output. A direct, unmocked complex-field adapter contract is a genuine remaining gap. | +| `packages/data/src/__tests__/data.types.test.ts` | Consolidate, 27 → 21 | Created typed filter objects only to assert their fields at runtime. | TypeScript and package declaration generation protect those assignments. Runtime data conversion and query behavior remain covered. | +| `packages/data/src/__tests__/mutation-input.node.test.ts` | Consolidate, 49 → 47 | Turned `PartialBy` examples into runtime property checks. | The compiler owns this generic type contract; mutation-name, identifier, and input transformation cases remain. | +| `packages/data/src/__tests__/query-builder/__tests__/meta-object.node.test.ts` | Consolidate, 2 → 1 | Snapshotted a very large complete query in addition to checking meaningful meta-type discovery. | Retained runtime meta-type discovery. Query serialization remains protected by the smaller builder snapshot suite. | +| `packages/data/src/__tests__/query-generator.test.ts` | Consolidate, 42 → 41 | Asserted only that the builder object exposed methods. | TypeScript and module resolution protect method existence; generated operation behavior and errors remain covered. | + +## Schema Builder tests + +| File | Decision | Existing assertion and value | Protection and retained coverage | +| --- | --- | --- | --- | +| `packages/schema-builder/src/__tests__/schema-builder-layout-motion.test.tsx` | Delete | Inspected CSS classes, motion props, crossfade decomposition, and scroll affordances. | Visual review owns layout and motion. Keyboard, drag-and-drop, route selection, and state behavior remain tested. | +| `packages/schema-builder/src/schema/schema-builder-policies/components/tables/create-table-card.test.tsx` | Delete | Mocked callback refs and counted scroll-listener allocation and cleanup for a shadow effect. | This was an internal implementation test for visual polish. Browser lifecycle and Strict Mode remain exercised by integration builds and higher-level component tests. | +| `packages/schema-builder/src/__tests__/schema-builder-render.test.tsx` | Rewrite, 3 → 1 | Included mount-without-crashing and static copy checks. | Retained cross-database scope rejection, which is an observable tenancy boundary. | +| `packages/schema-builder/src/__tests__/schema-builder-selectors.test.tsx` | Consolidate, 3 → 2 | Counted selector identities and rerenders caused by the current store decomposition. | Retained the host-data boundary and clearing an invalid user selection. | + +## Sheets tests + +| File | Decision | Existing assertion and value | Protection and retained coverage | +| --- | --- | --- | --- | +| `packages/sheets/src/cell-model/__tests__/kind-parity.test-d.ts` | Delete | Proved an internal migration type was assignable to its predecessor. | Package type checking and declaration generation protect the current type graph; the removed predecessor is not a public contract. | +| `packages/sheets/src/cell-model/__tests__/create-sheets-cell.test.ts` | Add focused contract | Replaces broad parity and registry fixtures with owned transformation behavior. | Protects email normalization, relation label/overflow rules, and suppression of draft placeholder IDs. | +| `packages/sheets/src/cell-model/views/__tests__/geometry-view.test.tsx` | Delete | Checked icons, abbreviated labels, and empty visual output. | Cell-type resolution remains covered; iconography and labels are visual presentation. | +| `packages/sheets/src/cell-model/views/__tests__/image-view.test.tsx` | Delete | Checked wrappers, CSS classes, placeholders, and every thumbnail/file-chip presentation branch. | Image editor upload behavior remains covered. Presentational rendering moves to Storybook/visual review. | +| `packages/sheets/src/cell-model/views/__tests__/loading-view.test.tsx` | Delete | Checked skeleton markup, animation class, and absence of text. | Loading-state functionality remains in the grid loading-skeleton test; shimmer styling is visual. | +| `packages/sheets/src/cell-model/views/__tests__/relation-view.test.tsx` | Delete | Checked badge and chip markup for four display variants. | The new cell contract test protects relation data semantics; relation-editor tests protect user behavior. Badge layout remains visual. | +| `packages/sheets/src/cell-model/views/__tests__/draft-action-view.test.tsx` | Consolidate, 7 → 4 | Included default label, static rendering, and no-handler checks. | Retained submit, saving, error/retry, and disabled-action behavior. | +| `packages/sheets/src/cell-types/__tests__/registry-sheetscell.test.ts` | Delete | Proved registered functions/components were returned and missing keys returned `undefined`. | The dynamic customization seam, registry gating, cell routing, and real DOM host tests protect registry behavior without restating Map lookups. | +| `packages/sheets/src/commands/__tests__/default-commands.test.ts` | Delete | Asserted exact mock calls for thin default-command delegates. | Pure selection/navigation helpers, command dispatch, keymaps, clipboard, fill, undo, and editor workflow tests protect the underlying behavior. | +| `packages/sheets/src/commands/__tests__/keymap.test.ts` | Consolidate, 15 → 14 | Froze the full default keymap object. | Retained meaningful key matching and platform behavior; static keymap shape is visible in code and exercised through dispatch. | +| `packages/sheets/src/commands/__tests__/registry.test.ts` | Consolidate, 4 → 3 | Included one test that only proved default command IDs were registered. | Retained dynamic override and isolation behavior; bootstrap/build protects static registration. | +| `packages/sheets/src/grid-dom/__tests__/sheets-dom.parity.test.tsx` | Delete | Compared every rendered fixture cell with a frozen migration golden. | Focused DOM interaction and render-safety tests remain. The removed comparison described the retired implementation rather than the current public contract. | +| `packages/sheets/src/grid-dom/__tests__/sheets-dom.smoke.test.tsx` | Delete | Asserted that a hand-fed grid rendered cells and no canvas. | Real editor, active-cell, virtualization, and render-safety tests already cross the same DOM boundary with meaningful behavior. | +| `packages/sheets/src/grid-dom/editors/__tests__/edit-intent.test.ts` | Rewrite, 23 → 3 | Enumerated every cell type and mirrored the routing table. | Three contracts retain family routing, readonly rejection, and explicit registry override precedence. | +| `packages/sheets/src/grid/__golden__/cell-display.sheets.parity.test.ts` | Delete | Compared all factory projections with frozen migration output. | Focused factory semantics and live editor tests protect owned behavior. Static family coverage is handled by TypeScript and the registry build. | +| `packages/sheets/src/grid/__golden__/coverage.meta.test.ts` | Delete | Tested whether the test fixtures covered every union member and branch. | This was coverage of the harness, not product behavior. TypeScript checks the cell-type records. | +| `packages/sheets/src/grid/__golden__/harness.self.test.ts` | Delete | Tested projection helpers and asserted the harness maps stayed aligned. | The deleted migration harness no longer has a product role. | +| `packages/sheets/src/grid/__golden__/post-processing.golden.test.ts` | Delete | Froze relation and draft post-processing plus fixture constants. | Row-model tests protect non-enumerable draft metadata. Focused `createSheetsCell` tests now protect relation transformation. | +| `packages/sheets/src/grid/__golden__/resolver.golden.test.ts` | Replace, 4 → 3 | Froze more than 80 table-driven mapping outputs and test-fixture size. | `cell-type-resolver.test.ts` retains metadata precedence, readonly identifiers/timestamps, and viewer-only search vectors. Static table inventory is intentionally not frozen. | +| `packages/sheets/src/grid/__tests__/cell-type-resolver.test.ts` | Add focused contract | Replaces the resolver golden with representative semantic branches. | Protects precedence across aliases, arrays, backend metadata, GraphQL fallback, readonly rules, and viewer-only activation. | +| `packages/sheets/src/grid/__golden__/selection.golden.test.ts` | Delete | Ran a large operation table and compared one aggregate JSON blob. | The focused `range-set.test.ts` cases make merge, split, offset, and large-range failures local and readable. | +| `packages/sheets/src/grid/__tests__/registry-fidelity.test.ts` | Delete | Repeated factory output across 21 cases and asserted that the fixture touched every family. | Focused cell contracts, editor behavior, and routing tests protect owned semantics without testing fixture completeness. | +| `packages/sheets/src/selection/__tests__/range-set.golden.test.ts` | Replace, 24 → 4 | Replayed the retired implementation's captured output across many sequences. | `range-set.test.ts` protects the interval invariants that matter: merging, splitting, offsets, and sparse large ranges. | +| `packages/sheets/src/selection/__tests__/range-set.test.ts` | Add focused contract | Replaces captured parity with direct domain assertions. | The tests fail on observable selection results and no longer depend on the old implementation. | +| `packages/sheets/src/stories/__tests__/stories-render.test.tsx` | Rewrite, 14 → 1 | Mounted every story, mostly proving it did not throw, then duplicated the 10k virtualization assertion. | Retained one 10,000-row DOM-window contract. Individual visual stories remain for manual review. | +| `packages/sheets/src/table/__tests__/grid-viewport-active-cell.test.tsx` | Rewrite assertions, 3 → 3 | Mixed active-cell ARIA behavior with a selected CSS-class assertion. | Removed the styling assertion; retained active-descendant and active-cell accessibility behavior. | +| `packages/sheets/src/table/__tests__/grid-viewport-header.test.tsx` | Consolidate, 4 → 3 | Included a static header-exists check. | Retained sort toggling, resize behavior, and keyboard interaction. | +| `packages/sheets/src/table/__tests__/grid-viewport-rowmarker.test.tsx` | Consolidate, 4 → 2 | Included static marker existence and absent-marker rendering checks. | Retained row selection and select-all behavior. | +| `packages/sheets/src/testing/__tests__/mock-execute-roundtrip.test.ts` | Delete | Validated that the repository's test mock recognized queries and echoed mutations. | Real query builders, the PostGraphile adapter, and `sheets-execute` tests protect production behavior. The mock does not receive its own contract suite. | + +## UI tests + +| File | Decision | Existing assertion and value | Protection and retained coverage | +| --- | --- | --- | --- | +| `packages/ui/test/app-shell.test.tsx` | Consolidate, 4 → 1 | Checked region presence, class names, and static layout composition. | Retained the sidebar's accessible collapsed/expanded state transition. | +| `packages/ui/test/button-as-child.test.tsx` | Consolidate, 5 → 2 | Checked CSS, touch-target, and motion classes in addition to slot semantics. | Retained ref/event composition through `asChild` and native button behavior. | +| `packages/ui/test/field-empty.test.tsx` | Delete | Checked legacy slots, upstream surface, CSS classes, and standard empty markup. | TypeScript and the package build protect exports and composition; visual examples own presentation. | +| `packages/ui/test/select-transition.test.tsx` | Delete | Inspected entering/exiting data attributes for a visual transition. | Select interaction tests in consumers and visual review protect user behavior; the animation implementation is not a stable contract. | +| `packages/ui/test/stack-animation.test.tsx` | Delete | Mocked motion internals and asserted exact tween/spring sequencing. | Stack context behavior remains covered. Timing and animation curves are visual implementation details. | + +## Removed support artifacts + +The audit deleted the unused Data snapshots `builder.test.ts.snap`, `meta-object.node.test.ts.snap`, and `meta-object.test.ts.snap`. It also deleted the Sheets migration harness, five golden JSON files, and their stale parity comments. The smaller `builder.node.test.ts.snap` remains because serialized GraphQL output is a public adapter contract. + +## High-value coverage retained + +- Console Kit still covers authorization, tenant isolation, capability policy, callback/error translation, provisioning cleanup, and fail-closed behavior. +- Data still covers query serialization, mutation input semantics, schema compatibility, error classification, row identity, and policy provisioning. +- Command Palette still covers execution, cancellation/background work, reducer transitions, key matching, runtime configuration validation, and dynamic registry changes. +- Sheets still covers selection invariants, adapter behavior, uploads, batch commit, undo/redo, editors, draft rows, cache/state lifecycles, error gating, accessibility, and virtualization. +- Schema Builder still covers keyboard and drag behavior, runtime providers, policy CRUD/masks, selection changes, and cross-database scope rejection. +- UI still covers portals, modal dismissal, SSR, controlled state, accessibility state, and child ref/event composition. + +## Remaining coupling and gaps + +- `console-kit-effects.test.tsx`, `feature-pack-interactions.test.tsx`, `constructive-console-kit.test.tsx`, and `schemas-route-selection.test.tsx` still have large mock surfaces. Their assertions protect real policy and workflow behavior, but future work should move more setup into contract fixtures or browser-level flows. +- Several Sheets editor and batch-commit tests mock host context heavily. They protect failure and side-effect behavior, but a small adapter integration harness would reduce coupling. +- The Data complex-field adapter has no direct unmocked contract test against `@constructive-io/graphql-query/custom-ast`; this is the clearest runtime coverage gap found. +- Public package types are validated by `tsc` and declaration builds, but there is no dedicated compatibility suite for intentional developer-facing type contracts. +- Layout, motion, responsive overflow, and ordinary component appearance now rely on Storybook and visual review by design; they should use visual regression only if the repository adopts a stable visual baseline workflow. +- Live provider behavior remains outside the normal suite and requires the existing explicit live-E2E environment. The local suite continues to test only repository-owned adapter behavior. diff --git a/apps/blocks/e2e/docs.smoke.spec.ts b/apps/blocks/e2e/docs.smoke.spec.ts index a020a81..5a424f2 100644 --- a/apps/blocks/e2e/docs.smoke.spec.ts +++ b/apps/blocks/e2e/docs.smoke.spec.ts @@ -1,156 +1,12 @@ import { expect, test } from '@playwright/test'; -const routes = [ - ['/blocks/', 'Build the product on Constructive.'], - ['/blocks/blocks/', 'Install the foundation your way'], - ['/blocks/blocks/ui/button/', 'Button'], - ['/blocks/blocks/ui/dialog/', 'Dialog'], -] as const; - -const billingBlocks = [ - 'billing-pricing-table', - 'billing-subscription-card', - 'billing-entitlements-list', - 'billing-usage-overview', - 'billing-credits-card', - 'billing-usage-history', - 'billing-activity-table', - 'billing-settings-page', -] as const; - -const featurePacks = [ - { id: 'data', previewHeading: 'Data', title: 'Data', variant: 'tables' }, - { id: 'auth', previewHeading: 'Account', title: 'Authentication', variant: 'account' }, - { - id: 'users', - previewHeading: 'App access', - title: 'App access', - variant: 'directory', - }, - { - id: 'organizations', - previewHeading: 'Organizations', - title: 'Organizations', - variant: 'memberships', - }, - { id: 'storage', previewHeading: 'Storage', title: 'Storage', variant: 'browser' }, - { id: 'billing', previewHeading: 'Billing', title: 'Billing', variant: 'organization' }, - { - id: 'notifications', - previewHeading: 'Notifications', - title: 'Notifications', - variant: 'inbox', - }, -] as const; - -const billingRoute = (name: string) => `/blocks/blocks/billing/${name}/`; -const featurePackRoute = (id: string) => `/blocks/blocks/features/${id}/`; - -for (const [route, heading] of routes) { - test(`${route} renders its documentation surface`, async ({ page }) => { - const response = await page.goto(route, { waitUntil: 'networkidle' }); - expect(response?.status()).toBe(200); - await expect(page.getByRole('heading', { level: 1, name: heading })).toBeVisible(); - await expect(page.locator('main')).toBeVisible(); - }); -} - -test('the primitive catalogs remain complete and legacy block routes stay removed', async ({ page }) => { - await page.goto('/blocks/', { waitUntil: 'networkidle' }); - await expect(page.locator('section[aria-labelledby="component-showcase"] a[href*="/ui/"]')).toHaveCount(29); - - await page.goto('/blocks/blocks/', { waitUntil: 'networkidle' }); - await expect(page.locator('section[aria-labelledby="primitive-catalog"] a[href*="/ui/"]')).toHaveCount(29); - - const legacyResponse = await page.goto('/blocks/blocks/auth/sign-in-card/', { - waitUntil: 'networkidle', - }); - expect(legacyResponse?.status()).toBe(404); -}); - -test('the billing catalog discovers all eight live pages', async ({ page }) => { - const catalogResponse = await page.goto('/blocks/blocks/billing/', { - waitUntil: 'networkidle', - }); - expect(catalogResponse?.status()).toBe(200); - await expect(page.getByRole('heading', { level: 1, name: 'Billing' })).toBeVisible(); - - const previewLinks = page.locator('section[aria-labelledby="billing-catalog-heading"]').getByRole('link'); - await expect(previewLinks).toHaveCount(8); - - const discoveredPaths = await previewLinks.evaluateAll((links) => - links.map((link) => new URL((link as HTMLAnchorElement).href).pathname.replace(/\/?$/, '/')).sort(), - ); - expect(discoveredPaths).toEqual(billingBlocks.map(billingRoute).sort()); - - for (const name of billingBlocks) { - await test.step(name, async () => { - const response = await page.goto(billingRoute(name), { - waitUntil: 'networkidle', - }); - expect(response?.status()).toBe(200); - await expect(page.locator('[data-slot="billing-showcase-preview"]')).toBeVisible(); - }); - } -}); - -test('the feature-pack catalog discovers all seven live pages', async ({ page }) => { - const consoleErrors: string[] = []; - const pageErrors: string[] = []; - page.on('console', (message) => { - if (message.type() === 'error') consoleErrors.push(message.text()); - }); - page.on('pageerror', (error) => pageErrors.push(error.message)); - - const catalogResponse = await page.goto('/blocks/blocks/features/', { - waitUntil: 'networkidle', - }); - expect(catalogResponse?.status()).toBe(200); - await expect(page.getByRole('heading', { level: 1, name: 'Feature packs' })).toBeVisible(); - - const previewLinks = page.locator('section[aria-labelledby="feature-pack-catalog-heading"]').getByRole('link'); - await expect(previewLinks).toHaveCount(7); - - const discoveredPaths = await previewLinks.evaluateAll((links) => - links.map((link) => new URL((link as HTMLAnchorElement).href).pathname.replace(/\/?$/, '/')).sort(), - ); - expect(discoveredPaths).toEqual(featurePacks.map(({ id }) => featurePackRoute(id)).sort()); - - for (const { id, previewHeading, title, variant } of featurePacks) { - await test.step(id, async () => { - const response = await page.goto(featurePackRoute(id), { - waitUntil: 'networkidle', - }); - expect(response?.status()).toBe(200); - const preview = page.locator('[data-slot="feature-pack-showcase-preview"]'); - await expect(preview).toBeVisible(); - - const frameTitle = `${title} feature pack inline live preview`; - const iframe = preview.getByTitle(frameTitle); - await expect(iframe).toHaveAttribute( - 'src', - `/blocks/blocks/features/${id}/preview/?variant=${variant}&state=ready`, - ); - await expect( - page.frameLocator(`iframe[title="${frameTitle}"]`).getByRole('heading', { - exact: true, - level: 1, - name: previewHeading, - }), - ).toBeVisible(); - }); - } - - expect(pageErrors).toEqual([]); - expect(consoleErrors).toEqual([]); -}); - -test('unknown feature-pack detail and preview routes return 404', async ({ page }) => { +test('removed and unknown documentation routes return 404', async ({ page }) => { for (const route of [ + '/blocks/blocks/auth/sign-in-card/', '/blocks/blocks/features/not-a-pack/', '/blocks/blocks/features/not-a-pack/preview/', ]) { const response = await page.goto(route, { waitUntil: 'domcontentloaded' }); - expect(response?.status()).toBe(404); + expect(response?.status(), route).toBe(404); } }); diff --git a/apps/blocks/e2e/mobile.interaction.spec.ts b/apps/blocks/e2e/mobile.interaction.spec.ts index 0fc9736..0d62a52 100644 --- a/apps/blocks/e2e/mobile.interaction.spec.ts +++ b/apps/blocks/e2e/mobile.interaction.spec.ts @@ -1,89 +1,23 @@ -import { - expect, - test, - type FrameLocator, - type Locator, - type Page, -} from '@playwright/test'; +import { expect, test } from '@playwright/test'; -const billingRoute = (name: string) => `/blocks/blocks/billing/${name}/`; -const applicationRoute = (name: string) => `/blocks/blocks/${name}/`; - -function billingPreviewFrame(page: Page): FrameLocator { - return page.frameLocator( - '[data-slot="billing-showcase-preview"] iframe[title$="live preview"]', - ); -} - -function applicationPreviewFrame(page: Page): FrameLocator { - return page.frameLocator( - '[data-slot="application-block-showcase-preview"] iframe[title$="live preview"]', - ); -} - -async function expectTouchTargets(targets: Locator) { - const targetCount = await targets.count(); - expect(targetCount).toBeGreaterThan(0); - - for (let index = 0; index < targetCount; index += 1) { - const target = targets.nth(index); - const targetName = - (await target.getAttribute('aria-label')) ?? - (await target.textContent()) ?? - `target ${index + 1}`; - const hitArea = await target.evaluate((element) => { - const box = element.getBoundingClientRect(); - const coarsePointerTarget = getComputedStyle(element, '::after'); - const pseudoMinWidth = Number.parseFloat(coarsePointerTarget.minWidth) || 0; - const pseudoMinHeight = Number.parseFloat(coarsePointerTarget.minHeight) || 0; - return { - width: Math.max(box.width, pseudoMinWidth), - height: Math.max(box.height, pseudoMinHeight), - }; - }); - expect(hitArea.width, `${targetName} width`).toBeGreaterThanOrEqual(44); - expect(hitArea.height, `${targetName} height`).toBeGreaterThanOrEqual(44); - } -} - -async function chooseShowcaseOption( - page: Page, - label: 'Account' | 'Resource state', - option: string, -) { - const preview = page.locator('[data-slot="billing-showcase-preview"]'); - const trigger = preview.getByRole('combobox', { name: label }); - await trigger.click(); - await page.getByRole('option', { name: option, exact: true }).click(); - await expect(trigger).toContainText(option); -} - -test('navigation changes at the shared 860px breakpoint', async ({ page }) => { - await page.setViewportSize({ width: 861, height: 900 }); +test('mobile navigation opens, follows a link, and closes', async ({ page }) => { + await page.setViewportSize({ width: 860, height: 900 }); await page.goto('/blocks/blocks/ui/button/', { waitUntil: 'networkidle' }); - const navigationMenu = page.getByRole('button', { name: 'Navigation menu', exact: true }); - await expect(navigationMenu).toBeHidden(); - await page.setViewportSize({ width: 860, height: 900 }); - await expect(navigationMenu).toBeVisible(); + const navigationMenu = page.getByRole('button', { name: 'Navigation menu', exact: true }); await expect(navigationMenu).toHaveAttribute('aria-expanded', 'false'); await navigationMenu.click(); await expect(navigationMenu).toHaveAttribute('aria-expanded', 'true'); - const closeNavigation = page.getByRole('button', { name: 'Close navigation', exact: true }); - await expect(closeNavigation).toBeVisible(); - await expect(closeNavigation).toHaveCount(1); - await expect(page.getByRole('button', { name: 'Dismiss navigation', exact: true })).toBeVisible(); + await page.getByRole('link', { name: 'Tooltip' }).click(); await expect(page).toHaveURL(/\/blocks\/blocks\/ui\/tooltip\/$/); - await expect(closeNavigation).toHaveCount(0); await expect(navigationMenu).toHaveAttribute('aria-expanded', 'false'); }); -test('mobile modal examples open, dismiss, and restore focus without hydration errors', async ({ page }) => { +test('mobile modal examples dismiss with Escape and restore focus', async ({ page }) => { const pageErrors: string[] = []; page.on('pageerror', (error) => pageErrors.push(error.message)); await page.goto('/blocks/blocks/ui/sheet/', { waitUntil: 'networkidle' }); - await expect(page.getByRole('button', { name: /Switch to (light|dark) theme/ })).toBeEnabled(); const trigger = page.getByRole('button', { name: 'Edit organization' }); await trigger.click(); @@ -94,209 +28,3 @@ test('mobile modal examples open, dismiss, and restore focus without hydration e await expect(trigger).toBeFocused(); expect(pageErrors).toEqual([]); }); - -test('mobile billing tables keep horizontal overflow inside their containers', async ({ - page, -}) => { - const response = await page.goto(billingRoute('billing-settings-page'), { - waitUntil: 'networkidle', - }); - expect(response?.status()).toBe(200); - - const preview = page.locator('[data-slot="billing-showcase-preview"]'); - await preview - .getByRole('button', { name: 'Mobile preview, 390 pixels' }) - .click(); - const frame = billingPreviewFrame(page); - const settings = frame.locator('[data-slot="billing-settings-page"]'); - await settings.getByRole('tab', { name: 'Usage' }).click(); - const tables = settings.getByRole('table'); - await expect(tables).toHaveCount(2); - - let overflowingContainers = 0; - for (let index = 0; index < 2; index += 1) { - const table = tables.nth(index); - const container = table.locator('xpath=..'); - await expect(container).toHaveAttribute('data-slot', 'table-container'); - expect( - await container.evaluate((element) => getComputedStyle(element).overflowX), - ).toBe('auto'); - - expect( - await container.evaluate( - (element) => element.getBoundingClientRect().width <= window.innerWidth, - ), - ).toBe(true); - - if ( - await container.evaluate( - (element) => element.scrollWidth > element.clientWidth, - ) - ) { - overflowingContainers += 1; - } - } - - expect(overflowingContainers).toBeGreaterThan(0); - expect( - await frame.locator('html').evaluate( - (element) => element.scrollWidth <= element.clientWidth, - ), - ).toBe(true); - expect( - await page.evaluate( - () => - document.documentElement.scrollWidth <= - document.documentElement.clientWidth, - ), - ).toBe(true); -}); - -test('mobile billing controls expose 44px touch targets and switch account context', async ({ - page, -}) => { - await page.goto(billingRoute('billing-settings-page'), { - waitUntil: 'networkidle', - }); - const preview = page.locator('[data-slot="billing-showcase-preview"]'); - await preview - .getByRole('button', { name: 'Mobile preview, 390 pixels' }) - .click(); - await chooseShowcaseOption(page, 'Account', 'Personal account'); - - const frame = billingPreviewFrame(page); - const settings = frame.locator('[data-slot="billing-settings-page"]'); - await expect(settings.getByText('Avery Chen', { exact: true }).first()).toBeVisible(); - await expect(settings.getByText('Personal account', { exact: true }).first()).toBeVisible(); - - await expectTouchTargets( - preview.locator( - '[data-slot="select-trigger"]:visible, [data-slot="button"]:visible', - ), - ); - await expectTouchTargets( - frame.locator( - '[data-slot="tabs-trigger"]:visible, [data-slot="billing-settings-page"] [data-slot="button"]:visible', - ), - ); -}); - -test('mobile application blocks keep their primary workflows inside the viewport', async ({ - page, -}) => { - await test.step('Org Chart fits every node and action target', async () => { - await page.goto(applicationRoute('org-chart'), { waitUntil: 'networkidle' }); - expect(await page.evaluate(() => window.scrollY)).toBe(0); - const frame = applicationPreviewFrame(page); - const nodes = frame.locator('.react-flow__node'); - await expect(nodes).toHaveCount(5); - - await expect - .poll(() => - nodes.evaluateAll((elements) => - elements.every((element) => { - const bounds = element.getBoundingClientRect(); - return bounds.left >= 0 && bounds.right <= window.innerWidth; - }), - ), - ) - .toBe(true); - - const actions = frame.getByRole('button', { name: /^Actions for / }); - await expect(actions).toHaveCount(5); - expect( - await actions.evaluateAll((elements) => - elements.every((element) => { - const bounds = element.getBoundingClientRect(); - return bounds.left >= 0 && bounds.right <= window.innerWidth; - }), - ), - ).toBe(true); - - for (const person of ['Maya Chen', 'Theo Brooks']) { - await frame - .getByRole('button', { name: new RegExp(`^Actions for ${person}`) }) - .click(); - const menu = frame.getByRole('menu'); - await expect(menu).toBeVisible(); - expect( - await menu.evaluate((element) => { - const bounds = element.getBoundingClientRect(); - return bounds.left >= 0 && bounds.right <= window.innerWidth; - }), - ).toBe(true); - await page.keyboard.press('Escape'); - await expect(menu).toBeHidden(); - } - - const preview = page.locator( - '[data-slot="application-block-showcase-preview"]', - ); - await preview - .getByRole('button', { name: 'Desktop preview, 1280 pixels' }) - .click(); - const fullscreenTrigger = preview.getByRole('button', { - name: 'Open full-screen preview', - }); - await fullscreenTrigger.click(); - const dialog = page.getByRole('dialog', { name: 'Org Chart preview' }); - const frameContainer = dialog.locator( - '[data-slot="application-block-preview-frame"]', - ); - const fullscreenFrame = dialog.locator('iframe'); - await expect - .poll(async () => Number(await frameContainer.getAttribute('data-preview-scale'))) - .toBeLessThan(1); - expect( - await fullscreenFrame.evaluate((element) => { - const frameBounds = element.getBoundingClientRect(); - const containerBounds = element.parentElement!.getBoundingClientRect(); - return frameBounds.width <= containerBounds.width + 1; - }), - ).toBe(true); - await page.keyboard.press('Escape'); - await expect(dialog).toBeHidden(); - await expect(fullscreenTrigger).toBeFocused(); - }); - - await test.step('Storage opens object details from the keyboard', async () => { - await page.goto(applicationRoute('storage-browser'), { - waitUntil: 'networkidle', - }); - const frame = applicationPreviewFrame(page); - const objectLink = frame.getByRole('button', { - name: 'Open details for launch-cover.png', - }); - await objectLink.focus(); - await objectLink.press('Enter'); - await expect(frame.getByRole('dialog')).toBeVisible(); - await page.keyboard.press('Escape'); - await expect(frame.getByRole('dialog')).toBeHidden(); - await expect(objectLink).toBeFocused(); - }); - - await test.step('Schema Builder create cards do not overflow', async () => { - await page.goto(applicationRoute('schema-builder'), { - waitUntil: 'networkidle', - }); - expect(await page.evaluate(() => window.scrollY)).toBe(0); - const frame = applicationPreviewFrame(page); - await frame.getByRole('button', { name: 'Create table' }).first().click(); - await expect(frame.getByTestId('table-name-input')).not.toBeFocused(); - const cards = frame.locator('[data-testid^="policy-card-"]'); - await expect(cards.first()).toBeVisible(); - expect( - await cards.evaluateAll((elements) => - elements.every((element) => { - const bounds = element.getBoundingClientRect(); - return bounds.left >= 0 && bounds.right <= window.innerWidth; - }), - ), - ).toBe(true); - expect( - await frame.locator('html').evaluate( - (element) => element.scrollWidth <= element.clientWidth, - ), - ).toBe(true); - }); -}); diff --git a/apps/blocks/src/app/sitemap.test.ts b/apps/blocks/src/app/sitemap.test.ts deleted file mode 100644 index a50f256..0000000 --- a/apps/blocks/src/app/sitemap.test.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { describe, expect, it } from 'vitest'; - -import { AI_COMPONENTS } from '@/lib/ai-components'; -import { APPLICATION_BLOCKS } from '@/lib/application-blocks'; -import { BASE_PRIMITIVES } from '@/lib/base-primitives'; -import { BILLING_BLOCKS } from '@/lib/billing-blocks'; -import { FEATURE_PACK_DOCS } from '@/lib/feature-packs'; -import { SOURCE_BLOCKS } from '@/lib/source-blocks'; - -import sitemap from './sitemap'; - -describe('sitemap', () => { - it('contains foundations, application blocks, seven feature packs, 29 primitives, AI catalog, and the complete billing catalog', () => { - const entries = sitemap(); - expect(BASE_PRIMITIVES).toHaveLength(29); - expect(entries).toHaveLength( - BASE_PRIMITIVES.length + - FEATURE_PACK_DOCS.length + - BILLING_BLOCKS.length + - APPLICATION_BLOCKS.length + - SOURCE_BLOCKS.length + - AI_COMPONENTS.length + - 8, - ); - expect(entries.map(({ url }) => url)).toEqual([ - 'http://localhost:3005/', - 'http://localhost:3005/blocks', - 'http://localhost:3005/blocks/styling', - 'http://localhost:3005/blocks/features', - ...FEATURE_PACK_DOCS.map(({ id }) => `http://localhost:3005/blocks/features/${id}`), - 'http://localhost:3005/blocks/command-palette', - 'http://localhost:3005/blocks/ai', - ...AI_COMPONENTS.map(({ name }) => `http://localhost:3005/blocks/ai/${name}`), - ...SOURCE_BLOCKS.map(({ name }) => `http://localhost:3005/blocks/${name}`), - ...APPLICATION_BLOCKS.map(({ name }) => `http://localhost:3005/blocks/${name}`), - 'http://localhost:3005/blocks/console-kit', - ...BASE_PRIMITIVES.map(({ name }) => `http://localhost:3005/blocks/ui/${name}`), - 'http://localhost:3005/blocks/billing', - ...BILLING_BLOCKS.map(({ name }) => `http://localhost:3005/blocks/billing/${name}`), - ]); - }); -}); diff --git a/apps/blocks/src/blocks/console-kit/constructive/constructive-console-kit.test.tsx b/apps/blocks/src/blocks/console-kit/constructive/constructive-console-kit.test.tsx index 4035874..38b875d 100644 --- a/apps/blocks/src/blocks/console-kit/constructive/constructive-console-kit.test.tsx +++ b/apps/blocks/src/blocks/console-kit/constructive/constructive-console-kit.test.tsx @@ -174,34 +174,6 @@ describe('ConstructiveConsoleKit external ownership', () => { } }); - it('supports a first-party selected-pack composition from the core barrel', () => { - const hostSession = embeddedSession('database-1'); - const hostTransport = transport('selected-packs'); - - render( - - ); - - const props = consoleKitCaptures.props.at(-1) as Readonly<{ - featureModules: readonly Readonly<{ id: string }>[]; - }>; - expect(props.featureModules.map((module) => module.id)).toEqual([ - 'data', - 'auth' - ]); - }); - it('initializes its wrapper-owned store from a valid semantic default route', async () => { const defaultRoute = { feature: 'data', diff --git a/apps/blocks/src/blocks/console-kit/store/console-kit-store.test.ts b/apps/blocks/src/blocks/console-kit/store/console-kit-store.test.ts index a75457c..cbea4df 100644 --- a/apps/blocks/src/blocks/console-kit/store/console-kit-store.test.ts +++ b/apps/blocks/src/blocks/console-kit/store/console-kit-store.test.ts @@ -7,106 +7,6 @@ import { import { createConsoleKitStore } from './console-kit-store'; describe('Console Kit store', () => { - it('composes navigation, runtime, and adapter slices in one isolated store', () => { - const first = createConsoleKitStore('data', { - databaseId: 'database-1', - organizationId: null - }, [storageConsoleStoreSlice]); - const second = createConsoleKitStore('auth'); - const adapter = {}; - - first.getState().setSession({ - status: 'authenticated', - identity: { - kind: 'authenticated', - cachePartition: 'login-1', - subjectId: 'user-1', - organizationId: 'org-1' - } - }); - first.getState().setAuthFlow({ status: 'entry', mode: 'sign-up' }); - first.getState().setRoute({ - feature: 'organizations', - screen: 'organizations' - }); - first.getState().setAdapterLoad('users', { - status: 'ready', - adapter, - requestKey: 'database-1:user-1:users', - props: { members: [] } - }); - first.getState().setMetadata('database-1:user-1:data', { - status: 'incompatible', - message: 'The endpoint is missing the current _meta contract.', - missing: ['Query._meta'] - }); - first.getState().setEndpoints({ - data: { id: 'database-1-data', kind: 'data', url: '/graphql' } - }); - first.getState().setPackCapability('data', { - status: 'ready', - packId: 'data', - supportedCapabilities: ['data.read'], - evidence: [] - }); - getConsoleKitStorageSlice(first).selectStorageBucket( - 'database-1:login-1', - 'documents' - ); - - const revisionAfterAuth = first.getState().metadataRevision; - expect(first.getState()).toMatchObject({ - route: { feature: 'organizations', screen: 'organizations' }, - authFlow: { status: 'entry', mode: 'sign-up' }, - context: { databaseId: 'database-1', organizationId: null }, - session: { status: 'authenticated' }, - metadataKey: 'database-1:user-1:data', - endpoints: { data: { id: 'database-1-data' } }, - packCapabilities: { data: { status: 'ready' } }, - storageSelection: { - scope: 'database-1:login-1', - bucketKey: 'documents' - }, - adapterLoads: { - users: { - status: 'ready', - adapter, - requestKey: 'database-1:user-1:users' - } - } - }); - expect(revisionAfterAuth).toBeGreaterThanOrEqual(1); - - first.getState().retryMetadata(); - expect(first.getState()).toMatchObject({ - metadataRevision: revisionAfterAuth + 1, - metadataKey: null, - metadata: { status: 'checking' }, - metadataByEndpoint: {} - }); - expect(second.getState()).toMatchObject({ - route: { feature: 'auth', screen: 'entry' }, - authFlow: { status: 'entry', mode: 'sign-in' }, - context: null, - session: { status: 'loading' }, - endpoints: {}, - packCapabilities: {}, - adapterLoads: {} - }); - expect(second.getState()).not.toHaveProperty('storageSelection'); - }); - - it('keeps adapter subscription and retry revisions in their own slice', () => { - const store = createConsoleKitStore('data'); - - store.getState().notifyAdapterChange(); - store.getState().retryAdapter('storage'); - store.getState().retryAdapter('storage'); - - expect(store.getState().adapterRevision).toBe(1); - expect(store.getState().adapterAttempts.storage).toBe(2); - }); - it('clears organization selection whenever the session identity changes', () => { const store = createConsoleKitStore('organizations', { databaseId: 'database-1', diff --git a/apps/blocks/src/components/billing-showcase/billing-showcase-preview.test.tsx b/apps/blocks/src/components/billing-showcase/billing-showcase-preview.test.tsx index d4273db..480335f 100644 --- a/apps/blocks/src/components/billing-showcase/billing-showcase-preview.test.tsx +++ b/apps/blocks/src/components/billing-showcase/billing-showcase-preview.test.tsx @@ -1,6 +1,6 @@ -import { act, fireEvent, render, screen, within } from '@testing-library/react'; +import { fireEvent, render, screen, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { describe, expect, it, vi } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { BillingShowcaseCanvas } from './billing-showcase-canvas'; import { BillingShowcasePreview } from './billing-showcase-preview'; @@ -13,92 +13,7 @@ function selectOption(triggerName: string, optionLabel: string) { fireEvent.click(option as HTMLElement); } -function mockStageWidth(width: number) { - const observers = new Set(); - - class ResizeObserverStub { - constructor(private readonly callback: ResizeObserverCallback) { - observers.add(callback); - } - - observe() { - this.callback([], this as unknown as ResizeObserver); - } - - unobserve() {} - - disconnect() { - observers.delete(this.callback); - } - } - - vi.stubGlobal('ResizeObserver', ResizeObserverStub); - - return { - apply(stage: HTMLElement) { - Object.defineProperty(stage, 'clientWidth', { - configurable: true, - get: () => width - }); - for (const callback of observers) { - callback([], {} as ResizeObserver); - } - }, - cleanup() { - vi.unstubAllGlobals(); - } - }; -} - describe('BillingShowcasePreview', () => { - it('switches the canonical subscription fixture with the account control', () => { - const { rerender } = render( - - ); - - expect(screen.getByText('Northstar Field Operations')).toBeInTheDocument(); - expect(screen.getByText('Scale')).toBeInTheDocument(); - - rerender( - - ); - - expect(screen.getByText('Avery Chen')).toBeInTheDocument(); - expect(screen.getByText('Developer')).toBeInTheDocument(); - }); - - it('drives canonical loading resources from the shared state control', () => { - const { rerender } = render( - - ); - expect(screen.getByRole('heading', { name: 'Plans and pricing' })).toBeInTheDocument(); - - rerender( - - ); - - expect(screen.getByLabelText('Loading plans and pricing…')).toHaveAttribute( - 'aria-busy', - 'true' - ); - }); - it('shows delegated actions without mutating the billing fixture', async () => { const user = userEvent.setup(); render( @@ -211,65 +126,4 @@ describe('BillingShowcasePreview', () => { ); }); - it('scales the desktop frame to the stage width without changing the logical viewport', () => { - const stageWidth = mockStageWidth(640); - - try { - const { container } = render( - - ); - const stage = container.querySelector( - '[data-slot="billing-preview-stage"]' - ); - expect(stage).not.toBeNull(); - act(() => { - stageWidth.apply(stage as HTMLElement); - }); - - const frame = screen.getByTitle('Pricing table inline live preview'); - const shell = container.querySelector( - '[data-slot="billing-preview-frame"]' - ); - - expect(frame).toHaveAttribute('width', '1280'); - expect(frame).toHaveAttribute('data-preview-viewport', 'desktop'); - expect(shell).toHaveAttribute('data-preview-scale', '0.500'); - expect(shell).toHaveStyle({ width: '640px' }); - } finally { - stageWidth.cleanup(); - } - }); - - it('does not upscale when the stage is wider than the device viewport', async () => { - const user = userEvent.setup(); - const stageWidth = mockStageWidth(900); - - try { - const { container } = render( - - ); - await user.click( - screen.getByRole('button', { name: 'Mobile preview, 390 pixels' }) - ); - - const stage = container.querySelector( - '[data-slot="billing-preview-stage"]' - ); - expect(stage).not.toBeNull(); - act(() => { - stageWidth.apply(stage as HTMLElement); - }); - - const frame = screen.getByTitle('Pricing table inline live preview'); - const shell = container.querySelector( - '[data-slot="billing-preview-frame"]' - ); - - expect(frame).toHaveAttribute('width', '390'); - expect(shell).toHaveAttribute('data-preview-scale', '1.000'); - expect(shell).toHaveStyle({ width: '390px' }); - } finally { - stageWidth.cleanup(); - } - }); }); diff --git a/apps/blocks/src/components/billing-showcase/billing-showcase-resources.test.ts b/apps/blocks/src/components/billing-showcase/billing-showcase-resources.test.ts index c1b1b80..ea7e837 100644 --- a/apps/blocks/src/components/billing-showcase/billing-showcase-resources.test.ts +++ b/apps/blocks/src/components/billing-showcase/billing-showcase-resources.test.ts @@ -1,38 +1,13 @@ import { describe, expect, it } from 'vitest'; import { - BILLING_SHOWCASE_ACCOUNT_OPTIONS, - BILLING_SHOWCASE_RESOURCE_STATE_OPTIONS, - BILLING_SHOWCASE_SETTINGS_STATE_OPTIONS, - getBillingShowcaseAccount, getBillingShowcaseSettingsResources, - getBillingShowcaseSubscriptionResources, isBillingShowcaseAccountKind, isBillingShowcaseResourceState, isBillingShowcaseSettingsState } from './billing-showcase-resources'; describe('billing showcase resources', () => { - it('keeps the two account variants and six shared resource states explicit', () => { - expect(BILLING_SHOWCASE_ACCOUNT_OPTIONS.map(({ value }) => value)).toEqual([ - 'organization', - 'personal' - ]); - expect( - BILLING_SHOWCASE_RESOURCE_STATE_OPTIONS.map(({ value }) => value) - ).toEqual([ - 'ready', - 'loading', - 'empty', - 'error', - 'stale', - 'estimated' - ]); - expect(BILLING_SHOWCASE_SETTINGS_STATE_OPTIONS.at(-1)?.value).toBe( - 'partial' - ); - }); - it('validates control values without accepting arbitrary strings', () => { expect(isBillingShowcaseAccountKind('personal')).toBe(true); expect(isBillingShowcaseAccountKind('workspace')).toBe(false); @@ -42,17 +17,6 @@ describe('billing showcase resources', () => { expect(isBillingShowcaseSettingsState('unknown')).toBe(false); }); - it('switches account and subscription fixtures together', () => { - expect(getBillingShowcaseAccount('personal').label).toBe('Avery Chen'); - expect( - getBillingShowcaseSubscriptionResources('personal').ready.status - ).toBe('ready'); - const personal = getBillingShowcaseSubscriptionResources('personal').ready; - expect(personal.status === 'ready' ? personal.data.planName : undefined).toBe( - 'Developer' - ); - }); - it('builds uniform settings states and preserves the partial-failure boundary', () => { const loading = getBillingShowcaseSettingsResources( 'loading', diff --git a/apps/blocks/src/components/docs/ui-demos.test.tsx b/apps/blocks/src/components/docs/ui-demos.test.tsx deleted file mode 100644 index 8aaaa64..0000000 --- a/apps/blocks/src/components/docs/ui-demos.test.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { cleanup, render, waitFor } from '@testing-library/react'; -import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest'; - -import { BASE_PRIMITIVES } from '@/lib/base-primitives'; - -import { UI_DEMOS } from './showcase-ui'; - -beforeAll(() => { - if (!Element.prototype.scrollIntoView) Element.prototype.scrollIntoView = function scrollIntoView() {}; - if (!Element.prototype.getAnimations) Element.prototype.getAnimations = () => []; - if (!window.matchMedia) { - window.matchMedia = ((query: string) => ({ - matches: false, - media: query, - onchange: null, - addListener: vi.fn(), - removeListener: vi.fn(), - addEventListener: vi.fn(), - removeEventListener: vi.fn(), - dispatchEvent: vi.fn(), - })) as unknown as typeof window.matchMedia; - } - if (!('ResizeObserver' in globalThis)) { - globalThis.ResizeObserver = class ResizeObserverStub { - observe() {} - unobserve() {} - disconnect() {} - takeRecords(): ResizeObserverEntry[] { - return []; - } - }; - } -}); - -afterEach(cleanup); - -describe('base primitive previews', () => { - it('matches the typed catalog exactly', () => { - expect(Object.keys(UI_DEMOS)).toEqual(BASE_PRIMITIVES.map(({ name }) => name)); - }); - - it.each(BASE_PRIMITIVES)('mounts the $name package preview', async ({ name }) => { - const Demo = UI_DEMOS[name]; - const { container, unmount } = render(); - await waitFor(() => expect(container.firstChild).not.toBeNull()); - unmount(); - }); -}); diff --git a/apps/blocks/src/components/site/registry-shell.test.tsx b/apps/blocks/src/components/site/registry-shell.test.tsx deleted file mode 100644 index c179e41..0000000 --- a/apps/blocks/src/components/site/registry-shell.test.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import { cleanup, render, screen } from '@testing-library/react'; -import { afterEach, describe, expect, it, vi } from 'vitest'; - -vi.mock('next/navigation', () => ({ - usePathname: vi.fn(() => '/'), -})); - -vi.mock('@/components/site/site-sidebar', () => ({ - SiteSidebar: () => , -})); - -vi.mock('@/components/site/site-topbar', () => ({ - SiteTopbar: () =>
Docs topbar
, -})); - -import { usePathname } from 'next/navigation'; - -import { RegistryShell } from './registry-shell'; - -const mockUsePathname = vi.mocked(usePathname); - -afterEach(() => { - cleanup(); - vi.clearAllMocks(); -}); - -describe('RegistryShell standalone previews', () => { - it.each([ - '/blocks/billing/billing-settings-page/preview', - '/blocks/features/data/preview', - '/blocks/features/notifications/preview/', - '/blocks/org-chart/preview', - '/blocks/storage-browser/preview/', - '/blocks/sheets/preview', - '/blocks/schema-builder/preview/', - ])('omits recursive documentation chrome on %s', (pathname) => { - mockUsePathname.mockReturnValue(pathname); - render( - -
Live preview
-
, - ); - - expect(screen.getByText('Live preview')).toBeVisible(); - expect(screen.queryByText('Docs sidebar')).not.toBeInTheDocument(); - expect(screen.queryByText('Docs topbar')).not.toBeInTheDocument(); - expect(screen.getByRole('main')).toHaveAttribute('id', 'main-content'); - }); - - it('lets the Console Kit app shell own the integration main landmark', () => { - mockUsePathname.mockReturnValue('/__integration/console-kit'); - render( - -
Console Kit
-
- ); - - expect(screen.getAllByRole('main')).toHaveLength(1); - expect(screen.getByRole('main')).toHaveAttribute('id', 'main-content'); - }); - - it('keeps the documentation chrome on a feature-pack detail route', () => { - mockUsePathname.mockReturnValue('/blocks/features/data'); - render( - -
Data docs
-
, - ); - - expect(screen.getByText('Docs sidebar')).toBeVisible(); - expect(screen.getByText('Docs topbar')).toBeVisible(); - expect(screen.getByText('Data docs')).toBeVisible(); - expect(screen.getAllByRole('main')).toHaveLength(1); - }); -}); diff --git a/apps/blocks/src/components/site/site-topbar.test.tsx b/apps/blocks/src/components/site/site-topbar.test.tsx index caf6443..36c972e 100644 --- a/apps/blocks/src/components/site/site-topbar.test.tsx +++ b/apps/blocks/src/components/site/site-topbar.test.tsx @@ -46,15 +46,6 @@ describe('SiteTopbar install command', () => { }); }); - it('normalizes trailing slashes before resolving the registry item', () => { - mockUsePathname.mockReturnValue('/blocks/ui/dialog/'); - render(); - - expect(screen.getByRole('button', { name: 'Copy Dialog install command' })).toHaveTextContent( - 'pnpm dlx shadcn@latest add @constructive/dialog', - ); - }); - it('copies the exact feature-pack registry root and exposes its breadcrumb', async () => { mockUsePathname.mockReturnValue('/blocks/features/organizations/'); render(); @@ -71,75 +62,17 @@ describe('SiteTopbar install command', () => { }); }); - it('offers install actions for documented source and application blocks', () => { - mockUsePathname.mockReturnValue('/blocks/billing/billing-usage-overview'); - const { unmount } = render(); - expect( - screen.getByRole('button', { - name: 'Copy Usage overview install command', - }), - ).toHaveTextContent('pnpm dlx shadcn@latest add @constructive/billing-usage-overview'); - - unmount(); - mockUsePathname.mockReturnValue('/blocks/org-chart'); - const orgChart = render(); - expect( - screen.getByRole('button', { - name: 'Copy Org Chart install command', - }), - ).toHaveTextContent('pnpm dlx shadcn@latest add @constructive/org-chart'); - - orgChart.unmount(); - mockUsePathname.mockReturnValue('/blocks/storage-browser'); - const storageBrowser = render(); - expect( - screen.getByRole('button', { - name: 'Copy Storage Browser install command', - }), - ).toHaveTextContent('pnpm dlx shadcn@latest add @constructive/storage-browser'); - - storageBrowser.unmount(); - mockUsePathname.mockReturnValue('/blocks/sheets'); - const sheets = render(); - expect(screen.getByRole('button', { name: 'Copy Sheets install command' })).toHaveTextContent( - 'pnpm dlx shadcn@latest add @constructive/sheets', - ); - - sheets.unmount(); - mockUsePathname.mockReturnValue('/blocks/schema-builder'); - const schemaBuilder = render(); - expect(screen.getByRole('button', { name: 'Copy Schema Builder install command' })).toHaveTextContent( - 'pnpm dlx shadcn@latest add @constructive/schema-builder', - ); - - schemaBuilder.unmount(); - mockUsePathname.mockReturnValue('/blocks/command-palette'); - const commandPalette = render(); - expect(screen.getByRole('button', { name: 'Copy Command Palette install command' })).toHaveTextContent( - 'pnpm dlx shadcn@latest add @constructive/command-palette', - ); - - commandPalette.unmount(); - mockUsePathname.mockReturnValue('/blocks/console-kit'); - render(); - expect(screen.getByRole('button', { name: 'Copy Console Kit install command' })).toHaveTextContent( - 'pnpm dlx shadcn@latest add @constructive/console-kit-nextjs', - ); - }); - - it.each([ - '/', - '/blocks', - '/blocks/styling', - '/blocks/features', - '/blocks/features/not-a-pack', - '/blocks/features/data/preview', - '/blocks/ui/not-a-registry-item', - ])('hides the install command on %s', (pathname) => { - mockUsePathname.mockReturnValue(pathname); - render(); - - expect(screen.queryByRole('button', { name: /install command/i })).not.toBeInTheDocument(); - expect(screen.queryByText(/pnpm dlx shadcn@latest add @constructive\//)).not.toBeInTheDocument(); + it('hides install commands for catalogs, previews, and unknown items', () => { + for (const pathname of [ + '/blocks', + '/blocks/features', + '/blocks/features/data/preview', + '/blocks/ui/not-a-registry-item', + ]) { + mockUsePathname.mockReturnValue(pathname); + const view = render(); + expect(screen.queryByRole('button', { name: /install command/i }), pathname).not.toBeInTheDocument(); + view.unmount(); + } }); }); diff --git a/apps/blocks/src/feature-packs/catalog.test.ts b/apps/blocks/src/feature-packs/catalog.test.ts index 13b7b7c..69539e3 100644 --- a/apps/blocks/src/feature-packs/catalog.test.ts +++ b/apps/blocks/src/feature-packs/catalog.test.ts @@ -1,11 +1,7 @@ -import { readFileSync } from 'node:fs'; - import { describe, expect, it } from 'vitest'; import { - ATOMIC_CAPABILITY_IDS, FEATURE_PACK_CATALOG, - FEATURE_PACK_IDS, FEATURE_PACK_MANIFESTS, PRESET_PROFILES, generateFeaturePackCatalog, @@ -14,61 +10,7 @@ import { type FeaturePackManifestV1 } from './index'; -function readSidecar(relativePath: string): unknown { - return JSON.parse( - readFileSync(new URL(relativePath, import.meta.url), 'utf8') - ); -} - describe('first-release feature pack catalog', () => { - it('contains exactly the approved first-release pack IDs', () => { - expect(FEATURE_PACK_CATALOG.featurePacks.map((pack) => pack.id)).toEqual( - FEATURE_PACK_IDS - ); - expect(new Set(ATOMIC_CAPABILITY_IDS).size).toBe( - ATOMIC_CAPABILITY_IDS.length - ); - }); - - it('maps backend presets to the approved pack profiles', () => { - expect( - Object.fromEntries( - PRESET_PROFILES.map((profile) => [ - profile.presetSlug, - profile.featurePacks - ]) - ) - ).toEqual({ - 'auth:hardened': ['data', 'auth', 'users'], - 'b2b:storage': [ - 'data', - 'auth', - 'users', - 'organizations', - 'storage' - ], - full: [ - 'data', - 'auth', - 'users', - 'organizations', - 'storage', - 'billing', - 'notifications' - ] - }); - expect(PRESET_PROFILES.every((profile) => profile.stability === 'stable')).toBe(true); - }); - - it('keeps search, i18n, and realtime optional', () => { - const data = FEATURE_PACK_MANIFESTS.find((pack) => pack.id === 'data'); - expect(data?.capabilities.optional).toEqual([ - 'data.search', - 'data.i18n', - 'data.realtime' - ]); - }); - it('generates dependency-ordered manifests for each preset', () => { expect( getPresetFeaturePacks(FEATURE_PACK_CATALOG, 'b2b-storage').map( @@ -77,21 +19,6 @@ describe('first-release feature pack catalog', () => { ).toEqual(['data', 'auth', 'users', 'organizations', 'storage']); }); - it('keeps installable JSON sidecars identical to the typed catalog', () => { - for (const manifest of FEATURE_PACK_MANIFESTS) { - expect( - readSidecar( - `../blocks/feature-packs/${manifest.id}/feature-pack.json` - ) - ).toEqual(manifest); - } - - for (const profile of PRESET_PROFILES) { - expect( - readSidecar(`../blocks/presets/${profile.id}.json`) - ).toEqual(profile); - } - }); }); describe('feature pack catalog validation', () => { diff --git a/apps/blocks/src/lib/ai-docs.test.ts b/apps/blocks/src/lib/ai-docs.test.ts deleted file mode 100644 index 655551d..0000000 --- a/apps/blocks/src/lib/ai-docs.test.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { describe, expect, it } from 'vitest'; - -import { AI_COMPONENTS } from './ai-components'; -import { AI_DOC } from './ai-docs'; - -describe('AI registry documentation', () => { - it('documents one aggregate registry item with source-installed imports', () => { - expect(AI_DOC.name).toBe('ai'); - expect(AI_DOC).not.toHaveProperty('kitName'); - expect(AI_DOC).not.toHaveProperty('npmImport'); - expect(AI_DOC.usage.example).toContain("from '@/components/ui/ai'"); - expect(AI_DOC.usage.example).toContain("from '@/components/ui/button'"); - expect(AI_DOC.usage.example).not.toContain('@constructive-io/ui'); - }); - - it('uses the installed AI barrel in every component example', () => { - for (const component of AI_COMPONENTS) { - expect(component.importExample).toContain("from '@/components/ui/ai'"); - expect(component.importExample).not.toContain('@constructive-io/ui'); - } - }); -}); diff --git a/apps/blocks/src/lib/base-primitives.test.ts b/apps/blocks/src/lib/base-primitives.test.ts deleted file mode 100644 index 2003591..0000000 --- a/apps/blocks/src/lib/base-primitives.test.ts +++ /dev/null @@ -1,72 +0,0 @@ -import fs from 'node:fs'; -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; - -import { describe, expect, it } from 'vitest'; - -import { BASE_PRIMITIVES, getBasePrimitive, packageImport, registryInstall } from './base-primitives'; - -const expectedNames = [ - 'alert', - 'alert-dialog', - 'avatar', - 'badge', - 'breadcrumb', - 'button', - 'card', - 'checkbox', - 'collapsible', - 'dialog', - 'drawer', - 'dropdown-menu', - 'input', - 'label', - 'pagination', - 'popover', - 'progress', - 'radio-group', - 'resizable', - 'scroll-area', - 'select', - 'separator', - 'sheet', - 'skeleton', - 'switch', - 'table', - 'tabs', - 'textarea', - 'tooltip', -] as const; - -const testDirectory = path.dirname(fileURLToPath(import.meta.url)); -const repositoryRoot = path.resolve(testDirectory, '..', '..', '..', '..'); - -describe('base primitive catalog', () => { - it('is the exact clean-slate route contract', () => { - expect(BASE_PRIMITIVES.map(({ name }) => name)).toEqual(expectedNames); - expect(new Set(expectedNames).size).toBe(expectedNames.length); - }); - - it('resolves every item to an npm export and registry item', () => { - const packageJson = JSON.parse(fs.readFileSync(path.join(repositoryRoot, 'packages/ui/package.json'), 'utf8')) as { - exports: Record; - }; - const registry = JSON.parse(fs.readFileSync(path.join(repositoryRoot, 'packages/ui/registry.json'), 'utf8')) as { - items: Array<{ name: string }>; - }; - const registryNames = new Set(registry.items.map(({ name }) => name)); - - for (const primitive of BASE_PRIMITIVES) { - expect(packageJson.exports[`./${primitive.name}`], primitive.name).toBeDefined(); - expect(registryNames.has(primitive.name), primitive.name).toBe(true); - expect(getBasePrimitive(primitive.name)).toBe(primitive); - } - }); - - it('keeps package and registry examples on their respective distribution paths', () => { - for (const primitive of BASE_PRIMITIVES) { - expect(packageImport(primitive)).toContain(`@constructive-io/ui/${primitive.name}`); - expect(registryInstall(primitive)).toBe(`pnpm dlx shadcn@latest add @constructive/${primitive.name}`); - } - }); -}); diff --git a/apps/blocks/src/lib/billing-blocks.test.ts b/apps/blocks/src/lib/billing-blocks.test.ts deleted file mode 100644 index e9b21f4..0000000 --- a/apps/blocks/src/lib/billing-blocks.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { describe, expect, it } from 'vitest'; - -import { - BILLING_BLOCKS, - getBillingBlock, - isBillingBlockName -} from './billing-blocks'; - -describe('billing block catalog', () => { - it('contains the eight unique customer-first visual blocks', () => { - expect(BILLING_BLOCKS).toHaveLength(8); - expect(new Set(BILLING_BLOCKS.map(({ name }) => name)).size).toBe(8); - expect(BILLING_BLOCKS.at(-1)?.name).toBe('billing-settings-page'); - }); - - it('keeps usage and accessibility guidance on every block', () => { - for (const block of BILLING_BLOCKS) { - expect(block.resource.length).toBeGreaterThan(0); - expect(block.whenToUse.length).toBeGreaterThan(0); - expect(block.usage.description.length).toBeGreaterThan(0); - expect(block.usage.example).toContain(block.exportName); - expect(block.state.description.length).toBeGreaterThan(0); - expect(block.accessibility.length).toBeGreaterThan(0); - } - }); - - it('resolves only catalogued billing block names', () => { - expect(isBillingBlockName('billing-usage-overview')).toBe(true); - expect(getBillingBlock('billing-usage-overview')?.title).toBe( - 'Usage overview' - ); - expect(isBillingBlockName('billing-invoice-history')).toBe(false); - expect(getBillingBlock('billing-invoice-history')).toBeUndefined(); - }); -}); diff --git a/apps/blocks/src/lib/feature-packs.test.ts b/apps/blocks/src/lib/feature-packs.test.ts deleted file mode 100644 index f7422bb..0000000 --- a/apps/blocks/src/lib/feature-packs.test.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { describe, expect, it } from 'vitest'; - -import { FEATURE_PACK_CATALOG, FEATURE_PACK_IDS, getFeaturePackManifest } from '@/feature-packs'; - -import { FEATURE_PACK_DOCS, getFeaturePackDoc, isFeaturePackDocId } from './feature-packs'; - -function endpointLabel(required: readonly string[], optional: readonly string[]) { - return [...required, ...optional.map((endpoint) => `optional ${endpoint}`)].join(', '); -} - -describe('feature-pack documentation catalog', () => { - it('contains the seven unique feature packs in canonical dependency order', () => { - expect(FEATURE_PACK_DOCS.map(({ id }) => id)).toEqual(FEATURE_PACK_IDS); - expect(new Set(FEATURE_PACK_DOCS.map(({ id }) => id)).size).toBe(7); - }); - - it('keeps manifest summaries and registry names aligned', () => { - for (const block of FEATURE_PACK_DOCS) { - const manifest = getFeaturePackManifest(FEATURE_PACK_CATALOG, block.id)!; - expect(block.registryName).toBe(`feature-pack-${block.id}`); - expect(block.dependencies).toEqual(manifest.dependencies); - expect(block.endpoints).toBe(endpointLabel(manifest.endpoints.required, manifest.endpoints.optional)); - } - }); - - it('provides complete billing-style editorial coverage for every root', () => { - for (const block of FEATURE_PACK_DOCS) { - expect(block.description.length).toBeGreaterThan(0); - expect(block.resource.length).toBeGreaterThan(0); - expect(block.whenToUse.length).toBeGreaterThanOrEqual(2); - expect(block.usage.description.length).toBeGreaterThan(0); - expect(block.usage.example).toContain(block.exportName); - expect(block.usage.example).toContain(`@/blocks/feature-packs/${block.id}/${block.id}-feature-pack`); - expect(block.state.description.length).toBeGreaterThan(0); - expect(block.surfaces.length).toBeGreaterThan(0); - expect(block.accessibility.length).toBeGreaterThan(0); - expect(block.api.length).toBeGreaterThan(0); - } - }); - - it('resolves only documented feature-pack ids', () => { - expect(isFeaturePackDocId('organizations')).toBe(true); - expect(getFeaturePackDoc('organizations')?.title).toBe('Organizations'); - expect(isFeaturePackDocId('analytics')).toBe(false); - expect(getFeaturePackDoc('analytics')).toBeUndefined(); - }); -}); diff --git a/packages/command-palette/src/__tests__/keybinding.test.ts b/packages/command-palette/src/__tests__/keybinding.test.ts index 5f4e843..9a2ee1c 100644 --- a/packages/command-palette/src/__tests__/keybinding.test.ts +++ b/packages/command-palette/src/__tests__/keybinding.test.ts @@ -1,277 +1,73 @@ -import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; -import { - kbd, - isMac, - isEditableTarget, - matchKeyBinding, - formatKeyBinding, -} from '../keybinding'; -import type { KeyBinding } from '../keybinding'; +import { afterEach, describe, expect, it } from 'vitest'; -// Helper to create a KeyboardEvent-like object -function fakeKeyEvent( - key: string, - opts: { - metaKey?: boolean; - ctrlKey?: boolean; - shiftKey?: boolean; - altKey?: boolean; - } = {} -): KeyboardEvent { +import { formatKeyBinding, isEditableTarget, isMac, kbd, matchKeyBinding } from '../keybinding'; + +const originalNavigator = globalThis.navigator; + +function setPlatform(platform?: string) { + Object.defineProperty(globalThis, 'navigator', { + configurable: true, + value: platform === undefined ? undefined : { platform }, + }); +} + +function keyEvent(key: string, modifiers: Partial = {}): KeyboardEvent { return { key, - metaKey: opts.metaKey ?? false, - ctrlKey: opts.ctrlKey ?? false, - shiftKey: opts.shiftKey ?? false, - altKey: opts.altKey ?? false, + metaKey: false, + ctrlKey: false, + shiftKey: false, + altKey: false, + ...modifiers, } as KeyboardEvent; } -describe('kbd factory', () => { - it('creates binding with key only', () => { - expect(kbd('enter')).toEqual({ key: 'enter' }); - }); - - it('creates binding with one modifier', () => { - expect(kbd('h', 'mod')).toEqual({ key: 'h', modifiers: ['mod'] }); - }); - - it('creates binding with multiple modifiers', () => { - expect(kbd('p', 'mod', 'shift')).toEqual({ - key: 'p', - modifiers: ['mod', 'shift'], - }); +afterEach(() => { + Object.defineProperty(globalThis, 'navigator', { + configurable: true, + value: originalNavigator, }); }); -describe('isMac', () => { - const originalNavigator = globalThis.navigator; - - afterEach(() => { - Object.defineProperty(globalThis, 'navigator', { - value: originalNavigator, - writable: true, - }); - }); - - it('returns false when navigator is undefined (SSR)', () => { - Object.defineProperty(globalThis, 'navigator', { - value: undefined, - writable: true, - }); +describe('keybinding', () => { + it('detects macOS without breaking server rendering', () => { + setPlatform(); expect(isMac()).toBe(false); - }); - - it('returns true for Mac platform', () => { - Object.defineProperty(globalThis, 'navigator', { - value: { platform: 'MacIntel' }, - writable: true, - }); + setPlatform('MacIntel'); expect(isMac()).toBe(true); - }); - - it('returns false for Windows platform', () => { - Object.defineProperty(globalThis, 'navigator', { - value: { platform: 'Win32' }, - writable: true, - }); + setPlatform('Win32'); expect(isMac()).toBe(false); }); -}); -describe('isEditableTarget', () => { - it('returns false for null', () => { + it('recognizes editable targets that must retain keyboard input', () => { + for (const tag of ['input', 'textarea', 'select'] as const) { + expect(isEditableTarget(document.createElement(tag)), tag).toBe(true); + } + const editable = document.createElement('div'); + editable.contentEditable = 'true'; + expect(isEditableTarget(editable)).toBe(true); + expect(isEditableTarget(document.createElement('div'))).toBe(false); expect(isEditableTarget(null)).toBe(false); }); - it('returns true for INPUT', () => { - const el = document.createElement('input'); - expect(isEditableTarget(el)).toBe(true); - }); - - it('returns true for TEXTAREA', () => { - const el = document.createElement('textarea'); - expect(isEditableTarget(el)).toBe(true); - }); - - it('returns true for SELECT', () => { - const el = document.createElement('select'); - expect(isEditableTarget(el)).toBe(true); - }); - - it('returns true for contentEditable', () => { - const el = document.createElement('div'); - el.contentEditable = 'true'; - expect(isEditableTarget(el)).toBe(true); - }); - - it('returns false for regular div', () => { - const el = document.createElement('div'); - expect(isEditableTarget(el)).toBe(false); - }); - - it('returns false for non-HTMLElement', () => { - expect(isEditableTarget({} as EventTarget)).toBe(false); - }); -}); - -describe('matchKeyBinding', () => { - // Set platform to non-Mac for predictable tests - const originalNavigator = globalThis.navigator; - - beforeEach(() => { - Object.defineProperty(globalThis, 'navigator', { - value: { platform: 'Win32' }, - writable: true, - }); - }); - - afterEach(() => { - Object.defineProperty(globalThis, 'navigator', { - value: originalNavigator, - writable: true, - }); - }); - - it('matches simple key', () => { - expect(matchKeyBinding(fakeKeyEvent('h'), kbd('h'))).toBe(true); + it('matches keys case-insensitively and rejects missing or extra modifiers', () => { + setPlatform('Win32'); + expect(matchKeyBinding(keyEvent('H'), kbd('h'))).toBe(true); + expect(matchKeyBinding(keyEvent('k', { ctrlKey: true }), kbd('k', 'mod'))).toBe(true); + expect(matchKeyBinding(keyEvent('k'), kbd('k', 'mod'))).toBe(false); + expect(matchKeyBinding(keyEvent('h', { shiftKey: true }), kbd('h'))).toBe(false); }); - it('case insensitive key match', () => { - expect(matchKeyBinding(fakeKeyEvent('H'), kbd('h'))).toBe(true); + it('maps the mod key to Command on macOS instead of Control', () => { + setPlatform('MacIntel'); + expect(matchKeyBinding(keyEvent('k', { metaKey: true }), kbd('k', 'mod'))).toBe(true); + expect(matchKeyBinding(keyEvent('k', { ctrlKey: true }), kbd('k', 'mod'))).toBe(false); }); - it('rejects wrong key', () => { - expect(matchKeyBinding(fakeKeyEvent('j'), kbd('h'))).toBe(false); - }); - - it('matches mod+key on Windows (ctrlKey)', () => { - expect( - matchKeyBinding(fakeKeyEvent('k', { ctrlKey: true }), kbd('k', 'mod')) - ).toBe(true); - }); - - it('rejects mod+key when ctrl is not pressed', () => { - expect(matchKeyBinding(fakeKeyEvent('k'), kbd('k', 'mod'))).toBe(false); - }); - - it('rejects extra modifiers (ctrl pressed but not required)', () => { - expect( - matchKeyBinding(fakeKeyEvent('h', { ctrlKey: true }), kbd('h')) - ).toBe(false); - }); - - it('rejects extra shift modifier', () => { - expect( - matchKeyBinding( - fakeKeyEvent('h', { ctrlKey: true, shiftKey: true }), - kbd('h', 'mod') - ) - ).toBe(false); - }); - - it('matches mod+shift+key', () => { - expect( - matchKeyBinding( - fakeKeyEvent('p', { ctrlKey: true, shiftKey: true }), - kbd('p', 'mod', 'shift') - ) - ).toBe(true); - }); - - it('matches alt+key', () => { - expect( - matchKeyBinding(fakeKeyEvent('a', { altKey: true }), kbd('a', 'alt')) - ).toBe(true); - }); - - describe('Mac platform', () => { - beforeEach(() => { - Object.defineProperty(globalThis, 'navigator', { - value: { platform: 'MacIntel' }, - writable: true, - }); - }); - - it('matches mod+key on Mac (metaKey)', () => { - expect( - matchKeyBinding(fakeKeyEvent('k', { metaKey: true }), kbd('k', 'mod')) - ).toBe(true); - }); - - it('rejects ctrlKey for mod on Mac', () => { - expect( - matchKeyBinding(fakeKeyEvent('k', { ctrlKey: true }), kbd('k', 'mod')) - ).toBe(false); - }); - }); -}); - -describe('formatKeyBinding', () => { - const originalNavigator = globalThis.navigator; - - afterEach(() => { - Object.defineProperty(globalThis, 'navigator', { - value: originalNavigator, - writable: true, - }); - }); - - it('formats single key', () => { - expect(formatKeyBinding(kbd('enter'))).toEqual(['enter']); - }); - - it('capitalizes single-char keys', () => { - expect(formatKeyBinding(kbd('h'))).toEqual(['H']); - }); - - describe('Mac', () => { - beforeEach(() => { - Object.defineProperty(globalThis, 'navigator', { - value: { platform: 'MacIntel' }, - writable: true, - }); - }); - - it('formats mod as ⌘', () => { - expect(formatKeyBinding(kbd('h', 'mod'))).toEqual(['⌘', 'H']); - }); - - it('formats shift as ⇧', () => { - expect(formatKeyBinding(kbd('p', 'mod', 'shift'))).toEqual([ - '⌘', - '⇧', - 'P', - ]); - }); - - it('formats alt as ⌥', () => { - expect(formatKeyBinding(kbd('a', 'alt'))).toEqual(['⌥', 'A']); - }); - }); - - describe('PC', () => { - beforeEach(() => { - Object.defineProperty(globalThis, 'navigator', { - value: { platform: 'Win32' }, - writable: true, - }); - }); - - it('formats mod as Ctrl', () => { - expect(formatKeyBinding(kbd('h', 'mod'))).toEqual(['Ctrl', 'H']); - }); - - it('formats shift as Shift', () => { - expect(formatKeyBinding(kbd('p', 'mod', 'shift'))).toEqual([ - 'Ctrl', - 'Shift', - 'P', - ]); - }); - - it('formats alt as Alt', () => { - expect(formatKeyBinding(kbd('a', 'alt'))).toEqual(['Alt', 'A']); - }); + it('formats shortcuts for the active platform', () => { + setPlatform('MacIntel'); + expect(formatKeyBinding(kbd('p', 'mod', 'shift'))).toEqual(['⌘', '⇧', 'P']); + setPlatform('Win32'); + expect(formatKeyBinding(kbd('p', 'mod', 'shift'))).toEqual(['Ctrl', 'Shift', 'P']); }); }); diff --git a/packages/command-palette/src/__tests__/multi-step-builder.test.ts b/packages/command-palette/src/__tests__/multi-step-builder.test.ts index 935f82b..e1074a2 100644 --- a/packages/command-palette/src/__tests__/multi-step-builder.test.ts +++ b/packages/command-palette/src/__tests__/multi-step-builder.test.ts @@ -1,135 +1,41 @@ -import { describe, it, expect, vi } from 'vitest'; +import { describe, expect, it } from 'vitest'; + import { multiStepCommand } from '../multi-step/builder'; -import { kbd } from '../keybinding'; import type { StepViewProps } from '../multi-step/types'; -// Minimal step component for testing -function StubStep(_props: StepViewProps) { +function StubStep(_props: StepViewProps) { return null; } const base = { id: 'test', label: 'Test', group: 'g' }; describe('multiStepCommand builder', () => { - it('builds a valid CommandDefinition with type multi-step', () => { - const cmd = multiStepCommand(base) - .step({ id: 's1', title: 'Step 1', Component: StubStep }) - .build(); - - expect(cmd.id).toBe('test'); - expect(cmd.type).toBe('multi-step'); - expect(cmd.multiStep).toBeDefined(); - expect(cmd.multiStep!.steps).toHaveLength(1); - expect(cmd.multiStep!.steps[0].id).toBe('s1'); - }); - - it('preserves all base fields', () => { - const cmd = multiStepCommand({ - ...base, - description: 'desc', - shortcut: kbd('n', 'mod'), - keywords: ['a', 'b'], - priority: 5, - }) - .step({ id: 's1', title: 'S1', Component: StubStep }) - .build(); - - expect(cmd.description).toBe('desc'); - expect(cmd.shortcut).toEqual(kbd('n', 'mod')); - expect(cmd.keywords).toEqual(['a', 'b']); - expect(cmd.priority).toBe(5); - }); - - it('supports multiple steps in order', () => { - const cmd = multiStepCommand(base) - .step({ id: 's1', title: 'First', Component: StubStep }) - .step({ id: 's2', title: 'Second', Component: StubStep }) - .step({ id: 's3', title: 'Third', Component: StubStep }) - .build(); - - const steps = cmd.multiStep!.steps; - expect(steps).toHaveLength(3); - expect(steps[0].id).toBe('s1'); - expect(steps[1].id).toBe('s2'); - expect(steps[2].id).toBe('s3'); - }); - - it('sets initialContext', () => { - const cmd = multiStepCommand<{ name: string }>(base) - .step({ id: 's1', title: 'S1', Component: StubStep }) + it('preserves the ordered flow configuration', () => { + const onComplete = () => undefined; + const command = multiStepCommand<{ name: string }>(base) + .step({ id: 'first', title: 'First', Component: StubStep }) + .step({ id: 'second', title: 'Second', Component: StubStep, skippable: true }) .initialContext({ name: 'default' }) + .onComplete(onComplete) .build(); - expect(cmd.multiStep!.initialContext).toEqual({ name: 'default' }); + expect(command.multiStep).toMatchObject({ + steps: [{ id: 'first' }, { id: 'second', skippable: true }], + initialContext: { name: 'default' }, + onComplete, + }); }); - it('sets onComplete callback', () => { - const fn = vi.fn(); - const cmd = multiStepCommand(base) - .step({ id: 's1', title: 'S1', Component: StubStep }) - .onComplete(fn) - .build(); - - expect(cmd.multiStep!.onComplete).toBe(fn); + it('rejects a flow without steps', () => { + expect(() => multiStepCommand(base).build()).toThrow('at least one step is required'); }); - it('sets onCancel callback', () => { - const fn = vi.fn(); - const cmd = multiStepCommand(base) - .step({ id: 's1', title: 'S1', Component: StubStep }) - .onCancel(fn) - .build(); - - expect(cmd.multiStep!.onCancel).toBe(fn); - }); - - it('throws if no steps added', () => { - expect(() => multiStepCommand(base).build()).toThrow( - 'at least one step is required' - ); - }); - - it('throws on duplicate step ids', () => { + it('rejects duplicate step ids', () => { expect(() => multiStepCommand(base) - .step({ id: 'dup', title: 'A', Component: StubStep }) - .step({ id: 'dup', title: 'B', Component: StubStep }) - .build() - ).toThrow('duplicate step id "dup"'); - }); - - it('supports fluent chaining', () => { - const builder = multiStepCommand<{ x: number }>(base); - const same = builder - .step({ id: 's1', title: 'S1', Component: StubStep }) - .initialContext({ x: 1 }) - .onComplete(() => {}) - .onCancel(() => {}); - - // All methods return the same builder - expect(same).toBe(builder); - }); - - it('includes step config fields (loader, validate, skippable)', () => { - const loader = vi.fn(); - const validate = vi.fn(); - - const cmd = multiStepCommand(base) - .step({ - id: 's1', - title: 'S1', - Component: StubStep, - loader, - validate, - skippable: true, - description: 'step desc', - }) - .build(); - - const step = cmd.multiStep!.steps[0]; - expect(step.loader).toBe(loader); - expect(step.validate).toBe(validate); - expect(step.skippable).toBe(true); - expect(step.description).toBe('step desc'); + .step({ id: 'duplicate', title: 'A', Component: StubStep }) + .step({ id: 'duplicate', title: 'B', Component: StubStep }) + .build(), + ).toThrow('duplicate step id "duplicate"'); }); }); diff --git a/packages/command-palette/src/__tests__/registry.test.ts b/packages/command-palette/src/__tests__/registry.test.ts index dbac64a..e9072d3 100644 --- a/packages/command-palette/src/__tests__/registry.test.ts +++ b/packages/command-palette/src/__tests__/registry.test.ts @@ -1,179 +1,49 @@ -import { describe, it, expect, vi } from 'vitest'; -import { CommandRegistryManager, createCommandRegistry } from '../registry'; +import { describe, expect, it, vi } from 'vitest'; + +import { CommandRegistryManager } from '../registry'; import type { CommandDefinition, CommandGroupDef } from '../types'; -function makeCommand(overrides: Partial = {}): CommandDefinition { - return { - id: 'test-cmd', - label: 'Test Command', - type: 'action', - group: 'test', - onSelect: () => {}, - ...overrides, - }; +function command(id: string, label = id): CommandDefinition { + return { id, label, type: 'action', group: 'test', onSelect: () => undefined }; } -function makeGroup(overrides: Partial = {}): CommandGroupDef { - return { - id: 'test', - label: 'Test Group', - priority: 1, - ...overrides, - }; +function group(id: string): CommandGroupDef { + return { id, label: id, priority: 1 }; } describe('CommandRegistryManager', () => { - it('initializes empty', () => { - const reg = new CommandRegistryManager(); - expect(reg.getCommands()).toEqual([]); - expect(reg.getGroups()).toEqual([]); - }); - - it('initializes with seed data', () => { - const reg = new CommandRegistryManager({ - groups: [makeGroup()], - commands: [makeCommand()], - }); - expect(reg.getCommands()).toHaveLength(1); - expect(reg.getGroups()).toHaveLength(1); - }); + it('supports dynamic command replacement and removal by id', () => { + const registry = new CommandRegistryManager({ commands: [command('a', 'First')], groups: [] }); - it('registers and retrieves commands', () => { - const reg = new CommandRegistryManager(); - reg.registerCommand(makeCommand({ id: 'a' })); - reg.registerCommand(makeCommand({ id: 'b' })); - expect(reg.getCommands()).toHaveLength(2); - expect(reg.getCommands().map((c) => c.id)).toEqual(['a', 'b']); - }); + registry.registerCommand(command('a', 'Replacement')); + registry.registerCommand(command('b')); + registry.unregisterCommand('b'); - it('unregisters commands', () => { - const reg = new CommandRegistryManager(); - reg.registerCommand(makeCommand({ id: 'a' })); - reg.registerCommand(makeCommand({ id: 'b' })); - reg.unregisterCommand('a'); - expect(reg.getCommands()).toHaveLength(1); - expect(reg.getCommands()[0].id).toBe('b'); + expect(registry.getCommands()).toEqual([expect.objectContaining({ id: 'a', label: 'Replacement' })]); }); - it('overwrites commands with same id', () => { - const reg = new CommandRegistryManager(); - reg.registerCommand(makeCommand({ id: 'a', label: 'First' })); - reg.registerCommand(makeCommand({ id: 'a', label: 'Second' })); - expect(reg.getCommands()).toHaveLength(1); - expect(reg.getCommands()[0].label).toBe('Second'); - }); + it('supports dynamic group registration and removal', () => { + const registry = new CommandRegistryManager({ groups: [group('a')], commands: [] }); - it('registers and retrieves groups', () => { - const reg = new CommandRegistryManager(); - reg.registerGroup(makeGroup({ id: 'g1' })); - reg.registerGroup(makeGroup({ id: 'g2' })); - expect(reg.getGroups()).toHaveLength(2); - }); + registry.registerGroup(group('b')); + registry.unregisterGroup('a'); - it('unregisters groups', () => { - const reg = new CommandRegistryManager(); - reg.registerGroup(makeGroup({ id: 'g1' })); - reg.registerGroup(makeGroup({ id: 'g2' })); - reg.unregisterGroup('g1'); - expect(reg.getGroups()).toHaveLength(1); - expect(reg.getGroups()[0].id).toBe('g2'); + expect(registry.getGroups()).toEqual([expect.objectContaining({ id: 'b' })]); }); - it('notifies subscribers on registerCommand', () => { - const reg = new CommandRegistryManager(); + it('notifies active subscribers for registry changes and stops after unsubscribe', () => { + const registry = new CommandRegistryManager(); const listener = vi.fn(); - reg.subscribe(listener); - reg.registerCommand(makeCommand()); - expect(listener).toHaveBeenCalledTimes(1); - }); + const unsubscribe = registry.subscribe(listener); - it('notifies subscribers on unregisterCommand', () => { - const reg = new CommandRegistryManager(); - reg.registerCommand(makeCommand({ id: 'a' })); - const listener = vi.fn(); - reg.subscribe(listener); - reg.unregisterCommand('a'); - expect(listener).toHaveBeenCalledTimes(1); - }); - - it('notifies subscribers on registerGroup', () => { - const reg = new CommandRegistryManager(); - const listener = vi.fn(); - reg.subscribe(listener); - reg.registerGroup(makeGroup()); - expect(listener).toHaveBeenCalledTimes(1); - }); - - it('notifies subscribers on unregisterGroup', () => { - const reg = new CommandRegistryManager(); - reg.registerGroup(makeGroup({ id: 'g1' })); - const listener = vi.fn(); - reg.subscribe(listener); - reg.unregisterGroup('g1'); - expect(listener).toHaveBeenCalledTimes(1); - }); - - it('unsubscribes correctly', () => { - const reg = new CommandRegistryManager(); - const listener = vi.fn(); - const unsub = reg.subscribe(listener); - unsub(); - reg.registerCommand(makeCommand()); - expect(listener).not.toHaveBeenCalled(); - }); - - it('supports multiple subscribers', () => { - const reg = new CommandRegistryManager(); - const l1 = vi.fn(); - const l2 = vi.fn(); - reg.subscribe(l1); - reg.subscribe(l2); - reg.registerCommand(makeCommand()); - expect(l1).toHaveBeenCalledTimes(1); - expect(l2).toHaveBeenCalledTimes(1); - }); -}); - -describe('createCommandRegistry', () => { - it('returns a CommandRegistryManager', () => { - const reg = createCommandRegistry(); - expect(reg).toBeInstanceOf(CommandRegistryManager); - }); - - it('passes initial data', () => { - const reg = createCommandRegistry({ - groups: [makeGroup()], - commands: [makeCommand()], - }); - expect(reg.getCommands()).toHaveLength(1); - expect(reg.getGroups()).toHaveLength(1); - }); -}); - -describe('Sorting behavior', () => { - it('groups sort by priority', () => { - const reg = createCommandRegistry({ - groups: [ - makeGroup({ id: 'c', priority: 10 }), - makeGroup({ id: 'a', priority: 1 }), - makeGroup({ id: 'b', priority: 5 }), - ], - commands: [], - }); - const sorted = reg.getGroups().sort((a, b) => a.priority - b.priority); - expect(sorted.map((g) => g.id)).toEqual(['a', 'b', 'c']); - }); + registry.registerCommand(command('a')); + registry.unregisterCommand('a'); + registry.registerGroup(group('g')); + registry.unregisterGroup('g'); + expect(listener).toHaveBeenCalledTimes(4); - it('commands sort by priority within a group', () => { - const commands = [ - makeCommand({ id: 'c', priority: 30, group: 'g1' }), - makeCommand({ id: 'a', priority: 5, group: 'g1' }), - makeCommand({ id: 'b', priority: 10, group: 'g1' }), - makeCommand({ id: 'd', group: 'g1' }), // default 99 - ]; - const sorted = commands.sort( - (a, b) => (a.priority ?? 99) - (b.priority ?? 99) - ); - expect(sorted.map((c) => c.id)).toEqual(['a', 'b', 'c', 'd']); + unsubscribe(); + registry.registerCommand(command('ignored')); + expect(listener).toHaveBeenCalledTimes(4); }); }); diff --git a/packages/data/src/__tests__/complex-fields.test.ts b/packages/data/src/__tests__/complex-fields.test.ts deleted file mode 100644 index b14a32e..0000000 --- a/packages/data/src/__tests__/complex-fields.test.ts +++ /dev/null @@ -1,547 +0,0 @@ -/** - * Tests for complex field handling - * Tests complex GraphQL types (GeometryPoint, Interval, etc.) and their AST generation with proper subfield selection - */ -import { beforeEach, describe, expect, it, vi } from "vitest"; -import type { CleanField } from "../data.types"; -import { - getCustomAstForCleanField, - requiresSubfieldSelection, -} from "@constructive-io/graphql-query/custom-ast"; -import { complexTable } from "./fixtures"; - -// Mock the query builder AST functions -vi.mock("@constructive-io/graphql-query/custom-ast", () => ({ - requiresSubfieldSelection: vi.fn(), - getCustomAstForCleanField: vi.fn(), - geometryPointAst: vi.fn(), - intervalAst: vi.fn(), - geometryGeometryCollectionAst: vi.fn(), - geoJsonAst: vi.fn(), -})); - -describe("complex-fields", () => { - beforeEach(() => { - vi.clearAllMocks(); - }); - - describe("complex field detection", () => { - it("should detect GeometryPoint as complex field", () => { - const field: CleanField = { - name: "location", - type: { - gqlType: "GeometryPoint", - isArray: false, - modifier: null, - pgAlias: null, - pgType: "geometry", - subtype: "Point", - typmod: null, - }, - }; - - vi.mocked(requiresSubfieldSelection).mockReturnValue(true); - - const result = requiresSubfieldSelection(field); - expect(result).toBe(true); - }); - - it("should detect Interval as complex field", () => { - const field: CleanField = { - name: "timeSpent", - type: { - gqlType: "Interval", - isArray: false, - modifier: null, - pgAlias: null, - pgType: "interval", - subtype: null, - typmod: null, - }, - }; - - vi.mocked(requiresSubfieldSelection).mockReturnValue(true); - - const result = requiresSubfieldSelection(field); - expect(result).toBe(true); - }); - - it("should detect GeometryGeometryCollection as complex field", () => { - const field: CleanField = { - name: "bounds", - type: { - gqlType: "GeometryGeometryCollection", - isArray: false, - modifier: null, - pgAlias: null, - pgType: "geometry", - subtype: "GeometryCollection", - typmod: null, - }, - }; - - vi.mocked(requiresSubfieldSelection).mockReturnValue(true); - - const result = requiresSubfieldSelection(field); - expect(result).toBe(true); - }); - - it("should detect GeoJSON as complex field", () => { - const field: CleanField = { - name: "geoData", - type: { - gqlType: "GeoJSON", - isArray: false, - modifier: null, - pgAlias: null, - pgType: "geometry", - subtype: null, - typmod: null, - }, - }; - - vi.mocked(requiresSubfieldSelection).mockReturnValue(true); - - const result = requiresSubfieldSelection(field); - expect(result).toBe(true); - }); - - it("should not detect simple types as complex fields", () => { - const simpleFields = [ - { gqlType: "String", expected: false }, - { gqlType: "Int", expected: false }, - { gqlType: "Boolean", expected: false }, - { gqlType: "UUID", expected: false }, - { gqlType: "JSON", expected: false }, - { gqlType: "Datetime", expected: false }, - ]; - - simpleFields.forEach(({ gqlType, expected }) => { - const field: CleanField = { - name: "testField", - type: { - gqlType, - isArray: false, - modifier: null, - pgAlias: null, - pgType: gqlType.toLowerCase(), - subtype: null, - typmod: null, - }, - }; - - vi.mocked(requiresSubfieldSelection).mockReturnValue(expected); - - const result = requiresSubfieldSelection(field); - expect(result).toBe(expected); - }); - }); - - it("should handle array complex fields", () => { - const field: CleanField = { - name: "locations", - type: { - gqlType: "GeometryPoint", - isArray: true, - modifier: null, - pgAlias: null, - pgType: "geometry[]", - subtype: "Point", - typmod: null, - }, - }; - - vi.mocked(requiresSubfieldSelection).mockReturnValue(true); - - const result = requiresSubfieldSelection(field); - expect(result).toBe(true); - }); - }); - - describe("AST generation for complex fields", () => { - it("should generate AST for GeometryPoint", () => { - const field: CleanField = { - name: "location", - type: { - gqlType: "GeometryPoint", - isArray: false, - modifier: null, - pgAlias: null, - pgType: "geometry", - subtype: "Point", - typmod: null, - }, - }; - - const mockAst = { - kind: "Field", - name: { kind: "Name", value: "location" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "x" } }, - { kind: "Field", name: { kind: "Name", value: "y" } }, - ], - }, - } as any; - - vi.mocked(getCustomAstForCleanField).mockReturnValue(mockAst); - - const result = getCustomAstForCleanField(field); - expect(result).toEqual(mockAst); - expect(getCustomAstForCleanField).toHaveBeenCalledWith(field); - }); - - it("should generate AST for Interval", () => { - const field: CleanField = { - name: "timeSpent", - type: { - gqlType: "Interval", - isArray: false, - modifier: null, - pgAlias: null, - pgType: "interval", - subtype: null, - typmod: null, - }, - }; - - const mockAst = { - kind: "Field", - name: { kind: "Name", value: "timeSpent" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "days" } }, - { kind: "Field", name: { kind: "Name", value: "hours" } }, - { kind: "Field", name: { kind: "Name", value: "minutes" } }, - { kind: "Field", name: { kind: "Name", value: "months" } }, - { kind: "Field", name: { kind: "Name", value: "seconds" } }, - { kind: "Field", name: { kind: "Name", value: "years" } }, - ], - }, - } as any; - - vi.mocked(getCustomAstForCleanField).mockReturnValue(mockAst); - - const result = getCustomAstForCleanField(field); - expect(result).toEqual(mockAst); - }); - - it("should generate AST for GeometryGeometryCollection", () => { - const field: CleanField = { - name: "bounds", - type: { - gqlType: "GeometryGeometryCollection", - isArray: false, - modifier: null, - pgAlias: null, - pgType: "geometry", - subtype: "GeometryCollection", - typmod: null, - }, - }; - - const mockAst = { - kind: "Field", - name: { kind: "Name", value: "bounds" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "Field", - name: { kind: "Name", value: "geometries" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "InlineFragment", - typeCondition: { - kind: "NamedType", - name: { kind: "Name", value: "GeometryPoint" }, - }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "x" } }, - { kind: "Field", name: { kind: "Name", value: "y" } }, - ], - }, - }, - ], - }, - }, - ], - }, - } as any; - - vi.mocked(getCustomAstForCleanField).mockReturnValue(mockAst); - - const result = getCustomAstForCleanField(field); - expect(result).toEqual(mockAst); - }); - - it("should generate AST for GeoJSON", () => { - const field: CleanField = { - name: "geoData", - type: { - gqlType: "GeoJSON", - isArray: false, - modifier: null, - pgAlias: null, - pgType: "geometry", - subtype: null, - typmod: null, - }, - }; - - const mockAst = { - kind: "Field", - name: { kind: "Name", value: "geoData" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "geojson" } }, - ], - }, - } as any; - - vi.mocked(getCustomAstForCleanField).mockReturnValue(mockAst); - - const result = getCustomAstForCleanField(field); - expect(result).toEqual(mockAst); - }); - }); - - describe("complex field integration", () => { - it("should handle complex fields in table definition", () => { - const complexFields = complexTable.fields.filter((field) => { - vi.mocked(requiresSubfieldSelection).mockImplementation((f: CleanField) => { - return [ - "GeometryPoint", - "Interval", - "GeometryGeometryCollection", - "GeoJSON", - ].includes(f.type.gqlType); - }); - return requiresSubfieldSelection(field); - }); - - expect(complexFields.length).toBeGreaterThan(0); - - complexFields.forEach((field) => { - expect([ - "GeometryPoint", - "Interval", - "GeometryGeometryCollection", - "GeoJSON", - ]).toContain(field.type.gqlType); - }); - }); - - it("should handle mixed simple and complex fields", () => { - const allFields = complexTable.fields; - - vi.mocked(requiresSubfieldSelection).mockImplementation((field: CleanField) => { - return [ - "GeometryPoint", - "Interval", - "GeometryGeometryCollection", - "GeoJSON", - ].includes(field.type.gqlType); - }); - - const complexFields = allFields.filter(requiresSubfieldSelection); - const simpleFields = allFields.filter( - (field) => !requiresSubfieldSelection(field), - ); - - expect(complexFields.length).toBeGreaterThan(0); - expect(simpleFields.length).toBeGreaterThan(0); - expect(complexFields.length + simpleFields.length).toBe(allFields.length); - }); - - it("should handle complex fields with different PostgreSQL subtypes", () => { - const geometryFields = [ - { - name: "point", - type: { gqlType: "GeometryPoint", subtype: "Point" }, - }, - { - name: "polygon", - type: { gqlType: "GeometryPolygon", subtype: "Polygon" }, - }, - { - name: "collection", - type: { - gqlType: "GeometryGeometryCollection", - subtype: "GeometryCollection", - }, - }, - ]; - - geometryFields.forEach((field) => { - vi.mocked(requiresSubfieldSelection).mockReturnValue(true); - expect(requiresSubfieldSelection(field as CleanField)).toBe(true); - }); - }); - }); - - describe("edge cases and error handling", () => { - it("should handle fields with null subtype", () => { - const field: CleanField = { - name: "testField", - type: { - gqlType: "GeometryPoint", - isArray: false, - modifier: null, - pgAlias: null, - pgType: "geometry", - subtype: null, - typmod: null, - }, - }; - - vi.mocked(requiresSubfieldSelection).mockReturnValue(true); - - const result = requiresSubfieldSelection(field); - expect(result).toBe(true); - }); - - it("should handle fields with undefined properties", () => { - const field: CleanField = { - name: "testField", - type: { - gqlType: "Interval", - isArray: false, - modifier: undefined, - pgAlias: undefined, - pgType: "interval", - subtype: undefined, - typmod: undefined, - }, - }; - - vi.mocked(requiresSubfieldSelection).mockReturnValue(true); - - const result = requiresSubfieldSelection(field); - expect(result).toBe(true); - }); - - it("should handle unknown complex types gracefully", () => { - const field: CleanField = { - name: "unknownField", - type: { - gqlType: "UnknownComplexType", - isArray: false, - modifier: null, - pgAlias: null, - pgType: "unknown", - subtype: null, - typmod: null, - }, - }; - - vi.mocked(requiresSubfieldSelection).mockReturnValue(false); - - const result = requiresSubfieldSelection(field); - expect(result).toBe(false); - }); - - it("should handle AST generation errors gracefully", () => { - const field: CleanField = { - name: "problematicField", - type: { - gqlType: "GeometryPoint", - isArray: false, - modifier: null, - pgAlias: null, - pgType: "geometry", - subtype: "Point", - typmod: null, - }, - }; - - vi.mocked(getCustomAstForCleanField).mockImplementation(() => { - throw new Error("AST generation failed"); - }); - - expect(() => getCustomAstForCleanField(field)).toThrow( - "AST generation failed", - ); - }); - - it("should handle complex fields with SRID information", () => { - const field: CleanField = { - name: "locationWithSRID", - type: { - gqlType: "GeometryPoint", - isArray: false, - modifier: "NOT NULL", - pgAlias: "point", - pgType: "geometry", - subtype: "Point", - typmod: 4326, // SRID - }, - }; - - vi.mocked(requiresSubfieldSelection).mockReturnValue(true); - - const result = requiresSubfieldSelection(field); - expect(result).toBe(true); - }); - }); - - describe("performance considerations", () => { - it("should efficiently detect complex fields in large tables", () => { - const largeFieldSet = Array.from({ length: 100 }, (_, i) => ({ - name: `field${i}`, - type: { - gqlType: i % 10 === 0 ? "GeometryPoint" : "String", - isArray: false, - modifier: null, - pgAlias: null, - pgType: i % 10 === 0 ? "geometry" : "text", - subtype: i % 10 === 0 ? "Point" : null, - typmod: null, - }, - })); - - vi.mocked(requiresSubfieldSelection).mockImplementation((field: CleanField) => { - return field.type.gqlType === "GeometryPoint"; - }); - - const complexFields = largeFieldSet.filter(requiresSubfieldSelection); - expect(complexFields).toHaveLength(10); - }); - - it("should cache AST generation results when possible", () => { - const field: CleanField = { - name: "location", - type: { - gqlType: "GeometryPoint", - isArray: false, - modifier: null, - pgAlias: null, - pgType: "geometry", - subtype: "Point", - typmod: null, - }, - }; - - const mockAst = { - kind: "Field", - name: { kind: "Name", value: "location" }, - } as any; - vi.mocked(getCustomAstForCleanField).mockReturnValue(mockAst); - - // Call multiple times - const result1 = getCustomAstForCleanField(field); - const result2 = getCustomAstForCleanField(field); - - expect(result1).toEqual(mockAst); - expect(result2).toEqual(mockAst); - expect(getCustomAstForCleanField).toHaveBeenCalledTimes(2); - }); - }); -}); diff --git a/packages/data/src/__tests__/data.types.test.ts b/packages/data/src/__tests__/data.types.test.ts index df409cf..7e2bb4d 100644 --- a/packages/data/src/__tests__/data.types.test.ts +++ b/packages/data/src/__tests__/data.types.test.ts @@ -3,9 +3,9 @@ * Tests core type definitions, utilities, and type conversion functions */ import { beforeEach, describe, expect, it } from "vitest"; -import type { CleanTable, Filter, MetaTable } from "../data.types"; +import type { CleanTable, MetaTable } from "../data.types"; import { cleanTable, pgFieldToCamelCase } from "../data.types"; -import { complexTable, filterFixtures, simpleTable } from "./fixtures"; +import { complexTable, simpleTable } from "./fixtures"; import { createMockMetaResponse, createMockTable } from "./test-helpers"; describe("data.types", () => { @@ -422,62 +422,6 @@ describe("data.types", () => { }); }); - describe("Filter type validation", () => { - it("should accept valid simple filters", () => { - const filter: Filter = filterFixtures.simple; - - expect(filter).toEqual({ - isActive: { equalTo: true }, - }); - }); - - it("should accept valid string operator filters", () => { - const filter: Filter = filterFixtures.stringOperators; - - expect(filter).toEqual({ - name: { includes: "test" }, - email: { endsWith: "@example.com" }, - description: { startsWith: "Important" }, - }); - }); - - it("should accept valid numeric operator filters", () => { - const filter: Filter = filterFixtures.numericOperators; - - expect(filter).toEqual({ - age: { greaterThan: 18 }, - score: { lessThanOrEqualTo: 100 }, - rating: { in: [4, 5] }, - }); - }); - - it("should accept valid logical operator filters", () => { - const filter: Filter = filterFixtures.logicalOperators; - - expect(filter).toHaveProperty("and"); - expect(filter).toHaveProperty("or"); - expect(Array.isArray(filter.and)).toBe(true); - expect(Array.isArray(filter.or)).toBe(true); - }); - - it("should accept valid nested filters", () => { - const filter: Filter = filterFixtures.nested; - - expect(filter).toHaveProperty("and"); - expect(Array.isArray(filter.and)).toBe(true); - expect(filter.and).toHaveLength(2); - }); - - it("should accept valid relational filters", () => { - const filter: Filter = filterFixtures.relational; - - expect(filter).toHaveProperty("posts"); - expect(filter).toHaveProperty("profile"); - expect(filter.posts).toHaveProperty("some"); - expect(filter.profile).toHaveProperty("every"); - }); - }); - describe("pgFieldToCamelCase", () => { it("should convert snake_case to camelCase", () => { expect(pgFieldToCamelCase("owner_id")).toBe("ownerId"); diff --git a/packages/data/src/__tests__/mutation-input.node.test.ts b/packages/data/src/__tests__/mutation-input.node.test.ts index 7f6533f..f8495a8 100644 --- a/packages/data/src/__tests__/mutation-input.node.test.ts +++ b/packages/data/src/__tests__/mutation-input.node.test.ts @@ -5,7 +5,6 @@ import { describe, expect, it } from "vitest"; import { - type PartialBy, prepareCreateInput, prepareMutationInput, preparePatchInput, @@ -363,30 +362,6 @@ describe("prepareMutationInput direct usage", () => { }); }); -// ========================================================================= -// PartialBy Type Utility (compile-time checks) -// ========================================================================= -describe("PartialBy type utility", () => { - it("makes specified keys optional (compile-time check)", () => { - type Input = { name: string; inheritsId: string; databaseId: string }; - type SafeInput = PartialBy; - - // This compiles — inheritsId is now optional - const valid: SafeInput = { name: "test", databaseId: "db-1" }; - expect(valid.name).toBe("test"); - expect(valid.databaseId).toBe("db-1"); - expect(valid).not.toHaveProperty("inheritsId"); - }); - - it("makes multiple keys optional", () => { - type Input = { name: string; inheritsId: string; databaseId: string }; - type SafeInput = PartialBy; - - const valid: SafeInput = { name: "test" }; - expect(valid.name).toBe("test"); - }); -}); - // ========================================================================= // stripEmpty // ========================================================================= diff --git a/packages/data/src/__tests__/query-builder/__tests__/__snapshots__/builder.test.ts.snap b/packages/data/src/__tests__/query-builder/__tests__/__snapshots__/builder.test.ts.snap deleted file mode 100644 index 5c41d1b..0000000 --- a/packages/data/src/__tests__/query-builder/__tests__/__snapshots__/builder.test.ts.snap +++ /dev/null @@ -1,507 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`create with custom selection 1`] = ` -"mutation createActionMutation($id: UUID, $slug: String, $photo: JSON, $shareImage: JSON, $title: String, $titleObjectTemplate: String, $url: String, $description: String, $discoveryHeader: String, $discoveryDescription: String, $notificationText: String, $notificationObjectTemplate: String, $enableNotifications: Boolean, $enableNotificationsText: String, $search: FullText, $location: GeoJSON, $locationRadius: BigFloat, $timeRequired: IntervalInput, $startDate: Datetime, $endDate: Datetime, $approved: Boolean, $published: Boolean, $isPrivate: Boolean, $rewardAmount: BigFloat, $activityFeedText: String, $callToAction: String, $completedActionText: String, $alreadyCompletedActionText: String, $selfVerifiable: Boolean, $isRecurring: Boolean, $recurringInterval: IntervalInput, $oncePerObject: Boolean, $minimumGroupMembers: Int, $limitedToLocation: Boolean, $tags: [String], $groupId: UUID, $ownerId: UUID!, $objectTypeId: Int, $rewardId: UUID, $verifyRewardId: UUID, $photoUpload: Upload, $shareImageUpload: Upload) { - createAction( - input: {action: {id: $id, slug: $slug, photo: $photo, shareImage: $shareImage, title: $title, titleObjectTemplate: $titleObjectTemplate, url: $url, description: $description, discoveryHeader: $discoveryHeader, discoveryDescription: $discoveryDescription, notificationText: $notificationText, notificationObjectTemplate: $notificationObjectTemplate, enableNotifications: $enableNotifications, enableNotificationsText: $enableNotificationsText, search: $search, location: $location, locationRadius: $locationRadius, timeRequired: $timeRequired, startDate: $startDate, endDate: $endDate, approved: $approved, published: $published, isPrivate: $isPrivate, rewardAmount: $rewardAmount, activityFeedText: $activityFeedText, callToAction: $callToAction, completedActionText: $completedActionText, alreadyCompletedActionText: $alreadyCompletedActionText, selfVerifiable: $selfVerifiable, isRecurring: $isRecurring, recurringInterval: $recurringInterval, oncePerObject: $oncePerObject, minimumGroupMembers: $minimumGroupMembers, limitedToLocation: $limitedToLocation, tags: $tags, groupId: $groupId, ownerId: $ownerId, objectTypeId: $objectTypeId, rewardId: $rewardId, verifyRewardId: $verifyRewardId, photoUpload: $photoUpload, shareImageUpload: $shareImageUpload}} - ) { - action { - id - photo - title - } - } -} -" -`; - -exports[`create with custom selection 2`] = `"createActionMutation"`; - -exports[`create with default scalar selection 1`] = ` -"mutation createActionMutation($id: UUID, $slug: String, $photo: JSON, $shareImage: JSON, $title: String, $titleObjectTemplate: String, $url: String, $description: String, $discoveryHeader: String, $discoveryDescription: String, $notificationText: String, $notificationObjectTemplate: String, $enableNotifications: Boolean, $enableNotificationsText: String, $search: FullText, $location: GeoJSON, $locationRadius: BigFloat, $timeRequired: IntervalInput, $startDate: Datetime, $endDate: Datetime, $approved: Boolean, $published: Boolean, $isPrivate: Boolean, $rewardAmount: BigFloat, $activityFeedText: String, $callToAction: String, $completedActionText: String, $alreadyCompletedActionText: String, $selfVerifiable: Boolean, $isRecurring: Boolean, $recurringInterval: IntervalInput, $oncePerObject: Boolean, $minimumGroupMembers: Int, $limitedToLocation: Boolean, $tags: [String], $groupId: UUID, $ownerId: UUID!, $objectTypeId: Int, $rewardId: UUID, $verifyRewardId: UUID, $photoUpload: Upload, $shareImageUpload: Upload) { - createAction( - input: {action: {id: $id, slug: $slug, photo: $photo, shareImage: $shareImage, title: $title, titleObjectTemplate: $titleObjectTemplate, url: $url, description: $description, discoveryHeader: $discoveryHeader, discoveryDescription: $discoveryDescription, notificationText: $notificationText, notificationObjectTemplate: $notificationObjectTemplate, enableNotifications: $enableNotifications, enableNotificationsText: $enableNotificationsText, search: $search, location: $location, locationRadius: $locationRadius, timeRequired: $timeRequired, startDate: $startDate, endDate: $endDate, approved: $approved, published: $published, isPrivate: $isPrivate, rewardAmount: $rewardAmount, activityFeedText: $activityFeedText, callToAction: $callToAction, completedActionText: $completedActionText, alreadyCompletedActionText: $alreadyCompletedActionText, selfVerifiable: $selfVerifiable, isRecurring: $isRecurring, recurringInterval: $recurringInterval, oncePerObject: $oncePerObject, minimumGroupMembers: $minimumGroupMembers, limitedToLocation: $limitedToLocation, tags: $tags, groupId: $groupId, ownerId: $ownerId, objectTypeId: $objectTypeId, rewardId: $rewardId, verifyRewardId: $verifyRewardId, photoUpload: $photoUpload, shareImageUpload: $shareImageUpload}} - ) { - action { - id - slug - photo - shareImage - title - titleObjectTemplate - url - description - discoveryHeader - discoveryDescription - notificationText - notificationObjectTemplate - enableNotifications - enableNotificationsText - search - location { - geojson - } - locationRadius - timeRequired { - days - hours - minutes - months - seconds - years - } - startDate - endDate - approved - published - isPrivate - rewardAmount - activityFeedText - callToAction - completedActionText - alreadyCompletedActionText - selfVerifiable - isRecurring - recurringInterval { - days - hours - minutes - months - seconds - years - } - oncePerObject - minimumGroupMembers - limitedToLocation - tags - createdBy - updatedBy - createdAt - updatedAt - } - } -} -" -`; - -exports[`create with default scalar selection 2`] = `"createActionMutation"`; - -exports[`delete 1`] = ` -"mutation deleteActionMutation($id: UUID!) { - deleteAction(input: {id: $id}) { - clientMutationId - } -} -" -`; - -exports[`delete 2`] = `"deleteActionMutation"`; - -exports[`expands further selections of custom ast fields in nested selection 1`] = ` -"query getActionGoalsQuery($first: Int, $last: Int, $after: Cursor, $before: Cursor, $offset: Int, $condition: ActionGoalCondition, $filter: ActionGoalFilter, $orderBy: [ActionGoalsOrderBy!]) { - actionGoals( - first: $first - last: $last - offset: $offset - after: $after - before: $before - condition: $condition - filter: $filter - orderBy: $orderBy - ) {totalCount, pageInfo { - hasNextPage - hasPreviousPage - endCursor - startCursor - }, nodes { - action { - id - location { - geojson - } - timeRequired { - days - hours - minutes - months - seconds - years - } - } - }} -} -" -`; - -exports[`expands further selections of custom ast fields in nested selection 2`] = `"getActionGoalsQuery"`; - -exports[`getAll 1`] = ` -"query getActionsQueryAll { - actions {totalCount, nodes { - id - photo - title - }} -} -" -`; - -exports[`getAll 2`] = `"getActionsQueryAll"`; - -exports[`getMany edges 1`] = ` -"query getActionsQuery($first: Int, $last: Int, $after: Cursor, $before: Cursor, $offset: Int, $condition: ActionCondition, $filter: ActionFilter, $orderBy: [ActionsOrderBy!]) { - actions( - first: $first - last: $last - offset: $offset - after: $after - before: $before - condition: $condition - filter: $filter - orderBy: $orderBy - ) {totalCount, pageInfo { - hasNextPage - hasPreviousPage - endCursor - startCursor - }, edges { - cursor - node { - id - photo - title - } - }} -} -" -`; - -exports[`getMany edges 2`] = `"getActionsQuery"`; - -exports[`getMany should select only scalar fields by default 1`] = ` -"query getActionsQuery($first: Int, $last: Int, $after: Cursor, $before: Cursor, $offset: Int, $condition: ActionCondition, $filter: ActionFilter, $orderBy: [ActionsOrderBy!]) { - actions( - first: $first - last: $last - offset: $offset - after: $after - before: $before - condition: $condition - filter: $filter - orderBy: $orderBy - ) {totalCount, pageInfo { - hasNextPage - hasPreviousPage - endCursor - startCursor - }, nodes { - id - slug - photo - shareImage - title - titleObjectTemplate - url - description - discoveryHeader - discoveryDescription - notificationText - notificationObjectTemplate - enableNotifications - enableNotificationsText - search - location { - geojson - } - locationRadius - timeRequired { - days - hours - minutes - months - seconds - years - } - startDate - endDate - approved - published - isPrivate - rewardAmount - activityFeedText - callToAction - completedActionText - alreadyCompletedActionText - selfVerifiable - isRecurring - recurringInterval { - days - hours - minutes - months - seconds - years - } - oncePerObject - minimumGroupMembers - limitedToLocation - tags - createdBy - updatedBy - createdAt - updatedAt - }} -} -" -`; - -exports[`getMany should select only scalar fields by default 2`] = `"getActionsQuery"`; - -exports[`getMany should whitelist selected fields 1`] = ` -"query getActionsQuery($first: Int, $last: Int, $after: Cursor, $before: Cursor, $offset: Int, $condition: ActionCondition, $filter: ActionFilter, $orderBy: [ActionsOrderBy!]) { - actions( - first: $first - last: $last - offset: $offset - after: $after - before: $before - condition: $condition - filter: $filter - orderBy: $orderBy - ) {totalCount, pageInfo { - hasNextPage - hasPreviousPage - endCursor - startCursor - }, nodes { - id - photo - title - }} -} -" -`; - -exports[`getMany should whitelist selected fields 2`] = `"getActionsQuery"`; - -exports[`getOne 1`] = ` -"query getActionQuery($id: UUID!) { - action(id: $id) { - id - photo - title - } -} -" -`; - -exports[`getOne 2`] = `"getActionQuery"`; - -exports[`selects all scalar fields of junction table by default 1`] = ` -"query getActionGoalsQuery($first: Int, $last: Int, $after: Cursor, $before: Cursor, $offset: Int, $condition: ActionGoalCondition, $filter: ActionGoalFilter, $orderBy: [ActionGoalsOrderBy!]) { - actionGoals( - first: $first - last: $last - offset: $offset - after: $after - before: $before - condition: $condition - filter: $filter - orderBy: $orderBy - ) {totalCount, pageInfo { - hasNextPage - hasPreviousPage - endCursor - startCursor - }, nodes { - createdBy - updatedBy - createdAt - updatedAt - actionId - goalId - }} -} -" -`; - -exports[`selects belongsTo relation field 1`] = ` -"query getActionsQuery($first: Int, $last: Int, $after: Cursor, $before: Cursor, $offset: Int, $condition: ActionCondition, $filter: ActionFilter, $orderBy: [ActionsOrderBy!]) { - actions( - first: $first - last: $last - offset: $offset - after: $after - before: $before - condition: $condition - filter: $filter - orderBy: $orderBy - ) {totalCount, pageInfo { - hasNextPage - hasPreviousPage - endCursor - startCursor - }, nodes { - owner { - id - } - }} -} -" -`; - -exports[`selects non-scalar custom types 1`] = `"getActionsQuery"`; - -exports[`selects relation field 1`] = ` -"query getActionsQuery($first: Int, $last: Int, $after: Cursor, $before: Cursor, $offset: Int, $condition: ActionCondition, $filter: ActionFilter, $orderBy: [ActionsOrderBy!]) { - actions( - first: $first - last: $last - offset: $offset - after: $after - before: $before - condition: $condition - filter: $filter - orderBy: $orderBy - ) {totalCount, pageInfo { - hasNextPage - hasPreviousPage - endCursor - startCursor - }, nodes { - id - slug - photo - title - url - goals(first: 3) {totalCount, nodes { - id - name - slug - shortName - icon - subHead - tags - search - createdBy - updatedBy - createdAt - updatedAt - }} - }} -} -" -`; - -exports[`should select totalCount in subfields by default 1`] = ` -"query getActionsQuery($first: Int, $last: Int, $after: Cursor, $before: Cursor, $offset: Int, $condition: ActionCondition, $filter: ActionFilter, $orderBy: [ActionsOrderBy!]) { - actions( - first: $first - last: $last - offset: $offset - after: $after - before: $before - condition: $condition - filter: $filter - orderBy: $orderBy - ) {totalCount, pageInfo { - hasNextPage - hasPreviousPage - endCursor - startCursor - }, nodes { - id - photo - title - }} -} -" -`; - -exports[`should select totalCount in subfields by default 2`] = `"getActionsQuery"`; - -exports[`update with custom selection 1`] = ` -"mutation updateActionMutation($id: UUID!, $slug: String, $photo: JSON, $shareImage: JSON, $title: String, $titleObjectTemplate: String, $url: String, $description: String, $discoveryHeader: String, $discoveryDescription: String, $notificationText: String, $notificationObjectTemplate: String, $enableNotifications: Boolean, $enableNotificationsText: String, $search: FullText, $location: GeoJSON, $locationRadius: BigFloat, $timeRequired: IntervalInput, $startDate: Datetime, $endDate: Datetime, $approved: Boolean, $published: Boolean, $isPrivate: Boolean, $rewardAmount: BigFloat, $activityFeedText: String, $callToAction: String, $completedActionText: String, $alreadyCompletedActionText: String, $selfVerifiable: Boolean, $isRecurring: Boolean, $recurringInterval: IntervalInput, $oncePerObject: Boolean, $minimumGroupMembers: Int, $limitedToLocation: Boolean, $tags: [String], $groupId: UUID, $ownerId: UUID, $objectTypeId: Int, $rewardId: UUID, $verifyRewardId: UUID, $photoUpload: Upload, $shareImageUpload: Upload) { - updateAction( - input: {id: $id, patch: {slug: $slug, photo: $photo, shareImage: $shareImage, title: $title, titleObjectTemplate: $titleObjectTemplate, url: $url, description: $description, discoveryHeader: $discoveryHeader, discoveryDescription: $discoveryDescription, notificationText: $notificationText, notificationObjectTemplate: $notificationObjectTemplate, enableNotifications: $enableNotifications, enableNotificationsText: $enableNotificationsText, search: $search, location: $location, locationRadius: $locationRadius, timeRequired: $timeRequired, startDate: $startDate, endDate: $endDate, approved: $approved, published: $published, isPrivate: $isPrivate, rewardAmount: $rewardAmount, activityFeedText: $activityFeedText, callToAction: $callToAction, completedActionText: $completedActionText, alreadyCompletedActionText: $alreadyCompletedActionText, selfVerifiable: $selfVerifiable, isRecurring: $isRecurring, recurringInterval: $recurringInterval, oncePerObject: $oncePerObject, minimumGroupMembers: $minimumGroupMembers, limitedToLocation: $limitedToLocation, tags: $tags, groupId: $groupId, ownerId: $ownerId, objectTypeId: $objectTypeId, rewardId: $rewardId, verifyRewardId: $verifyRewardId, photoUpload: $photoUpload, shareImageUpload: $shareImageUpload}} - ) { - action { - id - photo - title - } - } -} -" -`; - -exports[`update with custom selection 2`] = `"updateActionMutation"`; - -exports[`update with default scalar selection 1`] = ` -"mutation updateActionMutation($id: UUID!, $slug: String, $photo: JSON, $shareImage: JSON, $title: String, $titleObjectTemplate: String, $url: String, $description: String, $discoveryHeader: String, $discoveryDescription: String, $notificationText: String, $notificationObjectTemplate: String, $enableNotifications: Boolean, $enableNotificationsText: String, $search: FullText, $location: GeoJSON, $locationRadius: BigFloat, $timeRequired: IntervalInput, $startDate: Datetime, $endDate: Datetime, $approved: Boolean, $published: Boolean, $isPrivate: Boolean, $rewardAmount: BigFloat, $activityFeedText: String, $callToAction: String, $completedActionText: String, $alreadyCompletedActionText: String, $selfVerifiable: Boolean, $isRecurring: Boolean, $recurringInterval: IntervalInput, $oncePerObject: Boolean, $minimumGroupMembers: Int, $limitedToLocation: Boolean, $tags: [String], $groupId: UUID, $ownerId: UUID, $objectTypeId: Int, $rewardId: UUID, $verifyRewardId: UUID, $photoUpload: Upload, $shareImageUpload: Upload) { - updateAction( - input: {id: $id, patch: {slug: $slug, photo: $photo, shareImage: $shareImage, title: $title, titleObjectTemplate: $titleObjectTemplate, url: $url, description: $description, discoveryHeader: $discoveryHeader, discoveryDescription: $discoveryDescription, notificationText: $notificationText, notificationObjectTemplate: $notificationObjectTemplate, enableNotifications: $enableNotifications, enableNotificationsText: $enableNotificationsText, search: $search, location: $location, locationRadius: $locationRadius, timeRequired: $timeRequired, startDate: $startDate, endDate: $endDate, approved: $approved, published: $published, isPrivate: $isPrivate, rewardAmount: $rewardAmount, activityFeedText: $activityFeedText, callToAction: $callToAction, completedActionText: $completedActionText, alreadyCompletedActionText: $alreadyCompletedActionText, selfVerifiable: $selfVerifiable, isRecurring: $isRecurring, recurringInterval: $recurringInterval, oncePerObject: $oncePerObject, minimumGroupMembers: $minimumGroupMembers, limitedToLocation: $limitedToLocation, tags: $tags, groupId: $groupId, ownerId: $ownerId, objectTypeId: $objectTypeId, rewardId: $rewardId, verifyRewardId: $verifyRewardId, photoUpload: $photoUpload, shareImageUpload: $shareImageUpload}} - ) { - action { - id - slug - photo - shareImage - title - titleObjectTemplate - url - description - discoveryHeader - discoveryDescription - notificationText - notificationObjectTemplate - enableNotifications - enableNotificationsText - search - location { - geojson - } - locationRadius - timeRequired { - days - hours - minutes - months - seconds - years - } - startDate - endDate - approved - published - isPrivate - rewardAmount - activityFeedText - callToAction - completedActionText - alreadyCompletedActionText - selfVerifiable - isRecurring - recurringInterval { - days - hours - minutes - months - seconds - years - } - oncePerObject - minimumGroupMembers - limitedToLocation - tags - createdBy - updatedBy - createdAt - updatedAt - } - } -} -" -`; - -exports[`update with default scalar selection 2`] = `"updateActionMutation"`; diff --git a/packages/data/src/__tests__/query-builder/__tests__/__snapshots__/meta-object.node.test.ts.snap b/packages/data/src/__tests__/query-builder/__tests__/__snapshots__/meta-object.node.test.ts.snap deleted file mode 100644 index c1bf9d7..0000000 --- a/packages/data/src/__tests__/query-builder/__tests__/__snapshots__/meta-object.node.test.ts.snap +++ /dev/null @@ -1,6568 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`convertFromMetaSchema() > matches snapshot 1`] = ` -{ - "tables": [ - { - "fields": [ - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "goalId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "ownerId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "action", - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "Action", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "goal", - "name": "goalId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "Goal", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "owner", - "name": "ownerId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "ActionGoal", - "primaryConstraints": [ - { - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "goalId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "name", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "description", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "type", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "itemOrder", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "int", - "pgType": "int4", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "timeRequired", - "type": { - "gqlType": "Interval", - "isArray": false, - "modifier": null, - "pgAlias": "interval", - "pgType": "interval", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "isRequired", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "notificationText", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "embedCode", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "url", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "url", - "pgType": "url", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "media", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "upload", - "pgType": "upload", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "ownerId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "action", - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "Action", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "ActionItem", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "name", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "ownerId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "action", - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "Action", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "owner", - "name": "ownerId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "ActionResult", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "slug", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "citext", - "pgType": "citext", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "photo", - "type": { - "gqlType": "JSON", - "isArray": false, - "modifier": null, - "pgAlias": "image", - "pgType": "image", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "title", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "url", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "url", - "pgType": "url", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "description", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "discoveryHeader", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "discoveryDescription", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "enableNotifications", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "enableNotificationsText", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "search", - "type": { - "gqlType": "FullText", - "isArray": false, - "modifier": null, - "pgAlias": "tsvector", - "pgType": "tsvector", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "location", - "type": { - "gqlType": "GeoJSON", - "isArray": false, - "modifier": 1107460, - "pgAlias": "geometry", - "pgType": "geometry", - "subtype": "GeometryPoint", - "typmod": { - "gisType": "Point", - "hasM": false, - "hasZ": false, - "srid": 4326, - "subtype": 1, - }, - }, - }, - { - "name": "locationRadius", - "type": { - "gqlType": "BigFloat", - "isArray": false, - "modifier": null, - "pgAlias": "numeric", - "pgType": "numeric", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "timeRequired", - "type": { - "gqlType": "Interval", - "isArray": false, - "modifier": null, - "pgAlias": "interval", - "pgType": "interval", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "startDate", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "endDate", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "approved", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "rewardAmount", - "type": { - "gqlType": "BigFloat", - "isArray": false, - "modifier": null, - "pgAlias": "numeric", - "pgType": "numeric", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "activityFeedText", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "callToAction", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "completedActionText", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "alreadyCompletedActionText", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "tags", - "type": { - "gqlType": "[String]", - "isArray": true, - "modifier": null, - "pgAlias": "citext", - "pgType": "citext", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "ownerId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "owner", - "name": "ownerId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "Action", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "slug", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "citext", - "pgType": "citext", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "data", - "type": { - "gqlType": "JSON", - "isArray": false, - "modifier": null, - "pgAlias": "jsonb", - "pgType": "jsonb", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "senderId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "receiverId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "sender", - "name": "senderId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "receiver", - "name": "receiverId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "ClaimedInvite", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "ownerId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "service", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "identifier", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "details", - "type": { - "gqlType": "JSON", - "isArray": false, - "modifier": null, - "pgAlias": "jsonb", - "pgType": "jsonb", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "isVerified", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "owner", - "name": "ownerId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "ConnectedAccount", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "service", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "identifier", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "ownerId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "address", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "isVerified", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "isPrimary", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "owner", - "name": "ownerId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "CryptoAddress", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "address", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "ownerId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "email", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "email", - "pgType": "email", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "isVerified", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "isPrimary", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "owner", - "name": "ownerId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "Email", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "email", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "email", - "pgType": "email", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "audio", - "type": { - "gqlType": "JSON", - "isArray": false, - "modifier": null, - "pgAlias": "attachment", - "pgType": "attachment", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "audioDuration", - "type": { - "gqlType": "Interval", - "isArray": false, - "modifier": null, - "pgAlias": "interval", - "pgType": "interval", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "explanationTitle", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "explanation", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "goalId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "goal", - "name": "goalId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "Goal", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "GoalExplanation", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "name", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "slug", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "citext", - "pgType": "citext", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "shortName", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "icon", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "subHead", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "tags", - "type": { - "gqlType": "[String]", - "isArray": true, - "modifier": null, - "pgAlias": "citext", - "pgType": "citext", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "search", - "type": { - "gqlType": "FullText", - "isArray": false, - "modifier": null, - "pgAlias": "tsvector", - "pgType": "tsvector", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [], - "name": "Goal", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "name", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "email", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "email", - "pgType": "email", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "senderId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "inviteToken", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "inviteValid", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "inviteLimit", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "int", - "pgType": "int4", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "inviteCount", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "int", - "pgType": "int4", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "multiple", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "data", - "type": { - "gqlType": "JSON", - "isArray": false, - "modifier": null, - "pgAlias": "jsonb", - "pgType": "jsonb", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "expiresAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "sender", - "name": "senderId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "Invite", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "email", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "email", - "pgType": "email", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "senderId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "name", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "level", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "requiredCount", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "int", - "pgType": "int4", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "priority", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "int", - "pgType": "int4", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [], - "name": "LevelRequirement", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "name", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "level", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "name", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [], - "name": "Level", - "primaryConstraints": [ - { - "name": "name", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "name", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "memberIds", - "type": { - "gqlType": "[UUID]", - "isArray": true, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [], - "name": "MessageGroup", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "type", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "content", - "type": { - "gqlType": "JSON", - "isArray": false, - "modifier": null, - "pgAlias": "jsonb", - "pgType": "jsonb", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "upload", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "upload", - "pgType": "upload", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "senderId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "groupId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "sender", - "name": "senderId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "group", - "name": "groupId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "MessageGroup", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "Message", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "name", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "description", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "link", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "url", - "pgType": "url", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "publishedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "photo", - "type": { - "gqlType": "JSON", - "isArray": false, - "modifier": null, - "pgAlias": "image", - "pgType": "image", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [], - "name": "NewsUpdate", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "name", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "profilePicture", - "type": { - "gqlType": "JSON", - "isArray": false, - "modifier": null, - "pgAlias": "image", - "pgType": "image", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "description", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "website", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "url", - "pgType": "url", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "reputation", - "type": { - "gqlType": "BigFloat", - "isArray": false, - "modifier": null, - "pgAlias": "numeric", - "pgType": "numeric", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "tags", - "type": { - "gqlType": "[String]", - "isArray": true, - "modifier": null, - "pgAlias": "citext", - "pgType": "citext", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "organizationId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "organization", - "name": "organizationId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "OrganizationProfile", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "organizationId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "ownerId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "cc", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "number", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "isVerified", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "isPrimary", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "owner", - "name": "ownerId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "PhoneNumber", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "number", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "name", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "count", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "int", - "pgType": "int4", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [], - "name": "UserAchievement", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "name", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "value", - "type": { - "gqlType": "JSON", - "isArray": false, - "modifier": null, - "pgAlias": "jsonb", - "pgType": "jsonb", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "status", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userActionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionItemId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "user", - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "action", - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "Action", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "userAction", - "name": "userActionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "UserAction", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "actionItem", - "name": "actionItemId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "ActionItem", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "UserActionItem", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userActionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionItemId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userActionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "reacterId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "userAction", - "name": "userActionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "UserAction", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "user", - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "reacter", - "name": "reacterId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "action", - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "Action", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "UserActionReaction", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "value", - "type": { - "gqlType": "JSON", - "isArray": false, - "modifier": null, - "pgAlias": "jsonb", - "pgType": "jsonb", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userActionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionResultId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "user", - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "action", - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "Action", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "userAction", - "name": "userActionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "UserAction", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "actionResult", - "name": "actionResultId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "ActionResult", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "UserActionResult", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userActionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionResultId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionStarted", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "complete", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "verified", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "verifiedDate", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userRating", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "int", - "pgType": "int4", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "rejected", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "rejectedReason", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "location", - "type": { - "gqlType": "GeoJSON", - "isArray": false, - "modifier": 1107460, - "pgAlias": "geometry", - "pgType": "geometry", - "subtype": "GeometryPoint", - "typmod": { - "gisType": "Point", - "hasM": false, - "hasZ": false, - "srid": 4326, - "subtype": 1, - }, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "verifierId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "user", - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "verifier", - "name": "verifierId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "action", - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "Action", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "UserAction", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "income", - "type": { - "gqlType": "BigFloat", - "isArray": false, - "modifier": null, - "pgAlias": "numeric", - "pgType": "numeric", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "gender", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": 5, - "pgAlias": "char", - "pgType": "bpchar", - "subtype": null, - "typmod": { - "modifier": 5, - }, - }, - }, - { - "name": "race", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "age", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "int", - "pgType": "int4", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "dob", - "type": { - "gqlType": "Date", - "isArray": false, - "modifier": null, - "pgAlias": "date", - "pgType": "date", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "education", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "homeOwnership", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "smallint", - "pgType": "int2", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "treeHuggerLevel", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "smallint", - "pgType": "int2", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "diyLevel", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "smallint", - "pgType": "int2", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "gardenerLevel", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "smallint", - "pgType": "int2", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "freeTime", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "smallint", - "pgType": "int2", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "researchToDoer", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "smallint", - "pgType": "int2", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "user", - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "UserCharacteristic", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "accepted", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "requesterId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "responderId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "requester", - "name": "requesterId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "responder", - "name": "responderId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "UserConnection", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "requesterId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "responderId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "vcf", - "type": { - "gqlType": "JSON", - "isArray": false, - "modifier": null, - "pgAlias": "jsonb", - "pgType": "jsonb", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "fullName", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "emails", - "type": { - "gqlType": "[String]", - "isArray": true, - "modifier": null, - "pgAlias": "email", - "pgType": "email", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "device", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "user", - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "UserContact", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "type", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "content", - "type": { - "gqlType": "JSON", - "isArray": false, - "modifier": null, - "pgAlias": "jsonb", - "pgType": "jsonb", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "upload", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "upload", - "pgType": "upload", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "received", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "receiverRead", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "senderReaction", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "receiverReaction", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "senderId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "receiverId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "sender", - "name": "senderId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "receiver", - "name": "receiverId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "UserMessage", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "user", - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "action", - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "Action", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "UserPassAction", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "profilePicture", - "type": { - "gqlType": "JSON", - "isArray": false, - "modifier": null, - "pgAlias": "image", - "pgType": "image", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "bio", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "reputation", - "type": { - "gqlType": "BigFloat", - "isArray": false, - "modifier": null, - "pgAlias": "numeric", - "pgType": "numeric", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "displayName", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "tags", - "type": { - "gqlType": "[String]", - "isArray": true, - "modifier": null, - "pgAlias": "citext", - "pgType": "citext", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "desired", - "type": { - "gqlType": "[String]", - "isArray": true, - "modifier": null, - "pgAlias": "citext", - "pgType": "citext", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "user", - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "UserProfile", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "user", - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "action", - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "Action", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "UserSavedAction", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "firstName", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "lastName", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "searchRadius", - "type": { - "gqlType": "BigFloat", - "isArray": false, - "modifier": null, - "pgAlias": "numeric", - "pgType": "numeric", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "zip", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "int", - "pgType": "int4", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "location", - "type": { - "gqlType": "GeoJSON", - "isArray": false, - "modifier": 1107460, - "pgAlias": "geometry", - "pgType": "geometry", - "subtype": "GeometryPoint", - "typmod": { - "gisType": "Point", - "hasM": false, - "hasZ": false, - "srid": 4326, - "subtype": 1, - }, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "user", - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "UserSetting", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "name", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "count", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "int", - "pgType": "int4", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [], - "name": "UserStep", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "user", - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "action", - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "Action", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "UserViewedAction", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "type", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "int", - "pgType": "int4", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [], - "name": "User", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "zip", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "int", - "pgType": "int4", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "location", - "type": { - "gqlType": "GeoJSON", - "isArray": false, - "modifier": 1107460, - "pgAlias": "geometry", - "pgType": "geometry", - "subtype": "GeometryPoint", - "typmod": { - "gisType": "Point", - "hasM": false, - "hasZ": false, - "srid": 4326, - "subtype": 1, - }, - }, - }, - { - "name": "bbox", - "type": { - "gqlType": "GeoJSON", - "isArray": false, - "modifier": 1107468, - "pgAlias": "geometry", - "pgType": "geometry", - "subtype": "GeometryPolygon", - "typmod": { - "gisType": "Polygon", - "hasM": false, - "hasZ": false, - "srid": 4326, - "subtype": 3, - }, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [], - "name": "ZipCode", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "zip", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "int", - "pgType": "int4", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "ownerId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "service", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "identifier", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "details", - "type": { - "gqlType": "JSON", - "isArray": false, - "modifier": null, - "pgAlias": "jsonb", - "pgType": "jsonb", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "isVerified", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "owner", - "name": "ownerId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "AuthAccount", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "service", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "identifier", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - ], -} -`; diff --git a/packages/data/src/__tests__/query-builder/__tests__/__snapshots__/meta-object.test.ts.snap b/packages/data/src/__tests__/query-builder/__tests__/__snapshots__/meta-object.test.ts.snap deleted file mode 100644 index 9458975..0000000 --- a/packages/data/src/__tests__/query-builder/__tests__/__snapshots__/meta-object.test.ts.snap +++ /dev/null @@ -1,6568 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`convertFromMetaSchema() matches snapshot 1`] = ` -{ - "tables": [ - { - "fields": [ - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "goalId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "ownerId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "action", - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "Action", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "goal", - "name": "goalId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "Goal", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "owner", - "name": "ownerId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "ActionGoal", - "primaryConstraints": [ - { - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "goalId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "name", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "description", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "type", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "itemOrder", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "int", - "pgType": "int4", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "timeRequired", - "type": { - "gqlType": "Interval", - "isArray": false, - "modifier": null, - "pgAlias": "interval", - "pgType": "interval", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "isRequired", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "notificationText", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "embedCode", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "url", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "url", - "pgType": "url", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "media", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "upload", - "pgType": "upload", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "ownerId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "action", - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "Action", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "ActionItem", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "name", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "ownerId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "action", - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "Action", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "owner", - "name": "ownerId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "ActionResult", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "slug", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "citext", - "pgType": "citext", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "photo", - "type": { - "gqlType": "JSON", - "isArray": false, - "modifier": null, - "pgAlias": "image", - "pgType": "image", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "title", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "url", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "url", - "pgType": "url", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "description", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "discoveryHeader", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "discoveryDescription", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "enableNotifications", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "enableNotificationsText", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "search", - "type": { - "gqlType": "FullText", - "isArray": false, - "modifier": null, - "pgAlias": "tsvector", - "pgType": "tsvector", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "location", - "type": { - "gqlType": "GeoJSON", - "isArray": false, - "modifier": 1107460, - "pgAlias": "geometry", - "pgType": "geometry", - "subtype": "GeometryPoint", - "typmod": { - "gisType": "Point", - "hasM": false, - "hasZ": false, - "srid": 4326, - "subtype": 1, - }, - }, - }, - { - "name": "locationRadius", - "type": { - "gqlType": "BigFloat", - "isArray": false, - "modifier": null, - "pgAlias": "numeric", - "pgType": "numeric", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "timeRequired", - "type": { - "gqlType": "Interval", - "isArray": false, - "modifier": null, - "pgAlias": "interval", - "pgType": "interval", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "startDate", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "endDate", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "approved", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "rewardAmount", - "type": { - "gqlType": "BigFloat", - "isArray": false, - "modifier": null, - "pgAlias": "numeric", - "pgType": "numeric", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "activityFeedText", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "callToAction", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "completedActionText", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "alreadyCompletedActionText", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "tags", - "type": { - "gqlType": "[String]", - "isArray": true, - "modifier": null, - "pgAlias": "citext", - "pgType": "citext", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "ownerId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "owner", - "name": "ownerId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "Action", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "slug", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "citext", - "pgType": "citext", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "data", - "type": { - "gqlType": "JSON", - "isArray": false, - "modifier": null, - "pgAlias": "jsonb", - "pgType": "jsonb", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "senderId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "receiverId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "sender", - "name": "senderId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "receiver", - "name": "receiverId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "ClaimedInvite", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "ownerId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "service", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "identifier", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "details", - "type": { - "gqlType": "JSON", - "isArray": false, - "modifier": null, - "pgAlias": "jsonb", - "pgType": "jsonb", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "isVerified", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "owner", - "name": "ownerId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "ConnectedAccount", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "service", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "identifier", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "ownerId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "address", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "isVerified", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "isPrimary", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "owner", - "name": "ownerId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "CryptoAddress", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "address", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "ownerId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "email", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "email", - "pgType": "email", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "isVerified", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "isPrimary", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "owner", - "name": "ownerId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "Email", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "email", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "email", - "pgType": "email", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "audio", - "type": { - "gqlType": "JSON", - "isArray": false, - "modifier": null, - "pgAlias": "attachment", - "pgType": "attachment", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "audioDuration", - "type": { - "gqlType": "Interval", - "isArray": false, - "modifier": null, - "pgAlias": "interval", - "pgType": "interval", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "explanationTitle", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "explanation", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "goalId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "goal", - "name": "goalId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "Goal", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "GoalExplanation", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "name", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "slug", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "citext", - "pgType": "citext", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "shortName", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "icon", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "subHead", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "tags", - "type": { - "gqlType": "[String]", - "isArray": true, - "modifier": null, - "pgAlias": "citext", - "pgType": "citext", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "search", - "type": { - "gqlType": "FullText", - "isArray": false, - "modifier": null, - "pgAlias": "tsvector", - "pgType": "tsvector", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [], - "name": "Goal", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "name", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "email", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "email", - "pgType": "email", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "senderId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "inviteToken", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "inviteValid", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "inviteLimit", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "int", - "pgType": "int4", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "inviteCount", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "int", - "pgType": "int4", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "multiple", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "data", - "type": { - "gqlType": "JSON", - "isArray": false, - "modifier": null, - "pgAlias": "jsonb", - "pgType": "jsonb", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "expiresAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "sender", - "name": "senderId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "Invite", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "email", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "email", - "pgType": "email", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "senderId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "name", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "level", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "requiredCount", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "int", - "pgType": "int4", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "priority", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "int", - "pgType": "int4", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [], - "name": "LevelRequirement", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "name", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "level", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "name", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [], - "name": "Level", - "primaryConstraints": [ - { - "name": "name", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "name", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "memberIds", - "type": { - "gqlType": "[UUID]", - "isArray": true, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [], - "name": "MessageGroup", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "type", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "content", - "type": { - "gqlType": "JSON", - "isArray": false, - "modifier": null, - "pgAlias": "jsonb", - "pgType": "jsonb", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "upload", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "upload", - "pgType": "upload", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "senderId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "groupId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "sender", - "name": "senderId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "group", - "name": "groupId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "MessageGroup", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "Message", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "name", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "description", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "link", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "url", - "pgType": "url", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "publishedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "photo", - "type": { - "gqlType": "JSON", - "isArray": false, - "modifier": null, - "pgAlias": "image", - "pgType": "image", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [], - "name": "NewsUpdate", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "name", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "profilePicture", - "type": { - "gqlType": "JSON", - "isArray": false, - "modifier": null, - "pgAlias": "image", - "pgType": "image", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "description", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "website", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "url", - "pgType": "url", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "reputation", - "type": { - "gqlType": "BigFloat", - "isArray": false, - "modifier": null, - "pgAlias": "numeric", - "pgType": "numeric", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "tags", - "type": { - "gqlType": "[String]", - "isArray": true, - "modifier": null, - "pgAlias": "citext", - "pgType": "citext", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "organizationId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "organization", - "name": "organizationId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "OrganizationProfile", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "organizationId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "ownerId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "cc", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "number", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "isVerified", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "isPrimary", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "owner", - "name": "ownerId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "PhoneNumber", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "number", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "name", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "count", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "int", - "pgType": "int4", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [], - "name": "UserAchievement", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "name", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "value", - "type": { - "gqlType": "JSON", - "isArray": false, - "modifier": null, - "pgAlias": "jsonb", - "pgType": "jsonb", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "status", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userActionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionItemId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "user", - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "action", - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "Action", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "userAction", - "name": "userActionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "UserAction", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "actionItem", - "name": "actionItemId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "ActionItem", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "UserActionItem", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userActionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionItemId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userActionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "reacterId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "userAction", - "name": "userActionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "UserAction", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "user", - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "reacter", - "name": "reacterId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "action", - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "Action", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "UserActionReaction", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "value", - "type": { - "gqlType": "JSON", - "isArray": false, - "modifier": null, - "pgAlias": "jsonb", - "pgType": "jsonb", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userActionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionResultId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "user", - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "action", - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "Action", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "userAction", - "name": "userActionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "UserAction", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "actionResult", - "name": "actionResultId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "ActionResult", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "UserActionResult", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userActionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionResultId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionStarted", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "complete", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "verified", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "verifiedDate", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userRating", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "int", - "pgType": "int4", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "rejected", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "rejectedReason", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "location", - "type": { - "gqlType": "GeoJSON", - "isArray": false, - "modifier": 1107460, - "pgAlias": "geometry", - "pgType": "geometry", - "subtype": "GeometryPoint", - "typmod": { - "gisType": "Point", - "hasM": false, - "hasZ": false, - "srid": 4326, - "subtype": 1, - }, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "verifierId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "user", - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "verifier", - "name": "verifierId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "action", - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "Action", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "UserAction", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "income", - "type": { - "gqlType": "BigFloat", - "isArray": false, - "modifier": null, - "pgAlias": "numeric", - "pgType": "numeric", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "gender", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": 5, - "pgAlias": "char", - "pgType": "bpchar", - "subtype": null, - "typmod": { - "modifier": 5, - }, - }, - }, - { - "name": "race", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "age", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "int", - "pgType": "int4", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "dob", - "type": { - "gqlType": "Date", - "isArray": false, - "modifier": null, - "pgAlias": "date", - "pgType": "date", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "education", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "homeOwnership", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "smallint", - "pgType": "int2", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "treeHuggerLevel", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "smallint", - "pgType": "int2", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "diyLevel", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "smallint", - "pgType": "int2", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "gardenerLevel", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "smallint", - "pgType": "int2", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "freeTime", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "smallint", - "pgType": "int2", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "researchToDoer", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "smallint", - "pgType": "int2", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "user", - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "UserCharacteristic", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "accepted", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "requesterId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "responderId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "requester", - "name": "requesterId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "responder", - "name": "responderId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "UserConnection", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "requesterId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "responderId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "vcf", - "type": { - "gqlType": "JSON", - "isArray": false, - "modifier": null, - "pgAlias": "jsonb", - "pgType": "jsonb", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "fullName", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "emails", - "type": { - "gqlType": "[String]", - "isArray": true, - "modifier": null, - "pgAlias": "email", - "pgType": "email", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "device", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "user", - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "UserContact", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "type", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "content", - "type": { - "gqlType": "JSON", - "isArray": false, - "modifier": null, - "pgAlias": "jsonb", - "pgType": "jsonb", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "upload", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "upload", - "pgType": "upload", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "received", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "receiverRead", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "senderReaction", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "receiverReaction", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "senderId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "receiverId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "sender", - "name": "senderId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "receiver", - "name": "receiverId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "UserMessage", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "user", - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "action", - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "Action", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "UserPassAction", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "profilePicture", - "type": { - "gqlType": "JSON", - "isArray": false, - "modifier": null, - "pgAlias": "image", - "pgType": "image", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "bio", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "reputation", - "type": { - "gqlType": "BigFloat", - "isArray": false, - "modifier": null, - "pgAlias": "numeric", - "pgType": "numeric", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "displayName", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "tags", - "type": { - "gqlType": "[String]", - "isArray": true, - "modifier": null, - "pgAlias": "citext", - "pgType": "citext", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "desired", - "type": { - "gqlType": "[String]", - "isArray": true, - "modifier": null, - "pgAlias": "citext", - "pgType": "citext", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "user", - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "UserProfile", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "user", - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "action", - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "Action", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "UserSavedAction", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "firstName", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "lastName", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "searchRadius", - "type": { - "gqlType": "BigFloat", - "isArray": false, - "modifier": null, - "pgAlias": "numeric", - "pgType": "numeric", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "zip", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "int", - "pgType": "int4", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "location", - "type": { - "gqlType": "GeoJSON", - "isArray": false, - "modifier": 1107460, - "pgAlias": "geometry", - "pgType": "geometry", - "subtype": "GeometryPoint", - "typmod": { - "gisType": "Point", - "hasM": false, - "hasZ": false, - "srid": 4326, - "subtype": 1, - }, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "user", - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "UserSetting", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "name", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "count", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "int", - "pgType": "int4", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [], - "name": "UserStep", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "user", - "name": "userId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - { - "fromKey": { - "alias": "action", - "name": "actionId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "Action", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "UserViewedAction", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "type", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "int", - "pgType": "int4", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [], - "name": "User", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "zip", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "int", - "pgType": "int4", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "location", - "type": { - "gqlType": "GeoJSON", - "isArray": false, - "modifier": 1107460, - "pgAlias": "geometry", - "pgType": "geometry", - "subtype": "GeometryPoint", - "typmod": { - "gisType": "Point", - "hasM": false, - "hasZ": false, - "srid": 4326, - "subtype": 1, - }, - }, - }, - { - "name": "bbox", - "type": { - "gqlType": "GeoJSON", - "isArray": false, - "modifier": 1107468, - "pgAlias": "geometry", - "pgType": "geometry", - "subtype": "GeometryPolygon", - "typmod": { - "gisType": "Polygon", - "hasM": false, - "hasZ": false, - "srid": 4326, - "subtype": 3, - }, - }, - }, - { - "name": "createdBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedBy", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "createdAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "updatedAt", - "type": { - "gqlType": "Datetime", - "isArray": false, - "modifier": null, - "pgAlias": "timestamptz", - "pgType": "timestamptz", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [], - "name": "ZipCode", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "zip", - "type": { - "gqlType": "Int", - "isArray": false, - "modifier": null, - "pgAlias": "int", - "pgType": "int4", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - { - "fields": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "ownerId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "service", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "identifier", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "details", - "type": { - "gqlType": "JSON", - "isArray": false, - "modifier": null, - "pgAlias": "jsonb", - "pgType": "jsonb", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "isVerified", - "type": { - "gqlType": "Boolean", - "isArray": false, - "modifier": null, - "pgAlias": "boolean", - "pgType": "bool", - "subtype": null, - "typmod": null, - }, - }, - ], - "foreignConstraints": [ - { - "fromKey": { - "alias": "owner", - "name": "ownerId", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - "refTable": "User", - "toKey": { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - }, - ], - "name": "AuthAccount", - "primaryConstraints": [ - { - "name": "id", - "type": { - "gqlType": "UUID", - "isArray": false, - "modifier": null, - "pgAlias": "uuid", - "pgType": "uuid", - "subtype": null, - "typmod": null, - }, - }, - ], - "uniqueConstraints": [ - { - "name": "service", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - { - "name": "identifier", - "type": { - "gqlType": "String", - "isArray": false, - "modifier": null, - "pgAlias": "text", - "pgType": "text", - "subtype": null, - "typmod": null, - }, - }, - ], - }, - ], -} -`; diff --git a/packages/data/src/__tests__/query-builder/__tests__/meta-object.node.test.ts b/packages/data/src/__tests__/query-builder/__tests__/meta-object.node.test.ts index 07a5fbe..4f4825f 100644 --- a/packages/data/src/__tests__/query-builder/__tests__/meta-object.node.test.ts +++ b/packages/data/src/__tests__/query-builder/__tests__/meta-object.node.test.ts @@ -9,8 +9,4 @@ describe('convertFromMetaSchema()', () => { const validate = validateMetaObject(metaObj); expect(validate).toBe(true); }); - - it('matches snapshot', () => { - expect(convertFromMetaSchema(apiMetaSchema as any)).toMatchSnapshot(); - }); }); diff --git a/packages/data/src/__tests__/query-generator.test.ts b/packages/data/src/__tests__/query-generator.test.ts index 9a387a0..1432fd6 100644 --- a/packages/data/src/__tests__/query-generator.test.ts +++ b/packages/data/src/__tests__/query-generator.test.ts @@ -12,7 +12,6 @@ import { buildPostGraphileDelete, buildPostGraphileUpdate, buildSelect, - createASTQueryBuilder, toCamelCasePlural, toCamelCaseSingular, toPatchFieldName, @@ -359,13 +358,6 @@ describe("query-generator", () => { }); }); - describe("createASTQueryBuilder", () => { - it("creates builder with tables", () => { - expect(createASTQueryBuilder(allTables)).toBeDefined(); - expect(createASTQueryBuilder([testTable])).toBeDefined(); - }); - }); - describe("edge cases", () => { it("handles empty fields", () => { const emptyTable = createCleanTable("empty"); diff --git a/packages/schema-builder/src/__tests__/schema-builder-layout-motion.test.tsx b/packages/schema-builder/src/__tests__/schema-builder-layout-motion.test.tsx deleted file mode 100644 index cc86f0f..0000000 --- a/packages/schema-builder/src/__tests__/schema-builder-layout-motion.test.tsx +++ /dev/null @@ -1,392 +0,0 @@ -import { cleanup, fireEvent, render, screen, within } from '@testing-library/react'; -import { MotionConfig } from 'motion/react'; -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; -import type { ReactNode } from 'react'; - -const schemaMocks = vi.hoisted(() => ({ - currentSchema: null as Record | null, - currentTable: null as Record | null, - draggedType: { - configurable: {}, - defaultConstraints: { nullable: true }, - description: 'Plain text', - label: 'Text', - type: 'text' - }, - panelExpanded: true, - pushCard: vi.fn(), - sections: { app: true, system: false }, - selectTable: vi.fn(), - selectedTableId: 'app-table', - togglePanel: vi.fn(), - toggleSidebarSection: vi.fn() -})); - -vi.mock('motion/react', async () => { - const React = await vi.importActual('react'); - const ReducedMotionContext = React.createContext('never'); - - type MotionDivProps = React.HTMLAttributes & { - animate?: Record; - exit?: Record; - initial?: false | Record; - transition?: Record; - }; - - const MotionDiv = React.forwardRef( - ({ animate, exit, initial, style, transition, ...props }, ref) => { - const reducedMotion = React.useContext(ReducedMotionContext); - - return React.createElement('div', { - ...props, - ref, - style, - 'data-motion-animate': JSON.stringify(animate), - 'data-motion-exit': JSON.stringify(exit), - 'data-motion-initial': JSON.stringify(initial), - 'data-motion-reduced-motion': reducedMotion, - 'data-motion-style': JSON.stringify(style), - 'data-motion-transition': JSON.stringify(transition) - }); - } - ); - - return { - AnimatePresence: ({ children }: { children: ReactNode }) => children, - MotionConfig: ({ children, reducedMotion = 'never' }: { children: ReactNode; reducedMotion?: string }) => - React.createElement(ReducedMotionContext.Provider, { value: reducedMotion }, children), - motion: { div: MotionDiv } - }; -}); - -vi.mock('@constructive-io/ui/button', async () => { - const React = await vi.importActual('react'); - - return { - Button: React.forwardRef< - HTMLButtonElement, - React.ButtonHTMLAttributes & { size?: string; variant?: string } - >(({ children, size: _size, variant: _variant, ...props }, ref) => ( - - )) - }; -}); - -vi.mock('@constructive-io/ui/tooltip', async () => { - const React = await vi.importActual('react'); - - return { - Tooltip: ({ children }: { children: ReactNode }) => children, - TooltipContent: ({ children }: { children: ReactNode }) =>
{children}
, - TooltipProvider: ({ children }: { children: ReactNode }) => children, - TooltipTrigger: ({ children }: { children: ReactNode }) => - React.isValidElement(children) ? children : <>{children} - }; -}); - -vi.mock('@constructive-io/ui/dropdown-menu', async () => { - const React = await vi.importActual('react'); - - return { - DropdownMenu: ({ children }: { children: ReactNode }) => children, - DropdownMenuContent: ({ children }: { children: ReactNode }) =>
{children}
, - DropdownMenuItem: ({ children, ...props }: React.ButtonHTMLAttributes) => ( - - ), - DropdownMenuTrigger: ({ children }: { children: ReactNode }) => - React.isValidElement(children) ? children : <>{children} - }; -}); - -vi.mock('@constructive-io/ui/progressive-blur', () => ({ - ProgressiveBlur: ({ position }: { position: string }) =>
-})); - -vi.mock('@constructive-io/ui/stack', () => ({ - useCardStack: () => ({ push: schemaMocks.pushCard }) -})); - -vi.mock('@dnd-kit/core', () => ({ - DndContext: ({ children }: { children: ReactNode }) =>
{children}
, - DragOverlay: ({ children }: { children: ReactNode }) =>
{children}
, - useDraggable: ({ id }: { id: string }) => ({ - attributes: { 'aria-describedby': `${id}-instructions`, role: 'button', tabIndex: 0 }, - isDragging: false, - listeners: {}, - setNodeRef: vi.fn(), - transform: null - }) -})); - -vi.mock('@/blocks/schema/schema-builder-core/lib/gql/hooks/schema-builder', () => ({ - useSchemaBuilderSelectors: () => ({ - currentSchema: schemaMocks.currentSchema, - currentTable: schemaMocks.currentTable, - selectTable: schemaMocks.selectTable, - selectedTableId: schemaMocks.selectedTableId - }) -})); - -vi.mock('@/blocks/schema/schema-builder-core/store/app-store', () => ({ - useSidebarSectionActions: () => ({ toggleSidebarSection: schemaMocks.toggleSidebarSection }), - useSidebarSections: () => schemaMocks.sections, - useToggleTypesLibraryExpanded: () => schemaMocks.togglePanel, - useTypesLibraryExpanded: () => schemaMocks.panelExpanded -})); - -vi.mock('../schema/schema-builder-fields/components/table-editor/use-field-dnd', () => ({ - DEFAULT_DROP_ANIMATION: {}, - useFieldDnD: () => ({ - activeId: 'field-type-text', - customCollisionDetection: vi.fn(), - dragSource: 'panel', - draggedType: schemaMocks.draggedType, - handleAddFieldRef: vi.fn(), - handleDragCancel: vi.fn(), - handleDragEnd: vi.fn(), - handleDragStart: vi.fn(), - sensors: [] - }) -})); - -vi.mock('../schema/schema-builder-fields/components/table-editor/fields-section', () => ({ - FieldsSection: () =>
Field editor dropzone
-})); - -vi.mock('@/blocks/schema/schema-builder-tables/components/table-editor/table-metadata-section', () => ({ - TableMetadataSection: () =>
Field metadata
-})); - -vi.mock('@/blocks/schema/schema-builder-tables/components/table-editor/no-table-selected-view', () => ({ - NoTableSelectedView: () =>
No table selected
-})); - -vi.mock('../schema/schema-builder-tables/components/tables', () => ({ - CreateTableCard: () => null, - DeleteTableDialog: () => null -})); - -import { TableEditor } from '../schema/schema-builder-fields/components/table-editor/table-editor'; -import { TypesLibrary } from '../schema/schema-builder-fields/components/table-editor/types-library'; -import { - SchemaBuilderSidebar, - getScrollAffordanceState -} from '../schema/schema-builder-tables/components/schemas/schema-builder-sidebar'; - -const APP_TABLE = { - category: 'APP', - fields: [], - id: 'app-table', - name: 'posts' -}; - -const SYSTEM_TABLE = { - category: 'CORE', - fields: [], - id: 'system-table', - name: 'internal_jobs' -}; - -const LAYOUT_PROPERTIES = ['bottom', 'height', 'left', 'margin', 'padding', 'paddingRight', 'right', 'top', 'width']; - -class ResizeObserverStub { - observe() {} - unobserve() {} - disconnect() {} -} - -function renderWithMotion(children: ReactNode) { - return render({children}); -} - -function parseMotionValue(element: Element, attribute: string): unknown { - const value = element.getAttribute(attribute); - return value === null || value === undefined ? undefined : JSON.parse(value); -} - -function activateButtonWithEnter(button: HTMLElement) { - button.focus(); - fireEvent.keyDown(button, { code: 'Enter', key: 'Enter' }); - // jsdom does not synthesize the native button click generated by Enter. - fireEvent.click(button, { detail: 0 }); - fireEvent.keyUp(button, { code: 'Enter', key: 'Enter' }); -} - -function expectTransformOnlyMotion(container: ParentNode) { - const motionElements = [...container.querySelectorAll('[data-motion-animate]')]; - expect(motionElements.length).toBeGreaterThan(0); - - motionElements.forEach((element) => { - ['data-motion-initial', 'data-motion-animate', 'data-motion-exit', 'data-motion-transition'].forEach( - (attribute) => { - const value = parseMotionValue(element, attribute); - if (!value || typeof value !== 'object' || Array.isArray(value)) return; - expect(Object.keys(value as Record)).not.toEqual(expect.arrayContaining(LAYOUT_PROPERTIES)); - } - ); - expect(element.getAttribute('data-motion-reduced-motion')).toBe('user'); - }); -} - -beforeEach(() => { - schemaMocks.currentTable = APP_TABLE; - schemaMocks.currentSchema = { - id: 'schema-1', - name: 'public', - relationships: [], - tables: [APP_TABLE, SYSTEM_TABLE], - version: '1' - }; - schemaMocks.panelExpanded = true; - schemaMocks.sections = { app: true, system: false }; - schemaMocks.selectedTableId = APP_TABLE.id; - schemaMocks.pushCard.mockReset(); - schemaMocks.selectTable.mockReset(); - schemaMocks.togglePanel.mockReset(); - schemaMocks.toggleSidebarSection.mockReset(); - - vi.stubGlobal('ResizeObserver', ResizeObserverStub); - vi.stubGlobal('requestAnimationFrame', vi.fn(() => 1)); - vi.stubGlobal('cancelAnimationFrame', vi.fn()); -}); - -afterEach(() => { - cleanup(); - vi.restoreAllMocks(); - vi.unstubAllGlobals(); -}); - -describe('schema builder layout-safe motion', () => { - it('commits table editor panel geometry while preserving crossfades, toggles, and DnD content', () => { - const view = renderWithMotion(); - const editor = view.container.querySelector("[data-chat-component='table-editor']")!; - const [mainContent, panel] = [...editor.children] as HTMLElement[]; - - expect(mainContent.style.paddingRight).toBe('380px'); - expect(mainContent.hasAttribute('data-motion-animate')).toBe(false); - expect(panel.style.width).toBe('380px'); - expect(panel.dataset.state).toBe('expanded'); - expect(panel.hasAttribute('data-motion-animate')).toBe(false); - expect(screen.getByText('Field metadata')).toBeTruthy(); - expect(screen.getByTestId('fields-section').textContent).toContain('Field editor dropzone'); - expect(within(screen.getByTestId('drag-overlay')).getByText('Text')).toBeTruthy(); - - const crossfadeLayers = [...panel.querySelectorAll(':scope [data-motion-animate]')].slice(0, 2); - expect(crossfadeLayers.map((layer) => parseMotionValue(layer, 'data-motion-animate'))).toEqual([ - { opacity: 0 }, - { opacity: 1 } - ]); - expect(crossfadeLayers.map((layer) => layer.style.pointerEvents)).toEqual(['none', 'auto']); - - fireEvent.click(screen.getByRole('button', { name: 'Collapse panel' })); - expect(schemaMocks.togglePanel).toHaveBeenCalledTimes(1); - schemaMocks.panelExpanded = false; - view.rerender(); - - const collapsedEditor = view.container.querySelector("[data-chat-component='table-editor']")!; - const [collapsedMain, collapsedPanel] = [...collapsedEditor.children] as HTMLElement[]; - expect(collapsedMain.style.paddingRight).toBe('56px'); - expect(collapsedPanel.style.width).toBe('56px'); - expect(collapsedPanel.dataset.state).toBe('collapsed'); - expect(screen.getAllByRole('button', { name: 'Expand panel' })).toHaveLength(2); - expectTransformOnlyMotion(view.container); - - schemaMocks.currentTable = null; - view.rerender(); - const emptyEditor = view.container.querySelector("[data-chat-component='table-editor']")!; - expect((emptyEditor.firstElementChild as HTMLElement).style.paddingRight).toBe('0px'); - expect(emptyEditor.querySelector('[data-state]')).toBeNull(); - expect(screen.getByText('No table selected')).toBeTruthy(); - }); - - it('toggles type sections through the existing button semantics without layout animation', () => { - const view = renderWithMotion(); - const basicTrigger = screen.getByRole('button', { name: /Basic/ }); - const advancedTrigger = screen.getByRole('button', { name: /Advanced/ }); - - expect(basicTrigger.getAttribute('aria-expanded')).toBe('true'); - expect(advancedTrigger.getAttribute('aria-expanded')).toBe('false'); - expect(screen.getByText('Text', { selector: 'span' })).toBeTruthy(); - expect(view.container.querySelectorAll('[data-motion-animate][class*="overflow-hidden"]')).toHaveLength(1); - expect(view.container.querySelector('.overflow-auto')).toBeTruthy(); - - activateButtonWithEnter(advancedTrigger); - - expect(document.activeElement).toBe(advancedTrigger); - expect(advancedTrigger.getAttribute('aria-expanded')).toBe('true'); - expect(view.container.querySelectorAll('[data-motion-animate][class*="overflow-hidden"]')).toHaveLength(2); - - fireEvent.click(basicTrigger); - fireEvent.click(basicTrigger); - expect(basicTrigger.getAttribute('aria-expanded')).toBe('true'); - - const contentLayers = [...view.container.querySelectorAll('[data-motion-animate][class*="overflow-hidden"]')]; - contentLayers.forEach((layer) => { - expect(parseMotionValue(layer, 'data-motion-initial')).toEqual({ opacity: 0, y: -4 }); - expect(parseMotionValue(layer, 'data-motion-animate')).toEqual({ opacity: 1, y: 0 }); - expect(parseMotionValue(layer, 'data-motion-exit')).toEqual({ opacity: 0, y: -4 }); - }); - expectTransformOnlyMotion(view.container); - }); - - it('preserves sidebar presence, selected indicators, rapid reversal, and scroll affordances', () => { - const view = renderWithMotion(); - const appTrigger = screen.getByRole('button', { name: /Your Tables/ }); - const systemTrigger = screen.getByRole('button', { name: /System Tables/ }); - - expect(appTrigger.getAttribute('aria-expanded')).toBe('true'); - expect(systemTrigger.getAttribute('aria-expanded')).toBe('false'); - expect(screen.getByTestId('collapsible-content-Your Tables')).toBeTruthy(); - expect(screen.queryByTestId('collapsible-content-System Tables')).toBeNull(); - - activateButtonWithEnter(appTrigger); - expect(schemaMocks.toggleSidebarSection).toHaveBeenCalledWith('app'); - expect(document.activeElement).toBe(appTrigger); - - schemaMocks.sections = { app: false, system: false }; - view.rerender(); - expect(screen.queryByTestId('collapsible-content-Your Tables')).toBeNull(); - expect(screen.getByLabelText('Contains selected item')).toBeTruthy(); - - schemaMocks.sections = { app: true, system: false }; - view.rerender(); - schemaMocks.sections = { app: false, system: false }; - view.rerender(); - schemaMocks.sections = { app: true, system: true }; - view.rerender(); - - expect(screen.getByTestId('collapsible-content-Your Tables')).toBeTruthy(); - expect(screen.getByTestId('collapsible-content-System Tables')).toBeTruthy(); - expect(screen.queryByLabelText('Contains selected item')).toBeNull(); - - const viewport = screen - .getByTestId('collapsible-content-Your Tables') - .querySelector('.scrollbar-neutral-thin')!; - Object.defineProperties(viewport, { - clientHeight: { configurable: true, value: 100 }, - scrollHeight: { configurable: true, value: 400 }, - scrollTop: { configurable: true, value: 100, writable: true } - }); - fireEvent.scroll(viewport); - expect(screen.getByTestId('progressive-blur-top')).toBeTruthy(); - expect(screen.getByTestId('progressive-blur-bottom')).toBeTruthy(); - - expect(getScrollAffordanceState({ clientHeight: 100, scrollHeight: 400, scrollTop: 300 })).toEqual({ - hasOverflow: true, - showBottomBlur: false, - showTopBlur: true - }); - - const contentLayers = [...view.container.querySelectorAll('[data-testid^="collapsible-content-"]')]; - contentLayers.forEach((layer) => { - expect(parseMotionValue(layer, 'data-motion-initial')).toEqual({ opacity: 0, y: -4 }); - expect(parseMotionValue(layer, 'data-motion-animate')).toEqual({ opacity: 1, y: 0 }); - expect(parseMotionValue(layer, 'data-motion-exit')).toEqual({ opacity: 0, y: -4 }); - }); - expectTransformOnlyMotion(view.container); - }); -}); diff --git a/packages/schema-builder/src/__tests__/schema-builder-render.test.tsx b/packages/schema-builder/src/__tests__/schema-builder-render.test.tsx index 6379b05..6d9d615 100644 --- a/packages/schema-builder/src/__tests__/schema-builder-render.test.tsx +++ b/packages/schema-builder/src/__tests__/schema-builder-render.test.tsx @@ -1,135 +1,58 @@ -import { act, cleanup, render, screen } from '@testing-library/react'; +import { cleanup, render } from '@testing-library/react'; import { afterEach, describe, expect, it, vi } from 'vitest'; import type { ReactNode } from 'react'; import { createNoopSchemaBuilderAdapter } from '../testing'; -import { DEFAULT_SCHEMA_BUILDER_PREFERENCES, type SchemaBuilderProps } from '../types'; +import { DEFAULT_SCHEMA_BUILDER_PREFERENCES } from '../types'; vi.mock('@constructive-io/ui/stack', () => ({ CardStackProvider: ({ children }: { children: ReactNode }) => children, - CardStackViewport: () => null + CardStackViewport: () => null, })); - vi.mock('../schema/schema-builder/components/client-only', () => ({ - ClientOnly: ({ children }: { children: ReactNode }) => children + ClientOnly: ({ children }: { children: ReactNode }) => children, })); - vi.mock('../schema/schema-builder-core/lib/gql/hooks/schema-builder', () => ({ - SchemaBuilderDataProvider: ({ children }: { children: ReactNode }) => children + SchemaBuilderDataProvider: ({ children }: { children: ReactNode }) => children, +})); +vi.mock('../schema/schema-builder/components/schemas/schemas-route', () => ({ + SchemasRoute: () => null, })); - -vi.mock('../schema/schema-builder/components/schemas/schemas-route', async () => { - const { useReducedMotionConfig } = await vi.importActual('motion/react'); - - return { - SchemasRoute: ({ emptyState }: { emptyState?: ReactNode }) => { - const shouldReduceMotion = useReducedMotionConfig(); - - return ( -
- {emptyState} -
- ); - } - }; -}); import { SchemaBuilder } from '../components/schema-builder'; afterEach(cleanup); -let prefersReducedMotion = false; -const reducedMotionListeners = new Set(); -const reducedMotionQuery = { - get matches() { - return prefersReducedMotion; - }, - media: '(prefers-reduced-motion)', - onchange: null, - addEventListener: (_type: string, listener: EventListenerOrEventListenerObject) => { - reducedMotionListeners.add(listener); - }, - removeEventListener: (_type: string, listener: EventListenerOrEventListenerObject) => { - reducedMotionListeners.delete(listener); - }, - addListener: vi.fn(), - removeListener: vi.fn(), - dispatchEvent: vi.fn(() => true) -} as MediaQueryList; - -Object.defineProperty(window, 'matchMedia', { - configurable: true, - value: vi.fn(() => reducedMotionQuery) -}); - -function setReducedMotionPreference(matches: boolean) { - prefersReducedMotion = matches; - const event = { matches, media: reducedMotionQuery.media } as MediaQueryListEvent; - reducedMotionListeners.forEach((listener) => { - if (typeof listener === 'function') listener(event); - else listener.handleEvent(event); - }); -} - -function renderSchemaBuilder(overrides: Partial = {}) { - return render( - No database selected by this host

} - {...overrides} - /> - ); -} - describe('SchemaBuilder host boundary', () => { - it('forwards the host empty state to the schemas route', () => { - renderSchemaBuilder(); - - expect(screen.getByTestId('schemas-route-boundary')).toBeTruthy(); - expect(screen.getByText('No database selected by this host')).toBeTruthy(); - }); - - it('passes the user motion preference to package descendants', () => { - setReducedMotionPreference(false); - const firstRender = renderSchemaBuilder(); - expect(screen.getByTestId('schemas-route-boundary').getAttribute('data-reduced-motion')).toBe('false'); - - firstRender.unmount(); - act(() => setReducedMotionPreference(true)); - renderSchemaBuilder(); - - expect(screen.getByTestId('schemas-route-boundary').getAttribute('data-reduced-motion')).toBe('true'); - }); - it('rejects host data from a different database scope', () => { expect(() => - renderSchemaBuilder({ - dataState: { - availableSchemas: [], - routeOrgId: 'org-1', - routeDatabaseId: 'db-other', - selectedSchemaKey: '', - currentSchemaInfo: null, - currentSchema: null, - currentTable: null, - selectedTableId: null, - hasResolvedDatabaseLookup: true, - isLoading: false, - isFetching: false, - error: null, - refetch: vi.fn(async () => undefined), - selectTable: vi.fn() - } - }) + render( + undefined), + selectTable: vi.fn(), + }} + />, + ), ).toThrow(/dataState must match/); }); }); diff --git a/packages/schema-builder/src/__tests__/schema-builder-selectors.test.tsx b/packages/schema-builder/src/__tests__/schema-builder-selectors.test.tsx index fc37ed0..2858563 100644 --- a/packages/schema-builder/src/__tests__/schema-builder-selectors.test.tsx +++ b/packages/schema-builder/src/__tests__/schema-builder-selectors.test.tsx @@ -1,4 +1,4 @@ -import { memo, type ReactNode } from 'react'; +import type { ReactNode } from 'react'; import { act, cleanup, render, screen } from '@testing-library/react'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; @@ -71,7 +71,6 @@ const onPreferencesChange = vi.fn(); const onActiveTabChange = vi.fn(); let latestSelectors: UseSchemaBuilderSelectorsResult | null = null; -let dataConsumerRenderCount = 0; function SelectorProbe() { const selectors = useSchemaBuilderSelectors(); @@ -85,41 +84,30 @@ function SelectorProbe() { ); } -const StableDataConsumer = memo(function StableDataConsumer() { - dataConsumerRenderCount += 1; - const availableSchemas = useSchemaBuilderDataSelector((state) => state.availableSchemas); - return {availableSchemas.length}; -}); - function TestTree({ - unrelatedValue, onSelectedTableChange, children }: { - unrelatedValue: number; onSelectedTableChange: (selection: { tableId: string | null; tableName: string | null }) => void; children?: ReactNode; }) { return ( -
- - - - - {children} - - -
+ + + + {children} + + ); } @@ -130,7 +118,6 @@ function currentSelectors() { beforeEach(() => { latestSelectors = null; - dataConsumerRenderCount = 0; dataMocks.accessibleDatabaseCalls = 0; dataMocks.refetchDatabases.mockClear(); onPreferencesChange.mockClear(); @@ -174,49 +161,9 @@ describe('schema builder selector identities', () => { expect(dataMocks.accessibleDatabaseCalls).toBe(0); }); - it('keeps actions stable across unrelated rerenders and changes only real dependents', () => { - const firstSelectionHandler = vi.fn(); - const view = render( - - ); - const first = currentSelectors(); - - view.rerender( - - ); - const afterUnrelatedRender = currentSelectors(); - - expect(afterUnrelatedRender.selectOrg).toBe(first.selectOrg); - expect(afterUnrelatedRender.selectSchema).toBe(first.selectSchema); - expect(afterUnrelatedRender.selectTable).toBe(first.selectTable); - expect(afterUnrelatedRender.selectField).toBe(first.selectField); - expect(afterUnrelatedRender.clearAllSelections).toBe(first.clearAllSelections); - expect(afterUnrelatedRender.setActiveTab).toBe(first.setActiveTab); - expect(afterUnrelatedRender.setCurrentDatabaseApi).toBe(first.setCurrentDatabaseApi); - expect(afterUnrelatedRender.isCustomSchema).toBe(first.isCustomSchema); - expect(afterUnrelatedRender.getSchemaByKey).toBe(first.getSchemaByKey); - expect(afterUnrelatedRender.customSchemas).toBe(first.customSchemas); - expect(dataConsumerRenderCount).toBe(1); - - const nextSelectionHandler = vi.fn(); - view.rerender( - - ); - const afterDependencyChange = currentSelectors(); - - expect(afterDependencyChange.selectTable).not.toBe(first.selectTable); - expect(afterDependencyChange.clearAllSelections).not.toBe(first.clearAllSelections); - expect(afterDependencyChange.selectField).toBe(first.selectField); - expect(afterDependencyChange.setCurrentDatabaseApi).toBe(first.setCurrentDatabaseApi); - expect(afterDependencyChange.selectOrg).toBe(first.selectOrg); - expect(afterDependencyChange.selectSchema).toBe(first.selectSchema); - expect(afterDependencyChange.setActiveTab).toBe(first.setActiveTab); - expect(dataConsumerRenderCount).toBe(1); - }); - it('preserves field clearing for table selection and clearing all selections', () => { const onSelectedTableChange = vi.fn(); - render(); + render(); act(() => currentSelectors().selectField('field-1')); expect(screen.getByTestId('selected-field').textContent).toBe('field-1'); @@ -237,6 +184,5 @@ describe('schema builder selector identities', () => { tableId: null, tableName: null }); - expect(dataConsumerRenderCount).toBe(1); }); }); diff --git a/packages/schema-builder/src/schema/schema-builder-policies/components/tables/create-table-card.test.tsx b/packages/schema-builder/src/schema/schema-builder-policies/components/tables/create-table-card.test.tsx deleted file mode 100644 index cfb79c3..0000000 --- a/packages/schema-builder/src/schema/schema-builder-policies/components/tables/create-table-card.test.tsx +++ /dev/null @@ -1,257 +0,0 @@ -import { StrictMode, type ComponentProps, type PropsWithChildren } from 'react'; -import { cleanup, fireEvent, render, screen } from '@testing-library/react'; -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; - -const scrollHarness = vi.hoisted(() => ({ - key: 'first', - withViewport: true, - initialScrollTop: 0, - add: vi.fn(), - remove: vi.fn(), -})); - -const dependencyMocks = vi.hoisted(() => ({ - close: vi.fn(), - push: vi.fn(() => 'child-card'), - stack: { - has: vi.fn(() => false), - dismiss: vi.fn(), - updateProps: vi.fn(), - }, - mutateAsync: vi.fn(), - createTableWithPolicies: vi.fn(), - updateDefaults: vi.fn(), - getEnabledOperations: vi.fn(() => []), -})); - -vi.mock('@constructive-io/ui/scroll-area', async () => { - const React = await import('react'); - const patchedViewports = new WeakSet(); - - const ScrollArea = React.forwardRef>( - ({ children }, forwardedRef) => { - const nodeKey = scrollHarness.key; - const withViewport = scrollHarness.withViewport; - const attach = React.useCallback( - (node: HTMLDivElement | null) => { - if (!node) return; - const viewport = node.querySelector('[data-slot="scroll-area-viewport"]'); - if (viewport) { - viewport.scrollTop = scrollHarness.initialScrollTop; - if (!patchedViewports.has(viewport)) { - patchedViewports.add(viewport); - const addEventListener = viewport.addEventListener.bind(viewport); - const removeEventListener = viewport.removeEventListener.bind(viewport); - viewport.addEventListener = (( - type: string, - listener: EventListenerOrEventListenerObject, - options?: boolean | AddEventListenerOptions, - ) => { - if (type === 'scroll') scrollHarness.add(viewport, type, listener, options); - addEventListener(type, listener, options); - }) as typeof viewport.addEventListener; - viewport.removeEventListener = (( - type: string, - listener: EventListenerOrEventListenerObject, - options?: boolean | EventListenerOptions, - ) => { - if (type === 'scroll') scrollHarness.remove(viewport, type, listener, options); - removeEventListener(type, listener, options); - }) as typeof viewport.removeEventListener; - } - } - - if (typeof forwardedRef === 'function') return forwardedRef(node); - if (forwardedRef) forwardedRef.current = node; - return () => { - if (forwardedRef) forwardedRef.current = null; - }; - }, - [forwardedRef, nodeKey, withViewport], - ); - - return ( -
- {withViewport ?
{children}
: children} -
- ); - }, - ); - - return { ScrollArea }; -}); - -vi.mock('@constructive-io/ui/stack', () => ({ - useCardStack: () => dependencyMocks.stack, -})); - -vi.mock('@constructive-io/ui/toast', () => ({ - showErrorToast: vi.fn(), - showSuccessToast: vi.fn(), -})); - -vi.mock('@/blocks/schema/schema-builder-core/lib/gql/hooks/schema-builder', () => ({ - useSchemaBuilderSelectors: () => ({ - currentDatabase: { databaseId: 'database-1', schemaId: 'schema-1' }, - currentSchema: { tables: [] }, - }), -})); - -vi.mock('../../lib/gql/hooks/schema-builder/use-create-table', () => ({ - useCreateTable: () => ({ mutateAsync: dependencyMocks.mutateAsync, isPending: false }), -})); - -vi.mock('../policies/policy-hooks', () => ({ - getDefaultFormValues: vi.fn(() => ({})), - useCreateTableWithPolicies: () => ({ - createTableWithPolicies: dependencyMocks.createTableWithPolicies, - isCreating: false, - }), - usePolicyTypes: () => ({ policyTypes: [], error: null }), -})); - -vi.mock('../policies/use-crud-policy-state', () => ({ - useCrudPolicyState: () => ({ - defaults: { policyData: {} }, - operations: {}, - updateDefaults: dependencyMocks.updateDefaults, - getEnabledOperations: dependencyMocks.getEnabledOperations, - }), -})); - -vi.mock('../policies/composite-policy-builder', () => ({ - createEmptyCompositePolicyData: vi.fn(() => ({})), -})); - -vi.mock('../policies/policy-config-card', () => ({ PolicyConfigContent: () => null })); -vi.mock('../policies/policy-diagram', () => ({ PolicyDiagramByKey: () => null })); -vi.mock('../policies/policy-know-more-card', () => ({ PolicyKnowMoreCard: () => null })); - -import { CreateTableCard } from './create-table-card'; - -const card = { - id: 'create-table-card', - push: dependencyMocks.push, - close: dependencyMocks.close, - setTitle: vi.fn(), - setDescription: vi.fn(), - setWidth: vi.fn(), - setDismissable: vi.fn(), - updateProps: vi.fn(), -} as ComponentProps['card']; - -function cardElement(props: Partial> = {}) { - return ; -} - -function viewport(container: ParentNode = document) { - const node = container.querySelector('[data-slot="scroll-area-viewport"]'); - if (!node) throw new Error('Missing scroll viewport'); - return node; -} - -function stickyHeader() { - const node = screen.getByTestId('table-name-input').closest('.bg-card'); - if (!node) throw new Error('Missing sticky table-name header'); - return node; -} - -function expectListenerPair(addIndex: number, removeIndex: number) { - const addCall = scrollHarness.add.mock.calls[addIndex]; - const removeCall = scrollHarness.remove.mock.calls[removeIndex]; - expect(removeCall[0]).toBe(addCall[0]); - expect(removeCall[1]).toBe('scroll'); - expect(removeCall[2]).toBe(addCall[2]); -} - -beforeEach(() => { - scrollHarness.key = 'first'; - scrollHarness.withViewport = true; - scrollHarness.initialScrollTop = 0; - scrollHarness.add.mockClear(); - scrollHarness.remove.mockClear(); - vi.clearAllMocks(); -}); - -afterEach(cleanup); - -describe('CreateTableCard scroll listener ownership', () => { - it('samples the attached viewport and keeps the sticky shadow in sync with scroll', () => { - scrollHarness.initialScrollTop = 8; - const view = render(cardElement()); - const liveViewport = viewport(view.container); - - expect(scrollHarness.add).toHaveBeenCalledTimes(1); - expect(scrollHarness.add.mock.calls[0].slice(1)).toEqual([ - 'scroll', - expect.any(Function), - { passive: true }, - ]); - expect(stickyHeader().classList.contains('shadow-md')).toBe(true); - - liveViewport.scrollTop = 0; - fireEvent.scroll(liveViewport); - expect(stickyHeader().classList.contains('shadow-md')).toBe(false); - - liveViewport.scrollTop = 24; - fireEvent.scroll(liveViewport); - expect(stickyHeader().classList.contains('shadow-md')).toBe(true); - - view.unmount(); - expect(scrollHarness.remove).toHaveBeenCalledTimes(1); - expectListenerPair(0, 0); - }); - - it('releases a replaced viewport before attaching the next node', () => { - const view = render(cardElement({ tableName: 'first_table' })); - const firstViewport = viewport(view.container); - - scrollHarness.key = 'second'; - scrollHarness.initialScrollTop = 5; - view.rerender(cardElement({ tableName: 'second_table' })); - const secondViewport = viewport(view.container); - - expect(secondViewport).not.toBe(firstViewport); - expect(scrollHarness.add).toHaveBeenCalledTimes(2); - expect(scrollHarness.remove).toHaveBeenCalledTimes(1); - expectListenerPair(0, 0); - expect(stickyHeader().classList.contains('shadow-md')).toBe(true); - - secondViewport.scrollTop = 0; - fireEvent.scroll(secondViewport); - expect(stickyHeader().classList.contains('shadow-md')).toBe(false); - - firstViewport.scrollTop = 10; - fireEvent.scroll(firstViewport); - expect(stickyHeader().classList.contains('shadow-md')).toBe(false); - - secondViewport.scrollTop = 10; - fireEvent.scroll(secondViewport); - expect(stickyHeader().classList.contains('shadow-md')).toBe(true); - - view.unmount(); - expect(scrollHarness.remove).toHaveBeenCalledTimes(2); - expectListenerPair(1, 1); - }); - - it('pairs every Strict Mode callback-ref setup with cleanup', () => { - const view = render({cardElement()}); - - expect(scrollHarness.add).toHaveBeenCalledTimes(2); - expect(scrollHarness.remove).toHaveBeenCalledTimes(1); - expectListenerPair(0, 0); - - view.unmount(); - expect(scrollHarness.remove).toHaveBeenCalledTimes(2); - expectListenerPair(1, 1); - }); - - it('does not allocate a listener when the attached node has no viewport', () => { - scrollHarness.withViewport = false; - const view = render(cardElement()); - - expect(scrollHarness.add).not.toHaveBeenCalled(); - view.unmount(); - expect(scrollHarness.remove).not.toHaveBeenCalled(); - }); -}); diff --git a/packages/sheets/src/cell-model/__tests__/create-sheets-cell.test.ts b/packages/sheets/src/cell-model/__tests__/create-sheets-cell.test.ts new file mode 100644 index 0000000..9f5f8fd --- /dev/null +++ b/packages/sheets/src/cell-model/__tests__/create-sheets-cell.test.ts @@ -0,0 +1,46 @@ +import { describe, expect, it } from 'vitest'; + +import type { CellCreationMetadata } from '../../grid/grid-cell-types'; +import { createSheetsCell } from '../create-sheets-cell'; + +function metadata(cellType: string, overrides: Partial = {}): CellCreationMetadata { + return { + cellType, + fieldName: 'value', + canEdit: true, + isReadonly: false, + activationBehavior: 'double-click', + ...overrides, + }; +} + +describe('createSheetsCell', () => { + it('normalizes an email into navigable data without changing its display value', () => { + expect(createSheetsCell('ada@example.com', metadata('email'))).toMatchObject({ + kind: 'uri', + data: 'mailto:ada@example.com', + displayData: 'ada@example.com', + }); + }); + + it('derives relation labels, truncates them, and reports list overflow', () => { + const cell = createSheetsCell( + [{ displayName: 'Alexandria' }, { displayName: 'Bob' }, { displayName: 'Chen' }], + metadata('relation', { + relationInfo: { kind: 'hasMany', displayCandidates: ['displayName'] }, + relationOptions: { relationChipLimit: 2, relationLabelMaxLength: 6 }, + }), + ); + + expect(cell).toMatchObject({ kind: 'relation', data: ['Alexa…', 'Bob', '+1'], displayData: '' }); + }); + + it('does not expose a draft placeholder as a relation label', () => { + expect( + createSheetsCell( + { id: 'draft:pending', displayName: 'Unsaved record' }, + metadata('relation', { relationInfo: { kind: 'belongsTo', displayCandidates: ['displayName'] } }), + ), + ).toMatchObject({ kind: 'relation', data: '', displayData: '' }); + }); +}); diff --git a/packages/sheets/src/cell-model/__tests__/kind-parity.test-d.ts b/packages/sheets/src/cell-model/__tests__/kind-parity.test-d.ts deleted file mode 100644 index d523c5c..0000000 --- a/packages/sheets/src/cell-model/__tests__/kind-parity.test-d.ts +++ /dev/null @@ -1,13 +0,0 @@ -// TYPE-LEVEL guard: SheetsCellKind must equal the frozen DisplayKind -// member-for-member. Test file → may import the harness (vitest is fine here). -// The real check is these two assignments COMPILING; there is no runtime body. - -import type { DisplayKind } from '../../grid/__golden__/parity.harness'; -import type { SheetsCellKind } from '../sheets-cell'; - -// Mutual assignability ⇒ the two unions are identical sets of string literals. -const _kindIntoDisplay: DisplayKind = '' as SheetsCellKind; -const _displayIntoKind: SheetsCellKind = '' as DisplayKind; - -// Silence unused-locals without a runtime export. -export type _KindParity = [typeof _kindIntoDisplay, typeof _displayIntoKind]; diff --git a/packages/sheets/src/cell-model/create-sheets-cell.ts b/packages/sheets/src/cell-model/create-sheets-cell.ts index c1b3843..6842ce2 100644 --- a/packages/sheets/src/cell-model/create-sheets-cell.ts +++ b/packages/sheets/src/cell-model/create-sheets-cell.ts @@ -56,8 +56,8 @@ const SHEETS_CELL_FACTORIES: SheetsCellFactory[] = [ * its type (the host reads `SheetsCellResolution.typeKey`, but the cell carrying * `meta.cellType` keeps it self-contained for the editor adapters that already * read it — date/geometry). MERGE (not replace) so any factory-set meta key - * (e.g. geometry's `geometryType`) survives. Parity-safe: `projectSheetsCell` - * ignores `meta` entirely, so display/copy text is unchanged. + * (e.g. geometry's `geometryType`) survives. This does not change display or + * copy text because projection ignores `meta`. */ function withTypeKey(cell: SheetsCell, cellType: string): SheetsCell { return { ...cell, meta: { ...cell.meta, cellType: cellType as CellType } }; diff --git a/packages/sheets/src/cell-model/factories/boolean.ts b/packages/sheets/src/cell-model/factories/boolean.ts index 8ea89e9..efee7d9 100644 --- a/packages/sheets/src/cell-model/factories/boolean.ts +++ b/packages/sheets/src/cell-model/factories/boolean.ts @@ -1,14 +1,8 @@ // Boolean SheetsCell factory — native analogue of v1's BooleanCellFactory // (grid/cell-content-factory.ts). Claims BOOLEAN_TYPES (boolean, bit, toggle). // -// Parity contract (projectSheetsCell ≡ projectGlideCell): -// The glide BooleanCell carries `data: boolValue` and NO `displayData`, so -// projectGlideCell yields { displayText: '', copyText: String(boolValue) }. -// We mirror that: `data` holds EXACTLY the boolean glide put in `.data`, and -// `displayData` is '' (glide Boolean had none). Since kind 'boolean' is not -// geometry/custom, projectSheetsCell reads displayData -> '' and data -> -// String(boolValue), matching glide branch-for-branch. The glide BooleanCell -// sets no `readonly`, so readonly = false. +// The BooleanCell keeps the boolean in `data` for copying and leaves +// `displayData` empty because the view owns its visual representation. import type { CellCreationMetadata } from '../../grid/grid-cell-types'; import { BOOLEAN_TYPES } from '../../cell-types/cell-type-groups'; diff --git a/packages/sheets/src/cell-model/factories/datetime.ts b/packages/sheets/src/cell-model/factories/datetime.ts index 5ceba50..60a2507 100644 --- a/packages/sheets/src/cell-model/factories/datetime.ts +++ b/packages/sheets/src/cell-model/factories/datetime.ts @@ -3,7 +3,7 @@ // (date, datetime, time, timestamptz) and emits a neutral `text` cell carrying // the value's string form. v1 renders the raw value as formatted text: non-null // -> data/displayData = String(value); null/undefined -> both ''. This factory -// mirrors that exactly so projectSheetsCell deep-equals projectGlideCell. +// preserves that display and copy behavior. import type { CellType } from '../../cell-types/types'; import { DATE_TIME_TYPES } from '../../cell-types/cell-type-groups'; diff --git a/packages/sheets/src/cell-model/factories/geometry.ts b/packages/sheets/src/cell-model/factories/geometry.ts index fbf8209..c275a21 100644 --- a/packages/sheets/src/cell-model/factories/geometry.ts +++ b/packages/sheets/src/cell-model/factories/geometry.ts @@ -7,9 +7,8 @@ // it falls back to a compact JSON preview as both the copy value and the display // string — identical to v1's Text fallback. // -// Parity: `projectSheetsCell` special-cases kind `geometry` so copyText === -// displayText === displayData. The fallback therefore sets `data` and -// `displayData` to the SAME `compactJsonPreview(value, 80)` string v1 emits. +// Copy and display text intentionally use the same compact JSON preview when no +// geometry-specific builder is available. import type { CellCreationMetadata } from '../../grid/grid-cell-types'; import { GEOMETRY_TYPES } from '../../cell-types/cell-type-groups'; diff --git a/packages/sheets/src/cell-model/factories/number.ts b/packages/sheets/src/cell-model/factories/number.ts index fb6b936..2962a03 100644 --- a/packages/sheets/src/cell-model/factories/number.ts +++ b/packages/sheets/src/cell-model/factories/number.ts @@ -4,13 +4,8 @@ // the number family wins it before the text fallback; this is a deliberate v1 // quirk preserved for parity). // -// Parity contract (projectSheetsCell ≡ projectGlideCell): -// - null/undefined OR NaN coercion -> data: undefined, displayData: '' -// (projects copyText '' + displayText '') -// - otherwise -> data: numValue (number), displayData: String(numValue) -// Mirrors the glide NumberCell branch-for-branch; `data` holds EXACTLY what the -// glide factory put in `.data`, and `displayData` is the string form of glide's -// `.displayData`. The glide NumberCell sets no `readonly`, so readonly = false. +// Null, undefined, and invalid values become an empty cell. Valid values keep +// the numeric data and a string display value. import type { CellCreationMetadata } from '../../grid/grid-cell-types'; import { NUMBER_TYPES } from '../../cell-types/cell-type-groups'; diff --git a/packages/sheets/src/cell-model/factories/relation.ts b/packages/sheets/src/cell-model/factories/relation.ts index 31627f8..a9467b4 100644 --- a/packages/sheets/src/cell-model/factories/relation.ts +++ b/packages/sheets/src/cell-model/factories/relation.ts @@ -6,10 +6,8 @@ // copied verbatim from v1 (no import from cell-content-factory.ts — that module // stays glide-only and is deleted at cutover). // -// PARITY: projectSheetsCell(create(...)) deep-equals projectGlideCell(v1 create). -// single — glide Text: data = label, displayData = label. -// list — glide Bubble: data = finalData (chips + "+N"), displayData = '' (Bubble -// carries no displayData, so it projects to '' on the display side). +// Single relations use their label for data and display. List relations keep +// their final chips in data while the relation view owns their visual display. import type { CellCreationMetadata } from '../../grid/grid-cell-types'; import { compactJsonPreview } from '../../grid/sheets.formatters'; diff --git a/packages/sheets/src/cell-model/factories/text.ts b/packages/sheets/src/cell-model/factories/text.ts index 74f5309..f5b00b1 100644 --- a/packages/sheets/src/cell-model/factories/text.ts +++ b/packages/sheets/src/cell-model/factories/text.ts @@ -6,9 +6,8 @@ // // v1 emits a Text glide cell: null/undefined -> data/displayData ''; string -> // data/displayData = value; anything else -> compactJsonPreview(value, 80) for -// both. This factory mirrors that exactly so projectSheetsCell deep-equals -// projectGlideCell. `compactJsonPreview` is shared (sheets.formatters), so it is -// imported rather than copied. +// both. `compactJsonPreview` is shared (sheets.formatters), so it is imported +// rather than copied. import type { CellType } from '../../cell-types/types'; import type { CellCreationMetadata } from '../../grid/grid-cell-types'; diff --git a/packages/sheets/src/cell-model/factories/uri.ts b/packages/sheets/src/cell-model/factories/uri.ts index d4932f3..4ed2047 100644 --- a/packages/sheets/src/cell-model/factories/uri.ts +++ b/packages/sheets/src/cell-model/factories/uri.ts @@ -3,9 +3,8 @@ // render payload. All domain logic (email -> mailto, url passthrough, empty // guard) is reproduced here verbatim so this file is glide-free at cutover. // -// PARITY: projectSheetsCell(create(...)) deep-equals projectGlideCell of v1's -// UriCell — `data`/`displayData` are the SAME strings v1 emits. v1 drops only -// the `onClickUri` side-effect (the DOM view owns clicks). +// `data` holds the navigable URI while `displayData` keeps the user-facing +// string; the DOM view owns click behavior. import type { CellType } from '../../cell-types/types'; import type { CellCreationMetadata } from '../../grid/grid-cell-types'; diff --git a/packages/sheets/src/cell-model/sheets-cell.ts b/packages/sheets/src/cell-model/sheets-cell.ts index 53a0a2c..e1610b5 100644 --- a/packages/sheets/src/cell-model/sheets-cell.ts +++ b/packages/sheets/src/cell-model/sheets-cell.ts @@ -1,16 +1,13 @@ // Native cell model — the DOM render payload that replaces glide `GridCell`. // // Pure types: no @tanstack/* or virtual import. `SheetsCellKind` is the neutral -// 11-member render vocabulary, frozen by the Phase-0 parity harness -// (`grid/__golden__/parity.harness.ts` DisplayKind). The type-level guard in -// `__tests__/kind-parity.test-d.ts` asserts member-for-member equality without -// importing the harness into production code (it pulls vitest). +// render vocabulary consumed by the DOM cell host. import type { CellType } from '../cell-types/types'; /** - * Neutral RENDER kinds — equal to the frozen `DisplayKind` union. `loading` and - * `draft-action` originate from grid-internal state, not the `CellType` system. + * Neutral render kinds. `loading` and `draft-action` originate from grid-internal + * state, not the `CellType` system. */ export type SheetsCellKind = | 'text' diff --git a/packages/sheets/src/cell-model/views/__tests__/draft-action-view.test.tsx b/packages/sheets/src/cell-model/views/__tests__/draft-action-view.test.tsx index 2fe2b92..55c4eee 100644 --- a/packages/sheets/src/cell-model/views/__tests__/draft-action-view.test.tsx +++ b/packages/sheets/src/cell-model/views/__tests__/draft-action-view.test.tsx @@ -38,26 +38,6 @@ describe('DraftActionCellView (native DOM)', () => { }); } - it('idle renders an enabled "Save" button', async () => { - await mount({ status: 'idle' }); - - const btn = getButton(container); - expect(btn.disabled).toBe(false); - expect(btn.textContent).toContain('Save'); - expect(btn.textContent).not.toContain('Saving'); - // no spinner, no error indicator in the idle branch - expect(container.querySelector('.animate-spin')).toBeNull(); - expect(container.querySelector('[data-slot="draft-action-error"]')).toBeNull(); - }); - - it('defaults to idle when no status prop is given', async () => { - await mount({}); - - const btn = getButton(container); - expect(btn.disabled).toBe(false); - expect(btn.textContent).toContain('Save'); - }); - it('click fires onSubmit exactly once', async () => { const onSubmit = vi.fn(); await mount({ status: 'idle', onSubmit }); @@ -70,17 +50,6 @@ describe('DraftActionCellView (native DOM)', () => { expect(onSubmit).toHaveBeenCalledTimes(1); }); - it('does not throw when clicked without an onSubmit handler', async () => { - await mount({ status: 'idle' }); - - const btn = getButton(container); - await act(async () => { - btn.dispatchEvent(new MouseEvent('click', { bubbles: true })); - }); - - expect(btn.disabled).toBe(false); - }); - it('saving shows the spinner + "Saving..." and disables the button', async () => { const onSubmit = vi.fn(); await mount({ status: 'saving', onSubmit }); diff --git a/packages/sheets/src/cell-model/views/__tests__/geometry-view.test.tsx b/packages/sheets/src/cell-model/views/__tests__/geometry-view.test.tsx deleted file mode 100644 index 54e164d..0000000 --- a/packages/sheets/src/cell-model/views/__tests__/geometry-view.test.tsx +++ /dev/null @@ -1,126 +0,0 @@ -/* @vitest-environment jsdom */ -// -// Unit test for the enriched GeometryCellView. Asserts the view paints a geo ICON -// (tinted by geometry category) plus an ABBREVIATED type label — the DOM analogue -// of the canvas painter (grid/custom-cells/geometry-cell.tsx), NOT a plain -// monospace preview. Same component-test idiom as the editor tests: jsdom + -// react-dom/client createRoot + act (no @testing-library — not a dep here). - -import { act } from 'react'; -import { createRoot, type Root } from 'react-dom/client'; -import { afterEach, beforeEach, describe, expect, it } from 'vitest'; - -import { GeometryCellView } from '../geometry-view'; -import type { CellProps } from '../../cell-props'; -import type { SheetsCell } from '../../sheets-cell'; - -(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true; - -// Build CellProps around a hand-fed geometry SheetsCell. `data`/`displayData` -// mirror the factory's compact-JSON fallback unless overridden. -function makeProps(cell: Partial): CellProps { - const merged: SheetsCell = { - kind: 'geometry', - data: '', - displayData: '', - readonly: false, - ...cell, - }; - return { - cell: merged, - value: merged.data, - colKey: 'location', - rowId: 'row-1', - rowIndex: 0, - column: { key: 'location', name: 'Location', cellType: 'geometry' }, - isEditing: false, - onStartEdit: () => {}, - disabled: false, - }; -} - -describe('GeometryCellView (enriched DOM view)', () => { - let root: Root; - let container: HTMLDivElement; - - beforeEach(() => { - container = document.createElement('div'); - document.body.appendChild(container); - root = createRoot(container); - }); - - afterEach(async () => { - await act(async () => { - root.unmount(); - }); - container.remove(); - }); - - function render(props: CellProps) { - act(() => { - root.render(); - }); - } - - it('renders a point icon + "Point" label for a Point value (sniffed from preview)', () => { - const json = JSON.stringify({ type: 'Point', coordinates: [1, 2] }); - render(makeProps({ data: json, displayData: json })); - - const cell = container.querySelector('[data-slot="geometry-cell"]'); - expect(cell).not.toBeNull(); - expect(cell?.getAttribute('data-geometry-category')).toBe('point'); - - // An ICON is painted (inline SVG), not a monospace span. - const icon = cell?.querySelector('svg[data-geometry-icon="point"]'); - expect(icon).not.toBeNull(); - // Tinted by the point category colour (mirrors canvas ICON_COLORS blue). - expect(icon?.getAttribute('stroke')).toBe('#3b82f6'); - - // Abbreviated type label, NOT the raw JSON. - expect(cell?.textContent).toBe('Point'); - expect(cell?.textContent).not.toContain('coordinates'); - }); - - it('renders a map icon + abbreviated "Poly" label for a Polygon value', () => { - const json = JSON.stringify({ type: 'Polygon', coordinates: [[[0, 0], [1, 0], [1, 1], [0, 0]]] }); - render(makeProps({ data: json, displayData: json })); - - const cell = container.querySelector('[data-slot="geometry-cell"]'); - expect(cell?.getAttribute('data-geometry-category')).toBe('polygon'); - - const icon = cell?.querySelector('svg[data-geometry-icon="polygon"]'); - expect(icon).not.toBeNull(); - expect(icon?.getAttribute('stroke')).toBe('#10b981'); - - expect(cell?.textContent).toBe('Poly'); - }); - - it('prefers an explicit geometryType hint from cell.meta (deriveGeometry path)', () => { - render(makeProps({ data: 'POINT(1 2)', displayData: 'POINT(1 2)', meta: { geometryType: 'LineString' } })); - - const cell = container.querySelector('[data-slot="geometry-cell"]'); - expect(cell?.getAttribute('data-geometry-category')).toBe('line'); - expect(cell?.querySelector('svg[data-geometry-icon="line"]')).not.toBeNull(); - expect(cell?.textContent).toBe('Line'); - }); - - it('renders an invalid (warning) icon + "Invalid" label for an unparseable value', () => { - render(makeProps({ data: 'not-geojson', displayData: 'not-geojson' })); - - const cell = container.querySelector('[data-slot="geometry-cell"]'); - expect(cell?.getAttribute('data-geometry-category')).toBe('invalid'); - const icon = cell?.querySelector('svg[data-geometry-icon="invalid"]'); - expect(icon).not.toBeNull(); - expect(icon?.getAttribute('stroke')).toBe('#ef4444'); - expect(cell?.textContent).toBe('Invalid'); - }); - - it('paints nothing (no icon, no label) for an empty cell', () => { - render(makeProps({ data: '', displayData: '' })); - - const cell = container.querySelector('[data-slot="geometry-cell"]'); - expect(cell).not.toBeNull(); - expect(cell?.querySelector('svg')).toBeNull(); - expect(cell?.textContent).toBe(''); - }); -}); diff --git a/packages/sheets/src/cell-model/views/__tests__/image-view.test.tsx b/packages/sheets/src/cell-model/views/__tests__/image-view.test.tsx deleted file mode 100644 index 57f25cf..0000000 --- a/packages/sheets/src/cell-model/views/__tests__/image-view.test.tsx +++ /dev/null @@ -1,151 +0,0 @@ -/* @vitest-environment jsdom */ - -import { act } from 'react'; -import { createRoot, type Root } from 'react-dom/client'; -import { afterEach, beforeEach, describe, expect, it } from 'vitest'; - -import type { CellProps } from '../../cell-props'; -import type { SheetsCell } from '../../sheets-cell'; -import { ImageCellView } from '../image-view'; - -(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true; - -// The image factory puts the resolved url in a single-element array (`[url]`), -// collapsing empty/null/'' to `['']`. The view reads data[0]. -function makeProps(url: string): CellProps { - const cell: SheetsCell = { - kind: 'image', - data: [url], - displayData: JSON.stringify([url]), - readonly: false, - }; - return { - cell, - value: url, - colKey: 'avatar', - rowId: 'row-1', - rowIndex: 0, - column: { key: 'avatar', name: 'Avatar', cellType: 'image' }, - isEditing: false, - onStartEdit: () => {}, - disabled: false, - }; -} - -// A media value with rich metadata ({ url, mime, filename }) on a given column type — -// used to exercise the type-aware file-chip vs thumbnail branch. -function makeFileProps(value: unknown, cellType: string): CellProps { - const url = - typeof value === 'string' - ? value - : value && typeof value === 'object' && 'url' in value - ? String((value as { url?: unknown }).url ?? '') - : ''; - const cell: SheetsCell = { kind: 'image', data: [url], displayData: JSON.stringify([url]), readonly: false }; - return { - cell, - value, - colKey: 'attachment', - rowId: 'row-1', - rowIndex: 0, - column: { key: 'attachment', name: 'Attachment', cellType: cellType as CellProps['column']['cellType'] }, - isEditing: false, - onStartEdit: () => {}, - disabled: false, - }; -} - -describe('ImageCellView', () => { - let root: Root; - let container: HTMLDivElement; - - beforeEach(() => { - container = document.createElement('div'); - document.body.appendChild(container); - root = createRoot(container); - }); - - afterEach(async () => { - await act(async () => { - root.unmount(); - }); - container.remove(); - }); - - function render(props: CellProps) { - act(() => { - root.render(); - }); - } - - it('renders the slot wrapper', () => { - render(makeProps('https://example.com/a.png')); - expect(container.querySelector('[data-slot="image-cell"]')).not.toBeNull(); - }); - - it('non-empty url -> eager async-decode with cover + rounded classes', () => { - render(makeProps('https://example.com/a.png')); - const img = container.querySelector('img'); - expect(img).not.toBeNull(); - expect(img!.getAttribute('src')).toBe('https://example.com/a.png'); - // No loading="lazy": in a virtualized scroller it forces a fetch-after-mount on - // every remount (pop-in). Eager + async decode keeps scroll-back paints warm. - expect(img!.getAttribute('loading')).toBeNull(); - expect(img!.getAttribute('decoding')).toBe('async'); - expect(img!.className).toContain('object-cover'); - expect(img!.className).not.toContain('object-contain'); - expect(img!.className).toContain('rounded-sm'); - }); - - it('empty url -> muted placeholder, no ', () => { - render(makeProps('')); - expect(container.querySelector('img')).toBeNull(); - const placeholder = container.querySelector('[data-slot="image-cell"] > div'); - expect(placeholder).not.toBeNull(); - expect(placeholder!.className).toContain('bg-muted'); - expect(placeholder!.className).toContain('aspect-square'); - }); - - it('placeholder box backs the image (square + rounded) when a url is present', () => { - render(makeProps('https://example.com/a.png')); - const box = container.querySelector('[data-slot="image-cell"] > div'); - expect(box).not.toBeNull(); - expect(box!.className).toContain('aspect-square'); - expect(box!.className).toContain('bg-muted'); - expect(box!.className).toContain('rounded-sm'); - }); - - it('non-image file value on a non-image column -> file chip, no ', () => { - render( - makeFileProps( - { url: 'https://example.com/report.pdf', mime: 'application/pdf', filename: 'report.pdf' }, - 'upload', - ), - ); - expect(container.querySelector('[data-slot="file-cell"]')).not.toBeNull(); - expect(container.querySelector('img')).toBeNull(); - expect(container.textContent).toContain('report.pdf'); - }); - - it('image-mime value on a non-image column -> thumbnail (image path preserved)', () => { - render( - makeFileProps({ url: 'https://example.com/photo.png', mime: 'image/png', filename: 'photo.png' }, 'upload'), - ); - expect(container.querySelector('[data-slot="image-cell"]')).not.toBeNull(); - expect(container.querySelector('img')).not.toBeNull(); - }); - - it('image column always renders a thumbnail regardless of mime', () => { - render(makeFileProps({ url: 'https://example.com/x.bin', mime: 'application/pdf' }, 'image')); - expect(container.querySelector('[data-slot="image-cell"]')).not.toBeNull(); - expect(container.querySelector('[data-slot="file-cell"]')).toBeNull(); - }); - - it('bare-string url (no metadata) on a non-image column -> file chip with basename', () => { - // Mirrors the editor: without a known image mime we chip rather than risk a broken . - render(makeFileProps('https://example.com/files/report.pdf', 'upload')); - expect(container.querySelector('[data-slot="file-cell"]')).not.toBeNull(); - expect(container.querySelector('img')).toBeNull(); - expect(container.textContent).toContain('report.pdf'); - }); -}); diff --git a/packages/sheets/src/cell-model/views/__tests__/loading-view.test.tsx b/packages/sheets/src/cell-model/views/__tests__/loading-view.test.tsx deleted file mode 100644 index 25f01fb..0000000 --- a/packages/sheets/src/cell-model/views/__tests__/loading-view.test.tsx +++ /dev/null @@ -1,78 +0,0 @@ -/* @vitest-environment jsdom */ - -import { act } from 'react'; -import { createRoot, type Root } from 'react-dom/client'; -import { afterEach, beforeEach, describe, expect, it } from 'vitest'; - -import type { SheetsCell } from '../../sheets-cell'; -import type { CellProps } from '../../cell-props'; -import { LoadingCellView } from '../loading-view'; - -(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true; - -// An unloaded infinite-scroll row resolves to a neutral `loading` cell with no -// data/displayData — the view must render purely from the kind, ignoring value. -function makeProps(over: Partial = {}): CellProps { - const cell: SheetsCell = { kind: 'loading', data: null, displayData: '', readonly: true }; - return { - cell, - value: undefined, - colKey: 'name', - rowId: 'row-7', - rowIndex: 7, - column: { key: 'name', name: 'name', cellType: 'unknown' }, - isEditing: false, - onStartEdit: () => {}, - disabled: false, - ...over, - }; -} - -describe('LoadingCellView', () => { - let root: Root; - let container: HTMLDivElement; - - beforeEach(() => { - container = document.createElement('div'); - document.body.appendChild(container); - root = createRoot(container); - }); - - afterEach(async () => { - await act(async () => { - root.unmount(); - }); - container.remove(); - }); - - it('renders a skeleton placeholder gridcell with data-slot="loading-cell"', async () => { - await act(async () => { - root.render(); - }); - - const slot = container.querySelector('[data-slot="loading-cell"]'); - expect(slot).toBeTruthy(); - expect(slot?.getAttribute('role')).toBe('gridcell'); - expect(slot?.getAttribute('aria-busy')).toBe('true'); - }); - - it('paints a shimmer bar (animate-pulse) inside the cell', async () => { - await act(async () => { - root.render(); - }); - - const slot = container.querySelector('[data-slot="loading-cell"]'); - const bar = slot?.querySelector('span'); - expect(bar).toBeTruthy(); - expect(bar?.className).toContain('animate-pulse'); - }); - - it('renders no cell text regardless of value/displayData', async () => { - await act(async () => { - root.render(); - }); - - const slot = container.querySelector('[data-slot="loading-cell"]'); - expect(slot?.textContent).toBe(''); - }); -}); diff --git a/packages/sheets/src/cell-model/views/__tests__/relation-view.test.tsx b/packages/sheets/src/cell-model/views/__tests__/relation-view.test.tsx deleted file mode 100644 index be04f99..0000000 --- a/packages/sheets/src/cell-model/views/__tests__/relation-view.test.tsx +++ /dev/null @@ -1,107 +0,0 @@ -/* @vitest-environment jsdom */ -// -// Phase-7 RelationCellView enrichment test. Mirrors the canvas count-badge -// (grid/draw-relation-badge.ts): a LIST relation (cell.data is string[], possibly -// with a trailing "+N" overflow chip) renders a leading count badge before the -// chips; a SINGLE relation (string data) renders plain text with no badge. -// -// Uses the package's component-test idiom — jsdom + react-dom/client createRoot + -// act (no @testing-library — not a dep of this package). The view is pure (no -// virtualizer/layout), so it is rendered directly with hand-fed CellProps. -import { act } from 'react'; -import { createRoot, type Root } from 'react-dom/client'; -import { afterEach, beforeEach, describe, expect, it } from 'vitest'; - -import { RelationCellView } from '../relation-view'; -import type { CellProps } from '../../cell-props'; -import type { SheetsCell } from '../../sheets-cell'; - -(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true; - -function makeProps(cell: SheetsCell): CellProps { - return { - cell, - value: cell.data, - colKey: 'author', - rowId: 'r1', - rowIndex: 0, - column: { key: 'author', name: 'Author', cellType: 'relation' }, - isEditing: false, - onStartEdit: () => {}, - disabled: false, - }; -} - -function listCell(data: string[]): SheetsCell { - return { kind: 'relation', data, displayData: '', readonly: false }; -} - -function singleCell(label: string): SheetsCell { - return { kind: 'relation', data: label, displayData: label, readonly: false }; -} - -describe('RelationCellView (count badge + chips)', () => { - let root: Root; - let container: HTMLDivElement; - - beforeEach(() => { - container = document.createElement('div'); - document.body.appendChild(container); - root = createRoot(container); - }); - - afterEach(async () => { - await act(async () => { - root.unmount(); - }); - container.remove(); - }); - - async function render(cell: SheetsCell): Promise { - await act(async () => { - root.render(); - }); - const el = container.querySelector('[data-slot="relation-cell"]') as HTMLElement | null; - expect(el).not.toBeNull(); - return el as HTMLElement; - } - - it('renders a count badge before chips for a list relation', async () => { - const el = await render(listCell(['Alice', 'Bob'])); - - const badge = el.querySelector('[data-slot="relation-count-badge"]'); - expect(badge).not.toBeNull(); - expect(badge?.textContent).toBe('2'); - - // Both chips render alongside the badge. - expect(el.textContent).toContain('Alice'); - expect(el.textContent).toContain('Bob'); - - // Badge is the leading (first) child of the cell — painted before the chips. - expect(el.firstElementChild).toBe(badge); - }); - - it('counts a trailing "+N" overflow chip into the badge total (mirrors getRelationCount)', async () => { - // 3 visible chips + "+5" overflow -> total = (4 - 1) + 5 = 8. - const el = await render(listCell(['Alice', 'Bob', 'Carol', '+5'])); - - const badge = el.querySelector('[data-slot="relation-count-badge"]'); - expect(badge?.textContent).toBe('8'); - - // The "+5" overflow chip is still painted (canvas keeps it visible). - expect(el.textContent).toContain('+5'); - }); - - it('renders no badge for an empty list relation', async () => { - const el = await render(listCell([])); - - expect(el.querySelector('[data-slot="relation-count-badge"]')).toBeNull(); - }); - - it('renders a single relation as plain text with no badge', async () => { - const el = await render(singleCell('Acme Corp')); - - expect(el.querySelector('[data-slot="relation-count-badge"]')).toBeNull(); - expect(el.textContent).toBe('Acme Corp'); - }); -}); diff --git a/packages/sheets/src/cell-types/__tests__/registry-sheetscell.test.ts b/packages/sheets/src/cell-types/__tests__/registry-sheetscell.test.ts deleted file mode 100644 index a8ce82b..0000000 --- a/packages/sheets/src/cell-types/__tests__/registry-sheetscell.test.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { describe, expect, it } from 'vitest'; - -import { defineCellType } from '../define-cell-type'; -import { createCellTypeRegistry, type CellTypeBuiltins } from '../cell-type-registry'; -import type { SheetsCell } from '../../cell-model/sheets-cell'; -import type { CellProps } from '../../cell-model/cell-props'; -import type { EditorProps } from '../../grid-dom/editors/editor-props'; - -function sheetsCell(displayData: string): SheetsCell { - return { kind: 'text', data: displayData, displayData, readonly: false }; -} - -function makeBuiltins(overrides?: Partial): CellTypeBuiltins { - return { - toSheetsCell: (value) => sheetsCell(`builtin:${String(value)}`), - ...overrides, - }; -} - -const ctx = { - metadata: { cellType: 'x', fieldName: 'f', canEdit: true, isReadonly: false, activationBehavior: 'double-click' as const }, -}; - -function TextComponent(_props: CellProps) { - return null; -} - -function TextEditorComponent(_props: EditorProps) { - return null; -} - -describe('createCellTypeRegistry — native SheetsCell + cell component', () => { - const textDef = defineCellType({ - typeKey: 'text', - toSheetsCell: (v) => sheetsCell(`def:${String(v)}`), - cell: TextComponent, - editorComponent: TextEditorComponent, - }); - - it('toSheetsCell uses the def for a matching typeKey', () => { - const reg = createCellTypeRegistry([textDef], makeBuiltins()); - expect(reg.toSheetsCell('text', 'hi', ctx)).toMatchObject({ displayData: 'def:hi' }); - }); - - it('toSheetsCell falls back to builtins for an unregistered typeKey', () => { - const reg = createCellTypeRegistry([textDef], makeBuiltins()); - expect(reg.toSheetsCell('number', 7, ctx)).toMatchObject({ displayData: 'builtin:7' }); - }); - - it('getCellComponent returns the def component for a matching typeKey', () => { - const reg = createCellTypeRegistry([textDef], makeBuiltins()); - expect(reg.getCellComponent('text')).toBe(TextComponent); - }); - - it('getCellComponent returns undefined for an unregistered typeKey', () => { - const reg = createCellTypeRegistry([textDef], makeBuiltins()); - expect(reg.getCellComponent('number')).toBeUndefined(); - }); - - it('getEditorComponent returns the def editor for a matching typeKey', () => { - const reg = createCellTypeRegistry([textDef], makeBuiltins()); - expect(reg.getEditorComponent('text')).toBe(TextEditorComponent); - }); - - it('getEditorComponent returns undefined for an unregistered typeKey', () => { - const reg = createCellTypeRegistry([textDef], makeBuiltins()); - expect(reg.getEditorComponent('number')).toBeUndefined(); - }); -}); diff --git a/packages/sheets/src/commands/__tests__/default-commands.test.ts b/packages/sheets/src/commands/__tests__/default-commands.test.ts deleted file mode 100644 index d4fac2c..0000000 --- a/packages/sheets/src/commands/__tests__/default-commands.test.ts +++ /dev/null @@ -1,200 +0,0 @@ -/** - * PARITY spec — pins each DEFAULT_COMMANDS body to the VERBATIM sheets.tsx branch it was - * lifted from, using a spy ctx (mock-context). Asserts the exact ctx dispatcher calls + - * args + anchor resets + scroll order the original handler performed, BEFORE the Stage B - * wiring. This is the regression gate for the lift: if a body drifts from the original - * keydown/pointer handler, a call assertion here fails. - */ - -import { describe, expect, it } from 'vitest'; - -import { createGridCommandRegistry } from '../registry'; -import type { GridCommand } from '../types'; -import { emptySheetsSelection, selectAllCells, computeKeyNavTarget } from '../../selection/selection-model'; -import { createMockCtx, type MockCtx } from './mock-context'; - -const registry = createGridCommandRegistry(); -function cmd(id: string): GridCommand { - const c = registry.get(id); - if (!c) throw new Error(`missing command ${id}`); - return c; -} -function run(id: string, ctx: MockCtx, payload?: unknown) { - void cmd(id).run(ctx, payload); -} -function canRun(id: string, ctx: MockCtx, payload?: unknown): boolean { - const c = cmd(id); - return c.canRun ? c.canRun(ctx, payload) : true; -} - -describe('edit.undo / edit.redo (lifted: undo/redo keydown branch)', () => { - it('edit.undo calls ctx.undo', () => { - const ctx = createMockCtx(); - run('edit.undo', ctx); - expect(ctx.undo).toHaveBeenCalledTimes(1); - expect(ctx.redo).not.toHaveBeenCalled(); - }); - it('edit.redo calls ctx.redo', () => { - const ctx = createMockCtx(); - run('edit.redo', ctx); - expect(ctx.redo).toHaveBeenCalledTimes(1); - }); -}); - -describe('selection.all (lifted: Ctrl/Cmd+A branch)', () => { - it('canRun is false on an empty grid (original returned early)', () => { - expect(canRun('selection.all', createMockCtx({ rowCount: 0 }))).toBe(false); - expect(canRun('selection.all', createMockCtx({ colCount: 0 }))).toBe(false); - expect(canRun('selection.all', createMockCtx())).toBe(true); - }); - it('sets anchor [0,0] and selects the whole grid via selectAllCells', () => { - const sel = emptySheetsSelection; - const ctx = createMockCtx({ selection: sel, colCount: 4, rowCount: 7 }); - run('selection.all', ctx); - expect(ctx.setAnchor).toHaveBeenCalledWith([0, 0]); - expect(ctx.setSelection).toHaveBeenCalledWith(selectAllCells(sel, 4, 7)); - }); -}); - -describe('cell.move* (lifted: plain-arrow branch)', () => { - const cases: [string, number, number][] = [ - ['cell.moveUp', 0, -1], - ['cell.moveDown', 0, 1], - ['cell.moveLeft', -1, 0], - ['cell.moveRight', 1, 0], - ]; - for (const [id, dCol, dRow] of cases) { - it(`${id}: setAnchor(clamped target) → moveActiveCell(delta) → scrollToCell(target)`, () => { - const base: [number, number] = [1, 1]; - const ctx = createMockCtx({ selection: { current: { cell: base } } as never, colCount: 3, rowCount: 10 }); - run(id, ctx); - const nextCol = Math.max(0, Math.min(base[0] + dCol, 3 - 1)); - const nextRow = Math.max(0, Math.min(base[1] + dRow, 10 - 1)); - expect(ctx.setAnchor).toHaveBeenCalledWith([nextCol, nextRow]); - expect(ctx.moveActiveCell).toHaveBeenCalledWith(dCol, dRow, 3, 10); - expect(ctx.scrollToCell).toHaveBeenCalledWith(nextCol, nextRow); - // plain move never extends. - expect(ctx.extendToCell).not.toHaveBeenCalled(); - }); - } - it('clamps at the grid edge (no overflow past col/row bounds)', () => { - const ctx = createMockCtx({ selection: { current: { cell: [0, 0] } } as never, colCount: 3, rowCount: 10 }); - run('cell.moveLeft', ctx); - expect(ctx.setAnchor).toHaveBeenCalledWith([0, 0]); - expect(ctx.scrollToCell).toHaveBeenCalledWith(0, 0); - }); - it('canRun false on empty grid', () => { - expect(canRun('cell.moveDown', createMockCtx({ rowCount: 0 }))).toBe(false); - }); -}); - -describe('cell.extend* (lifted: shift-arrow branch)', () => { - const cases: [string, number, number][] = [ - ['cell.extendUp', 0, -1], - ['cell.extendDown', 0, 1], - ['cell.extendLeft', -1, 0], - ['cell.extendRight', 1, 0], - ]; - for (const [id, dCol, dRow] of cases) { - it(`${id}: extendToCell(clamped target) → scrollToCell(target), never moveActiveCell`, () => { - const base: [number, number] = [1, 1]; - const ctx = createMockCtx({ selection: { current: { cell: base } } as never, colCount: 3, rowCount: 10 }); - run(id, ctx); - const nextCol = Math.max(0, Math.min(base[0] + dCol, 3 - 1)); - const nextRow = Math.max(0, Math.min(base[1] + dRow, 10 - 1)); - expect(ctx.extendToCell).toHaveBeenCalledWith(nextCol, nextRow); - expect(ctx.scrollToCell).toHaveBeenCalledWith(nextCol, nextRow); - expect(ctx.moveActiveCell).not.toHaveBeenCalled(); - // extend keeps the anchor put (never resets it). - expect(ctx.setAnchor).not.toHaveBeenCalled(); - }); - } -}); - -describe('cell.navAbsolute (lifted: Tab/Home/End/PageUp/PageDown branch)', () => { - it('computes the target via computeKeyNavTarget, resets anchor, activates, scrolls', () => { - const base: [number, number] = [1, 2]; - const ctx = createMockCtx({ selection: { current: { cell: base } } as never, colCount: 3, rowCount: 10 }); - const payload = { key: 'End', ctrlOrMeta: true, shift: false, pageRows: 5 }; - run('cell.navAbsolute', ctx, payload); - const target = computeKeyNavTarget('End', { ctrlOrMeta: true, shift: false }, base, 3, 10, 5)!; - expect(ctx.setAnchor).toHaveBeenCalledWith([target[0], target[1]]); - expect(ctx.setActiveCell).toHaveBeenCalledWith(target[0], target[1]); - expect(ctx.scrollToCell).toHaveBeenCalledWith(target[0], target[1]); - }); - it('no-ops when computeKeyNavTarget yields null (unhandled key)', () => { - const ctx = createMockCtx(); - run('cell.navAbsolute', ctx, { key: 'Escape', ctrlOrMeta: false, shift: false, pageRows: 5 }); - expect(ctx.setActiveCell).not.toHaveBeenCalled(); - expect(ctx.scrollToCell).not.toHaveBeenCalled(); - }); - it('canRun false on empty grid', () => { - expect(canRun('cell.navAbsolute', createMockCtx({ colCount: 0 }))).toBe(false); - }); -}); - -describe('editor.openActive (lifted: Enter/F2 branch)', () => { - it('canRun opens the editor at the active cell and returns its boolean', () => { - const opened = createMockCtx({ openEditorAtActiveResult: true }); - expect(canRun('editor.openActive', opened)).toBe(true); - expect(opened.openEditorAtActive).toHaveBeenCalledWith(); - - const notOpened = createMockCtx({ openEditorAtActiveResult: false }); - expect(canRun('editor.openActive', notOpened)).toBe(false); - }); -}); - -describe('editor.typeToEdit (lifted: type-to-edit sentinel)', () => { - it('canRun opens the editor seeded with the typed char and returns its boolean', () => { - const ctx = createMockCtx({ openEditorAtActiveResult: true }); - expect(canRun('editor.typeToEdit', ctx, { char: 'q' })).toBe(true); - expect(ctx.openEditorAtActive).toHaveBeenCalledWith('q'); - }); - it('returns false (key falls through) when the cell is non-editable', () => { - const ctx = createMockCtx({ openEditorAtActiveResult: false }); - expect(canRun('editor.typeToEdit', ctx, { char: 'q' })).toBe(false); - }); -}); - -describe('cell.activate / cell.extendToClicked (lifted: handleActivateCell)', () => { - it('cell.activate resets the anchor and parks the active cell', () => { - const ctx = createMockCtx(); - run('cell.activate', ctx, { col: 2, row: 4 }); - expect(ctx.setAnchor).toHaveBeenCalledWith([2, 4]); - expect(ctx.setActiveCell).toHaveBeenCalledWith(2, 4); - }); - it('cell.extendToClicked extends from the latched anchor (shift-click), no anchor reset', () => { - const ctx = createMockCtx(); - run('cell.extendToClicked', ctx, { col: 2, row: 4 }); - expect(ctx.extendToCell).toHaveBeenCalledWith(2, 4); - expect(ctx.setActiveCell).not.toHaveBeenCalled(); - expect(ctx.setAnchor).not.toHaveBeenCalled(); - }); -}); - -describe('editor.open (lifted: dblclick openEditor)', () => { - it('opens the overlay at the explicit cell + anchor rect', () => { - const ctx = createMockCtx(); - const rect = { top: 1 } as DOMRect; - run('editor.open', ctx, { rowIndex: 3, colKey: 'name', anchorRect: rect, initialText: 'x' }); - expect(ctx.openEditorAt).toHaveBeenCalledWith(3, 'name', rect, 'x'); - }); -}); - -describe('structural pointer commands (routed for invariant completeness)', () => { - it('header.sortToggle delegates to ctx.sortToggle(colKey)', () => { - const ctx = createMockCtx(); - run('header.sortToggle', ctx, { colKey: 'name' }); - expect(ctx.sortToggle).toHaveBeenCalledWith('name'); - }); - it('rowmarker.toggleRow delegates to ctx.toggleRow(rowIndex, shift)', () => { - const ctx = createMockCtx(); - run('rowmarker.toggleRow', ctx, { rowIndex: 5, shift: true }); - expect(ctx.toggleRow).toHaveBeenCalledWith(5, true); - }); - it('rowmarker.toggleAll delegates to ctx.toggleAll', () => { - const ctx = createMockCtx(); - run('rowmarker.toggleAll', ctx); - expect(ctx.toggleAll).toHaveBeenCalledTimes(1); - }); -}); diff --git a/packages/sheets/src/commands/__tests__/keymap.test.ts b/packages/sheets/src/commands/__tests__/keymap.test.ts index 8af1bda..3e6a410 100644 --- a/packages/sheets/src/commands/__tests__/keymap.test.ts +++ b/packages/sheets/src/commands/__tests__/keymap.test.ts @@ -7,7 +7,7 @@ import { afterEach, describe, expect, it } from 'vitest'; -import { DEFAULT_KEYMAP, resolveKeyCommand } from '../keymap'; +import { resolveKeyCommand } from '../keymap'; function ev(key: string, mods: Partial<{ ctrl: boolean; meta: boolean; shift: boolean; alt: boolean }> = {}): KeyboardEvent { return { @@ -124,19 +124,3 @@ describe('first-match-wins', () => { expect(resolveKeyCommand(ev('x'), map)).toBe('k'); }); }); - -describe('DEFAULT_KEYMAP shape', () => { - it('declares pointer triggers for the structural gestures', () => { - const pointers = DEFAULT_KEYMAP.filter((b) => b.trigger.kind === 'pointer').map((b) => b.command); - expect(pointers).toEqual( - expect.arrayContaining([ - 'cell.activate', - 'cell.extendToClicked', - 'editor.open', - 'header.sortToggle', - 'rowmarker.toggleRow', - 'rowmarker.toggleAll', - ]), - ); - }); -}); diff --git a/packages/sheets/src/commands/__tests__/registry.test.ts b/packages/sheets/src/commands/__tests__/registry.test.ts index a020537..2661e85 100644 --- a/packages/sheets/src/commands/__tests__/registry.test.ts +++ b/packages/sheets/src/commands/__tests__/registry.test.ts @@ -1,6 +1,6 @@ /** - * Spec for createGridCommandRegistry (registry.ts). Covers default population, consumer - * override REPLACING a default by id, and a new id ADDING a command (the Phase 2 seam). + * Dynamic command customization is runtime behavior; static default registration is + * covered by module construction and the package build. */ import { describe, expect, it, vi } from 'vitest'; @@ -8,14 +8,6 @@ import { describe, expect, it, vi } from 'vitest'; import { createGridCommandRegistry, DEFAULT_COMMANDS } from '../registry'; describe('createGridCommandRegistry', () => { - it('populates every default command by id', () => { - const registry = createGridCommandRegistry(); - for (const cmd of DEFAULT_COMMANDS) { - expect(registry.get(cmd.id)).toBe(cmd); - } - expect(registry.size).toBe(DEFAULT_COMMANDS.length); - }); - it('a consumer override with a matching id REPLACES the default', () => { const override = { id: 'edit.undo', run: vi.fn() }; const registry = createGridCommandRegistry([override]); diff --git a/packages/sheets/src/grid-dom/__tests__/sheets-dom.parity.test.tsx b/packages/sheets/src/grid-dom/__tests__/sheets-dom.parity.test.tsx deleted file mode 100644 index a2d814f..0000000 --- a/packages/sheets/src/grid-dom/__tests__/sheets-dom.parity.test.tsx +++ /dev/null @@ -1,175 +0,0 @@ -/* @vitest-environment jsdom */ -// -// CAPSTONE test (post-cutover). The TanStack DOM grid is the ONLY render path; the -// canvas glide grid is gone, so this proves two things over the all-cell-types -// showcase fixture the Storybook showcase renders: -// -// • DOM mount — the real public is rendered through the -// in-memory mock adapter (createMockAdapter + the showcase fixture) in jsdom, -// exactly as the stories do, and we wait for it to populate (role="gridcell" -// nodes appear once the mock query resolves). This is the genuine DOM render -// path on the showcase data, drawing zero canvas. -// -// • Display golden — for every (row, column) the native pipeline -// (createSheetsCell -> projectSheetsCell) projects to a stable -// { displayText, copyText } pair, frozen in `sheets-dom.golden.json`. With the -// glide reference removed, the native projection IS the source of truth; the -// committed golden locks it so a future regression to the dispatcher/factories -// over the showcase fixture is caught. Cell types are resolved through the -// PRODUCTION channel `resolveCellType(name, fieldMeta)` (reads field.type.pgAlias) -// — the same resolution useSheetsContent performs at runtime. -import { act } from 'react'; -import { createRoot, type Root } from 'react-dom/client'; -import { afterEach, beforeEach, describe, expect, it } from 'vitest'; -import { QueryClient } from '@tanstack/react-query'; - -import { SheetsProvider } from '../../context/sheets-provider'; -import type { SheetsConfig } from '../../context/sheets-context'; -import { Sheets } from '../../grid/sheets'; -import { buildAllCellTypesTable, makeRows } from '../../stories/_support/fixtures'; -import { createMockAdapter } from '../../stories/_support/mock-adapter'; -import { resolveCellType, type FieldMetadata } from '../../cell-types/cell-type-resolver'; -import { createSheetsCell } from '../../cell-model/create-sheets-cell'; -import { - makeMetadata, - fakeGeometrySheetsCell, - RELATION_INFO_BELONGS_TO, -} from '../../grid/__golden__/display-cases'; -import { assertOrUpdateGolden, projectSheetsCell } from '../../grid/__golden__/parity.harness'; -import type { CellCreationMetadata } from '../../grid/grid-cell-types'; -import type { MetaField } from '../../forms/types'; -import type { SheetsRow } from '../../grid/row-model'; - -(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true; - -// ─── Shared showcase fixture (the all-cell-types table the showcase renders) ───────────────────── -const TABLE = buildAllCellTypesTable(); -const TABLE_NAME = TABLE.name; // 'demo' -const ROWS: SheetsRow[] = makeRows(TABLE, 12, 7); -const FIELDS = (TABLE.fields ?? []) as MetaField[]; - -// Build the CellCreationMetadata the grid would build for a column: cellType resolved through the -// PRODUCTION channel (resolveCellType reads field.type.pgAlias), relationInfo supplied for the -// relation column exactly as useSheetsContent does (the relation factory keys canHandle on the -// resolved 'relation' cellType, so display parity needs the same metadata). -function metadataForField(field: MetaField): CellCreationMetadata { - const fieldMeta = field as unknown as FieldMetadata; - const { cellType } = resolveCellType(field.name, fieldMeta); - const isRelation = cellType === 'relation'; - return makeMetadata(cellType, { - fieldName: field.name, - fieldMeta, - ...(isRelation ? { relationInfo: RELATION_INFO_BELONGS_TO, relationOptions: {} } : {}), - }); -} - -const proto = window.HTMLElement.prototype; - -describe('CAPSTONE — DOM grid renders + projects the showcase fixture (frozen golden)', () => { - let root: Root; - let container: HTMLDivElement; - const origW = Object.getOwnPropertyDescriptor(proto, 'offsetWidth'); - const origH = Object.getOwnPropertyDescriptor(proto, 'offsetHeight'); - const origRect = proto.getBoundingClientRect; - - beforeEach(() => { - // jsdom does no layout; shim element geometry so TanStack Virtual emits a realistic, bounded - // window (sized scroll element + a real per-row height the row virtualizer measures). - Object.defineProperty(proto, 'offsetWidth', { configurable: true, get: () => 1200 }); - Object.defineProperty(proto, 'offsetHeight', { configurable: true, get: () => 800 }); - proto.getBoundingClientRect = function () { - return { width: 1200, height: 33, top: 0, left: 0, right: 1200, bottom: 33, x: 0, y: 0, toJSON() {} } as DOMRect; - }; - container = document.createElement('div'); - document.body.appendChild(container); - root = createRoot(container); - }); - - afterEach(async () => { - await act(async () => { - root.unmount(); - }); - container.remove(); - if (origW) Object.defineProperty(proto, 'offsetWidth', origW); - else delete (proto as unknown as Record).offsetWidth; - if (origH) Object.defineProperty(proto, 'offsetHeight', origH); - else delete (proto as unknown as Record).offsetHeight; - proto.getBoundingClientRect = origRect; - }); - - it('mounts the real DOM grid over the showcase fixture and it populates', async () => { - const adapter = createMockAdapter({ table: TABLE, rows: ROWS }); - const config: SheetsConfig = { - endpoint: 'mock://parity', - auth: { mode: 'embedded', getToken: () => 't' }, - adapter, - queryClient: new QueryClient({ - defaultOptions: { queries: { retry: false, gcTime: 0, staleTime: 0 }, mutations: { retry: false } }, - }), - }; - - await act(async () => { - root.render( -
- - - -
, - ); - }); - - // Poll for the grid to populate after the async mock query resolves. - await waitFor(() => { - expect(container.querySelector('[data-part-id="sheets-viewport"]')).not.toBeNull(); - expect(container.querySelector('[data-impl="dom"]')).not.toBeNull(); - expect(container.querySelectorAll('[role="gridcell"]').length).toBeGreaterThan(0); - }); - - // Zero canvas on the DOM render path. - expect(container.querySelectorAll('canvas').length).toBe(0); - }); - - it('projects each (row, column) of the showcase fixture to the frozen golden', () => { - // The native DOM pipeline is the ONLY display path now, so its projection IS the - // source of truth — frozen here so a dispatcher/factory regression over the showcase - // fixture is caught. cell types are resolved through the production resolver. - const rows = FIELDS.map((field) => { - const meta = metadataForField(field); - const cells = ROWS.map((row, r) => { - const value = (row as Record)[field.name]; - const { displayText, copyText } = projectSheetsCell(createSheetsCell(value, meta, fakeGeometrySheetsCell)); - return { row: r, displayText, copyText }; - }); - return { field: field.name, cellType: meta.cellType, cells }; - }); - - // Guard against a vacuous golden: the fixture must actually exercise every column × row. - const assertions = rows.reduce((n, f) => n + f.cells.length, 0); - expect(assertions).toBe(FIELDS.length * ROWS.length); - expect(FIELDS.length).toBeGreaterThanOrEqual(46); - - assertOrUpdateGolden('sheets-dom.golden', rows); - }); -}); - -// ─── Minimal async poller (no @testing-library in this package; mirrors the smoke-test idiom) ───── -async function waitFor(assert: () => void, { timeout = 4000, interval = 25 } = {}): Promise { - const start = Date.now(); - let lastError: unknown; - // eslint-disable-next-line no-constant-condition - while (true) { - try { - await act(async () => { - await Promise.resolve(); - }); - assert(); - return; - } catch (error) { - lastError = error; - if (Date.now() - start > timeout) throw lastError; - await act(async () => { - await new Promise((resolve) => setTimeout(resolve, interval)); - }); - } - } -} diff --git a/packages/sheets/src/grid-dom/__tests__/sheets-dom.smoke.test.tsx b/packages/sheets/src/grid-dom/__tests__/sheets-dom.smoke.test.tsx deleted file mode 100644 index c8c2f4e..0000000 --- a/packages/sheets/src/grid-dom/__tests__/sheets-dom.smoke.test.tsx +++ /dev/null @@ -1,139 +0,0 @@ -/* @vitest-environment jsdom */ -// -// Phase-1 DOM-spine smoke test. Renders the S4 GridViewport over the S3 table -// instance with the S5 SheetsCellHost, fed HAND-CRAFTED rows — NOT the full -// GraphQL-backed Sheets (its data hooks pull @constructive-io/data, awkward -// under vitest; full integration is Chrome-verified at Phase 8). -// -// Uses the package's existing component-test idiom: jsdom + react-dom/client -// createRoot + act (no @testing-library — not a dep of this package). -// -// jsdom does NO layout, so TanStack Virtual's `getRect` (reads offsetWidth/ -// offsetHeight) sees 0 → outerSize 0 → range null → zero virtual items. We shim -// offsetWidth/offsetHeight on HTMLElement so the scroll element reports a real -// viewport and every fixture row/column virtualizes in. -import { act } from 'react'; -import { createRoot, type Root } from 'react-dom/client'; -import { afterEach, beforeEach, describe, expect, it } from 'vitest'; - -import { useSheetsTableInstance, type SheetsColumnDescriptor } from '../../table/use-sheets-table-instance'; -import { GridViewport, type RenderCell } from '../../table/grid-viewport'; -import { SheetsCellHost } from '../sheets-cell-host'; -import { createSheetsCell } from '../../cell-model/create-sheets-cell'; -import { makeMetadata } from '../../grid/__golden__/display-cases'; -import type { CellType } from '../../cell-types/types'; -import type { SheetsCellResolution } from '../use-sheets-content'; -import type { SheetsRow } from '../../grid/row-model'; - -(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true; - -const COLUMNS: SheetsColumnDescriptor[] = [ - { key: 'name', name: 'Name', size: 160 }, - { key: 'count', name: 'Count', size: 160 }, - { key: 'active', name: 'Active', size: 160 } -]; - -const ROWS: SheetsRow[] = [ - { id: 'r1', name: 'Alpha', count: 11, active: true }, - { id: 'r2', name: 'Bravo', count: 22, active: false }, - { id: 'r3', name: 'Charlie', count: 33, active: true } -]; - -// Map each fixture column to the CellType the resolver should build it as. The host -// now requires an injected resolver (it no longer guesses kind from the JS value), -// so the test stands in for the real `getSheetsCellContent`: read the hand-fed value -// and dispatch through the same `createSheetsCell` family selection the shell uses. -const COL_TYPE: Record = { name: 'text', count: 'number', active: 'boolean' }; - -const getSheetsCellContent = (rowIndex: number, colKey: string): SheetsCellResolution => { - const value = (ROWS[rowIndex] as Record)?.[colKey]; - const typeKey = COL_TYPE[colKey] ?? 'unknown'; - const cell = createSheetsCell(value, makeMetadata(typeKey)); - return { cell, component: undefined, colKey, typeKey }; -}; - -const renderCell: RenderCell = (c, ctx) => ( - -); - -const noop = () => {}; -const EMPTY = {} as never; - -function Harness() { - const table = useSheetsTableInstance({ - columns: COLUMNS, - data: ROWS, - columnSizing: EMPTY, - columnPinning: EMPTY, - rowSelection: EMPTY, - onColumnSizingChange: noop, - onColumnPinningChange: noop, - onRowSelectionChange: noop - }); - return ; -} - -describe('DOM spine smoke (GridViewport + SheetsCellHost, hand-fed)', () => { - let root: Root; - let container: HTMLDivElement; - const proto = window.HTMLElement.prototype; - const origW = Object.getOwnPropertyDescriptor(proto, 'offsetWidth'); - const origH = Object.getOwnPropertyDescriptor(proto, 'offsetHeight'); - - beforeEach(() => { - // Give the (layout-less) jsdom elements a non-zero size so TanStack - // Virtual's getRect yields outerSize > 0 and emits virtual items. - Object.defineProperty(proto, 'offsetWidth', { configurable: true, get: () => 1000 }); - Object.defineProperty(proto, 'offsetHeight', { configurable: true, get: () => 1000 }); - container = document.createElement('div'); - document.body.appendChild(container); - root = createRoot(container); - }); - - afterEach(async () => { - await act(async () => { - root.unmount(); - }); - container.remove(); - if (origW) Object.defineProperty(proto, 'offsetWidth', origW); - else delete (proto as unknown as Record).offsetWidth; - if (origH) Object.defineProperty(proto, 'offsetHeight', origH); - else delete (proto as unknown as Record).offsetHeight; - }); - - it('renders real gridcell nodes, value text, the dom marker, and zero canvas', async () => { - await act(async () => { - root.render(); - }); - - // (a) Real DOM nodes with role=gridcell. 3 rows × 3 cols (col0 sticky + - // 2 virtualized) = 9. Asserts count > 0 AND the exact expected count. - const gridcells = container.querySelectorAll('[role="gridcell"]'); - expect(gridcells.length).toBeGreaterThan(0); - expect(gridcells.length).toBe(ROWS.length * COLUMNS.length); - - // (b) text / number values surface as DOM text. Booleans render via BooleanCellView - // as a read-only design-system Checkbox (role=checkbox + aria-checked), NOT the - // literal "true"/"false". - const text = container.textContent ?? ''; - expect(text).toContain('Alpha'); // text - expect(text).toContain('Charlie'); - expect(text).toContain('11'); // number - expect(text).toContain('33'); - expect(text).not.toContain('true'); // booleans never surface as literal strings - expect(text).not.toContain('false'); - // Boolean cells exist (one per row's `active` column), each a read-only Checkbox; - // both polarities are present (some checked, some unchecked). - const boolCells = Array.from(container.querySelectorAll('[data-slot="boolean-cell"]')); - expect(boolCells.length).toBe(ROWS.length); - expect(boolCells.every((c) => c.querySelector('[data-slot="checkbox"]'))).toBe(true); // each renders a Checkbox - expect(boolCells.some((c) => c.querySelector('[data-slot="checkbox"][aria-checked="true"]'))).toBe(true); // some checked - expect(boolCells.some((c) => c.querySelector('[data-slot="checkbox"][aria-checked="false"]'))).toBe(true); // some unchecked - - // (c) the DOM-path marker is present (GridViewport root). - expect(container.querySelector('[data-impl="dom"]')).not.toBeNull(); - - // (d) ZERO canvas elements — the canvas render path is not on the DOM spine. - expect(container.querySelectorAll('canvas').length).toBe(0); - }); -}); diff --git a/packages/sheets/src/grid-dom/editors/__tests__/edit-intent.test.ts b/packages/sheets/src/grid-dom/editors/__tests__/edit-intent.test.ts index 7d422d5..e7f8a87 100644 --- a/packages/sheets/src/grid-dom/editors/__tests__/edit-intent.test.ts +++ b/packages/sheets/src/grid-dom/editors/__tests__/edit-intent.test.ts @@ -1,217 +1,40 @@ -/* @vitest-environment node */ - -// EXHAUSTIVE routing contract for the edit-intent resolver — the regression guard -// for the bug this fix closes: EVERY non-text cell used to mis-route to TextEditor -// because the host read a `meta.cellType` the factories never set. Here we assert -// every CellType lands in exactly the bucket the AUTHORITATIVE enumeration prescribes, -// that readonly always wins, and that a consumer override wins for overlay types (but -// never flips inline-toggle or readonly). -// -// EXHAUSTIVENESS is anchored to the SAME authoritative list every golden asserts -// through — ALL_CELL_TYPES from grid/__golden__/parity.harness (the runtime mirror of -// the CellType union, typed `Record` upstream so it can only lag in the -// safe direction). Importing it here — rather than re-listing the union — means a new -// CellType is forced into a bucket by THIS test the moment it joins the union: the -// `for (const type of ALL_CELL_TYPES)` loop visits it, and its `default`-less -// classification below makes an unclassified member fail loudly. - import { describe, expect, it } from 'vitest'; import type { CellType } from '../../../cell-types/types'; -import { - BOOLEAN_TYPES, - DATE_TIME_TYPES, - GEOMETRY_TYPES, - MEDIA_TYPES, - NUMBER_TYPES, - isArrayCellType, -} from '../../../cell-types/cell-type-groups'; -import { ALL_CELL_TYPES } from '../../../grid/__golden__/parity.harness'; import { DOM_EDITOR_REGISTRY, type NativeEditor } from '../editor-registry-dom'; -import { resolveEditIntent, type EditIntent } from '../edit-intent'; - -// ─── The authoritative per-type expectation, derived FROM the canonical groups ── -// -// `expectedIntentFor` is the spec's enumeration expressed as code, but — like the -// resolver — it is DRIVEN OFF the same cell-type-groups Sets, so it stays exhaustive -// by construction (never a hand-maintained per-type table that could drift). For each -// CellType it returns the intent the resolver MUST produce, keyed to the DOM registry -// editor for overlay types: -// • boolean/toggle/bit -> { mode: 'inline-toggle' } -// • NUMBER_TYPES -> { mode: 'inline-edit' } (simple text-representable, in-cell) -// • DATE_TIME_TYPES -> overlay DateEditorDom (key === type) -// • interval -> overlay IntervalEditorDom -// • json | jsonb -> overlay JsonEditorDom (key === type) -// • isArrayCellType (incl. tags) -> overlay ArrayEditorDom (key === type) -// • GEOMETRY_TYPES -> overlay GeometryEditorDom (key === type) -// • inet -> overlay InetEditorDom (validated editor — NOT inline) -// • MEDIA_TYPES -> overlay ImageEditorDom (key 'image') -// • relation -> overlay RelationEditorDom -// • url -> overlay UrlEditorDom (the explicit text exception — NOT inline) -// • tsvector -> overlay TsvectorEditorDom (viewer-only, still overlay) -// • everything text-like (text/textarea/email/phone/citext/bpchar/uuid/color/ -// origin/unknown) -> { mode: 'inline-edit' } (simple text — edits in place; -// color is just a text string, no dedicated editor) -type ExpectedOverlay = { mode: 'overlay'; editor: NativeEditor }; -type ExpectedIntent = { mode: 'inline-toggle' } | { mode: 'inline-edit' } | ExpectedOverlay; - -function overlayWith(editor: NativeEditor): ExpectedOverlay { - return { mode: 'overlay', editor }; -} +import { resolveEditIntent } from '../edit-intent'; -function expectedIntentFor(type: CellType): ExpectedIntent { - if (BOOLEAN_TYPES.has(type)) return { mode: 'inline-toggle' }; - if (NUMBER_TYPES.has(type)) return { mode: 'inline-edit' }; - if (DATE_TIME_TYPES.has(type)) return overlayWith(DOM_EDITOR_REGISTRY[type]); - if (type === 'interval') return overlayWith(DOM_EDITOR_REGISTRY.interval); - if (type === 'json' || type === 'jsonb') return overlayWith(DOM_EDITOR_REGISTRY[type]); - if (isArrayCellType(type)) return overlayWith(DOM_EDITOR_REGISTRY[type]); - if (GEOMETRY_TYPES.has(type)) return overlayWith(DOM_EDITOR_REGISTRY[type]); - if (type === 'inet') return overlayWith(DOM_EDITOR_REGISTRY.inet); - if (MEDIA_TYPES.has(type)) return overlayWith(DOM_EDITOR_REGISTRY.image); - if (type === 'relation') return overlayWith(DOM_EDITOR_REGISTRY.relation); - if (type === 'url') return overlayWith(DOM_EDITOR_REGISTRY.url); - if (type === 'tsvector') return overlayWith(DOM_EDITOR_REGISTRY.tsvector); - // Text-like floor — text/textarea/email/phone/citext/bpchar/uuid/color/origin/unknown — edits inline. - return { mode: 'inline-edit' }; -} +describe('resolveEditIntent', () => { + it('routes representative cell families to their observable editing modes', () => { + const cases: Array<[CellType, ReturnType]> = [ + ['boolean', { mode: 'inline-toggle' }], + ['integer', { mode: 'inline-edit' }], + ['text', { mode: 'inline-edit' }], + ['date', { mode: 'overlay', editor: DOM_EDITOR_REGISTRY.date }], + ['text-array', { mode: 'overlay', editor: DOM_EDITOR_REGISTRY.array }], + ['geometry-point', { mode: 'overlay', editor: DOM_EDITOR_REGISTRY.geometry }], + ['upload', { mode: 'overlay', editor: DOM_EDITOR_REGISTRY.image }], + ['relation', { mode: 'overlay', editor: DOM_EDITOR_REGISTRY.relation }], + ['url', { mode: 'overlay', editor: DOM_EDITOR_REGISTRY.url }], + ['tsvector', { mode: 'overlay', editor: DOM_EDITOR_REGISTRY.tsvector }], + ]; -describe('resolveEditIntent — EXHAUSTIVE enumeration (every ALL_CELL_TYPES member)', () => { - it(`covers all ${ALL_CELL_TYPES.length} CellTypes with exactly the prescribed intent`, () => { - // The "all combinations" guard: visit EVERY authoritative CellType and assert the - // resolver yields the group-derived expectation. A new union member (hence a new - // ALL_CELL_TYPES entry) is checked here automatically — a missing classification - // in either map fails this loop. - for (const type of ALL_CELL_TYPES) { - const actual = resolveEditIntent(type, {}); - const expected = expectedIntentFor(type); - expect(actual, `${type} -> ${JSON.stringify(expected)}`).toEqual(expected); - - // Overlay editors must resolve to a real component (never an empty portal). - if (expected.mode === 'overlay') { - expect((actual as ExpectedOverlay).editor, `${type} editor is defined`).toBeTruthy(); - } + for (const [cellType, expected] of cases) { + expect(resolveEditIntent(cellType, {}), cellType).toEqual(expected); } }); - it('the enumeration spans the full 46-member CellType union', () => { - // Belt-and-suspenders: the list is the authoritative one (no duplicates), so its - // length is the CellType-union cardinality. Pin it so an accidental edit to the - // harness list (or a union change without a list update) is visible here too. - expect(new Set(ALL_CELL_TYPES).size).toBe(ALL_CELL_TYPES.length); - expect(ALL_CELL_TYPES.length).toBe(46); - }); -}); - -describe('resolveEditIntent — per-family spot checks (spec language)', () => { - it('booleans (boolean/toggle/bit) are inline-toggle, never an overlay', () => { - for (const t of ['boolean', 'toggle', 'bit'] as CellType[]) { - expect(resolveEditIntent(t, {})).toEqual({ mode: 'inline-toggle' }); + it('lets readonly state override every editing route', () => { + for (const cellType of ['boolean', 'text', 'relation'] as CellType[]) { + expect(resolveEditIntent(cellType, { readonly: true })).toEqual({ mode: 'none' }); } }); - it('number family (incl. rating) edits inline (simple text-representable)', () => { - for (const t of ['number', 'integer', 'smallint', 'decimal', 'currency', 'percentage', 'rating'] as CellType[]) { - expect(resolveEditIntent(t, {})).toEqual({ mode: 'inline-edit' }); - } - }); - - it('date/time family routes to DateEditorDom', () => { - for (const t of ['date', 'datetime', 'time', 'timestamptz'] as CellType[]) { - expect(resolveEditIntent(t, {})).toEqual({ mode: 'overlay', editor: DOM_EDITOR_REGISTRY.date }); - } - }); - - it('interval routes to IntervalEditorDom', () => { - expect(resolveEditIntent('interval', {})).toEqual({ mode: 'overlay', editor: DOM_EDITOR_REGISTRY.interval }); - }); - - it('json/jsonb route to JsonEditorDom', () => { - for (const t of ['json', 'jsonb'] as CellType[]) { - expect(resolveEditIntent(t, {})).toEqual({ mode: 'overlay', editor: DOM_EDITOR_REGISTRY.json }); - } - }); - - it('array family (incl. tags) routes to ArrayEditorDom', () => { - for (const t of ['array', 'text-array', 'uuid-array', 'number-array', 'integer-array', 'date-array', 'tags'] as CellType[]) { - expect(resolveEditIntent(t, {})).toEqual({ mode: 'overlay', editor: DOM_EDITOR_REGISTRY[t] }); - expect((resolveEditIntent(t, {}) as ExpectedOverlay).editor).toBe(DOM_EDITOR_REGISTRY.array); - } - }); - - it('geometry family routes to GeometryEditorDom', () => { - for (const t of ['geometry', 'geometry-point', 'geometry-collection'] as CellType[]) { - expect(resolveEditIntent(t, {})).toEqual({ mode: 'overlay', editor: DOM_EDITOR_REGISTRY.geometry }); - } - }); - - it('media family (image/file/video/audio/upload) reuses the image editor', () => { - for (const t of ['image', 'file', 'video', 'audio', 'upload'] as CellType[]) { - expect(resolveEditIntent(t, {})).toEqual({ mode: 'overlay', editor: DOM_EDITOR_REGISTRY.image }); - } - }); - - it('inet routes to InetEditorDom', () => { - expect(resolveEditIntent('inet', {})).toEqual({ mode: 'overlay', editor: DOM_EDITOR_REGISTRY.inet }); - }); - - it('relation routes to RelationEditorDom', () => { - expect(resolveEditIntent('relation', {})).toEqual({ mode: 'overlay', editor: DOM_EDITOR_REGISTRY.relation }); - }); - - it('url routes to UrlEditorDom (was the text overlay before the fix)', () => { - expect(resolveEditIntent('url', {})).toEqual({ mode: 'overlay', editor: DOM_EDITOR_REGISTRY.url }); - }); - - it('tsvector is viewer-only but still opens an overlay (not none)', () => { - expect(resolveEditIntent('tsvector', {})).toEqual({ mode: 'overlay', editor: DOM_EDITOR_REGISTRY.tsvector }); - }); - - it('text-like family edits inline (the simple text floor)', () => { - for (const t of ['text', 'textarea', 'email', 'phone', 'citext', 'bpchar', 'uuid', 'color', 'origin', 'unknown'] as CellType[]) { - expect(resolveEditIntent(t, {})).toEqual({ mode: 'inline-edit' }); - } - }); - - it('url is the explicit text exception — it keeps its overlay editor, NOT inline', () => { - expect(resolveEditIntent('url', {})).toEqual({ mode: 'overlay', editor: DOM_EDITOR_REGISTRY.url }); - }); - - it('color is NOT a dedicated editor — it edits inline, and no color key leaks into the registry', () => { - expect(resolveEditIntent('color', {})).toEqual({ mode: 'inline-edit' }); - expect(DOM_EDITOR_REGISTRY.color).toBeUndefined(); - }); -}); - -describe('resolveEditIntent — readonly & override precedence', () => { - it('readonly wins for EVERY CellType, regardless of bucket', () => { - for (const type of ALL_CELL_TYPES) { - expect(resolveEditIntent(type, { readonly: true }), `${type} readonly -> none`).toEqual({ mode: 'none' }); - } - }); - - it('a consumer override flips an inline text type to its overlay editor', () => { - const Custom = (() => null) as unknown as NativeEditor; - const intent: EditIntent = resolveEditIntent('text', {}, (k) => (k === 'text' ? Custom : undefined)); - expect(intent).toEqual({ mode: 'overlay', editor: Custom }); - }); - - it('a consumer override flips an inline number type to its overlay editor', () => { + it('uses consumer editors only for editable inline cells', () => { const Custom = (() => null) as unknown as NativeEditor; - expect(resolveEditIntent('integer', {}, () => Custom)).toEqual({ mode: 'overlay', editor: Custom }); - }); - - it('without an override, an inline type stays inline (override is opt-in)', () => { - expect(resolveEditIntent('text', {}, () => undefined)).toEqual({ mode: 'inline-edit' }); - }); - it('a consumer override does NOT flip an inline-toggle', () => { - const Custom = (() => null) as unknown as NativeEditor; + expect(resolveEditIntent('text', {}, () => Custom)).toEqual({ mode: 'overlay', editor: Custom }); expect(resolveEditIntent('boolean', {}, () => Custom)).toEqual({ mode: 'inline-toggle' }); - }); - - it('a consumer override does NOT flip a readonly none', () => { - const Custom = (() => null) as unknown as NativeEditor; expect(resolveEditIntent('text', { readonly: true }, () => Custom)).toEqual({ mode: 'none' }); }); }); diff --git a/packages/sheets/src/grid/__golden__/cell-display.golden.json b/packages/sheets/src/grid/__golden__/cell-display.golden.json deleted file mode 100644 index d2b69fb..0000000 --- a/packages/sheets/src/grid/__golden__/cell-display.golden.json +++ /dev/null @@ -1,496 +0,0 @@ -[ - { - "copyText": "hello world", - "displayText": "hello world", - "intendedKind": "text", - "label": "text — string", - "typeKey": "text", - "value": "hello world" - }, - { - "copyText": "line one\nline two", - "displayText": "line one\nline two", - "intendedKind": "text", - "label": "textarea — multiline string", - "typeKey": "textarea", - "value": "line one\nline two" - }, - { - "copyText": "mailto:ada@example.com", - "displayText": "ada@example.com", - "intendedKind": "uri", - "label": "email — address (mailto data)", - "typeKey": "email", - "value": "ada@example.com" - }, - { - "copyText": "https://example.com/path", - "displayText": "https://example.com/path", - "intendedKind": "uri", - "label": "url — http link", - "typeKey": "url", - "value": "https://example.com/path" - }, - { - "copyText": "+1 555 0100", - "displayText": "+1 555 0100", - "intendedKind": "text", - "label": "phone — digits string", - "typeKey": "phone", - "value": "+1 555 0100" - }, - { - "copyText": "MixedCase", - "displayText": "MixedCase", - "intendedKind": "text", - "label": "citext — case-insensitive text", - "typeKey": "citext", - "value": "MixedCase" - }, - { - "copyText": "AB ", - "displayText": "AB ", - "intendedKind": "text", - "label": "bpchar — padded char", - "typeKey": "bpchar", - "value": "AB " - }, - { - "copyText": "42.5", - "displayText": "42.5", - "intendedKind": "number", - "label": "number — float", - "typeKey": "number", - "value": 42.5 - }, - { - "copyText": "17", - "displayText": "17", - "intendedKind": "number", - "label": "integer — int", - "typeKey": "integer", - "value": 17 - }, - { - "copyText": "7", - "displayText": "7", - "intendedKind": "number", - "label": "smallint — small int", - "typeKey": "smallint", - "value": 7 - }, - { - "copyText": "123.45", - "displayText": "123.45", - "intendedKind": "number", - "label": "decimal — string coerced", - "typeKey": "decimal", - "value": "123.45" - }, - { - "copyText": "1999.99", - "displayText": "1999.99", - "intendedKind": "number", - "label": "currency — number", - "typeKey": "currency", - "value": 1999.99 - }, - { - "copyText": "0.42", - "displayText": "0.42", - "intendedKind": "number", - "label": "percentage — number", - "typeKey": "percentage", - "value": 0.42 - }, - { - "copyText": "2026-06-11", - "displayText": "2026-06-11", - "intendedKind": "text", - "label": "date — iso date", - "typeKey": "date", - "value": "2026-06-11" - }, - { - "copyText": "2026-06-11T10:00:00", - "displayText": "2026-06-11T10:00:00", - "intendedKind": "text", - "label": "datetime — iso datetime", - "typeKey": "datetime", - "value": "2026-06-11T10:00:00" - }, - { - "copyText": "10:00:00", - "displayText": "10:00:00", - "intendedKind": "text", - "label": "time — clock", - "typeKey": "time", - "value": "10:00:00" - }, - { - "copyText": "2026-06-11T10:00:00Z", - "displayText": "2026-06-11T10:00:00Z", - "intendedKind": "text", - "label": "timestamptz — iso tz", - "typeKey": "timestamptz", - "value": "2026-06-11T10:00:00Z" - }, - { - "copyText": "{\"years\":1,\"months\":6}", - "displayText": "1y 6mo", - "intendedKind": "text", - "label": "interval — object formatted", - "typeKey": "interval", - "value": { - "months": 6, - "years": 1 - } - }, - { - "copyText": "true", - "displayText": "", - "intendedKind": "boolean", - "label": "boolean — true", - "typeKey": "boolean", - "value": true - }, - { - "copyText": "true", - "displayText": "", - "intendedKind": "boolean", - "label": "toggle — truthy coercion", - "typeKey": "toggle", - "value": 1 - }, - { - "copyText": "true", - "displayText": "", - "intendedKind": "boolean", - "label": "bit — string coercion (truthy)", - "typeKey": "bit", - "value": "1" - }, - { - "copyText": "{\"a\":1,\"b\":\"two\"}", - "displayText": "{\"a\":1,\"b\":\"two\"}", - "intendedKind": "text", - "label": "json — object preview", - "typeKey": "json", - "value": { - "a": 1, - "b": "two" - } - }, - { - "copyText": "{\"nested\":{\"x\":[1,2]}}", - "displayText": "{\"nested\":{\"x\":[1,2]}}", - "intendedKind": "text", - "label": "jsonb — nested object preview", - "typeKey": "jsonb", - "value": { - "nested": { - "x": [ - 1, - 2 - ] - } - } - }, - { - "copyText": "[\"a\",\"b\",\"c\"]", - "displayText": "", - "intendedKind": "badges", - "label": "array — generic list", - "typeKey": "array", - "value": [ - "a", - "b", - "c" - ] - }, - { - "copyText": "[\"x\",\"y\"]", - "displayText": "", - "intendedKind": "badges", - "label": "text-array — strings", - "typeKey": "text-array", - "value": [ - "x", - "y" - ] - }, - { - "copyText": "[\"11111111-1111\",\"22222222-2222\"]", - "displayText": "", - "intendedKind": "badges", - "label": "uuid-array — uuids", - "typeKey": "uuid-array", - "value": [ - "11111111-1111", - "22222222-2222" - ] - }, - { - "copyText": "[\"1\",\"2\",\"3\"]", - "displayText": "", - "intendedKind": "badges", - "label": "number-array — numbers", - "typeKey": "number-array", - "value": [ - 1, - 2, - 3 - ] - }, - { - "copyText": "[\"10\",\"20\"]", - "displayText": "", - "intendedKind": "badges", - "label": "integer-array — ints", - "typeKey": "integer-array", - "value": [ - 10, - 20 - ] - }, - { - "copyText": "[\"2026-01-01\",\"2026-02-02\"]", - "displayText": "", - "intendedKind": "badges", - "label": "date-array — date strings", - "typeKey": "date-array", - "value": [ - "2026-01-01", - "2026-02-02" - ] - }, - { - "copyText": "geo:{\"type\":\"Point\",\"coordinates\":[1,2]}", - "displayText": "geo:{\"type\":\"Point\",\"coordinates\":[1,2]}", - "intendedKind": "geometry", - "label": "geometry — point geojson", - "typeKey": "geometry", - "value": { - "coordinates": [ - 1, - 2 - ], - "type": "Point" - } - }, - { - "copyText": "geo:{\"x\":1,\"y\":2}", - "displayText": "geo:{\"x\":1,\"y\":2}", - "intendedKind": "geometry", - "label": "geometry-point — bare xy", - "typeKey": "geometry-point", - "value": { - "x": 1, - "y": 2 - } - }, - { - "copyText": "geo:{\"type\":\"GeometryCollection\",\"geometries\":[]}", - "displayText": "geo:{\"type\":\"GeometryCollection\",\"geometries\":[]}", - "intendedKind": "geometry", - "label": "geometry-collection — collection", - "typeKey": "geometry-collection", - "value": { - "geometries": [], - "type": "GeometryCollection" - } - }, - { - "copyText": "192.168.0.1", - "displayText": "192.168.0.1", - "intendedKind": "text", - "label": "inet — ip address", - "typeKey": "inet", - "value": "192.168.0.1" - }, - { - "copyText": "[\"https://example.com/a.png\"]", - "displayText": "[\"https://example.com/a.png\"]", - "intendedKind": "image", - "label": "image — url string", - "typeKey": "image", - "value": "https://example.com/a.png" - }, - { - "copyText": "[\"https://example.com/doc.pdf\"]", - "displayText": "[\"https://example.com/doc.pdf\"]", - "intendedKind": "image", - "label": "file — object with url", - "typeKey": "file", - "value": { - "url": "https://example.com/doc.pdf" - } - }, - { - "copyText": "[\"https://example.com/v.mp4\"]", - "displayText": "[\"https://example.com/v.mp4\"]", - "intendedKind": "image", - "label": "video — url string", - "typeKey": "video", - "value": "https://example.com/v.mp4" - }, - { - "copyText": "[\"https://example.com/a.mp3\"]", - "displayText": "[\"https://example.com/a.mp3\"]", - "intendedKind": "image", - "label": "audio — url string", - "typeKey": "audio", - "value": "https://example.com/a.mp3" - }, - { - "copyText": "[\"/uploads/file.bin\"]", - "displayText": "[\"/uploads/file.bin\"]", - "intendedKind": "image", - "label": "upload — object with path", - "typeKey": "upload", - "value": { - "path": "/uploads/file.bin" - } - }, - { - "copyText": "550e8400-e29b-41d4-a716-446655440000", - "displayText": "550e8400-e29b-41d4-a716-446655440000", - "intendedKind": "text", - "label": "uuid — string", - "typeKey": "uuid", - "value": "550e8400-e29b-41d4-a716-446655440000" - }, - { - "copyText": "#3b82f6", - "displayText": "#3b82f6", - "intendedKind": "text", - "label": "color — hex string", - "typeKey": "color", - "value": "#3b82f6" - }, - { - "copyText": "4", - "displayText": "4", - "intendedKind": "number", - "label": "rating — number (NUMBER_TYPES quirk)", - "typeKey": "rating", - "value": 4 - }, - { - "copyText": "[\"red\",\"green\",\"blue\"]", - "displayText": "", - "intendedKind": "badges", - "label": "tags — comma string split", - "typeKey": "tags", - "value": "red, green, blue" - }, - { - "copyText": "[\"cat A\",\"sat B\",\"mat\"]", - "displayText": "", - "intendedKind": "badges", - "label": "tsvector — lexeme preview", - "typeKey": "tsvector", - "value": "'cat':1A 'sat':2B,3 'mat':4" - }, - { - "copyText": "system", - "displayText": "system", - "intendedKind": "text", - "label": "origin — string fallback", - "typeKey": "origin", - "value": "system" - }, - { - "copyText": "Ada Lovelace", - "displayText": "Ada Lovelace", - "intendedKind": "relation", - "label": "relation — belongsTo object (displayName)", - "typeKey": "relation", - "value": { - "displayName": "Ada Lovelace", - "id": "u1" - } - }, - { - "copyText": "mystery", - "displayText": "mystery", - "intendedKind": "text", - "label": "unknown — string fallback", - "typeKey": "unknown", - "value": "mystery" - }, - { - "copyText": "", - "displayText": "", - "intendedKind": "number", - "label": "number — NaN string -> empty", - "typeKey": "number", - "value": "not-a-number" - }, - { - "copyText": "{\"a\":1,\"b\":2}", - "displayText": "{\"a\":1,\"b\":2}", - "intendedKind": "text", - "label": "text — object -> JSON preview", - "typeKey": "text", - "value": { - "a": 1, - "b": 2 - } - }, - { - "copyText": "[\"alpha\",\"beta\"]", - "displayText": "", - "intendedKind": "badges", - "label": "tags — array form", - "typeKey": "tags", - "value": [ - "alpha", - "beta" - ] - }, - { - "copyText": "[\"{\\\"k\\\":1}\",\"{\\\"k\\\":2}\"]", - "displayText": "", - "intendedKind": "badges", - "label": "array — object items JSON-stringified", - "typeKey": "array", - "value": [ - { - "k": 1 - }, - { - "k": 2 - } - ] - }, - { - "copyText": "[\"https://example.com/s.png\"]", - "displayText": "[\"https://example.com/s.png\"]", - "intendedKind": "image", - "label": "image — object src key precedence", - "typeKey": "image", - "value": { - "src": "https://example.com/s.png" - } - }, - { - "copyText": "1 day", - "displayText": "1 day", - "intendedKind": "text", - "label": "interval — string passthrough", - "typeKey": "interval", - "value": "1 day" - }, - { - "copyText": "{\"years\":0,\"months\":0}", - "displayText": "0s", - "intendedKind": "text", - "label": "interval — zero object -> 0s", - "typeKey": "interval", - "value": { - "months": 0, - "years": 0 - } - } -] diff --git a/packages/sheets/src/grid/__golden__/cell-display.sheets.parity.test.ts b/packages/sheets/src/grid/__golden__/cell-display.sheets.parity.test.ts deleted file mode 100644 index ffe8b3d..0000000 --- a/packages/sheets/src/grid/__golden__/cell-display.sheets.parity.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -// BREADTH gate — DISPATCHER-level display golden (native pipeline vs frozen v1). -// -// The `createSheetsCell` DISPATCHER must select the right family and project the -// SAME { displayText, copyText } v1 froze, across EVERY CellType + edge case in -// the shared fixture table. The committed `cell-display.golden.json` holds those -// frozen v1 values; here we run each case through the live native pipeline -// (createSheetsCell -> projectSheetsCell) and assert it reproduces the golden. -// -// If a dispatcher precedence drifts (e.g. text-fallback no longer last, or a family -// claims the wrong types) the projected pair for that case diverges from the frozen -// golden and this fails. `intendedKind` (from CELL_TYPE_TO_DISPLAY_KIND) is recorded -// per row so the golden also pins the neutral render kind each type resolves to. - -import { describe, it } from 'vitest'; - -import { createSheetsCell } from '../../cell-model/create-sheets-cell'; - -import { ALL_CASES, fakeGeometrySheetsCell, makeMetadata } from './display-cases'; -import { CELL_TYPE_TO_DISPLAY_KIND, assertOrUpdateGolden, projectSheetsCell } from './parity.harness'; - -describe('BREADTH — createSheetsCell dispatcher projects to the frozen cell-display golden', () => { - it('matches the committed golden for every case', () => { - const rows = ALL_CASES.map(({ label, typeKey, value, metaOverrides }) => { - const meta = makeMetadata(typeKey, metaOverrides); - const { displayText, copyText } = projectSheetsCell(createSheetsCell(value, meta, fakeGeometrySheetsCell)); - return { label, typeKey, value, intendedKind: CELL_TYPE_TO_DISPLAY_KIND[typeKey], displayText, copyText }; - }); - - assertOrUpdateGolden('cell-display.golden', rows); - }); -}); diff --git a/packages/sheets/src/grid/__golden__/coverage.meta.test.ts b/packages/sheets/src/grid/__golden__/coverage.meta.test.ts deleted file mode 100644 index e8546dc..0000000 --- a/packages/sheets/src/grid/__golden__/coverage.meta.test.ts +++ /dev/null @@ -1,110 +0,0 @@ -/** - * PHASE-0 GATE — union-breadth + branch-reachability meta-test. - * - * This is the breadth gate over the committed goldens. It extends the spirit of - * `__tests__/registry-fidelity.test.ts:152` ("covers every built-in factory - * family at least once") from the ~7 representative families to the FULL - * `CellType` union, and it pins that the resolver golden actually exercises the - * editability branches (readonly / non-editable / viewer-only), not just the - * happy path. - * - * Unlike the capture suites, this test reads the COMMITTED *.golden.json files - * (not the live factories). It fails loudly if a future union member is added - * without a display-golden case, or if the resolver golden is regenerated in a - * way that drops a reachable branch — either of which would silently shrink - * Phase-0 coverage and let the upcoming SheetsCell flip regress unobserved. - * - * Authoritative enumeration is `ALL_CELL_TYPES` (the runtime mirror of the - * `CellType` union) and `CELL_TYPE_TO_DISPLAY_KIND` (the total type -> kind map), - * both from `parity.harness`. - */ - -import { describe, expect, it } from 'vitest'; - -import { ALL_CELL_TYPES, CELL_TYPE_TO_DISPLAY_KIND, loadGolden } from './parity.harness'; - -// ─── Shapes of the committed goldens this gate reads ──────────────────────── - -interface CellDisplayRow { - typeKey: string; -} - -interface ResolverRow { - label: string; - fieldName: string; - resolution: { - cellType: string; - canEdit: boolean; - canActivate: boolean; - activationBehavior: 'single-click' | 'double-click'; - isReadonly: boolean; - }; -} - -describe('PHASE-0 GATE — golden breadth + branch coverage', () => { - // ── (1) cell-display golden covers EVERY CellType union member ── - describe('cell-display golden covers the full CellType union', () => { - const rows = loadGolden('cell-display.golden') as CellDisplayRow[]; - const coveredTypeKeys = new Set(rows.map((r) => r.typeKey)); - - it('has at least one display-golden case per ALL_CELL_TYPES member', () => { - const missing = ALL_CELL_TYPES.filter((t) => !coveredTypeKeys.has(t)); - expect(missing, `CellType members with no cell-display golden case: ${missing.join(', ')}`).toEqual([]); - }); - - it('introduces no display-golden typeKey outside the CellType union', () => { - const known = new Set(ALL_CELL_TYPES); - const extras = [...coveredTypeKeys].filter((t) => !known.has(t)); - expect(extras, `cell-display golden typeKeys not in ALL_CELL_TYPES: ${extras.join(', ')}`).toEqual([]); - }); - }); - - // ── (2) CELL_TYPE_TO_DISPLAY_KIND is total over the union ── - describe('CELL_TYPE_TO_DISPLAY_KIND has a DisplayKind for every union member', () => { - it('maps every ALL_CELL_TYPES member to a defined DisplayKind', () => { - const unmapped = ALL_CELL_TYPES.filter((t) => CELL_TYPE_TO_DISPLAY_KIND[t] === undefined); - expect(unmapped, `CellType members missing a DisplayKind: ${unmapped.join(', ')}`).toEqual([]); - }); - }); - - // ── (3) resolver golden exercises the editability branches ── - describe('resolver golden exercises readonly / non-editable / viewer-only branches', () => { - const rows = loadGolden('resolver.golden') as ResolverRow[]; - - const readonlyRows = rows.filter((r) => r.resolution.isReadonly === true); - // READONLY_CONDITIONS keys: id (only when gqlType === UUID), createdAt, updatedAt. - const readonlyIdUuid = readonlyRows.filter((r) => r.fieldName === 'id' && r.resolution.cellType === 'uuid'); - const readonlyCreatedAt = readonlyRows.filter((r) => r.fieldName === 'createdAt'); - const readonlyUpdatedAt = readonlyRows.filter((r) => r.fieldName === 'updatedAt'); - - // NON_EDITABLE outcome: canEdit === false (whatever the cause — readonly name - // OR NON_EDITABLE_TYPES membership). - const nonEditableRows = rows.filter((r) => r.resolution.canEdit === false); - // VIEWER_ONLY outcome: the viewer-only lock — NOT editable but STILL activatable - // (canEdit false && canActivate true). In v1 only tsvector is VIEWER_ONLY. - const viewerOnlyRows = rows.filter((r) => r.resolution.canEdit === false && r.resolution.canActivate === true); - - it('covers the id+UUID readonly branch', () => { - expect(readonlyIdUuid.length, 'no readonly id+UUID row in resolver.golden').toBeGreaterThan(0); - }); - - it('covers the createdAt readonly branch', () => { - expect(readonlyCreatedAt.length, 'no readonly createdAt row in resolver.golden').toBeGreaterThan(0); - }); - - it('covers the updatedAt readonly branch', () => { - expect(readonlyUpdatedAt.length, 'no readonly updatedAt row in resolver.golden').toBeGreaterThan(0); - }); - - it('covers at least one NON_EDITABLE outcome (canEdit === false)', () => { - expect(nonEditableRows.length, 'no canEdit:false row in resolver.golden').toBeGreaterThan(0); - }); - - it('covers at least one VIEWER_ONLY outcome (canEdit false but canActivate true)', () => { - expect( - viewerOnlyRows.length, - 'no viewer-only row (canEdit:false + canActivate:true) in resolver.golden', - ).toBeGreaterThan(0); - }); - }); -}); diff --git a/packages/sheets/src/grid/__golden__/display-cases.ts b/packages/sheets/src/grid/__golden__/display-cases.ts index 5145104..59c28ba 100644 --- a/packages/sheets/src/grid/__golden__/display-cases.ts +++ b/packages/sheets/src/grid/__golden__/display-cases.ts @@ -1,19 +1,12 @@ -/** - * SHARED display fixtures — the native (glide-free) case tables + helpers that - * drive the cell-display golden. The committed `cell-display.golden.json` freezes - * the expected `{ displayText, copyText }` per case; the cell-display parity test - * iterates these and asserts the native pipeline reproduces the frozen values. - */ +/** Shared deterministic display fixtures used by focused editor tests. */ import type { CellType } from '../../cell-types/types'; import type { SheetsCell } from '../../cell-model/sheets-cell'; import type { CellCreationMetadata } from '../grid-cell-types'; import type { RelationInfo } from '../../store/relation-info-slice'; -// Deterministic geometry SheetsCell — the native render payload a geometry factory -// emits. We do NOT import the real createGeometryCell (it pulls optional leaflet -// peers); a stable `geo:` displayText keeps the golden reproducible while -// exercising projectSheetsCell's geometry branch (display === copy) faithfully. +// Deterministic geometry SheetsCell. We do not import the real +// createGeometryCell because it pulls optional Leaflet peers. export function fakeGeometrySheetsCell(value: unknown): SheetsCell { return { kind: 'geometry', diff --git a/packages/sheets/src/grid/__golden__/harness.self.test.ts b/packages/sheets/src/grid/__golden__/harness.self.test.ts deleted file mode 100644 index a8ec4b7..0000000 --- a/packages/sheets/src/grid/__golden__/harness.self.test.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { describe, expect, it } from 'vitest'; - -import type { CellType } from '../../cell-types/types'; -import type { SheetsCell } from '../../cell-model/sheets-cell'; - -import { - ALL_CELL_TYPES, - CELL_TYPE_TO_DISPLAY_KIND, - projectSheetsCell, - type DisplayKind, -} from './parity.harness'; - -// SELF-TEST for the shared parity contract. Validates the native projection is -// total + deterministic for the kinds the factories emit, and that the -// CellType -> DisplayKind map is total over the union. Geometry projection is -// exercised here via a hand-built SheetsCell (display === copy) and downstream in -// the golden suites. - -describe('projectSheetsCell — neutral projection over factory-emitted kinds', () => { - it('text cell projects displayData/data verbatim', () => { - const cell: SheetsCell = { kind: 'text', data: 'hello world', displayData: 'hello world', readonly: false }; - expect(projectSheetsCell(cell)).toEqual({ displayText: 'hello world', copyText: 'hello world' }); - }); - - it('number cell projects string displayData and stringified numeric data', () => { - const cell: SheetsCell = { kind: 'number', data: 42.5, displayData: '42.5', readonly: false }; - expect(projectSheetsCell(cell)).toEqual({ displayText: '42.5', copyText: '42.5' }); - }); - - it('badges cell has no display text (-> empty) and JSON-stringifies its string[] data', () => { - const cell: SheetsCell = { kind: 'badges', data: ['alpha', 'beta', 'gamma'], displayData: '', readonly: false }; - expect(projectSheetsCell(cell)).toEqual({ - displayText: '', - copyText: '["alpha","beta","gamma"]', - }); - }); - - it('is total for null-ish fields (no throw, empty strings)', () => { - // A number cell with data:undefined + empty displayData is what the factory - // emits for null/NaN numeric input. Projection must stay total. - const cell: SheetsCell = { kind: 'number', data: undefined, displayData: '', readonly: false }; - expect(projectSheetsCell(cell)).toEqual({ displayText: '', copyText: '' }); - }); - - it('geometry-shaped cell projects display === copy from displayData', () => { - const cell: SheetsCell = { - kind: 'geometry', - data: { type: 'Point', coordinates: [2, 1] }, - displayData: 'Point (1.0000, 2.0000)', - readonly: false, - }; - expect(projectSheetsCell(cell)).toEqual({ - displayText: 'Point (1.0000, 2.0000)', - copyText: 'Point (1.0000, 2.0000)', - }); - }); -}); - -describe('CELL_TYPE_TO_DISPLAY_KIND — total over the CellType union', () => { - const VALID_KINDS = new Set([ - 'text', - 'number', - 'boolean', - 'badges', - 'uri', - 'image', - 'geometry', - 'relation', - 'loading', - 'draft-action', - 'custom', - ]); - - it('every ALL_CELL_TYPES entry has a valid DisplayKind', () => { - for (const cellType of ALL_CELL_TYPES) { - const kind = CELL_TYPE_TO_DISPLAY_KIND[cellType]; - expect(kind, `missing/invalid DisplayKind for "${cellType}"`).toBeDefined(); - expect(VALID_KINDS.has(kind), `unexpected DisplayKind "${kind}" for "${cellType}"`).toBe(true); - } - }); - - it('the map has no keys beyond ALL_CELL_TYPES (no accidental extras)', () => { - const known = new Set(ALL_CELL_TYPES); - for (const key of Object.keys(CELL_TYPE_TO_DISPLAY_KIND)) { - expect(known.has(key), `unexpected key "${key}" in CELL_TYPE_TO_DISPLAY_KIND`).toBe(true); - } - }); - - it('ALL_CELL_TYPES has no duplicates and matches the map key count', () => { - expect(new Set(ALL_CELL_TYPES).size).toBe(ALL_CELL_TYPES.length); - expect(Object.keys(CELL_TYPE_TO_DISPLAY_KIND).length).toBe(ALL_CELL_TYPES.length); - }); - - it('pins the family-anchor mappings (image/uri/badges/number/boolean/geometry/relation/text)', () => { - const expectations: Array<[CellType, DisplayKind]> = [ - ['image', 'image'], - ['upload', 'image'], - ['url', 'uri'], - ['email', 'uri'], - ['tags', 'badges'], - ['tsvector', 'badges'], - ['text-array', 'badges'], - ['number', 'number'], - ['rating', 'number'], - ['boolean', 'boolean'], - ['bit', 'boolean'], - ['geometry', 'geometry'], - ['relation', 'relation'], - ['date', 'text'], - ['interval', 'text'], - ['json', 'text'], - ['text', 'text'], - ['unknown', 'text'], - ]; - for (const [cellType, expected] of expectations) { - expect(CELL_TYPE_TO_DISPLAY_KIND[cellType], cellType).toBe(expected); - } - }); -}); diff --git a/packages/sheets/src/grid/__golden__/parity.harness.ts b/packages/sheets/src/grid/__golden__/parity.harness.ts deleted file mode 100644 index 8ede63d..0000000 --- a/packages/sheets/src/grid/__golden__/parity.harness.ts +++ /dev/null @@ -1,298 +0,0 @@ -/** - * SHARED PARITY CONTRACT — the native (glide-free) projection every golden - * asserts through. With the glide cutover complete, the committed `*.golden.json` - * snapshots ARE the frozen v1 truth, and the live native pipeline - * (`createSheetsCell` -> {@link projectSheetsCell}) is asserted against them. - * - * Two concepts live here: - * - * 1. A neutral RENDER vocabulary — {@link DisplayKind} — the ~11 native kinds, - * plus a total map from every {@link CellType} union member to its - * `DisplayKind`. - * - * 2. A total, deterministic projection — {@link projectSheetsCell} — that - * reduces any native `SheetsCell` the factories emit to a - * `{ displayText, copyText }` pair (the frozen golden shape). - * - * 3. A golden assert/update helper — {@link assertOrUpdateGolden} — colocated - * JSON snapshots with stable key order, switched by `UPDATE_GOLDEN`. - * - * Nothing here imports the real `createGeometryCell` (it pulls optional leaflet - * peers); the geometry custom-cell is projected from its `displayData`. - */ - -import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'; -import { dirname, join } from 'node:path'; -import { fileURLToPath } from 'node:url'; - -import { expect } from 'vitest'; - -import type { CellType } from '../../cell-types/types'; -import type { SheetsCell } from '../../cell-model/sheets-cell'; - -// ─── (a) Neutral render vocabulary ────────────────────────────────────────── - -/** - * The ~11 RENDER-LEVEL native kinds — the canonical neutral vocabulary every - * phase projects into. - * - * `loading` and `draft-action` have no `CellType` source (they originate from - * grid-internal state, not the data type system) but are part of the native - * render surface, so they are reserved here for downstream phases. - */ -export type DisplayKind = - | 'text' - | 'number' - | 'boolean' - | 'badges' - | 'uri' - | 'image' - | 'geometry' - | 'relation' - | 'loading' - | 'draft-action' - | 'custom'; - -// ─── (b) CellType -> DisplayKind (compiler-enforced exhaustive) ────────────── - -/** - * Maps EVERY member of the {@link CellType} union to its {@link DisplayKind}. - * - * Typed as `Record` so a missing union member is a - * COMPILE error (the spike's exhaustiveness guard). Each entry is assigned by - * which `cell-content-factory` family handles that type: - * - * ImageCellFactory (MEDIA_TYPES) -> 'image' - * UriCellFactory (url|email) -> 'uri' - * ArrayCellFactory (array-like + tsvector) -> 'badges' - * DateTimeCellFactory + IntervalCellFactory -> 'text' (formatted text in v1) - * GeometryCellFactory(GEOMETRY_TYPES) -> 'geometry' - * RelationCellFactory(relation) -> 'relation' - * NumberCellFactory (NUMBER_TYPES) -> 'number' - * BooleanCellFactory (BOOLEAN_TYPES) -> 'boolean' - * TextCellFactory (everything else, fallback) -> 'text' - * - * NOTE on the two v1 quirks captured here (see `deviations`): - * - `rating` is in NUMBER_TYPES (cell-type-groups.ts), so the NumberCellFactory - * claims it before the text fallback -> 'number' (NOT 'text'), even though the - * task's text-fallback list names 'rating'. Mapped to 'number' to match the - * ACTUAL v1 factory selection. - * - `relation` maps to 'relation' for BOTH single and list; the native - * RelationCellView owns both, even though v1 glide emits Text (single) and - * Bubble (list). The neutral kind intentionally collapses that glide split. - */ -export const CELL_TYPE_TO_DISPLAY_KIND: Record = { - // Text-based cells — TextCellFactory fallback display - text: 'text', - textarea: 'text', - email: 'uri', - url: 'uri', - phone: 'text', - citext: 'text', - bpchar: 'text', - // Numeric cells — NumberCellFactory - number: 'number', - integer: 'number', - smallint: 'number', - decimal: 'number', - currency: 'number', - percentage: 'number', - // Date/time cells — DateTime + Interval factories (formatted text in v1) - date: 'text', - datetime: 'text', - time: 'text', - timestamptz: 'text', - interval: 'text', - // Boolean cells — BooleanCellFactory - boolean: 'boolean', - toggle: 'boolean', - bit: 'boolean', - // Structured data cells - json: 'text', - jsonb: 'text', - array: 'badges', - // Array cells (specific types) — ArrayCellFactory - 'text-array': 'badges', - 'uuid-array': 'badges', - 'number-array': 'badges', - 'integer-array': 'badges', - 'date-array': 'badges', - // Geometric cells — GeometryCellFactory - geometry: 'geometry', - 'geometry-point': 'geometry', - 'geometry-collection': 'geometry', - // Network cells — text fallback - inet: 'text', - // Media cells — ImageCellFactory - image: 'image', - file: 'image', - video: 'image', - audio: 'image', - upload: 'image', - // Special cells - uuid: 'text', - color: 'text', - // `rating` is in NUMBER_TYPES -> NumberCellFactory claims it (v1 actual). quirk. - rating: 'number', - tags: 'badges', - tsvector: 'badges', - origin: 'text', - // Relation cells — RelationCellFactory (single AND list -> one neutral kind) - relation: 'relation', - // Fallback - unknown: 'text', -}; - -/** - * Runtime mirror of the {@link CellType} union — the authoritative enumeration - * for the Gate stage and for the harness self-test's totality check. Kept in the - * SAME order as the union in `cell-types/types.ts`. If a member is added to the - * union but not here, `CELL_TYPE_TO_DISPLAY_KIND` (typed `Record`) - * still fails to compile, so this array can only ever lag in the safe direction. - */ -export const ALL_CELL_TYPES: CellType[] = [ - 'text', - 'textarea', - 'email', - 'url', - 'phone', - 'citext', - 'bpchar', - 'number', - 'integer', - 'smallint', - 'decimal', - 'currency', - 'percentage', - 'date', - 'datetime', - 'time', - 'timestamptz', - 'interval', - 'boolean', - 'toggle', - 'bit', - 'json', - 'jsonb', - 'array', - 'text-array', - 'uuid-array', - 'number-array', - 'integer-array', - 'date-array', - 'geometry', - 'geometry-point', - 'geometry-collection', - 'inet', - 'image', - 'file', - 'video', - 'audio', - 'upload', - 'uuid', - 'color', - 'rating', - 'tags', - 'tsvector', - 'origin', - 'relation', - 'unknown', -]; - -// ─── (c) Native neutral projection ────────────────────────────────────────── - -/** - * Stringify one cell field deterministically: - * - null / undefined -> '' - * - Array (image/badges data) -> JSON.stringify(array) - * - string -> as-is - * - anything else -> String(value) - */ -function stringifyField(value: unknown): string { - if (value === null || value === undefined) return ''; - if (Array.isArray(value)) return JSON.stringify(value); - if (typeof value === 'string') return value; - return String(value); -} - -/** - * Reduce a native {@link SheetsCell} to the neutral `{ displayText, copyText }` - * pair the committed goldens freeze. TOTAL + DETERMINISTIC over every kind the - * factories emit. - * - * - geometry / custom — the SheetsCell carries the rendered text in - * `displayData`, and (mirroring v1's Custom cell where copyData === displayText) - * both display and copy come from `cell.displayData`, used verbatim. - * - everything else — display from `displayData`, copy from `data`, both run - * through the same `stringifyField` (arrays -> JSON; null/undefined -> ''). - * badges/boolean carry no display text, so they project to '' on the display - * side — that is the frozen v1 truth. - */ -export function projectSheetsCell(cell: SheetsCell): { displayText: string; copyText: string } { - if (cell.kind === 'geometry' || cell.kind === 'custom') { - return { displayText: cell.displayData, copyText: cell.displayData }; - } - return { - displayText: stringifyField(cell.displayData), - copyText: stringifyField(cell.data), - }; -} - -// ─── (d) + (e) Golden assert / update + load / write helpers ──────────────── - -const GOLDEN_DIR = dirname(fileURLToPath(import.meta.url)); - -function goldenPath(name: string): string { - return join(GOLDEN_DIR, `${name}.json`); -} - -/** - * Recursively sort object keys so written goldens have a STABLE byte layout - * regardless of insertion order. Arrays keep their order (order is meaningful); - * primitives pass through. - */ -function sortKeysDeep(value: unknown): unknown { - if (Array.isArray(value)) return value.map(sortKeysDeep); - if (value && typeof value === 'object') { - const out: Record = {}; - for (const key of Object.keys(value as Record).sort()) { - out[key] = sortKeysDeep((value as Record)[key]); - } - return out; - } - return value; -} - -/** Read + parse the golden JSON for `name`. Throws if it is missing. */ -export function loadGolden(name: string): unknown { - const path = goldenPath(name); - if (!existsSync(path)) { - throw new Error( - `Golden "${name}" not found at ${path}. Generate it with UPDATE_GOLDEN=1 pnpm exec vitest run .`, - ); - } - return JSON.parse(readFileSync(path, 'utf8')); -} - -/** Write `data` as a stable-key-order golden JSON (pretty, trailing newline). */ -export function writeGolden(name: string, data: unknown): void { - if (!existsSync(GOLDEN_DIR)) mkdirSync(GOLDEN_DIR, { recursive: true }); - const stable = sortKeysDeep(data); - writeFileSync(goldenPath(name), `${JSON.stringify(stable, null, 2)}\n`, 'utf8'); -} - -/** - * Golden gate. With `UPDATE_GOLDEN` set, (re)writes `.json` and returns. - * Otherwise reads the committed golden and asserts deep equality against - * `actual`. `actual` is normalized through the same stable-key-order pass so - * the comparison is order-insensitive on object keys — matching what was - * written — while staying a strict `toEqual`. - */ -export function assertOrUpdateGolden(name: string, actual: unknown): void { - if (process.env.UPDATE_GOLDEN) { - writeGolden(name, actual); - return; - } - const expected = loadGolden(name); - expect(sortKeysDeep(actual)).toEqual(expected); -} diff --git a/packages/sheets/src/grid/__golden__/post-processing.golden.json b/packages/sheets/src/grid/__golden__/post-processing.golden.json deleted file mode 100644 index 4157da3..0000000 --- a/packages/sheets/src/grid/__golden__/post-processing.golden.json +++ /dev/null @@ -1,293 +0,0 @@ -{ - "draft": [ - { - "after": { - "copyText": "", - "displayText": "" - }, - "before": { - "copyText": "draft:k-001", - "displayText": "draft:k-001" - }, - "cellType": "text", - "colKey": "id", - "isDraftRow": true, - "label": "draft id column — draft: key blanked", - "rawValue": "draft:k-001", - "rowKeys": [ - "id" - ], - "suppressed": true - }, - { - "after": { - "copyText": "", - "displayText": "" - }, - "before": { - "copyText": "draft:k-001", - "displayText": "draft:k-001" - }, - "cellType": "text", - "colKey": "parentId", - "isDraftRow": true, - "label": "draft non-id column equal to draft id — blanked", - "rawValue": "draft:k-001", - "rowKeys": [ - "id", - "parentId" - ], - "suppressed": true - }, - { - "after": { - "copyText": "Hello draft", - "displayText": "Hello draft" - }, - "before": { - "copyText": "Hello draft", - "displayText": "Hello draft" - }, - "cellType": "text", - "colKey": "name", - "isDraftRow": true, - "label": "draft normal column — untouched", - "rawValue": "Hello draft", - "rowKeys": [ - "id", - "name" - ], - "suppressed": false - }, - { - "after": { - "copyText": "Working title", - "displayText": "Working title" - }, - "before": { - "copyText": "Working title", - "displayText": "Working title" - }, - "cellType": "text", - "colKey": "title", - "isDraftRow": true, - "label": "draft error-status column — untouched", - "rawValue": "Working title", - "rowKeys": [ - "id", - "title" - ], - "suppressed": false - }, - { - "after": { - "copyText": "server-id-42", - "displayText": "server-id-42" - }, - "before": { - "copyText": "server-id-42", - "displayText": "server-id-42" - }, - "cellType": "text", - "colKey": "id", - "isDraftRow": true, - "label": "draft id column with server id — not blanked", - "rawValue": "server-id-42", - "rowKeys": [ - "id" - ], - "suppressed": false - } - ], - "relation": [ - { - "copyText": "Ada Lovelace", - "displayText": "Ada Lovelace", - "kind": "belongsTo", - "label": "belongsTo — label from displayName", - "relationOptions": {}, - "typeKey": "relation", - "value": { - "displayName": "Ada Lovelace", - "id": "u1" - } - }, - { - "copyText": "Grace Hopper", - "displayText": "Grace Hopper", - "kind": "belongsTo", - "label": "belongsTo — fallback to name candidate", - "relationOptions": {}, - "typeKey": "relation", - "value": { - "id": "u2", - "name": "Grace Hopper" - } - }, - { - "copyText": "u3", - "displayText": "u3", - "kind": "belongsTo", - "label": "belongsTo — id last resort", - "relationOptions": {}, - "typeKey": "relation", - "value": { - "id": "u3" - } - }, - { - "copyText": "", - "displayText": "", - "kind": "belongsTo", - "label": "belongsTo — null value", - "relationOptions": {}, - "typeKey": "relation", - "value": null - }, - { - "copyText": "plain-id", - "displayText": "plain-id", - "kind": "belongsTo", - "label": "belongsTo — scalar string value", - "relationOptions": {}, - "typeKey": "relation", - "value": "plain-id" - }, - { - "copyText": "", - "displayText": "", - "kind": "belongsTo", - "label": "belongsTo — draft: placeholder suppressed", - "relationOptions": {}, - "typeKey": "relation", - "value": "draft:abc123" - }, - { - "copyText": "", - "displayText": "", - "kind": "belongsTo", - "label": "belongsTo — object id draft: suppressed", - "relationOptions": {}, - "typeKey": "relation", - "value": { - "id": "draft:xyz" - } - }, - { - "copyText": "A very long author disp…", - "displayText": "A very long author disp…", - "kind": "belongsTo", - "label": "belongsTo — label truncated at default max", - "relationOptions": {}, - "typeKey": "relation", - "value": { - "displayName": "A very long author display name that exceeds the limit" - } - }, - { - "copyText": "A very …", - "displayText": "A very …", - "kind": "belongsTo", - "label": "belongsTo — label truncated at explicit max 8", - "relationOptions": { - "relationLabelMaxLength": 8 - }, - "typeKey": "relation", - "value": { - "displayName": "A very long author display name that exceeds the limit" - } - }, - { - "copyText": "[\"one\",\"two\"]", - "displayText": "", - "kind": "hasMany", - "label": "hasMany — within chip limit", - "relationOptions": {}, - "typeKey": "relation", - "value": [ - { - "title": "one" - }, - { - "title": "two" - } - ] - }, - { - "copyText": "[\"one\",\"two\",\"three\",\"+2\"]", - "displayText": "", - "kind": "hasMany", - "label": "hasMany — overflow +N at default chip limit", - "relationOptions": {}, - "typeKey": "relation", - "value": [ - { - "title": "one" - }, - { - "title": "two" - }, - { - "title": "three" - }, - { - "title": "four" - }, - { - "title": "five" - } - ] - }, - { - "copyText": "[\"one\",\"two\",\"+2\"]", - "displayText": "", - "kind": "hasMany", - "label": "hasMany — overflow +N at explicit chip limit 2", - "relationOptions": { - "relationChipLimit": 2 - }, - "typeKey": "relation", - "value": [ - { - "title": "one" - }, - { - "title": "two" - }, - { - "title": "three" - }, - { - "title": "four" - } - ] - }, - { - "copyText": "[]", - "displayText": "", - "kind": "hasMany", - "label": "hasMany — empty list", - "relationOptions": {}, - "typeKey": "relation", - "value": [] - }, - { - "copyText": "[\"A ver…\",\"short\"]", - "displayText": "", - "kind": "hasMany", - "label": "hasMany — per-chip truncation at explicit max 6", - "relationOptions": { - "relationLabelMaxLength": 6 - }, - "typeKey": "relation", - "value": [ - { - "title": "A very long author display name that exceeds the limit" - }, - { - "title": "short" - } - ] - } - ] -} diff --git a/packages/sheets/src/grid/__golden__/post-processing.golden.test.ts b/packages/sheets/src/grid/__golden__/post-processing.golden.test.ts deleted file mode 100644 index 2c20364..0000000 --- a/packages/sheets/src/grid/__golden__/post-processing.golden.test.ts +++ /dev/null @@ -1,212 +0,0 @@ -import { describe, expect, it } from 'vitest'; - -import { createSheetsCell } from '../../cell-model/create-sheets-cell'; -import type { CellCreationMetadata } from '../grid-cell-types'; -import { attachDraftMeta, getDraftMeta, type DraftMeta } from '../row-model'; -import { suppressDraftIdText } from '../../grid-dom/use-sheets-content'; -import type { RelationInfo } from '../../store/relation-info-slice'; - -import { assertOrUpdateGolden, projectSheetsCell } from './parity.harness'; - -// ───────────────────────────────────────────────────────────────────────────── -// PARITY GOLDEN — relation derivation + draft POST-PROCESSING (native pipeline). -// -// Focused battery over the two behaviors that decorate the raw factory output. The -// committed `post-processing.golden.json` froze the v1 values; here we run each -// case through the live NATIVE pipeline and assert it reproduces that golden: -// -// 1. RELATION label derivation (relation SheetsCell factory): belongsTo single -// label from displayCandidates; belongsTo null; hasMany list longer than the -// chip limit -> '+N' overflow chip; a label longer than the max length -> -// truncation with an ellipsis. Captured through createSheetsCell + -// projectSheetsCell (single relations carry data === displayData === label; -// list relations carry chips in `data` with no display text -> displayText '', -// copyText the JSON-stringified chips — the frozen v1 truth). -// -// 2. DRAFT suppression (use-sheets-content.suppressDraftIdText): the draft slice -// stores `id = 'draft:'`; the rendered id cell would leak that raw string, -// so the native suppressor BLANKS its display/data while preserving kind. Run -// against the real exported helper + the real attachDraftMeta. -// -// Native draft VISUAL styling (styleHint.draft/error) is a flag the host paints -// from, not part of the projected pair, so it is intentionally NOT routed here. -// ───────────────────────────────────────────────────────────────────────────── - -const RELATION_CHIP_LIMIT_DEFAULT = 3; // DEFAULT_MAX_RELATION_CHIPS in the relation factory -const RELATION_LABEL_MAX_DEFAULT = 24; // DEFAULT_RELATION_LABEL_MAX_LEN in the relation factory - -function makeMetadata(cellType: string, overrides?: Partial): CellCreationMetadata { - return { - cellType, - fieldName: overrides?.fieldName ?? cellType, - canEdit: true, - isReadonly: false, - activationBehavior: 'double-click', - ...overrides, - }; -} - -const BELONGS_TO: RelationInfo = { - kind: 'belongsTo', - relatedTable: 'users', - relationField: 'author', - foreignKeyField: 'authorId', - displayCandidates: ['displayName', 'name'], -}; - -const HAS_MANY: RelationInfo = { - kind: 'hasMany', - relatedTable: 'comments', - relationField: 'comments', - foreignKeyField: 'postId', - displayCandidates: ['title'], -}; - -// ── Relation battery ──────────────────────────────────────────────────────── - -function relationRow(label: string, value: unknown, info: RelationInfo, relationOptions: Record = {}) { - const metadata = makeMetadata('relation', { relationInfo: info, relationOptions }); - const cell = createSheetsCell(value, metadata); - const { displayText, copyText } = projectSheetsCell(cell); - return { - label, - typeKey: 'relation', - kind: info.kind, - value, - relationOptions, - displayText, - copyText, - }; -} - -const LONG_LABEL = 'A very long author display name that exceeds the limit'; // 53 chars > 24 - -function buildRelationRows() { - return [ - // belongsTo single -> label derived from displayCandidates (displayName wins over id) - relationRow('belongsTo — label from displayName', { id: 'u1', displayName: 'Ada Lovelace' }, BELONGS_TO), - // belongsTo single -> falls through to second candidate (name) when displayName absent - relationRow('belongsTo — fallback to name candidate', { id: 'u2', name: 'Grace Hopper' }, BELONGS_TO), - // belongsTo single -> id is LAST resort when no name-like candidate present - relationRow('belongsTo — id last resort', { id: 'u3' }, BELONGS_TO), - // belongsTo null -> empty label - relationRow('belongsTo — null value', null, BELONGS_TO), - // belongsTo scalar string -> used directly as label - relationRow('belongsTo — scalar string value', 'plain-id', BELONGS_TO), - // belongsTo draft placeholder -> suppressed to empty by deriveRelationLabel - relationRow('belongsTo — draft: placeholder suppressed', 'draft:abc123', BELONGS_TO), - relationRow('belongsTo — object id draft: suppressed', { id: 'draft:xyz' }, BELONGS_TO), - // belongsTo label longer than max length -> truncated with ellipsis (default max 24) - relationRow('belongsTo — label truncated at default max', { displayName: LONG_LABEL }, BELONGS_TO), - // belongsTo label truncated at a SMALLER explicit max - relationRow('belongsTo — label truncated at explicit max 8', { displayName: LONG_LABEL }, BELONGS_TO, { - relationLabelMaxLength: 8, - }), - // hasMany within limit -> all chips, no overflow - relationRow('hasMany — within chip limit', [{ title: 'one' }, { title: 'two' }], HAS_MANY), - // hasMany LONGER than default chip limit (3) -> '+N' overflow chip - relationRow( - 'hasMany — overflow +N at default chip limit', - [{ title: 'one' }, { title: 'two' }, { title: 'three' }, { title: 'four' }, { title: 'five' }], - HAS_MANY, - ), - // hasMany overflow at an explicit SMALLER chip limit - relationRow( - 'hasMany — overflow +N at explicit chip limit 2', - [{ title: 'one' }, { title: 'two' }, { title: 'three' }, { title: 'four' }], - HAS_MANY, - { relationChipLimit: 2 }, - ), - // hasMany empty list -> empty bubble (no chips) - relationRow('hasMany — empty list', [], HAS_MANY), - // hasMany with per-item truncation inside chips - relationRow( - 'hasMany — per-chip truncation at explicit max 6', - [{ title: LONG_LABEL }, { title: 'short' }], - HAS_MANY, - { relationLabelMaxLength: 6 }, - ), - ]; -} - -// ── Draft suppression battery ───────────────────────────────────────────────── - -const DRAFT_ID = 'draft:k-001'; -const DRAFT_META: DraftMeta = { isDraft: true, draftRowId: DRAFT_ID, status: 'idle', errors: null }; - -function draftRow(label: string, colKey: string, rawValue: unknown, cellType: string, draftMeta: DraftMeta) { - // Attach draft meta to the ROW via the real (non-enumerable) helper, then read - // it back exactly as use-sheets-content does — proving DRAFT_META survives and is - // invisible to enumeration. - const row = attachDraftMeta({ id: DRAFT_ID, [colKey]: rawValue }, draftMeta); - const meta = getDraftMeta(row); - const isDraft = Boolean(meta?.isDraft); - - const metadata = makeMetadata(cellType, { fieldName: colKey }); - const baseCell = createSheetsCell(rawValue, metadata); - - const suppressed = suppressDraftIdText(baseCell, { - isDraftRow: isDraft, - colKey, - rawValue, - draftId: meta?.draftRowId, - }); - - const before = projectSheetsCell(baseCell); - const after = projectSheetsCell(suppressed); - - return { - label, - colKey, - cellType, - rawValue, - isDraftRow: isDraft, - // non-enumerable Symbol must NOT leak into row keys - rowKeys: Object.keys(row), - before, - after, - suppressed: before.displayText !== after.displayText || before.copyText !== after.copyText, - }; -} - -function buildDraftRows() { - return [ - // id column holding the raw draft: key -> BLANKED (the headline suppression) - draftRow('draft id column — draft: key blanked', 'id', DRAFT_ID, 'text', DRAFT_META), - // a non-id column whose value EQUALS the draft id -> also blanked - draftRow('draft non-id column equal to draft id — blanked', 'parentId', DRAFT_ID, 'text', DRAFT_META), - // a normal draft-row data column -> untouched (suppression is narrow) - draftRow('draft normal column — untouched', 'name', 'Hello draft', 'text', DRAFT_META), - // a draft row in error status with a normal value -> suppression still narrow (no blanking) - draftRow('draft error-status column — untouched', 'title', 'Working title', 'text', { - isDraft: true, - draftRowId: DRAFT_ID, - status: 'error', - errors: { title: 'Required' }, - }), - // id column with a NON-draft id value (server-shaped) -> NOT blanked - draftRow('draft id column with server id — not blanked', 'id', 'server-id-42', 'text', DRAFT_META), - ]; -} - -describe('PARITY — post-processing golden (relation derivation + draft suppression)', () => { - it('pins the relation/draft constants this battery assumes', () => { - expect(RELATION_CHIP_LIMIT_DEFAULT).toBe(3); - expect(RELATION_LABEL_MAX_DEFAULT).toBe(24); - }); - - it('draft meta is non-enumerable on every draft row', () => { - for (const row of buildDraftRows()) { - expect(row.rowKeys).not.toContain('isDraft'); - expect(row.isDraftRow).toBe(true); - } - }); - - it('matches the committed golden', () => { - const golden = { - relation: buildRelationRows(), - draft: buildDraftRows(), - }; - assertOrUpdateGolden('post-processing.golden', golden); - }); -}); diff --git a/packages/sheets/src/grid/__golden__/resolver.golden.json b/packages/sheets/src/grid/__golden__/resolver.golden.json deleted file mode 100644 index 0131c12..0000000 --- a/packages/sheets/src/grid/__golden__/resolver.golden.json +++ /dev/null @@ -1,2243 +0,0 @@ -[ - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String" - } - }, - "fieldName": "name", - "label": "gql String -> text", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "text", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Int" - } - }, - "fieldName": "count", - "label": "gql Int -> integer", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "integer", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Float" - } - }, - "fieldName": "ratio", - "label": "gql Float -> number", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "number", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Boolean" - } - }, - "fieldName": "active", - "label": "gql Boolean -> boolean", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "boolean", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "ID" - } - }, - "fieldName": "ref", - "label": "gql ID -> uuid", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "uuid", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "DateTime" - } - }, - "fieldName": "at", - "label": "gql DateTime -> datetime", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "datetime", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Datetime" - } - }, - "fieldName": "at2", - "label": "gql Datetime -> datetime", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "datetime", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Date" - } - }, - "fieldName": "day", - "label": "gql Date -> date", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "date", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Time" - } - }, - "fieldName": "clock", - "label": "gql Time -> time", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "time", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "JSON" - } - }, - "fieldName": "blob", - "label": "gql JSON -> json", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "json", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "JSONObject" - } - }, - "fieldName": "obj", - "label": "gql JSONObject -> json", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "json", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Upload" - } - }, - "fieldName": "doc", - "label": "gql Upload -> file", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "file", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "URL" - } - }, - "fieldName": "link", - "label": "gql URL -> url", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "url", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "EmailAddress" - } - }, - "fieldName": "mail", - "label": "gql EmailAddress -> email", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "email", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "PhoneNumber" - } - }, - "fieldName": "tel", - "label": "gql PhoneNumber -> phone", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "phone", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "UUID" - } - }, - "fieldName": "guid", - "label": "gql UUID -> uuid", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "uuid", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "BigFloat" - } - }, - "fieldName": "big", - "label": "gql BigFloat -> number", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "number", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "BigInt" - } - }, - "fieldName": "bigi", - "label": "gql BigInt -> integer", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "integer", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Relation" - } - }, - "fieldName": "owner", - "label": "gql Relation -> relation", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "relation", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Interval" - } - }, - "fieldName": "dur", - "label": "gql Interval -> interval", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "interval", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "GeometryGeometryCollection" - } - }, - "fieldName": "shapes", - "label": "gql GeometryGeometryCollection -> geometry-collection", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "geometry-collection", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Mystery" - } - }, - "fieldName": "weird", - "label": "gql unknown -> text (default)", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "text", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String!" - } - }, - "fieldName": "req", - "label": "gql non-null String! -> text (modifier stripped)", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "text", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "" - } - }, - "fieldName": "empty", - "label": "gql empty string -> text", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "text", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "GeoJSON", - "subtype": "GeometryPoint" - } - }, - "fieldName": "pt", - "label": "GeoJSON GeometryPoint -> geometry-point", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "geometry-point", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "GeoJSON", - "subtype": "GeometryPolygon" - } - }, - "fieldName": "poly", - "label": "GeoJSON GeometryPolygon -> geometry-collection", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "geometry-collection", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "GeoJSON" - } - }, - "fieldName": "geo", - "label": "GeoJSON default -> geometry", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "geometry", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "GeoJSON", - "subtype": "GeometryLine" - } - }, - "fieldName": "geo2", - "label": "GeoJSON unrecognized subtype -> geometry", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "geometry", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "JSON", - "pgAlias": "image" - } - }, - "fieldName": "pic", - "label": "alias image -> image", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "image", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "JSON", - "pgAlias": "file" - } - }, - "fieldName": "att", - "label": "alias file -> file", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "file", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "JSON", - "pgAlias": "video" - } - }, - "fieldName": "clip", - "label": "alias video -> video", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "video", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "JSON", - "pgAlias": "audio" - } - }, - "fieldName": "snd", - "label": "alias audio -> audio", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "audio", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "JSON", - "pgAlias": "upload" - } - }, - "fieldName": "up", - "label": "alias upload -> upload", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "upload", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgAlias": "relation" - } - }, - "fieldName": "rel", - "label": "alias relation -> relation", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "relation", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgAlias": "email" - } - }, - "fieldName": "em", - "label": "alias email -> email", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "email", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgAlias": "url" - } - }, - "fieldName": "u", - "label": "alias url -> url", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "url", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgAlias": "phone" - } - }, - "fieldName": "ph", - "label": "alias phone -> phone", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "phone", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgAlias": "citext" - } - }, - "fieldName": "ci", - "label": "alias citext -> citext", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "citext", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgAlias": "textarea" - } - }, - "fieldName": "ta", - "label": "alias textarea -> textarea", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "textarea", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Float", - "pgAlias": "number" - } - }, - "fieldName": "n", - "label": "alias number -> number", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "number", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Float", - "pgAlias": "currency" - } - }, - "fieldName": "price", - "label": "alias currency -> currency", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "currency", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Float", - "pgAlias": "percentage" - } - }, - "fieldName": "pct", - "label": "alias percentage -> percentage", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "percentage", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Float", - "pgAlias": "numeric" - } - }, - "fieldName": "num", - "label": "alias numeric -> decimal", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "decimal", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Float", - "pgAlias": "decimal" - } - }, - "fieldName": "dec", - "label": "alias decimal -> decimal", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "decimal", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Int", - "pgAlias": "int" - } - }, - "fieldName": "i", - "label": "alias int -> integer", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "integer", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Int", - "pgAlias": "integer" - } - }, - "fieldName": "i2", - "label": "alias integer -> integer", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "integer", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Int", - "pgAlias": "smallint" - } - }, - "fieldName": "si", - "label": "alias smallint -> smallint", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "smallint", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Boolean", - "pgAlias": "boolean" - } - }, - "fieldName": "b", - "label": "alias boolean -> boolean", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "boolean", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Boolean", - "pgAlias": "toggle" - } - }, - "fieldName": "tg", - "label": "alias toggle -> toggle", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "toggle", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Date", - "pgAlias": "date" - } - }, - "fieldName": "d", - "label": "alias date -> date", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "date", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "DateTime", - "pgAlias": "datetime" - } - }, - "fieldName": "dt", - "label": "alias datetime -> datetime", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "datetime", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Time", - "pgAlias": "time" - } - }, - "fieldName": "t", - "label": "alias time -> time", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "time", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "DateTime", - "pgAlias": "timestamptz" - } - }, - "fieldName": "tstz", - "label": "alias timestamptz -> timestamptz", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "timestamptz", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Interval", - "pgAlias": "interval" - } - }, - "fieldName": "iv", - "label": "alias interval -> interval", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "interval", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "JSON", - "pgAlias": "json" - } - }, - "fieldName": "j", - "label": "alias json -> json", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "json", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "JSON", - "pgAlias": "jsonb" - } - }, - "fieldName": "jb", - "label": "alias jsonb -> jsonb", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "jsonb", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgAlias": "inet" - } - }, - "fieldName": "ip", - "label": "alias inet -> inet", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "inet", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "GeoJSON", - "pgAlias": "geometry" - } - }, - "fieldName": "g", - "label": "alias geometry -> geometry", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "geometry", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "GeoJSON", - "pgAlias": "geometry-point" - } - }, - "fieldName": "gp", - "label": "alias geometry-point -> geometry-point", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "geometry-point", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "GeoJSON", - "pgAlias": "geometry-collection" - } - }, - "fieldName": "gc", - "label": "alias geometry-collection -> geometry-collection", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "geometry-collection", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgAlias": "color" - } - }, - "fieldName": "c", - "label": "alias color -> color", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "color", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Int", - "pgAlias": "rating" - } - }, - "fieldName": "r", - "label": "alias rating -> rating", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "rating", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgAlias": "tags" - } - }, - "fieldName": "tg2", - "label": "alias tags -> tags", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "tags", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "UUID", - "pgAlias": "uuid" - } - }, - "fieldName": "uu", - "label": "alias uuid -> uuid", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "uuid", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgAlias": "tsvector" - } - }, - "fieldName": "search", - "label": "alias tsvector -> tsvector (NON_EDITABLE + VIEWER_ONLY)", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": false, - "cellType": "tsvector", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgAlias": "origin" - } - }, - "fieldName": "og", - "label": "alias origin -> origin", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "origin", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgAlias": "char" - } - }, - "fieldName": "ch", - "label": "alias char -> bpchar", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "bpchar", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgAlias": "bpchar" - } - }, - "fieldName": "bp", - "label": "alias bpchar -> bpchar", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "bpchar", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgAlias": "text" - } - }, - "fieldName": "tx", - "label": "alias text -> text", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "text", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgAlias": "bit" - } - }, - "fieldName": "bt", - "label": "alias bit -> bit", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "bit", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "JSON", - "pgAlias": "IMAGE" - } - }, - "fieldName": "pic2", - "label": "alias UPPERCASE IMAGE -> image (case-insensitive)", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "image", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Int", - "pgAlias": "zzz-not-real" - } - }, - "fieldName": "fall", - "label": "alias unknown -> falls through to gqlType", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "integer", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgAlias": "zzz", - "pgType": "uuid" - } - }, - "fieldName": "fall2", - "label": "alias unknown -> falls through to pgType", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "uuid", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "isArray": true, - "pgAlias": "array" - } - }, - "fieldName": "arr", - "label": "alias array -> array", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "array", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "isArray": true, - "pgAlias": "text-array" - } - }, - "fieldName": "ta_arr", - "label": "alias text-array -> text-array", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "text-array", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Int", - "isArray": true, - "pgAlias": "integer-array" - } - }, - "fieldName": "i_arr", - "label": "alias integer-array -> integer-array", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "integer-array", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "BigFloat", - "isArray": true, - "pgAlias": "number-array" - } - }, - "fieldName": "n_arr", - "label": "alias number-array -> number-array", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "number-array", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "UUID", - "isArray": true, - "pgAlias": "uuid-array" - } - }, - "fieldName": "u_arr", - "label": "alias uuid-array -> uuid-array", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "uuid-array", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Date", - "isArray": true, - "pgAlias": "date-array" - } - }, - "fieldName": "d_arr", - "label": "alias date-array -> date-array", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "date-array", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "isArray": true, - "pgAlias": "image" - } - }, - "fieldName": "pics", - "label": "alias image WINS over isArray -> image", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "image", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "isArray": true - } - }, - "fieldName": "sarr", - "label": "array String -> text-array", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "text-array", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "UUID", - "isArray": true - } - }, - "fieldName": "uarr", - "label": "array UUID -> uuid-array", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "uuid-array", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "BigFloat", - "isArray": true - } - }, - "fieldName": "narr", - "label": "array BigFloat -> number-array", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "number-array", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Int", - "isArray": true - } - }, - "fieldName": "iarr", - "label": "array Int -> integer-array", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "integer-array", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Date", - "isArray": true - } - }, - "fieldName": "darr", - "label": "array Date -> date-array", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "date-array", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Foo", - "isArray": true - } - }, - "fieldName": "garr", - "label": "array unknown base -> array (generic)", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "array", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "[String]", - "isArray": true - } - }, - "fieldName": "barr", - "label": "array bracket notation [String] -> text-array", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "text-array", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "[Int!]!", - "isArray": true - } - }, - "fieldName": "barr2", - "label": "array bracket non-null [Int!]! -> integer-array", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "integer-array", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgType": "varchar" - } - }, - "fieldName": "vc", - "label": "pgType varchar -> text", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "text", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgType": "citext" - } - }, - "fieldName": "pgci", - "label": "pgType citext -> citext", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "citext", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgType": "bpchar" - } - }, - "fieldName": "pgbp", - "label": "pgType bpchar -> bpchar", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "bpchar", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Int", - "pgType": "int4" - } - }, - "fieldName": "i4", - "label": "pgType int4 -> integer", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "integer", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Int", - "pgType": "int2" - } - }, - "fieldName": "i2pg", - "label": "pgType int2 -> smallint", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "smallint", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgType": "bigint" - } - }, - "fieldName": "bi", - "label": "pgType bigint -> integer", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "integer", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgType": "numeric" - } - }, - "fieldName": "pgnum", - "label": "pgType numeric -> decimal", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "decimal", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Float", - "pgType": "real" - } - }, - "fieldName": "rl", - "label": "pgType real -> number", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "number", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Float", - "pgType": "double precision" - } - }, - "fieldName": "dp", - "label": "pgType double precision -> number", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "number", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgType": "money" - } - }, - "fieldName": "mny", - "label": "pgType money -> currency", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "currency", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Boolean", - "pgType": "bool" - } - }, - "fieldName": "bl", - "label": "pgType bool -> boolean", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "boolean", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgType": "bit varying" - } - }, - "fieldName": "bv", - "label": "pgType bit varying -> bit", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "bit", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgType": "timestamp" - } - }, - "fieldName": "ts", - "label": "pgType timestamp -> datetime", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "datetime", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgType": "timestamptz" - } - }, - "fieldName": "tstz2", - "label": "pgType timestamptz -> timestamptz", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "timestamptz", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgType": "timetz" - } - }, - "fieldName": "ttz", - "label": "pgType timetz -> time", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "time", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgType": "interval" - } - }, - "fieldName": "ivpg", - "label": "pgType interval -> interval", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "interval", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "JSON", - "pgType": "jsonb" - } - }, - "fieldName": "jbpg", - "label": "pgType jsonb -> jsonb", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "jsonb", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgType": "uuid" - } - }, - "fieldName": "uupg", - "label": "pgType uuid -> uuid", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "uuid", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgType": "tsvector" - } - }, - "fieldName": "tsv", - "label": "pgType tsvector -> tsvector (NON_EDITABLE)", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": false, - "cellType": "tsvector", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgType": "cidr" - } - }, - "fieldName": "cidr", - "label": "pgType cidr -> inet", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "inet", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgType": "macaddr" - } - }, - "fieldName": "mac", - "label": "pgType macaddr -> text", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "text", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgType": "point" - } - }, - "fieldName": "ptpg", - "label": "pgType point -> geometry-point", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "geometry-point", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgType": "polygon" - } - }, - "fieldName": "polypg", - "label": "pgType polygon -> geometry", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "geometry", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgType": "geography" - } - }, - "fieldName": "geog", - "label": "pgType geography -> geometry", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "geometry", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgType": "email" - } - }, - "fieldName": "empg", - "label": "pgType email -> email", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "email", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgType": "origin" - } - }, - "fieldName": "ogpg", - "label": "pgType origin -> origin", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "origin", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgType": "_text" - } - }, - "fieldName": "txtarr", - "label": "pgType _text -> array", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "array", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Boolean", - "pgType": "zzz-not-real" - } - }, - "fieldName": "pgfall", - "label": "pgType unknown -> falls through to gqlType", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "boolean", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "UUID" - } - }, - "fieldName": "id", - "label": "readonly: id WITH gqlType UUID -> readonly uuid", - "resolution": { - "activationBehavior": "double-click", - "canActivate": false, - "canEdit": false, - "cellType": "uuid", - "isReadonly": true - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "Int" - } - }, - "fieldName": "id", - "label": "readonly: id WITHOUT UUID (Int) -> NOT readonly integer", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "integer", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String" - } - }, - "fieldName": "id", - "label": "readonly: id String -> NOT readonly text", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "text", - "isReadonly": false - } - }, - { - "fieldMeta": null, - "fieldName": "id", - "label": "readonly: id with NO fieldMeta -> NOT readonly, text", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "text", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "DateTime" - } - }, - "fieldName": "createdAt", - "label": "readonly: createdAt -> readonly (always)", - "resolution": { - "activationBehavior": "double-click", - "canActivate": false, - "canEdit": false, - "cellType": "datetime", - "isReadonly": true - } - }, - { - "fieldMeta": null, - "fieldName": "createdAt", - "label": "readonly: createdAt with NO fieldMeta -> readonly text", - "resolution": { - "activationBehavior": "double-click", - "canActivate": false, - "canEdit": false, - "cellType": "text", - "isReadonly": true - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "DateTime" - } - }, - "fieldName": "updatedAt", - "label": "readonly: updatedAt -> readonly (always)", - "resolution": { - "activationBehavior": "double-click", - "canActivate": false, - "canEdit": false, - "cellType": "datetime", - "isReadonly": true - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String" - } - }, - "fieldName": "title", - "label": "editable: normal field -> NOT readonly", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "text", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "col", - "type": { - "gqlType": "String", - "pgType": "tsvector" - } - }, - "fieldName": "updatedAt", - "label": "tsvector readonly-name interplay: updatedAt+tsvector", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": false, - "cellType": "tsvector", - "isReadonly": true - } - }, - { - "fieldMeta": null, - "fieldName": "whatever", - "label": "undefined fieldMeta -> text default", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "text", - "isReadonly": false - } - }, - { - "fieldMeta": { - "name": "whatever2" - }, - "fieldName": "whatever2", - "label": "fieldMeta present but no .type -> text default", - "resolution": { - "activationBehavior": "double-click", - "canActivate": true, - "canEdit": true, - "cellType": "text", - "isReadonly": false - } - } -] diff --git a/packages/sheets/src/grid/__golden__/resolver.golden.test.ts b/packages/sheets/src/grid/__golden__/resolver.golden.test.ts deleted file mode 100644 index 3be6e53..0000000 --- a/packages/sheets/src/grid/__golden__/resolver.golden.test.ts +++ /dev/null @@ -1,227 +0,0 @@ -/** - * PHASE-0 GOLDEN — resolveCellType() schema-metadata -> cell-type pipeline. - * - * Pure data, no glide. Snapshots the CURRENT v1 behavior of - * `resolveCellType(fieldName, fieldMeta)` for ~80 cases that exercise every - * reachable branch of the resolution pipeline: - * - * resolveCellType - * -> READONLY_CONDITIONS (id+UUID / createdAt / updatedAt) - * -> CellRegistry.findByMatch (the runtime singleton is EMPTY at module - * load — nothing registers entries — so this ALWAYS returns undefined and - * the resolver ALWAYS falls through to mapToFrontendCellType. Captured - * as-is; see `deviations`.) - * -> mapToFrontendCellType (pgAlias > isArray > pgType > gqlType) - * -> canEdit / canActivate / activationBehavior (NON_EDITABLE_TYPES + - * VIEWER_ONLY_TYPES = {tsvector}; SINGLE_CLICK_CELL_TYPES is EMPTY so - * activationBehavior is ALWAYS 'double-click'). - * - * The golden is generated by the REAL resolver (UPDATE_GOLDEN=1), not by any - * hand-predicted table, so it freezes actual behavior including bugs. - */ - -import { describe, expect, it } from 'vitest'; - -import type { FieldMetadata } from '../../cell-types/cell-type-resolver'; -import { resolveCellType } from '../../cell-types/cell-type-resolver'; - -import { assertOrUpdateGolden } from './parity.harness'; - -type FieldType = FieldMetadata['type']; - -interface ResolverCase { - label: string; - fieldName: string; - fieldMeta?: FieldMetadata; -} - -/** Build a nested FieldMetadata for an ordinary (non-readonly) column name. */ -function meta(type: FieldType): FieldMetadata { - return { name: 'col', type }; -} - -// ─── Case table — ~80 cases, grouped by which branch they exercise ────────── - -const CASES: ResolverCase[] = [ - // ── (A) gqlType fallback — every scalar branch of mapGraphQLTypeToCellType - { label: 'gql String -> text', fieldName: 'name', fieldMeta: meta({ gqlType: 'String' }) }, - { label: 'gql Int -> integer', fieldName: 'count', fieldMeta: meta({ gqlType: 'Int' }) }, - { label: 'gql Float -> number', fieldName: 'ratio', fieldMeta: meta({ gqlType: 'Float' }) }, - { label: 'gql Boolean -> boolean', fieldName: 'active', fieldMeta: meta({ gqlType: 'Boolean' }) }, - { label: 'gql ID -> uuid', fieldName: 'ref', fieldMeta: meta({ gqlType: 'ID' }) }, - { label: 'gql DateTime -> datetime', fieldName: 'at', fieldMeta: meta({ gqlType: 'DateTime' }) }, - { label: 'gql Datetime -> datetime', fieldName: 'at2', fieldMeta: meta({ gqlType: 'Datetime' }) }, - { label: 'gql Date -> date', fieldName: 'day', fieldMeta: meta({ gqlType: 'Date' }) }, - { label: 'gql Time -> time', fieldName: 'clock', fieldMeta: meta({ gqlType: 'Time' }) }, - { label: 'gql JSON -> json', fieldName: 'blob', fieldMeta: meta({ gqlType: 'JSON' }) }, - { label: 'gql JSONObject -> json', fieldName: 'obj', fieldMeta: meta({ gqlType: 'JSONObject' }) }, - { label: 'gql Upload -> file', fieldName: 'doc', fieldMeta: meta({ gqlType: 'Upload' }) }, - { label: 'gql URL -> url', fieldName: 'link', fieldMeta: meta({ gqlType: 'URL' }) }, - { label: 'gql EmailAddress -> email', fieldName: 'mail', fieldMeta: meta({ gqlType: 'EmailAddress' }) }, - { label: 'gql PhoneNumber -> phone', fieldName: 'tel', fieldMeta: meta({ gqlType: 'PhoneNumber' }) }, - { label: 'gql UUID -> uuid', fieldName: 'guid', fieldMeta: meta({ gqlType: 'UUID' }) }, - { label: 'gql BigFloat -> number', fieldName: 'big', fieldMeta: meta({ gqlType: 'BigFloat' }) }, - { label: 'gql BigInt -> integer', fieldName: 'bigi', fieldMeta: meta({ gqlType: 'BigInt' }) }, - { label: 'gql Relation -> relation', fieldName: 'owner', fieldMeta: meta({ gqlType: 'Relation' }) }, - { label: 'gql Interval -> interval', fieldName: 'dur', fieldMeta: meta({ gqlType: 'Interval' }) }, - { - label: 'gql GeometryGeometryCollection -> geometry-collection', - fieldName: 'shapes', - fieldMeta: meta({ gqlType: 'GeometryGeometryCollection' }), - }, - { label: 'gql unknown -> text (default)', fieldName: 'weird', fieldMeta: meta({ gqlType: 'Mystery' }) }, - { label: 'gql non-null String! -> text (modifier stripped)', fieldName: 'req', fieldMeta: meta({ gqlType: 'String!' }) }, - { label: 'gql empty string -> text', fieldName: 'empty', fieldMeta: meta({ gqlType: '' }) }, - - // ── (B) GeoJSON subtype branches - { label: 'GeoJSON GeometryPoint -> geometry-point', fieldName: 'pt', fieldMeta: meta({ gqlType: 'GeoJSON', subtype: 'GeometryPoint' }) }, - { label: 'GeoJSON GeometryPolygon -> geometry-collection', fieldName: 'poly', fieldMeta: meta({ gqlType: 'GeoJSON', subtype: 'GeometryPolygon' }) }, - { label: 'GeoJSON default -> geometry', fieldName: 'geo', fieldMeta: meta({ gqlType: 'GeoJSON' }) }, - { label: 'GeoJSON unrecognized subtype -> geometry', fieldName: 'geo2', fieldMeta: meta({ gqlType: 'GeoJSON', subtype: 'GeometryLine' }) }, - - // ── (C) pgAlias branch — every alias key (priority 1, even over arrays) - { label: 'alias image -> image', fieldName: 'pic', fieldMeta: meta({ gqlType: 'JSON', pgAlias: 'image' }) }, - { label: 'alias file -> file', fieldName: 'att', fieldMeta: meta({ gqlType: 'JSON', pgAlias: 'file' }) }, - { label: 'alias video -> video', fieldName: 'clip', fieldMeta: meta({ gqlType: 'JSON', pgAlias: 'video' }) }, - { label: 'alias audio -> audio', fieldName: 'snd', fieldMeta: meta({ gqlType: 'JSON', pgAlias: 'audio' }) }, - { label: 'alias upload -> upload', fieldName: 'up', fieldMeta: meta({ gqlType: 'JSON', pgAlias: 'upload' }) }, - { label: 'alias relation -> relation', fieldName: 'rel', fieldMeta: meta({ gqlType: 'String', pgAlias: 'relation' }) }, - { label: 'alias email -> email', fieldName: 'em', fieldMeta: meta({ gqlType: 'String', pgAlias: 'email' }) }, - { label: 'alias url -> url', fieldName: 'u', fieldMeta: meta({ gqlType: 'String', pgAlias: 'url' }) }, - { label: 'alias phone -> phone', fieldName: 'ph', fieldMeta: meta({ gqlType: 'String', pgAlias: 'phone' }) }, - { label: 'alias citext -> citext', fieldName: 'ci', fieldMeta: meta({ gqlType: 'String', pgAlias: 'citext' }) }, - { label: 'alias textarea -> textarea', fieldName: 'ta', fieldMeta: meta({ gqlType: 'String', pgAlias: 'textarea' }) }, - { label: 'alias number -> number', fieldName: 'n', fieldMeta: meta({ gqlType: 'Float', pgAlias: 'number' }) }, - { label: 'alias currency -> currency', fieldName: 'price', fieldMeta: meta({ gqlType: 'Float', pgAlias: 'currency' }) }, - { label: 'alias percentage -> percentage', fieldName: 'pct', fieldMeta: meta({ gqlType: 'Float', pgAlias: 'percentage' }) }, - { label: 'alias numeric -> decimal', fieldName: 'num', fieldMeta: meta({ gqlType: 'Float', pgAlias: 'numeric' }) }, - { label: 'alias decimal -> decimal', fieldName: 'dec', fieldMeta: meta({ gqlType: 'Float', pgAlias: 'decimal' }) }, - { label: 'alias int -> integer', fieldName: 'i', fieldMeta: meta({ gqlType: 'Int', pgAlias: 'int' }) }, - { label: 'alias integer -> integer', fieldName: 'i2', fieldMeta: meta({ gqlType: 'Int', pgAlias: 'integer' }) }, - { label: 'alias smallint -> smallint', fieldName: 'si', fieldMeta: meta({ gqlType: 'Int', pgAlias: 'smallint' }) }, - { label: 'alias boolean -> boolean', fieldName: 'b', fieldMeta: meta({ gqlType: 'Boolean', pgAlias: 'boolean' }) }, - { label: 'alias toggle -> toggle', fieldName: 'tg', fieldMeta: meta({ gqlType: 'Boolean', pgAlias: 'toggle' }) }, - { label: 'alias date -> date', fieldName: 'd', fieldMeta: meta({ gqlType: 'Date', pgAlias: 'date' }) }, - { label: 'alias datetime -> datetime', fieldName: 'dt', fieldMeta: meta({ gqlType: 'DateTime', pgAlias: 'datetime' }) }, - { label: 'alias time -> time', fieldName: 't', fieldMeta: meta({ gqlType: 'Time', pgAlias: 'time' }) }, - { label: 'alias timestamptz -> timestamptz', fieldName: 'tstz', fieldMeta: meta({ gqlType: 'DateTime', pgAlias: 'timestamptz' }) }, - { label: 'alias interval -> interval', fieldName: 'iv', fieldMeta: meta({ gqlType: 'Interval', pgAlias: 'interval' }) }, - { label: 'alias json -> json', fieldName: 'j', fieldMeta: meta({ gqlType: 'JSON', pgAlias: 'json' }) }, - { label: 'alias jsonb -> jsonb', fieldName: 'jb', fieldMeta: meta({ gqlType: 'JSON', pgAlias: 'jsonb' }) }, - { label: 'alias inet -> inet', fieldName: 'ip', fieldMeta: meta({ gqlType: 'String', pgAlias: 'inet' }) }, - { label: 'alias geometry -> geometry', fieldName: 'g', fieldMeta: meta({ gqlType: 'GeoJSON', pgAlias: 'geometry' }) }, - { label: 'alias geometry-point -> geometry-point', fieldName: 'gp', fieldMeta: meta({ gqlType: 'GeoJSON', pgAlias: 'geometry-point' }) }, - { label: 'alias geometry-collection -> geometry-collection', fieldName: 'gc', fieldMeta: meta({ gqlType: 'GeoJSON', pgAlias: 'geometry-collection' }) }, - { label: 'alias color -> color', fieldName: 'c', fieldMeta: meta({ gqlType: 'String', pgAlias: 'color' }) }, - { label: 'alias rating -> rating', fieldName: 'r', fieldMeta: meta({ gqlType: 'Int', pgAlias: 'rating' }) }, - { label: 'alias tags -> tags', fieldName: 'tg2', fieldMeta: meta({ gqlType: 'String', pgAlias: 'tags' }) }, - { label: 'alias uuid -> uuid', fieldName: 'uu', fieldMeta: meta({ gqlType: 'UUID', pgAlias: 'uuid' }) }, - { label: 'alias tsvector -> tsvector (NON_EDITABLE + VIEWER_ONLY)', fieldName: 'search', fieldMeta: meta({ gqlType: 'String', pgAlias: 'tsvector' }) }, - { label: 'alias origin -> origin', fieldName: 'og', fieldMeta: meta({ gqlType: 'String', pgAlias: 'origin' }) }, - { label: 'alias char -> bpchar', fieldName: 'ch', fieldMeta: meta({ gqlType: 'String', pgAlias: 'char' }) }, - { label: 'alias bpchar -> bpchar', fieldName: 'bp', fieldMeta: meta({ gqlType: 'String', pgAlias: 'bpchar' }) }, - { label: 'alias text -> text', fieldName: 'tx', fieldMeta: meta({ gqlType: 'String', pgAlias: 'text' }) }, - { label: 'alias bit -> bit', fieldName: 'bt', fieldMeta: meta({ gqlType: 'String', pgAlias: 'bit' }) }, - { label: 'alias UPPERCASE IMAGE -> image (case-insensitive)', fieldName: 'pic2', fieldMeta: meta({ gqlType: 'JSON', pgAlias: 'IMAGE' }) }, - { label: 'alias unknown -> falls through to gqlType', fieldName: 'fall', fieldMeta: meta({ gqlType: 'Int', pgAlias: 'zzz-not-real' }) }, - { label: 'alias unknown -> falls through to pgType', fieldName: 'fall2', fieldMeta: meta({ gqlType: 'String', pgAlias: 'zzz', pgType: 'uuid' }) }, - - // ── (D) pgAlias array keys (priority 1 beats the isArray branch) - { label: 'alias array -> array', fieldName: 'arr', fieldMeta: meta({ gqlType: 'String', isArray: true, pgAlias: 'array' }) }, - { label: 'alias text-array -> text-array', fieldName: 'ta_arr', fieldMeta: meta({ gqlType: 'String', isArray: true, pgAlias: 'text-array' }) }, - { label: 'alias integer-array -> integer-array', fieldName: 'i_arr', fieldMeta: meta({ gqlType: 'Int', isArray: true, pgAlias: 'integer-array' }) }, - { label: 'alias number-array -> number-array', fieldName: 'n_arr', fieldMeta: meta({ gqlType: 'BigFloat', isArray: true, pgAlias: 'number-array' }) }, - { label: 'alias uuid-array -> uuid-array', fieldName: 'u_arr', fieldMeta: meta({ gqlType: 'UUID', isArray: true, pgAlias: 'uuid-array' }) }, - { label: 'alias date-array -> date-array', fieldName: 'd_arr', fieldMeta: meta({ gqlType: 'Date', isArray: true, pgAlias: 'date-array' }) }, - { label: 'alias image WINS over isArray -> image', fieldName: 'pics', fieldMeta: meta({ gqlType: 'String', isArray: true, pgAlias: 'image' }) }, - - // ── (E) isArray branch (no pgAlias) — arrayTypeMap + generic fallback - { label: 'array String -> text-array', fieldName: 'sarr', fieldMeta: meta({ gqlType: 'String', isArray: true }) }, - { label: 'array UUID -> uuid-array', fieldName: 'uarr', fieldMeta: meta({ gqlType: 'UUID', isArray: true }) }, - { label: 'array BigFloat -> number-array', fieldName: 'narr', fieldMeta: meta({ gqlType: 'BigFloat', isArray: true }) }, - { label: 'array Int -> integer-array', fieldName: 'iarr', fieldMeta: meta({ gqlType: 'Int', isArray: true }) }, - { label: 'array Date -> date-array', fieldName: 'darr', fieldMeta: meta({ gqlType: 'Date', isArray: true }) }, - { label: 'array unknown base -> array (generic)', fieldName: 'garr', fieldMeta: meta({ gqlType: 'Foo', isArray: true }) }, - { label: 'array bracket notation [String] -> text-array', fieldName: 'barr', fieldMeta: meta({ gqlType: '[String]', isArray: true }) }, - { label: 'array bracket non-null [Int!]! -> integer-array', fieldName: 'barr2', fieldMeta: meta({ gqlType: '[Int!]!', isArray: true }) }, - - // ── (F) pgType branch (no pgAlias, not array) — mapBackendTypeToCellType - { label: 'pgType varchar -> text', fieldName: 'vc', fieldMeta: meta({ gqlType: 'String', pgType: 'varchar' }) }, - { label: 'pgType citext -> citext', fieldName: 'pgci', fieldMeta: meta({ gqlType: 'String', pgType: 'citext' }) }, - { label: 'pgType bpchar -> bpchar', fieldName: 'pgbp', fieldMeta: meta({ gqlType: 'String', pgType: 'bpchar' }) }, - { label: 'pgType int4 -> integer', fieldName: 'i4', fieldMeta: meta({ gqlType: 'Int', pgType: 'int4' }) }, - { label: 'pgType int2 -> smallint', fieldName: 'i2pg', fieldMeta: meta({ gqlType: 'Int', pgType: 'int2' }) }, - { label: 'pgType bigint -> integer', fieldName: 'bi', fieldMeta: meta({ gqlType: 'String', pgType: 'bigint' }) }, - { label: 'pgType numeric -> decimal', fieldName: 'pgnum', fieldMeta: meta({ gqlType: 'String', pgType: 'numeric' }) }, - { label: 'pgType real -> number', fieldName: 'rl', fieldMeta: meta({ gqlType: 'Float', pgType: 'real' }) }, - { label: 'pgType double precision -> number', fieldName: 'dp', fieldMeta: meta({ gqlType: 'Float', pgType: 'double precision' }) }, - { label: 'pgType money -> currency', fieldName: 'mny', fieldMeta: meta({ gqlType: 'String', pgType: 'money' }) }, - { label: 'pgType bool -> boolean', fieldName: 'bl', fieldMeta: meta({ gqlType: 'Boolean', pgType: 'bool' }) }, - { label: 'pgType bit varying -> bit', fieldName: 'bv', fieldMeta: meta({ gqlType: 'String', pgType: 'bit varying' }) }, - { label: 'pgType timestamp -> datetime', fieldName: 'ts', fieldMeta: meta({ gqlType: 'String', pgType: 'timestamp' }) }, - { label: 'pgType timestamptz -> timestamptz', fieldName: 'tstz2', fieldMeta: meta({ gqlType: 'String', pgType: 'timestamptz' }) }, - { label: 'pgType timetz -> time', fieldName: 'ttz', fieldMeta: meta({ gqlType: 'String', pgType: 'timetz' }) }, - { label: 'pgType interval -> interval', fieldName: 'ivpg', fieldMeta: meta({ gqlType: 'String', pgType: 'interval' }) }, - { label: 'pgType jsonb -> jsonb', fieldName: 'jbpg', fieldMeta: meta({ gqlType: 'JSON', pgType: 'jsonb' }) }, - { label: 'pgType uuid -> uuid', fieldName: 'uupg', fieldMeta: meta({ gqlType: 'String', pgType: 'uuid' }) }, - { label: 'pgType tsvector -> tsvector (NON_EDITABLE)', fieldName: 'tsv', fieldMeta: meta({ gqlType: 'String', pgType: 'tsvector' }) }, - { label: 'pgType cidr -> inet', fieldName: 'cidr', fieldMeta: meta({ gqlType: 'String', pgType: 'cidr' }) }, - { label: 'pgType macaddr -> text', fieldName: 'mac', fieldMeta: meta({ gqlType: 'String', pgType: 'macaddr' }) }, - { label: 'pgType point -> geometry-point', fieldName: 'ptpg', fieldMeta: meta({ gqlType: 'String', pgType: 'point' }) }, - { label: 'pgType polygon -> geometry', fieldName: 'polypg', fieldMeta: meta({ gqlType: 'String', pgType: 'polygon' }) }, - { label: 'pgType geography -> geometry', fieldName: 'geog', fieldMeta: meta({ gqlType: 'String', pgType: 'geography' }) }, - { label: 'pgType email -> email', fieldName: 'empg', fieldMeta: meta({ gqlType: 'String', pgType: 'email' }) }, - { label: 'pgType origin -> origin', fieldName: 'ogpg', fieldMeta: meta({ gqlType: 'String', pgType: 'origin' }) }, - { label: 'pgType _text -> array', fieldName: 'txtarr', fieldMeta: meta({ gqlType: 'String', pgType: '_text' }) }, - { label: 'pgType unknown -> falls through to gqlType', fieldName: 'pgfall', fieldMeta: meta({ gqlType: 'Boolean', pgType: 'zzz-not-real' }) }, - - // ── (G) READONLY matrix - { label: 'readonly: id WITH gqlType UUID -> readonly uuid', fieldName: 'id', fieldMeta: meta({ gqlType: 'UUID' }) }, - { label: 'readonly: id WITHOUT UUID (Int) -> NOT readonly integer', fieldName: 'id', fieldMeta: meta({ gqlType: 'Int' }) }, - { label: 'readonly: id String -> NOT readonly text', fieldName: 'id', fieldMeta: meta({ gqlType: 'String' }) }, - { label: 'readonly: id with NO fieldMeta -> NOT readonly, text', fieldName: 'id', fieldMeta: undefined }, - { label: 'readonly: createdAt -> readonly (always)', fieldName: 'createdAt', fieldMeta: meta({ gqlType: 'DateTime' }) }, - { label: 'readonly: createdAt with NO fieldMeta -> readonly text', fieldName: 'createdAt', fieldMeta: undefined }, - { label: 'readonly: updatedAt -> readonly (always)', fieldName: 'updatedAt', fieldMeta: meta({ gqlType: 'DateTime' }) }, - { label: 'editable: normal field -> NOT readonly', fieldName: 'title', fieldMeta: meta({ gqlType: 'String' }) }, - - // ── (H) tsvector locks (NON_EDITABLE_TYPES + VIEWER_ONLY_TYPES) — canActivate true, canEdit false - { label: 'tsvector readonly-name interplay: updatedAt+tsvector', fieldName: 'updatedAt', fieldMeta: meta({ gqlType: 'String', pgType: 'tsvector' }) }, - - // ── (I) undefined fieldMeta (and fieldMeta with no .type) -> default text - { label: 'undefined fieldMeta -> text default', fieldName: 'whatever', fieldMeta: undefined }, - { label: 'fieldMeta present but no .type -> text default', fieldName: 'whatever2', fieldMeta: { name: 'whatever2' } }, -]; - -describe('resolveCellType golden — schema-metadata -> cell-type pipeline', () => { - const results = CASES.map((c) => ({ - label: c.label, - fieldName: c.fieldName, - fieldMeta: c.fieldMeta ?? null, - resolution: resolveCellType(c.fieldName, c.fieldMeta), - })); - - it('matches the committed golden', () => { - assertOrUpdateGolden('resolver.golden', results); - }); - - it('every case has activationBehavior === double-click (locks the EMPTY single-click set)', () => { - for (const r of results) { - expect(r.resolution.activationBehavior, r.label).toBe('double-click'); - } - }); - - it('has at least ~80 cases', () => { - expect(results.length).toBeGreaterThanOrEqual(80); - }); - - it('tsvector is non-editable but still activatable (viewer-only lock)', () => { - // Reachable via alias and via pgType; both must lock the same way. - for (const r of results.filter((x) => x.resolution.cellType === 'tsvector')) { - expect(r.resolution.canEdit, `${r.label} canEdit`).toBe(false); - // canActivate is canEdit || VIEWER_ONLY.has -> true, UNLESS a readonly - // field-name ALSO forced canEdit false via isReadonly (still viewer-only). - expect(r.resolution.canActivate, `${r.label} canActivate`).toBe(true); - } - }); -}); diff --git a/packages/sheets/src/grid/__golden__/selection.golden.json b/packages/sheets/src/grid/__golden__/selection.golden.json deleted file mode 100644 index ead4fb3..0000000 --- a/packages/sheets/src/grid/__golden__/selection.golden.json +++ /dev/null @@ -1,1054 +0,0 @@ -[ - { - "name": "empty", - "ops": [], - "result": { - "first": null, - "hasIndexProbes": [ - { - "index": 0, - "present": false - }, - { - "index": 1, - "present": false - }, - { - "index": -1, - "present": false - } - ], - "items": [], - "last": null, - "length": 0 - } - }, - { - "name": "add-single-index", - "ops": [ - { - "index": 5, - "op": "add" - } - ], - "result": { - "first": 5, - "hasIndexProbes": [ - { - "index": 4, - "present": false - }, - { - "index": 5, - "present": true - }, - { - "index": 6, - "present": false - } - ], - "items": [ - 5 - ], - "last": 5, - "length": 1 - } - }, - { - "name": "add-three-single-indices-out-of-order-stay-separate", - "ops": [ - { - "index": 5, - "op": "add" - }, - { - "index": 1, - "op": "add" - }, - { - "index": 3, - "op": "add" - } - ], - "result": { - "first": 1, - "hasIndexProbes": [ - { - "index": 0, - "present": false - }, - { - "index": 1, - "present": true - }, - { - "index": 2, - "present": false - }, - { - "index": 3, - "present": true - }, - { - "index": 4, - "present": false - }, - { - "index": 5, - "present": true - }, - { - "index": 6, - "present": false - } - ], - "items": [ - 1, - 3, - 5 - ], - "last": 5, - "length": 3 - } - }, - { - "name": "add-range", - "ops": [ - { - "end": 5, - "op": "addRange", - "start": 2 - } - ], - "result": { - "first": 2, - "hasIndexProbes": [ - { - "index": 1, - "present": false - }, - { - "index": 2, - "present": true - }, - { - "index": 3, - "present": true - }, - { - "index": 4, - "present": true - }, - { - "index": 5, - "present": false - } - ], - "items": [ - 2, - 3, - 4 - ], - "last": 4, - "length": 3 - } - }, - { - "name": "add-adjacent-ranges-merge", - "ops": [ - { - "end": 3, - "op": "addRange", - "start": 0 - }, - { - "end": 5, - "op": "addRange", - "start": 3 - } - ], - "result": { - "first": 0, - "hasIndexProbes": [ - { - "index": 0, - "present": true - }, - { - "index": 2, - "present": true - }, - { - "index": 3, - "present": true - }, - { - "index": 4, - "present": true - }, - { - "index": 5, - "present": false - } - ], - "items": [ - 0, - 1, - 2, - 3, - 4 - ], - "last": 4, - "length": 5 - } - }, - { - "name": "add-index-adjacent-to-range-merges", - "ops": [ - { - "end": 3, - "op": "addRange", - "start": 0 - }, - { - "index": 3, - "op": "add" - } - ], - "result": { - "first": 0, - "hasIndexProbes": [ - { - "index": 0, - "present": true - }, - { - "index": 3, - "present": true - }, - { - "index": 4, - "present": false - } - ], - "items": [ - 0, - 1, - 2, - 3 - ], - "last": 3, - "length": 4 - } - }, - { - "name": "add-overlapping-ranges-merge", - "ops": [ - { - "end": 4, - "op": "addRange", - "start": 0 - }, - { - "end": 6, - "op": "addRange", - "start": 2 - } - ], - "result": { - "first": 0, - "hasIndexProbes": [ - { - "index": 0, - "present": true - }, - { - "index": 3, - "present": true - }, - { - "index": 5, - "present": true - }, - { - "index": 6, - "present": false - } - ], - "items": [ - 0, - 1, - 2, - 3, - 4, - 5 - ], - "last": 5, - "length": 6 - } - }, - { - "name": "add-contained-range-is-noop", - "ops": [ - { - "end": 10, - "op": "addRange", - "start": 0 - }, - { - "end": 5, - "op": "addRange", - "start": 3 - } - ], - "result": { - "first": 0, - "hasIndexProbes": [ - { - "index": 0, - "present": true - }, - { - "index": 4, - "present": true - }, - { - "index": 9, - "present": true - }, - { - "index": 10, - "present": false - } - ], - "items": [ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9 - ], - "last": 9, - "length": 10 - } - }, - { - "name": "add-ranges-with-gap-stay-separate", - "ops": [ - { - "end": 2, - "op": "addRange", - "start": 0 - }, - { - "end": 7, - "op": "addRange", - "start": 5 - } - ], - "result": { - "first": 0, - "hasIndexProbes": [ - { - "index": 0, - "present": true - }, - { - "index": 1, - "present": true - }, - { - "index": 2, - "present": false - }, - { - "index": 3, - "present": false - }, - { - "index": 4, - "present": false - }, - { - "index": 5, - "present": true - }, - { - "index": 6, - "present": true - }, - { - "index": 7, - "present": false - } - ], - "items": [ - 0, - 1, - 5, - 6 - ], - "last": 6, - "length": 4 - } - }, - { - "name": "remove-index-splits-range", - "ops": [ - { - "end": 10, - "op": "addRange", - "start": 0 - }, - { - "index": 4, - "op": "remove" - } - ], - "result": { - "first": 0, - "hasIndexProbes": [ - { - "index": 3, - "present": true - }, - { - "index": 4, - "present": false - }, - { - "index": 5, - "present": true - }, - { - "index": 9, - "present": true - }, - { - "index": 10, - "present": false - } - ], - "items": [ - 0, - 1, - 2, - 3, - 5, - 6, - 7, - 8, - 9 - ], - "last": 9, - "length": 9 - } - }, - { - "name": "remove-range-splits-range", - "ops": [ - { - "end": 10, - "op": "addRange", - "start": 0 - }, - { - "end": 6, - "op": "removeRange", - "start": 3 - } - ], - "result": { - "first": 0, - "hasIndexProbes": [ - { - "index": 2, - "present": true - }, - { - "index": 3, - "present": false - }, - { - "index": 5, - "present": false - }, - { - "index": 6, - "present": true - }, - { - "index": 9, - "present": true - } - ], - "items": [ - 0, - 1, - 2, - 6, - 7, - 8, - 9 - ], - "last": 9, - "length": 7 - } - }, - { - "name": "toggle-index-add-then-remove-empties", - "ops": [ - { - "index": 7, - "op": "add" - }, - { - "index": 7, - "op": "remove" - } - ], - "result": { - "first": null, - "hasIndexProbes": [ - { - "index": 6, - "present": false - }, - { - "index": 7, - "present": false - }, - { - "index": 8, - "present": false - } - ], - "items": [], - "last": null, - "length": 0 - } - }, - { - "name": "remove-entire-range-empties", - "ops": [ - { - "end": 3, - "op": "addRange", - "start": 0 - }, - { - "end": 3, - "op": "removeRange", - "start": 0 - } - ], - "result": { - "first": null, - "hasIndexProbes": [ - { - "index": 0, - "present": false - }, - { - "index": 1, - "present": false - }, - { - "index": 2, - "present": false - } - ], - "items": [], - "last": null, - "length": 0 - } - }, - { - "name": "remove-absent-index-is-noop", - "ops": [ - { - "end": 3, - "op": "addRange", - "start": 0 - }, - { - "index": 5, - "op": "remove" - } - ], - "result": { - "first": 0, - "hasIndexProbes": [ - { - "index": 0, - "present": true - }, - { - "index": 2, - "present": true - }, - { - "index": 5, - "present": false - } - ], - "items": [ - 0, - 1, - 2 - ], - "last": 2, - "length": 3 - } - }, - { - "name": "remove-trims-two-adjacent-ranges", - "ops": [ - { - "end": 3, - "op": "addRange", - "start": 0 - }, - { - "end": 7, - "op": "addRange", - "start": 4 - }, - { - "end": 5, - "op": "removeRange", - "start": 2 - } - ], - "result": { - "first": 0, - "hasIndexProbes": [ - { - "index": 0, - "present": true - }, - { - "index": 1, - "present": true - }, - { - "index": 2, - "present": false - }, - { - "index": 4, - "present": false - }, - { - "index": 5, - "present": true - }, - { - "index": 6, - "present": true - } - ], - "items": [ - 0, - 1, - 5, - 6 - ], - "last": 6, - "length": 4 - } - }, - { - "name": "remove-spanning-three-ranges", - "ops": [ - { - "end": 2, - "op": "addRange", - "start": 0 - }, - { - "end": 6, - "op": "addRange", - "start": 4 - }, - { - "end": 10, - "op": "addRange", - "start": 8 - }, - { - "end": 9, - "op": "removeRange", - "start": 1 - } - ], - "result": { - "first": 0, - "hasIndexProbes": [ - { - "index": 0, - "present": true - }, - { - "index": 1, - "present": false - }, - { - "index": 4, - "present": false - }, - { - "index": 5, - "present": false - }, - { - "index": 8, - "present": true - }, - { - "index": 9, - "present": true - } - ], - "items": [ - 0, - 8, - 9 - ], - "last": 9, - "length": 3 - } - }, - { - "name": "offset-shifts-all-ranges", - "ops": [ - { - "end": 5, - "op": "addRange", - "start": 2 - }, - { - "amount": 10, - "op": "offset" - } - ], - "result": { - "first": 12, - "hasIndexProbes": [ - { - "index": 11, - "present": false - }, - { - "index": 12, - "present": true - }, - { - "index": 14, - "present": true - }, - { - "index": 15, - "present": false - } - ], - "items": [ - 12, - 13, - 14 - ], - "last": 14, - "length": 3 - } - }, - { - "name": "offset-zero-is-identity", - "ops": [ - { - "index": 3, - "op": "add" - }, - { - "amount": 0, - "op": "offset" - } - ], - "result": { - "first": 3, - "hasIndexProbes": [ - { - "index": 2, - "present": false - }, - { - "index": 3, - "present": true - }, - { - "index": 4, - "present": false - } - ], - "items": [ - 3 - ], - "last": 3, - "length": 1 - } - }, - { - "name": "offset-negative-shift", - "ops": [ - { - "end": 15, - "op": "addRange", - "start": 12 - }, - { - "amount": -10, - "op": "offset" - } - ], - "result": { - "first": 2, - "hasIndexProbes": [ - { - "index": 1, - "present": false - }, - { - "index": 2, - "present": true - }, - { - "index": 4, - "present": true - }, - { - "index": 5, - "present": false - } - ], - "items": [ - 2, - 3, - 4 - ], - "last": 4, - "length": 3 - } - }, - { - "name": "from-single-index", - "ops": [ - { - "index": 7, - "op": "fromSingleIndex" - } - ], - "result": { - "first": 7, - "hasIndexProbes": [ - { - "index": 6, - "present": false - }, - { - "index": 7, - "present": true - }, - { - "index": 8, - "present": false - } - ], - "items": [ - 7 - ], - "last": 7, - "length": 1 - } - }, - { - "name": "from-single-range", - "ops": [ - { - "end": 5, - "op": "fromSingleRange", - "start": 2 - } - ], - "result": { - "first": 2, - "hasIndexProbes": [ - { - "index": 1, - "present": false - }, - { - "index": 2, - "present": true - }, - { - "index": 4, - "present": true - }, - { - "index": 5, - "present": false - } - ], - "items": [ - 2, - 3, - 4 - ], - "last": 4, - "length": 3 - } - }, - { - "name": "from-single-index-then-add-resets-baseline", - "ops": [ - { - "index": 99, - "op": "add" - }, - { - "index": 2, - "op": "fromSingleIndex" - }, - { - "index": 4, - "op": "add" - } - ], - "result": { - "first": 2, - "hasIndexProbes": [ - { - "index": 2, - "present": true - }, - { - "index": 3, - "present": false - }, - { - "index": 4, - "present": true - }, - { - "index": 99, - "present": false - } - ], - "items": [ - 2, - 4 - ], - "last": 4, - "length": 2 - } - }, - { - "name": "large-range-0-to-10000", - "ops": [ - { - "end": 10000, - "op": "addRange", - "start": 0 - } - ], - "result": { - "first": 0, - "hasIndexProbes": [ - { - "index": -1, - "present": false - }, - { - "index": 0, - "present": true - }, - { - "index": 1, - "present": true - }, - { - "index": 5000, - "present": true - }, - { - "index": 9998, - "present": true - }, - { - "index": 9999, - "present": true - }, - { - "index": 10000, - "present": false - } - ], - "last": 9999, - "length": 10000 - } - }, - { - "name": "large-range-split-by-remove", - "ops": [ - { - "end": 10000, - "op": "addRange", - "start": 0 - }, - { - "end": 6000, - "op": "removeRange", - "start": 4000 - } - ], - "result": { - "first": 0, - "hasIndexProbes": [ - { - "index": 0, - "present": true - }, - { - "index": 3999, - "present": true - }, - { - "index": 4000, - "present": false - }, - { - "index": 5000, - "present": false - }, - { - "index": 5999, - "present": false - }, - { - "index": 6000, - "present": true - }, - { - "index": 9999, - "present": true - } - ], - "last": 9999, - "length": 8000 - } - } -] diff --git a/packages/sheets/src/grid/__golden__/selection.golden.test.ts b/packages/sheets/src/grid/__golden__/selection.golden.test.ts deleted file mode 100644 index df2d4b2..0000000 --- a/packages/sheets/src/grid/__golden__/selection.golden.test.ts +++ /dev/null @@ -1,389 +0,0 @@ -/** - * Canonical behavioral spec for selection/range-set.ts (Phase 6). The native - * RangeSet MUST reproduce every (ops -> result) here. RangeSet is canonical; - * TanStack rowSelection is only a one-way derived mirror. - * - * ────────────────────────────────────────────────────────────────────────── - * WHAT THIS GOLDEN FREEZES - * - * The committed `selection.golden.json` was captured from glide's - * `CompactSelection` — the reference the grid relied on pre-cutover. The native - * {@link RangeSet} REPLACES it, and exposes the same small surface the grid reads: - * - * - `selection.rows.toArray()` -> selected row indices, ascending - * - `selection.rows.toArray().length` / `selection.rows.length` -> selected count - * - `hasIndex`, `first`, `last`, `offset`, `add`, `remove`, `fromSingleSelection` - * -> the construction/mutation ops used to build the value the grid reads. - * - * Each named case is an `ops` PROGRAM (a serializable op list) starting from - * `RangeSet.empty()`. The SAME op list is both (a) the committed spec and (b) the - * executable input here, so the recorded `result` provably derives from those ops - * — there is no hand-transcribed expectation to drift. - * - * RangeSet re-runs these exact op programs through its own interpreter and MUST - * produce the byte-identical `result` objects the frozen golden holds. This file - * IS the RangeSet contract. - * - * NOTE — the frozen golden captures glide's quirks AS-IS, which RangeSet faithfully - * reproduces (see `deviations`): - * - ADJACENCY MERGES. `add([0,3])` then `add([3,5])` -> a single `[0,5)` range - * (mergeRanges treats touching ranges as joinable). Likewise `add([0,3])` - * then `add(3)` -> `[0,4)`. The RangeSet must merge on touch, not only on - * overlap. - * - remove() SPLICES DURING ITERATION. `remove` mutates `items` in place while - * looping `items.entries()`. For the multi-range removals captured here the - * observable result is still the set-theoretic difference; the contract is the - * RESULT, and the result is what RangeSet must match. (Case - * "remove-spanning-three-ranges" exercises the path that fully drops a middle - * range mid-iteration.) - * - `first()`/`last()` on empty -> `undefined` (NOT -1, NOT 0). Recorded as the - * JSON value `null` after serialization. - */ - -import { describe, it } from 'vitest'; - -import { RangeSet } from '../../selection/range-set'; - -import { assertOrUpdateGolden } from './parity.harness'; - -// ─── Serializable op vocabulary ───────────────────────────────────────────── -// -// Every op is a plain JSON object: this is exactly what gets written into the -// golden, AND what the interpreter below executes over the native RangeSet. - -type Op = - | { op: 'add'; index: number } // add(number) -> covers [index, index+1) - | { op: 'addRange'; start: number; end: number } // add([start, end)) - | { op: 'remove'; index: number } // remove(number) - | { op: 'removeRange'; start: number; end: number } // remove([start, end)) - | { op: 'offset'; amount: number } // offset(amount) - | { op: 'fromSingleIndex'; index: number } // RESET to fromSingleSelection(index) - | { op: 'fromSingleRange'; start: number; end: number }; // RESET to fromSingleSelection([start,end)) - -/** - * Run an op PROGRAM against the native {@link RangeSet}, starting from `empty()`. - * `fromSingle*` ops RESET the accumulator to a fresh single-selection (mirroring - * how a selection is built from one click), every other op threads the immutable - * result forward. RangeSet must reproduce the frozen golden these ops generate. - */ -function runOps(ops: Op[]): RangeSet { - let sel = RangeSet.empty(); - for (const o of ops) { - switch (o.op) { - case 'add': - sel = sel.add(o.index); - break; - case 'addRange': - sel = sel.add([o.start, o.end]); - break; - case 'remove': - sel = sel.remove(o.index); - break; - case 'removeRange': - sel = sel.remove([o.start, o.end]); - break; - case 'offset': - sel = sel.offset(o.amount); - break; - case 'fromSingleIndex': - sel = RangeSet.fromSingleSelection(o.index); - break; - case 'fromSingleRange': - sel = RangeSet.fromSingleSelection([o.start, o.end]); - break; - } - } - return sel; -} - -interface HasIndexProbe { - index: number; - present: boolean; -} - -interface SequenceResult { - /** Materialized selected indices (ascending). OMITTED for the huge case. */ - items?: number[]; - /** Selected count (== items.length for finite cases; the value the controls show). */ - length: number; - /** first() — start of the lowest range, or null when empty. */ - first: number | null; - /** last() — last selected index, or null when empty. */ - last: number | null; - /** Membership probes at chosen boundary/inside/outside indices. */ - hasIndexProbes: HasIndexProbe[]; -} - -interface GoldenSequence { - name: string; - ops: Op[]; - result: SequenceResult; -} - -/** - * Build a recorded result from a selection. `probeIndices` chooses which - * `hasIndex` boundaries to pin. `sampleItems=false` omits the materialized array - * (used ONLY for the 10k case, where `length`+`first`+`last`+probes fully pin - * behavior without dumping ten thousand entries). - */ -function record(sel: RangeSet, probeIndices: number[], sampleItems = true): SequenceResult { - const first = sel.first(); - const last = sel.last(); - const result: SequenceResult = { - length: sel.length, - first: first === undefined ? null : first, - last: last === undefined ? null : last, - hasIndexProbes: probeIndices.map((index) => ({ index, present: sel.hasIndex(index) })), - }; - if (sampleItems) result.items = sel.toArray(); - return result; -} - -// ─── The op-sequence battery ──────────────────────────────────────────────── - -interface CaseSpec { - name: string; - ops: Op[]; - probes: number[]; - sampleItems?: boolean; -} - -const CASES: CaseSpec[] = [ - // — empties / single index — - { - name: 'empty', - ops: [], - probes: [0, 1, -1], - }, - { - name: 'add-single-index', - ops: [{ op: 'add', index: 5 }], - probes: [4, 5, 6], - }, - { - name: 'add-three-single-indices-out-of-order-stay-separate', - // 5,1,3 -> ascending, three width-1 ranges (no merge: they are not adjacent) - ops: [ - { op: 'add', index: 5 }, - { op: 'add', index: 1 }, - { op: 'add', index: 3 }, - ], - probes: [0, 1, 2, 3, 4, 5, 6], - }, - - // — range add — - { - name: 'add-range', - // add([2,5)) -> indices 2,3,4 (end-exclusive) - ops: [{ op: 'addRange', start: 2, end: 5 }], - probes: [1, 2, 3, 4, 5], - }, - - // — adjacency merge (QUIRK: touching ranges merge) — - { - name: 'add-adjacent-ranges-merge', - // [0,3) then [3,5) -> single [0,5) - ops: [ - { op: 'addRange', start: 0, end: 3 }, - { op: 'addRange', start: 3, end: 5 }, - ], - probes: [0, 2, 3, 4, 5], - }, - { - name: 'add-index-adjacent-to-range-merges', - // [0,3) then add(3) -> single [0,4) - ops: [ - { op: 'addRange', start: 0, end: 3 }, - { op: 'add', index: 3 }, - ], - probes: [0, 3, 4], - }, - - // — overlap merge — - { - name: 'add-overlapping-ranges-merge', - // [0,4) then [2,6) -> single [0,6) - ops: [ - { op: 'addRange', start: 0, end: 4 }, - { op: 'addRange', start: 2, end: 6 }, - ], - probes: [0, 3, 5, 6], - }, - { - name: 'add-contained-range-is-noop', - // [0,10) then [3,5) (fully inside) -> still [0,10) - ops: [ - { op: 'addRange', start: 0, end: 10 }, - { op: 'addRange', start: 3, end: 5 }, - ], - probes: [0, 4, 9, 10], - }, - - // — gap (NO merge) — - { - name: 'add-ranges-with-gap-stay-separate', - // [0,2) then [5,7) -> two ranges - ops: [ - { op: 'addRange', start: 0, end: 2 }, - { op: 'addRange', start: 5, end: 7 }, - ], - probes: [0, 1, 2, 3, 4, 5, 6, 7], - }, - - // — remove that splits a range — - { - name: 'remove-index-splits-range', - // [0,10) remove(4) -> [0,4) + [5,10) - ops: [ - { op: 'addRange', start: 0, end: 10 }, - { op: 'remove', index: 4 }, - ], - probes: [3, 4, 5, 9, 10], - }, - { - name: 'remove-range-splits-range', - // [0,10) remove([3,6)) -> [0,3) + [6,10) - ops: [ - { op: 'addRange', start: 0, end: 10 }, - { op: 'removeRange', start: 3, end: 6 }, - ], - probes: [2, 3, 5, 6, 9], - }, - - // — toggle (add then remove the same index) — - { - name: 'toggle-index-add-then-remove-empties', - // add(7) then remove(7) -> empty - ops: [ - { op: 'add', index: 7 }, - { op: 'remove', index: 7 }, - ], - probes: [6, 7, 8], - }, - { - name: 'remove-entire-range-empties', - // [0,3) remove([0,3)) -> empty - ops: [ - { op: 'addRange', start: 0, end: 3 }, - { op: 'removeRange', start: 0, end: 3 }, - ], - probes: [0, 1, 2], - }, - { - name: 'remove-absent-index-is-noop', - // [0,3) remove(5) -> still [0,3) - ops: [ - { op: 'addRange', start: 0, end: 3 }, - { op: 'remove', index: 5 }, - ], - probes: [0, 2, 5], - }, - - // — remove across multiple ranges (splice-during-iteration path) — - { - name: 'remove-trims-two-adjacent-ranges', - // [0,3) + [4,7) remove([2,5)) -> [0,2) + [5,7) - ops: [ - { op: 'addRange', start: 0, end: 3 }, - { op: 'addRange', start: 4, end: 7 }, - { op: 'removeRange', start: 2, end: 5 }, - ], - probes: [0, 1, 2, 4, 5, 6], - }, - { - name: 'remove-spanning-three-ranges', - // [0,2) + [4,6) + [8,10) remove([1,9)) -> [0,1) + [8,10) - // (middle range [4,6) fully dropped mid-iteration — the splice-while-looping path) - ops: [ - { op: 'addRange', start: 0, end: 2 }, - { op: 'addRange', start: 4, end: 6 }, - { op: 'addRange', start: 8, end: 10 }, - { op: 'removeRange', start: 1, end: 9 }, - ], - probes: [0, 1, 4, 5, 8, 9], - }, - - // — offset — - { - name: 'offset-shifts-all-ranges', - // [2,5) offset(+10) -> [12,15) - ops: [ - { op: 'addRange', start: 2, end: 5 }, - { op: 'offset', amount: 10 }, - ], - probes: [11, 12, 14, 15], - }, - { - name: 'offset-zero-is-identity', - // add(3) offset(0) -> still [3,4) - ops: [ - { op: 'add', index: 3 }, - { op: 'offset', amount: 0 }, - ], - probes: [2, 3, 4], - }, - { - name: 'offset-negative-shift', - // [12,15) offset(-10) -> [2,5) - ops: [ - { op: 'addRange', start: 12, end: 15 }, - { op: 'offset', amount: -10 }, - ], - probes: [1, 2, 4, 5], - }, - - // — fromSingleSelection constructors (RESET semantics) — - { - name: 'from-single-index', - // fromSingleSelection(7) -> [7,8) - ops: [{ op: 'fromSingleIndex', index: 7 }], - probes: [6, 7, 8], - }, - { - name: 'from-single-range', - // fromSingleSelection([2,5)) -> [2,5) - ops: [{ op: 'fromSingleRange', start: 2, end: 5 }], - probes: [1, 2, 4, 5], - }, - { - name: 'from-single-index-then-add-resets-baseline', - // fromSingleSelection(2) RESETS, then add(4) -> [2,3) + [4,5) - ops: [ - { op: 'add', index: 99 }, // discarded by the reset below - { op: 'fromSingleIndex', index: 2 }, - { op: 'add', index: 4 }, - ], - probes: [2, 3, 4, 99], - }, - - // — large range: record length/first/last + sampled probes only (NO 10k dump) — - { - name: 'large-range-0-to-10000', - // add([0,10000)) -> length 10000, first 0, last 9999; probe the boundaries. - ops: [{ op: 'addRange', start: 0, end: 10000 }], - probes: [-1, 0, 1, 5000, 9998, 9999, 10000], - sampleItems: false, - }, - { - name: 'large-range-split-by-remove', - // add([0,10000)) remove([4000,6000)) -> [0,4000) + [6000,10000); length 8000. - ops: [ - { op: 'addRange', start: 0, end: 10000 }, - { op: 'removeRange', start: 4000, end: 6000 }, - ], - probes: [0, 3999, 4000, 5000, 5999, 6000, 9999], - sampleItems: false, - }, -]; - -describe('selection golden — native RangeSet behavioral spec', () => { - it('reproduces every (ops -> result) the frozen golden froze', () => { - const sequences: GoldenSequence[] = CASES.map(({ name, ops, probes, sampleItems }) => ({ - name, - ops, - result: record(runOps(ops), probes, sampleItems !== false), - })); - - assertOrUpdateGolden('selection.golden', sequences); - }); -}); diff --git a/packages/sheets/src/grid/__golden__/sheets-dom.golden.json b/packages/sheets/src/grid/__golden__/sheets-dom.golden.json deleted file mode 100644 index 4882794..0000000 --- a/packages/sheets/src/grid/__golden__/sheets-dom.golden.json +++ /dev/null @@ -1,3038 +0,0 @@ -[ - { - "cellType": "uuid", - "cells": [ - { - "copyText": "00fb8673-8b42-4c83-5484-f3e32248c1e8", - "displayText": "00fb8673-8b42-4c83-5484-f3e32248c1e8", - "row": 0 - }, - { - "copyText": "e47eac5e-fd02-4c11-e494-cb07f116cb47", - "displayText": "e47eac5e-fd02-4c11-e494-cb07f116cb47", - "row": 1 - }, - { - "copyText": "6407cd8b-ff5d-4dce-bb54-c5c09937ae40", - "displayText": "6407cd8b-ff5d-4dce-bb54-c5c09937ae40", - "row": 2 - }, - { - "copyText": "61a22780-4f9c-4ea6-d7c6-1d2233c08581", - "displayText": "61a22780-4f9c-4ea6-d7c6-1d2233c08581", - "row": 3 - }, - { - "copyText": "6de45a09-e593-4c42-25a8-83bc7358b18f", - "displayText": "6de45a09-e593-4c42-25a8-83bc7358b18f", - "row": 4 - }, - { - "copyText": "6ba704b7-7bb2-49b3-a28e-b2a4ba7a1653", - "displayText": "6ba704b7-7bb2-49b3-a28e-b2a4ba7a1653", - "row": 5 - }, - { - "copyText": "8608258a-b3a6-447e-2420-a4d3df2491fe", - "displayText": "8608258a-b3a6-447e-2420-a4d3df2491fe", - "row": 6 - }, - { - "copyText": "37760c69-c30f-491c-fd6f-2fdf61f7c21e", - "displayText": "37760c69-c30f-491c-fd6f-2fdf61f7c21e", - "row": 7 - }, - { - "copyText": "1d2c49c3-ee8a-4918-f938-82c42193f22c", - "displayText": "1d2c49c3-ee8a-4918-f938-82c42193f22c", - "row": 8 - }, - { - "copyText": "11fcefca-f13a-4adf-c5ca-15d01efebcce", - "displayText": "11fcefca-f13a-4adf-c5ca-15d01efebcce", - "row": 9 - }, - { - "copyText": "5cb8ebab-30bf-4072-0dad-da811b0a9104", - "displayText": "5cb8ebab-30bf-4072-0dad-da811b0a9104", - "row": 10 - }, - { - "copyText": "b92351a8-e942-418b-d42d-19ea3c46bdc3", - "displayText": "b92351a8-e942-418b-d42d-19ea3c46bdc3", - "row": 11 - } - ], - "field": "id" - }, - { - "cellType": "text", - "cells": [ - { - "copyText": "golf golf", - "displayText": "golf golf", - "row": 0 - }, - { - "copyText": "bravo echo", - "displayText": "bravo echo", - "row": 1 - }, - { - "copyText": "foxtrot echo", - "displayText": "foxtrot echo", - "row": 2 - }, - { - "copyText": "alpha alpha", - "displayText": "alpha alpha", - "row": 3 - }, - { - "copyText": "foxtrot foxtrot", - "displayText": "foxtrot foxtrot", - "row": 4 - }, - { - "copyText": "foxtrot bravo", - "displayText": "foxtrot bravo", - "row": 5 - }, - { - "copyText": "bravo golf", - "displayText": "bravo golf", - "row": 6 - }, - { - "copyText": "alpha india", - "displayText": "alpha india", - "row": 7 - }, - { - "copyText": "golf bravo", - "displayText": "golf bravo", - "row": 8 - }, - { - "copyText": "golf alpha", - "displayText": "golf alpha", - "row": 9 - }, - { - "copyText": "juliet echo", - "displayText": "juliet echo", - "row": 10 - }, - { - "copyText": "foxtrot hotel", - "displayText": "foxtrot hotel", - "row": 11 - } - ], - "field": "text" - }, - { - "cellType": "textarea", - "cells": [ - { - "copyText": "juliet\ngolf juliet", - "displayText": "juliet\ngolf juliet", - "row": 0 - }, - { - "copyText": "echo\nfoxtrot bravo", - "displayText": "echo\nfoxtrot bravo", - "row": 1 - }, - { - "copyText": "alpha\nalpha echo", - "displayText": "alpha\nalpha echo", - "row": 2 - }, - { - "copyText": "hotel\ncharlie charlie", - "displayText": "hotel\ncharlie charlie", - "row": 3 - }, - { - "copyText": "juliet\ngolf india", - "displayText": "juliet\ngolf india", - "row": 4 - }, - { - "copyText": "alpha\njuliet india", - "displayText": "alpha\njuliet india", - "row": 5 - }, - { - "copyText": "echo\nhotel golf", - "displayText": "echo\nhotel golf", - "row": 6 - }, - { - "copyText": "foxtrot\nalpha hotel", - "displayText": "foxtrot\nalpha hotel", - "row": 7 - }, - { - "copyText": "india\ngolf echo", - "displayText": "india\ngolf echo", - "row": 8 - }, - { - "copyText": "bravo\necho delta", - "displayText": "bravo\necho delta", - "row": 9 - }, - { - "copyText": "golf\ngolf bravo", - "displayText": "golf\ngolf bravo", - "row": 10 - }, - { - "copyText": "india\ndelta hotel", - "displayText": "india\ndelta hotel", - "row": 11 - } - ], - "field": "textarea" - }, - { - "cellType": "email", - "cells": [ - { - "copyText": "mailto:edsger.lovelace@example.com", - "displayText": "edsger.lovelace@example.com", - "row": 0 - }, - { - "copyText": "mailto:ken.torvalds@example.com", - "displayText": "ken.torvalds@example.com", - "row": 1 - }, - { - "copyText": "mailto:alan.liskov@example.com", - "displayText": "alan.liskov@example.com", - "row": 2 - }, - { - "copyText": "mailto:barbara.ritchie@example.com", - "displayText": "barbara.ritchie@example.com", - "row": 3 - }, - { - "copyText": "mailto:ken.turing@example.com", - "displayText": "ken.turing@example.com", - "row": 4 - }, - { - "copyText": "mailto:alan.thompson@example.com", - "displayText": "alan.thompson@example.com", - "row": 5 - }, - { - "copyText": "mailto:margaret.ritchie@example.com", - "displayText": "margaret.ritchie@example.com", - "row": 6 - }, - { - "copyText": "mailto:margaret.knuth@example.com", - "displayText": "margaret.knuth@example.com", - "row": 7 - }, - { - "copyText": "mailto:grace.hopper@example.com", - "displayText": "grace.hopper@example.com", - "row": 8 - }, - { - "copyText": "mailto:edsger.turing@example.com", - "displayText": "edsger.turing@example.com", - "row": 9 - }, - { - "copyText": "mailto:grace.thompson@example.com", - "displayText": "grace.thompson@example.com", - "row": 10 - }, - { - "copyText": "mailto:edsger.lovelace@example.com", - "displayText": "edsger.lovelace@example.com", - "row": 11 - } - ], - "field": "email" - }, - { - "cellType": "url", - "cells": [ - { - "copyText": "https://example.com/bravo/0.html", - "displayText": "https://example.com/bravo/0.html", - "row": 0 - }, - { - "copyText": "https://example.com/golf/1.html", - "displayText": "https://example.com/golf/1.html", - "row": 1 - }, - { - "copyText": "https://example.com/hotel/2.html", - "displayText": "https://example.com/hotel/2.html", - "row": 2 - }, - { - "copyText": "https://example.com/charlie/3.html", - "displayText": "https://example.com/charlie/3.html", - "row": 3 - }, - { - "copyText": "https://example.com/alpha/4.html", - "displayText": "https://example.com/alpha/4.html", - "row": 4 - }, - { - "copyText": "https://example.com/hotel/5.html", - "displayText": "https://example.com/hotel/5.html", - "row": 5 - }, - { - "copyText": "https://example.com/delta/6.html", - "displayText": "https://example.com/delta/6.html", - "row": 6 - }, - { - "copyText": "https://example.com/india/7.html", - "displayText": "https://example.com/india/7.html", - "row": 7 - }, - { - "copyText": "https://example.com/delta/8.html", - "displayText": "https://example.com/delta/8.html", - "row": 8 - }, - { - "copyText": "https://example.com/foxtrot/9.html", - "displayText": "https://example.com/foxtrot/9.html", - "row": 9 - }, - { - "copyText": "https://example.com/hotel/10.html", - "displayText": "https://example.com/hotel/10.html", - "row": 10 - }, - { - "copyText": "https://example.com/india/11.html", - "displayText": "https://example.com/india/11.html", - "row": 11 - } - ], - "field": "url" - }, - { - "cellType": "phone", - "cells": [ - { - "copyText": "+1 555 4212", - "displayText": "+1 555 4212", - "row": 0 - }, - { - "copyText": "+1 555 8491", - "displayText": "+1 555 8491", - "row": 1 - }, - { - "copyText": "+1 555 8182", - "displayText": "+1 555 8182", - "row": 2 - }, - { - "copyText": "+1 555 5211", - "displayText": "+1 555 5211", - "row": 3 - }, - { - "copyText": "+1 555 9286", - "displayText": "+1 555 9286", - "row": 4 - }, - { - "copyText": "+1 555 2346", - "displayText": "+1 555 2346", - "row": 5 - }, - { - "copyText": "+1 555 7684", - "displayText": "+1 555 7684", - "row": 6 - }, - { - "copyText": "+1 555 2555", - "displayText": "+1 555 2555", - "row": 7 - }, - { - "copyText": "+1 555 2754", - "displayText": "+1 555 2754", - "row": 8 - }, - { - "copyText": "+1 555 1775", - "displayText": "+1 555 1775", - "row": 9 - }, - { - "copyText": "+1 555 5037", - "displayText": "+1 555 5037", - "row": 10 - }, - { - "copyText": "+1 555 6107", - "displayText": "+1 555 6107", - "row": 11 - } - ], - "field": "phone" - }, - { - "cellType": "citext", - "cells": [ - { - "copyText": "EchoCase", - "displayText": "EchoCase", - "row": 0 - }, - { - "copyText": "AlphaCase", - "displayText": "AlphaCase", - "row": 1 - }, - { - "copyText": "HotelCase", - "displayText": "HotelCase", - "row": 2 - }, - { - "copyText": "DeltaCase", - "displayText": "DeltaCase", - "row": 3 - }, - { - "copyText": "GolfCase", - "displayText": "GolfCase", - "row": 4 - }, - { - "copyText": "CharlieCase", - "displayText": "CharlieCase", - "row": 5 - }, - { - "copyText": "FoxtrotCase", - "displayText": "FoxtrotCase", - "row": 6 - }, - { - "copyText": "IndiaCase", - "displayText": "IndiaCase", - "row": 7 - }, - { - "copyText": "GolfCase", - "displayText": "GolfCase", - "row": 8 - }, - { - "copyText": "CharlieCase", - "displayText": "CharlieCase", - "row": 9 - }, - { - "copyText": "JulietCase", - "displayText": "JulietCase", - "row": 10 - }, - { - "copyText": "AlphaCase", - "displayText": "AlphaCase", - "row": 11 - } - ], - "field": "citext" - }, - { - "cellType": "bpchar", - "cells": [ - { - "copyText": "AL ", - "displayText": "AL ", - "row": 0 - }, - { - "copyText": "BR ", - "displayText": "BR ", - "row": 1 - }, - { - "copyText": "HO ", - "displayText": "HO ", - "row": 2 - }, - { - "copyText": "BR ", - "displayText": "BR ", - "row": 3 - }, - { - "copyText": "CH ", - "displayText": "CH ", - "row": 4 - }, - { - "copyText": "FO ", - "displayText": "FO ", - "row": 5 - }, - { - "copyText": "DE ", - "displayText": "DE ", - "row": 6 - }, - { - "copyText": "EC ", - "displayText": "EC ", - "row": 7 - }, - { - "copyText": "FO ", - "displayText": "FO ", - "row": 8 - }, - { - "copyText": "GO ", - "displayText": "GO ", - "row": 9 - }, - { - "copyText": "EC ", - "displayText": "EC ", - "row": 10 - }, - { - "copyText": "AL ", - "displayText": "AL ", - "row": 11 - } - ], - "field": "bpchar" - }, - { - "cellType": "number", - "cells": [ - { - "copyText": "88.11", - "displayText": "88.11", - "row": 0 - }, - { - "copyText": "717.18", - "displayText": "717.18", - "row": 1 - }, - { - "copyText": "503.44", - "displayText": "503.44", - "row": 2 - }, - { - "copyText": "917.09", - "displayText": "917.09", - "row": 3 - }, - { - "copyText": "980.47", - "displayText": "980.47", - "row": 4 - }, - { - "copyText": "310.26", - "displayText": "310.26", - "row": 5 - }, - { - "copyText": "605.09", - "displayText": "605.09", - "row": 6 - }, - { - "copyText": "676.45", - "displayText": "676.45", - "row": 7 - }, - { - "copyText": "585.37", - "displayText": "585.37", - "row": 8 - }, - { - "copyText": "27.82", - "displayText": "27.82", - "row": 9 - }, - { - "copyText": "33.68", - "displayText": "33.68", - "row": 10 - }, - { - "copyText": "169.86", - "displayText": "169.86", - "row": 11 - } - ], - "field": "number" - }, - { - "cellType": "integer", - "cells": [ - { - "copyText": "768", - "displayText": "768", - "row": 0 - }, - { - "copyText": "892", - "displayText": "892", - "row": 1 - }, - { - "copyText": "929", - "displayText": "929", - "row": 2 - }, - { - "copyText": "301", - "displayText": "301", - "row": 3 - }, - { - "copyText": "143", - "displayText": "143", - "row": 4 - }, - { - "copyText": "902", - "displayText": "902", - "row": 5 - }, - { - "copyText": "961", - "displayText": "961", - "row": 6 - }, - { - "copyText": "842", - "displayText": "842", - "row": 7 - }, - { - "copyText": "997", - "displayText": "997", - "row": 8 - }, - { - "copyText": "955", - "displayText": "955", - "row": 9 - }, - { - "copyText": "334", - "displayText": "334", - "row": 10 - }, - { - "copyText": "289", - "displayText": "289", - "row": 11 - } - ], - "field": "integer" - }, - { - "cellType": "smallint", - "cells": [ - { - "copyText": "36", - "displayText": "36", - "row": 0 - }, - { - "copyText": "28", - "displayText": "28", - "row": 1 - }, - { - "copyText": "91", - "displayText": "91", - "row": 2 - }, - { - "copyText": "76", - "displayText": "76", - "row": 3 - }, - { - "copyText": "96", - "displayText": "96", - "row": 4 - }, - { - "copyText": "93", - "displayText": "93", - "row": 5 - }, - { - "copyText": "42", - "displayText": "42", - "row": 6 - }, - { - "copyText": "64", - "displayText": "64", - "row": 7 - }, - { - "copyText": "47", - "displayText": "47", - "row": 8 - }, - { - "copyText": "26", - "displayText": "26", - "row": 9 - }, - { - "copyText": "15", - "displayText": "15", - "row": 10 - }, - { - "copyText": "82", - "displayText": "82", - "row": 11 - } - ], - "field": "smallint" - }, - { - "cellType": "decimal", - "cells": [ - { - "copyText": "426.72", - "displayText": "426.72", - "row": 0 - }, - { - "copyText": "839.85", - "displayText": "839.85", - "row": 1 - }, - { - "copyText": "528.14", - "displayText": "528.14", - "row": 2 - }, - { - "copyText": "470.18", - "displayText": "470.18", - "row": 3 - }, - { - "copyText": "914.34", - "displayText": "914.34", - "row": 4 - }, - { - "copyText": "45.43", - "displayText": "45.43", - "row": 5 - }, - { - "copyText": "236.91", - "displayText": "236.91", - "row": 6 - }, - { - "copyText": "528.14", - "displayText": "528.14", - "row": 7 - }, - { - "copyText": "497.74", - "displayText": "497.74", - "row": 8 - }, - { - "copyText": "641.31", - "displayText": "641.31", - "row": 9 - }, - { - "copyText": "989.95", - "displayText": "989.95", - "row": 10 - }, - { - "copyText": "605.21", - "displayText": "605.21", - "row": 11 - } - ], - "field": "decimal" - }, - { - "cellType": "currency", - "cells": [ - { - "copyText": "2343.31", - "displayText": "2343.31", - "row": 0 - }, - { - "copyText": "3221.78", - "displayText": "3221.78", - "row": 1 - }, - { - "copyText": "4727.95", - "displayText": "4727.95", - "row": 2 - }, - { - "copyText": "3307.75", - "displayText": "3307.75", - "row": 3 - }, - { - "copyText": "4767.22", - "displayText": "4767.22", - "row": 4 - }, - { - "copyText": "2158.85", - "displayText": "2158.85", - "row": 5 - }, - { - "copyText": "3688.13", - "displayText": "3688.13", - "row": 6 - }, - { - "copyText": "2968.25", - "displayText": "2968.25", - "row": 7 - }, - { - "copyText": "2800.51", - "displayText": "2800.51", - "row": 8 - }, - { - "copyText": "4767.2", - "displayText": "4767.2", - "row": 9 - }, - { - "copyText": "3671.15", - "displayText": "3671.15", - "row": 10 - }, - { - "copyText": "4742.3", - "displayText": "4742.3", - "row": 11 - } - ], - "field": "currency" - }, - { - "cellType": "percentage", - "cells": [ - { - "copyText": "0.95", - "displayText": "0.95", - "row": 0 - }, - { - "copyText": "0.32", - "displayText": "0.32", - "row": 1 - }, - { - "copyText": "0.92", - "displayText": "0.92", - "row": 2 - }, - { - "copyText": "0.87", - "displayText": "0.87", - "row": 3 - }, - { - "copyText": "0.15", - "displayText": "0.15", - "row": 4 - }, - { - "copyText": "0.36", - "displayText": "0.36", - "row": 5 - }, - { - "copyText": "0.53", - "displayText": "0.53", - "row": 6 - }, - { - "copyText": "0.98", - "displayText": "0.98", - "row": 7 - }, - { - "copyText": "0.8", - "displayText": "0.8", - "row": 8 - }, - { - "copyText": "0.2", - "displayText": "0.2", - "row": 9 - }, - { - "copyText": "0.12", - "displayText": "0.12", - "row": 10 - }, - { - "copyText": "0.32", - "displayText": "0.32", - "row": 11 - } - ], - "field": "percentage" - }, - { - "cellType": "date", - "cells": [ - { - "copyText": "2024-09-10", - "displayText": "2024-09-10", - "row": 0 - }, - { - "copyText": "2020-02-28", - "displayText": "2020-02-28", - "row": 1 - }, - { - "copyText": "2022-11-23", - "displayText": "2022-11-23", - "row": 2 - }, - { - "copyText": "2025-04-22", - "displayText": "2025-04-22", - "row": 3 - }, - { - "copyText": "2020-06-22", - "displayText": "2020-06-22", - "row": 4 - }, - { - "copyText": "2021-09-04", - "displayText": "2021-09-04", - "row": 5 - }, - { - "copyText": "2021-09-19", - "displayText": "2021-09-19", - "row": 6 - }, - { - "copyText": "2022-05-20", - "displayText": "2022-05-20", - "row": 7 - }, - { - "copyText": "2021-03-17", - "displayText": "2021-03-17", - "row": 8 - }, - { - "copyText": "2020-10-02", - "displayText": "2020-10-02", - "row": 9 - }, - { - "copyText": "2021-08-19", - "displayText": "2021-08-19", - "row": 10 - }, - { - "copyText": "2023-12-06", - "displayText": "2023-12-06", - "row": 11 - } - ], - "field": "date" - }, - { - "cellType": "datetime", - "cells": [ - { - "copyText": "2020-11-10T08:08:00Z", - "displayText": "2020-11-10T08:08:00Z", - "row": 0 - }, - { - "copyText": "2023-01-15T16:15:00Z", - "displayText": "2023-01-15T16:15:00Z", - "row": 1 - }, - { - "copyText": "2021-06-27T11:23:00Z", - "displayText": "2021-06-27T11:23:00Z", - "row": 2 - }, - { - "copyText": "2025-10-18T13:39:00Z", - "displayText": "2025-10-18T13:39:00Z", - "row": 3 - }, - { - "copyText": "2022-07-19T12:33:00Z", - "displayText": "2022-07-19T12:33:00Z", - "row": 4 - }, - { - "copyText": "2020-02-16T11:56:00Z", - "displayText": "2020-02-16T11:56:00Z", - "row": 5 - }, - { - "copyText": "2023-02-14T12:21:00Z", - "displayText": "2023-02-14T12:21:00Z", - "row": 6 - }, - { - "copyText": "2020-01-02T10:34:00Z", - "displayText": "2020-01-02T10:34:00Z", - "row": 7 - }, - { - "copyText": "2023-11-20T00:24:00Z", - "displayText": "2023-11-20T00:24:00Z", - "row": 8 - }, - { - "copyText": "2020-10-03T03:34:00Z", - "displayText": "2020-10-03T03:34:00Z", - "row": 9 - }, - { - "copyText": "2022-11-11T13:25:00Z", - "displayText": "2022-11-11T13:25:00Z", - "row": 10 - }, - { - "copyText": "2025-05-20T00:14:00Z", - "displayText": "2025-05-20T00:14:00Z", - "row": 11 - } - ], - "field": "datetime" - }, - { - "cellType": "time", - "cells": [ - { - "copyText": "04:37:00", - "displayText": "04:37:00", - "row": 0 - }, - { - "copyText": "10:46:00", - "displayText": "10:46:00", - "row": 1 - }, - { - "copyText": "17:20:00", - "displayText": "17:20:00", - "row": 2 - }, - { - "copyText": "08:00:00", - "displayText": "08:00:00", - "row": 3 - }, - { - "copyText": "17:44:00", - "displayText": "17:44:00", - "row": 4 - }, - { - "copyText": "09:17:00", - "displayText": "09:17:00", - "row": 5 - }, - { - "copyText": "19:41:00", - "displayText": "19:41:00", - "row": 6 - }, - { - "copyText": "17:25:00", - "displayText": "17:25:00", - "row": 7 - }, - { - "copyText": "09:57:00", - "displayText": "09:57:00", - "row": 8 - }, - { - "copyText": "02:51:00", - "displayText": "02:51:00", - "row": 9 - }, - { - "copyText": "18:37:00", - "displayText": "18:37:00", - "row": 10 - }, - { - "copyText": "09:52:00", - "displayText": "09:52:00", - "row": 11 - } - ], - "field": "time" - }, - { - "cellType": "timestamptz", - "cells": [ - { - "copyText": "2024-12-16T08:02:00Z", - "displayText": "2024-12-16T08:02:00Z", - "row": 0 - }, - { - "copyText": "2021-03-06T03:44:00Z", - "displayText": "2021-03-06T03:44:00Z", - "row": 1 - }, - { - "copyText": "2023-09-18T18:44:00Z", - "displayText": "2023-09-18T18:44:00Z", - "row": 2 - }, - { - "copyText": "2024-04-17T19:53:00Z", - "displayText": "2024-04-17T19:53:00Z", - "row": 3 - }, - { - "copyText": "2022-04-28T17:32:00Z", - "displayText": "2022-04-28T17:32:00Z", - "row": 4 - }, - { - "copyText": "2021-11-05T18:28:00Z", - "displayText": "2021-11-05T18:28:00Z", - "row": 5 - }, - { - "copyText": "2024-05-24T15:12:00Z", - "displayText": "2024-05-24T15:12:00Z", - "row": 6 - }, - { - "copyText": "2020-08-03T04:12:00Z", - "displayText": "2020-08-03T04:12:00Z", - "row": 7 - }, - { - "copyText": "2021-12-07T20:49:00Z", - "displayText": "2021-12-07T20:49:00Z", - "row": 8 - }, - { - "copyText": "2024-11-24T08:03:00Z", - "displayText": "2024-11-24T08:03:00Z", - "row": 9 - }, - { - "copyText": "2020-01-08T12:59:00Z", - "displayText": "2020-01-08T12:59:00Z", - "row": 10 - }, - { - "copyText": "2021-03-16T17:53:00Z", - "displayText": "2021-03-16T17:53:00Z", - "row": 11 - } - ], - "field": "timestamptz" - }, - { - "cellType": "interval", - "cells": [ - { - "copyText": "{\"years\":2,\"months\":7,\"days\":25}", - "displayText": "2y 7mo 25d", - "row": 0 - }, - { - "copyText": "{\"years\":2,\"months\":0,\"days\":0}", - "displayText": "2y", - "row": 1 - }, - { - "copyText": "{\"years\":2,\"months\":1,\"days\":22}", - "displayText": "2y 1mo 22d", - "row": 2 - }, - { - "copyText": "{\"years\":1,\"months\":3,\"days\":10}", - "displayText": "1y 3mo 10d", - "row": 3 - }, - { - "copyText": "{\"years\":1,\"months\":1,\"days\":23}", - "displayText": "1y 1mo 23d", - "row": 4 - }, - { - "copyText": "{\"years\":1,\"months\":2,\"days\":6}", - "displayText": "1y 2mo 6d", - "row": 5 - }, - { - "copyText": "{\"years\":1,\"months\":4,\"days\":21}", - "displayText": "1y 4mo 21d", - "row": 6 - }, - { - "copyText": "{\"years\":2,\"months\":0,\"days\":16}", - "displayText": "2y 16d", - "row": 7 - }, - { - "copyText": "{\"years\":1,\"months\":3,\"days\":23}", - "displayText": "1y 3mo 23d", - "row": 8 - }, - { - "copyText": "{\"years\":1,\"months\":3,\"days\":9}", - "displayText": "1y 3mo 9d", - "row": 9 - }, - { - "copyText": "{\"years\":1,\"months\":6,\"days\":18}", - "displayText": "1y 6mo 18d", - "row": 10 - }, - { - "copyText": "{\"years\":1,\"months\":4,\"days\":17}", - "displayText": "1y 4mo 17d", - "row": 11 - } - ], - "field": "interval" - }, - { - "cellType": "boolean", - "cells": [ - { - "copyText": "false", - "displayText": "", - "row": 0 - }, - { - "copyText": "true", - "displayText": "", - "row": 1 - }, - { - "copyText": "true", - "displayText": "", - "row": 2 - }, - { - "copyText": "false", - "displayText": "", - "row": 3 - }, - { - "copyText": "true", - "displayText": "", - "row": 4 - }, - { - "copyText": "true", - "displayText": "", - "row": 5 - }, - { - "copyText": "false", - "displayText": "", - "row": 6 - }, - { - "copyText": "true", - "displayText": "", - "row": 7 - }, - { - "copyText": "false", - "displayText": "", - "row": 8 - }, - { - "copyText": "false", - "displayText": "", - "row": 9 - }, - { - "copyText": "false", - "displayText": "", - "row": 10 - }, - { - "copyText": "true", - "displayText": "", - "row": 11 - } - ], - "field": "boolean" - }, - { - "cellType": "toggle", - "cells": [ - { - "copyText": "true", - "displayText": "", - "row": 0 - }, - { - "copyText": "true", - "displayText": "", - "row": 1 - }, - { - "copyText": "false", - "displayText": "", - "row": 2 - }, - { - "copyText": "true", - "displayText": "", - "row": 3 - }, - { - "copyText": "false", - "displayText": "", - "row": 4 - }, - { - "copyText": "false", - "displayText": "", - "row": 5 - }, - { - "copyText": "true", - "displayText": "", - "row": 6 - }, - { - "copyText": "false", - "displayText": "", - "row": 7 - }, - { - "copyText": "false", - "displayText": "", - "row": 8 - }, - { - "copyText": "true", - "displayText": "", - "row": 9 - }, - { - "copyText": "false", - "displayText": "", - "row": 10 - }, - { - "copyText": "true", - "displayText": "", - "row": 11 - } - ], - "field": "toggle" - }, - { - "cellType": "bit", - "cells": [ - { - "copyText": "true", - "displayText": "", - "row": 0 - }, - { - "copyText": "true", - "displayText": "", - "row": 1 - }, - { - "copyText": "true", - "displayText": "", - "row": 2 - }, - { - "copyText": "true", - "displayText": "", - "row": 3 - }, - { - "copyText": "true", - "displayText": "", - "row": 4 - }, - { - "copyText": "true", - "displayText": "", - "row": 5 - }, - { - "copyText": "true", - "displayText": "", - "row": 6 - }, - { - "copyText": "true", - "displayText": "", - "row": 7 - }, - { - "copyText": "true", - "displayText": "", - "row": 8 - }, - { - "copyText": "true", - "displayText": "", - "row": 9 - }, - { - "copyText": "true", - "displayText": "", - "row": 10 - }, - { - "copyText": "true", - "displayText": "", - "row": 11 - } - ], - "field": "bit" - }, - { - "cellType": "json", - "cells": [ - { - "copyText": "{\"key\":\"india\",\"n\":96,\"ok\":true}", - "displayText": "{\"key\":\"india\",\"n\":96,\"ok\":true}", - "row": 0 - }, - { - "copyText": "{\"key\":\"foxtrot\",\"n\":70,\"ok\":false}", - "displayText": "{\"key\":\"foxtrot\",\"n\":70,\"ok\":false}", - "row": 1 - }, - { - "copyText": "{\"key\":\"hotel\",\"n\":77,\"ok\":true}", - "displayText": "{\"key\":\"hotel\",\"n\":77,\"ok\":true}", - "row": 2 - }, - { - "copyText": "{\"key\":\"foxtrot\",\"n\":74,\"ok\":false}", - "displayText": "{\"key\":\"foxtrot\",\"n\":74,\"ok\":false}", - "row": 3 - }, - { - "copyText": "{\"key\":\"alpha\",\"n\":50,\"ok\":false}", - "displayText": "{\"key\":\"alpha\",\"n\":50,\"ok\":false}", - "row": 4 - }, - { - "copyText": "{\"key\":\"juliet\",\"n\":90,\"ok\":false}", - "displayText": "{\"key\":\"juliet\",\"n\":90,\"ok\":false}", - "row": 5 - }, - { - "copyText": "{\"key\":\"india\",\"n\":35,\"ok\":false}", - "displayText": "{\"key\":\"india\",\"n\":35,\"ok\":false}", - "row": 6 - }, - { - "copyText": "{\"key\":\"india\",\"n\":15,\"ok\":true}", - "displayText": "{\"key\":\"india\",\"n\":15,\"ok\":true}", - "row": 7 - }, - { - "copyText": "{\"key\":\"india\",\"n\":84,\"ok\":false}", - "displayText": "{\"key\":\"india\",\"n\":84,\"ok\":false}", - "row": 8 - }, - { - "copyText": "{\"key\":\"juliet\",\"n\":51,\"ok\":true}", - "displayText": "{\"key\":\"juliet\",\"n\":51,\"ok\":true}", - "row": 9 - }, - { - "copyText": "{\"key\":\"golf\",\"n\":58,\"ok\":false}", - "displayText": "{\"key\":\"golf\",\"n\":58,\"ok\":false}", - "row": 10 - }, - { - "copyText": "{\"key\":\"foxtrot\",\"n\":15,\"ok\":false}", - "displayText": "{\"key\":\"foxtrot\",\"n\":15,\"ok\":false}", - "row": 11 - } - ], - "field": "json" - }, - { - "cellType": "jsonb", - "cells": [ - { - "copyText": "{\"nested\":{\"items\":[5,9]},\"tag\":\"juliet\"}", - "displayText": "{\"nested\":{\"items\":[5,9]},\"tag\":\"juliet\"}", - "row": 0 - }, - { - "copyText": "{\"nested\":{\"items\":[5,0]},\"tag\":\"foxtrot\"}", - "displayText": "{\"nested\":{\"items\":[5,0]},\"tag\":\"foxtrot\"}", - "row": 1 - }, - { - "copyText": "{\"nested\":{\"items\":[4,9]},\"tag\":\"juliet\"}", - "displayText": "{\"nested\":{\"items\":[4,9]},\"tag\":\"juliet\"}", - "row": 2 - }, - { - "copyText": "{\"nested\":{\"items\":[5,0]},\"tag\":\"bravo\"}", - "displayText": "{\"nested\":{\"items\":[5,0]},\"tag\":\"bravo\"}", - "row": 3 - }, - { - "copyText": "{\"nested\":{\"items\":[2,6]},\"tag\":\"echo\"}", - "displayText": "{\"nested\":{\"items\":[2,6]},\"tag\":\"echo\"}", - "row": 4 - }, - { - "copyText": "{\"nested\":{\"items\":[2,6]},\"tag\":\"delta\"}", - "displayText": "{\"nested\":{\"items\":[2,6]},\"tag\":\"delta\"}", - "row": 5 - }, - { - "copyText": "{\"nested\":{\"items\":[0,9]},\"tag\":\"echo\"}", - "displayText": "{\"nested\":{\"items\":[0,9]},\"tag\":\"echo\"}", - "row": 6 - }, - { - "copyText": "{\"nested\":{\"items\":[7,8]},\"tag\":\"golf\"}", - "displayText": "{\"nested\":{\"items\":[7,8]},\"tag\":\"golf\"}", - "row": 7 - }, - { - "copyText": "{\"nested\":{\"items\":[2,7]},\"tag\":\"india\"}", - "displayText": "{\"nested\":{\"items\":[2,7]},\"tag\":\"india\"}", - "row": 8 - }, - { - "copyText": "{\"nested\":{\"items\":[6,0]},\"tag\":\"alpha\"}", - "displayText": "{\"nested\":{\"items\":[6,0]},\"tag\":\"alpha\"}", - "row": 9 - }, - { - "copyText": "{\"nested\":{\"items\":[3,5]},\"tag\":\"delta\"}", - "displayText": "{\"nested\":{\"items\":[3,5]},\"tag\":\"delta\"}", - "row": 10 - }, - { - "copyText": "{\"nested\":{\"items\":[3,4]},\"tag\":\"india\"}", - "displayText": "{\"nested\":{\"items\":[3,4]},\"tag\":\"india\"}", - "row": 11 - } - ], - "field": "jsonb" - }, - { - "cellType": "array", - "cells": [ - { - "copyText": "[\"foxtrot\",\"india\",\"alpha\"]", - "displayText": "", - "row": 0 - }, - { - "copyText": "[\"hotel\",\"india\",\"alpha\"]", - "displayText": "", - "row": 1 - }, - { - "copyText": "[\"bravo\",\"golf\",\"charlie\"]", - "displayText": "", - "row": 2 - }, - { - "copyText": "[\"golf\",\"delta\",\"foxtrot\"]", - "displayText": "", - "row": 3 - }, - { - "copyText": "[\"juliet\",\"echo\",\"golf\"]", - "displayText": "", - "row": 4 - }, - { - "copyText": "[\"foxtrot\",\"juliet\",\"bravo\"]", - "displayText": "", - "row": 5 - }, - { - "copyText": "[\"bravo\",\"delta\",\"delta\"]", - "displayText": "", - "row": 6 - }, - { - "copyText": "[\"golf\",\"charlie\",\"hotel\"]", - "displayText": "", - "row": 7 - }, - { - "copyText": "[\"alpha\",\"foxtrot\",\"alpha\"]", - "displayText": "", - "row": 8 - }, - { - "copyText": "[\"juliet\",\"charlie\",\"golf\"]", - "displayText": "", - "row": 9 - }, - { - "copyText": "[\"hotel\",\"hotel\",\"charlie\"]", - "displayText": "", - "row": 10 - }, - { - "copyText": "[\"echo\",\"alpha\",\"echo\"]", - "displayText": "", - "row": 11 - } - ], - "field": "array" - }, - { - "cellType": "text-array", - "cells": [ - { - "copyText": "[\"foxtrot\",\"delta\",\"hotel\"]", - "displayText": "", - "row": 0 - }, - { - "copyText": "[\"foxtrot\",\"delta\",\"alpha\"]", - "displayText": "", - "row": 1 - }, - { - "copyText": "[\"foxtrot\",\"echo\",\"foxtrot\"]", - "displayText": "", - "row": 2 - }, - { - "copyText": "[\"hotel\",\"bravo\",\"hotel\"]", - "displayText": "", - "row": 3 - }, - { - "copyText": "[\"juliet\",\"alpha\",\"foxtrot\"]", - "displayText": "", - "row": 4 - }, - { - "copyText": "[\"foxtrot\",\"india\",\"alpha\"]", - "displayText": "", - "row": 5 - }, - { - "copyText": "[\"delta\",\"bravo\",\"foxtrot\"]", - "displayText": "", - "row": 6 - }, - { - "copyText": "[\"hotel\",\"charlie\",\"india\"]", - "displayText": "", - "row": 7 - }, - { - "copyText": "[\"charlie\",\"foxtrot\",\"alpha\"]", - "displayText": "", - "row": 8 - }, - { - "copyText": "[\"alpha\",\"alpha\",\"alpha\"]", - "displayText": "", - "row": 9 - }, - { - "copyText": "[\"hotel\",\"golf\",\"bravo\"]", - "displayText": "", - "row": 10 - }, - { - "copyText": "[\"juliet\",\"india\",\"foxtrot\"]", - "displayText": "", - "row": 11 - } - ], - "field": "textArray" - }, - { - "cellType": "uuid-array", - "cells": [ - { - "copyText": "[\"8cf8d6ab-8430-40fd-a099-bf4fac9d2f89\",\"263ddbaf-9bf7-439c-d387-8247720201ef\"]", - "displayText": "", - "row": 0 - }, - { - "copyText": "[\"e4245cdd-cbda-4bb4-6a53-79a6b3b876d1\",\"88aa16fb-8b5f-41c7-373d-f8f2720bc307\"]", - "displayText": "", - "row": 1 - }, - { - "copyText": "[\"6a9d13d1-d5a5-4c32-791f-1a82cea02934\",\"6217bfaa-7515-499e-1417-be1cf7ed703c\"]", - "displayText": "", - "row": 2 - }, - { - "copyText": "[\"0f869b6a-7357-4139-b51c-f292c2d6f795\",\"7a8e2252-1609-4f9f-0a4e-b02fcbc6ea69\"]", - "displayText": "", - "row": 3 - }, - { - "copyText": "[\"40e1b290-f634-4933-01cd-5deb3a018434\",\"c3c395b8-d367-43da-b80f-c4ac5427ee38\"]", - "displayText": "", - "row": 4 - }, - { - "copyText": "[\"8429d6af-71b7-4df4-e9bf-50ac5f3002b5\",\"92cd12b7-92ed-41cc-0eb6-03438689589e\"]", - "displayText": "", - "row": 5 - }, - { - "copyText": "[\"92776b4c-b63f-40d2-9615-86c94b5ceabf\",\"d45544b6-a5c2-47fa-ae04-f8d316fa13a8\"]", - "displayText": "", - "row": 6 - }, - { - "copyText": "[\"e58a9277-0577-4669-0012-ee33e5643985\",\"df0d0384-7118-44a0-574f-fb4d5a706b6c\"]", - "displayText": "", - "row": 7 - }, - { - "copyText": "[\"3d2ab556-a735-4dea-3b03-7da8e2ecbbf6\",\"6cc45c4d-fd08-4765-37aa-004f8cf9ae53\"]", - "displayText": "", - "row": 8 - }, - { - "copyText": "[\"e77b8a34-7f3a-4b55-0f9e-4541a412c058\",\"1b8b6403-739d-4829-ba65-d38852820d25\"]", - "displayText": "", - "row": 9 - }, - { - "copyText": "[\"ef743ad8-b025-48a9-6a70-a03871708893\",\"9d3da72c-1958-4864-ae39-a9279a14dc5a\"]", - "displayText": "", - "row": 10 - }, - { - "copyText": "[\"f0e6788b-6b3b-49aa-0df0-51af59b4dd96\",\"262069ab-7e06-458e-d31b-a9535557509c\"]", - "displayText": "", - "row": 11 - } - ], - "field": "uuidArray" - }, - { - "cellType": "number-array", - "cells": [ - { - "copyText": "[\"6.01\",\"17.45\"]", - "displayText": "", - "row": 0 - }, - { - "copyText": "[\"66.49\",\"31.2\"]", - "displayText": "", - "row": 1 - }, - { - "copyText": "[\"14.45\",\"36.74\"]", - "displayText": "", - "row": 2 - }, - { - "copyText": "[\"74.01\",\"64.25\"]", - "displayText": "", - "row": 3 - }, - { - "copyText": "[\"61.64\",\"45.1\"]", - "displayText": "", - "row": 4 - }, - { - "copyText": "[\"26.11\",\"73.03\"]", - "displayText": "", - "row": 5 - }, - { - "copyText": "[\"3.03\",\"83.86\"]", - "displayText": "", - "row": 6 - }, - { - "copyText": "[\"13.13\",\"40.83\"]", - "displayText": "", - "row": 7 - }, - { - "copyText": "[\"64.28\",\"80.22\"]", - "displayText": "", - "row": 8 - }, - { - "copyText": "[\"75.38\",\"29.19\"]", - "displayText": "", - "row": 9 - }, - { - "copyText": "[\"88.56\",\"19.46\"]", - "displayText": "", - "row": 10 - }, - { - "copyText": "[\"47.93\",\"82.13\"]", - "displayText": "", - "row": 11 - } - ], - "field": "numberArray" - }, - { - "cellType": "integer-array", - "cells": [ - { - "copyText": "[\"40\",\"49\"]", - "displayText": "", - "row": 0 - }, - { - "copyText": "[\"53\",\"94\"]", - "displayText": "", - "row": 1 - }, - { - "copyText": "[\"79\",\"67\"]", - "displayText": "", - "row": 2 - }, - { - "copyText": "[\"11\",\"94\"]", - "displayText": "", - "row": 3 - }, - { - "copyText": "[\"35\",\"31\"]", - "displayText": "", - "row": 4 - }, - { - "copyText": "[\"88\",\"16\"]", - "displayText": "", - "row": 5 - }, - { - "copyText": "[\"41\",\"8\"]", - "displayText": "", - "row": 6 - }, - { - "copyText": "[\"74\",\"14\"]", - "displayText": "", - "row": 7 - }, - { - "copyText": "[\"84\",\"9\"]", - "displayText": "", - "row": 8 - }, - { - "copyText": "[\"7\",\"35\"]", - "displayText": "", - "row": 9 - }, - { - "copyText": "[\"58\",\"86\"]", - "displayText": "", - "row": 10 - }, - { - "copyText": "[\"79\",\"59\"]", - "displayText": "", - "row": 11 - } - ], - "field": "integerArray" - }, - { - "cellType": "date-array", - "cells": [ - { - "copyText": "[\"2024-05-11\",\"2024-09-23\"]", - "displayText": "", - "row": 0 - }, - { - "copyText": "[\"2021-07-20\",\"2021-02-18\"]", - "displayText": "", - "row": 1 - }, - { - "copyText": "[\"2022-07-22\",\"2024-08-09\"]", - "displayText": "", - "row": 2 - }, - { - "copyText": "[\"2025-11-08\",\"2024-11-13\"]", - "displayText": "", - "row": 3 - }, - { - "copyText": "[\"2025-07-26\",\"2024-07-18\"]", - "displayText": "", - "row": 4 - }, - { - "copyText": "[\"2023-01-25\",\"2022-01-10\"]", - "displayText": "", - "row": 5 - }, - { - "copyText": "[\"2020-03-13\",\"2025-01-24\"]", - "displayText": "", - "row": 6 - }, - { - "copyText": "[\"2025-10-04\",\"2021-07-24\"]", - "displayText": "", - "row": 7 - }, - { - "copyText": "[\"2023-08-23\",\"2023-11-22\"]", - "displayText": "", - "row": 8 - }, - { - "copyText": "[\"2023-04-01\",\"2025-10-05\"]", - "displayText": "", - "row": 9 - }, - { - "copyText": "[\"2022-10-18\",\"2022-08-10\"]", - "displayText": "", - "row": 10 - }, - { - "copyText": "[\"2022-05-21\",\"2021-10-21\"]", - "displayText": "", - "row": 11 - } - ], - "field": "dateArray" - }, - { - "cellType": "geometry", - "cells": [ - { - "copyText": "geo:{\"type\":\"Point\",\"coordinates\":[5.3982,53.6979]}", - "displayText": "geo:{\"type\":\"Point\",\"coordinates\":[5.3982,53.6979]}", - "row": 0 - }, - { - "copyText": "geo:{\"type\":\"Point\",\"coordinates\":[-68.5712,8.4246]}", - "displayText": "geo:{\"type\":\"Point\",\"coordinates\":[-68.5712,8.4246]}", - "row": 1 - }, - { - "copyText": "geo:{\"type\":\"Point\",\"coordinates\":[-56.593,80.7422]}", - "displayText": "geo:{\"type\":\"Point\",\"coordinates\":[-56.593,80.7422]}", - "row": 2 - }, - { - "copyText": "geo:{\"type\":\"Point\",\"coordinates\":[-6.2532,-12.5181]}", - "displayText": "geo:{\"type\":\"Point\",\"coordinates\":[-6.2532,-12.5181]}", - "row": 3 - }, - { - "copyText": "geo:{\"type\":\"Point\",\"coordinates\":[-66.8936,41.4211]}", - "displayText": "geo:{\"type\":\"Point\",\"coordinates\":[-66.8936,41.4211]}", - "row": 4 - }, - { - "copyText": "geo:{\"type\":\"Point\",\"coordinates\":[42.8503,18.2985]}", - "displayText": "geo:{\"type\":\"Point\",\"coordinates\":[42.8503,18.2985]}", - "row": 5 - }, - { - "copyText": "geo:{\"type\":\"Point\",\"coordinates\":[-24.915,-82.7899]}", - "displayText": "geo:{\"type\":\"Point\",\"coordinates\":[-24.915,-82.7899]}", - "row": 6 - }, - { - "copyText": "geo:{\"type\":\"Point\",\"coordinates\":[-61.5576,70.8895]}", - "displayText": "geo:{\"type\":\"Point\",\"coordinates\":[-61.5576,70.8895]}", - "row": 7 - }, - { - "copyText": "geo:{\"type\":\"Point\",\"coordinates\":[-4.9481,-59.1728]}", - "displayText": "geo:{\"type\":\"Point\",\"coordinates\":[-4.9481,-59.1728]}", - "row": 8 - }, - { - "copyText": "geo:{\"type\":\"Point\",\"coordinates\":[78.5617,80.0919]}", - "displayText": "geo:{\"type\":\"Point\",\"coordinates\":[78.5617,80.0919]}", - "row": 9 - }, - { - "copyText": "geo:{\"type\":\"Point\",\"coordinates\":[-8.2256,37.3817]}", - "displayText": "geo:{\"type\":\"Point\",\"coordinates\":[-8.2256,37.3817]}", - "row": 10 - }, - { - "copyText": "geo:{\"type\":\"Point\",\"coordinates\":[46.0122,35.4564]}", - "displayText": "geo:{\"type\":\"Point\",\"coordinates\":[46.0122,35.4564]}", - "row": 11 - } - ], - "field": "geometry" - }, - { - "cellType": "geometry-point", - "cells": [ - { - "copyText": "geo:{\"x\":41.22,\"y\":56.26}", - "displayText": "geo:{\"x\":41.22,\"y\":56.26}", - "row": 0 - }, - { - "copyText": "geo:{\"x\":45.34,\"y\":67.2}", - "displayText": "geo:{\"x\":45.34,\"y\":67.2}", - "row": 1 - }, - { - "copyText": "geo:{\"x\":82.74,\"y\":37}", - "displayText": "geo:{\"x\":82.74,\"y\":37}", - "row": 2 - }, - { - "copyText": "geo:{\"x\":48.54,\"y\":60.32}", - "displayText": "geo:{\"x\":48.54,\"y\":60.32}", - "row": 3 - }, - { - "copyText": "geo:{\"x\":79.91,\"y\":96.21}", - "displayText": "geo:{\"x\":79.91,\"y\":96.21}", - "row": 4 - }, - { - "copyText": "geo:{\"x\":88.25,\"y\":16.58}", - "displayText": "geo:{\"x\":88.25,\"y\":16.58}", - "row": 5 - }, - { - "copyText": "geo:{\"x\":74.55,\"y\":87.82}", - "displayText": "geo:{\"x\":74.55,\"y\":87.82}", - "row": 6 - }, - { - "copyText": "geo:{\"x\":85.07,\"y\":11.99}", - "displayText": "geo:{\"x\":85.07,\"y\":11.99}", - "row": 7 - }, - { - "copyText": "geo:{\"x\":61.9,\"y\":19.2}", - "displayText": "geo:{\"x\":61.9,\"y\":19.2}", - "row": 8 - }, - { - "copyText": "geo:{\"x\":50.76,\"y\":37.84}", - "displayText": "geo:{\"x\":50.76,\"y\":37.84}", - "row": 9 - }, - { - "copyText": "geo:{\"x\":2.47,\"y\":48.58}", - "displayText": "geo:{\"x\":2.47,\"y\":48.58}", - "row": 10 - }, - { - "copyText": "geo:{\"x\":72.83,\"y\":83.55}", - "displayText": "geo:{\"x\":72.83,\"y\":83.55}", - "row": 11 - } - ], - "field": "geometryPoint" - }, - { - "cellType": "geometry-collection", - "cells": [ - { - "copyText": "geo:{\"type\":\"GeometryCollection\",\"geometries\":[]}", - "displayText": "geo:{\"type\":\"GeometryCollection\",\"geometries\":[]}", - "row": 0 - }, - { - "copyText": "geo:{\"type\":\"GeometryCollection\",\"geometries\":[]}", - "displayText": "geo:{\"type\":\"GeometryCollection\",\"geometries\":[]}", - "row": 1 - }, - { - "copyText": "geo:{\"type\":\"GeometryCollection\",\"geometries\":[]}", - "displayText": "geo:{\"type\":\"GeometryCollection\",\"geometries\":[]}", - "row": 2 - }, - { - "copyText": "geo:{\"type\":\"GeometryCollection\",\"geometries\":[]}", - "displayText": "geo:{\"type\":\"GeometryCollection\",\"geometries\":[]}", - "row": 3 - }, - { - "copyText": "geo:{\"type\":\"GeometryCollection\",\"geometries\":[]}", - "displayText": "geo:{\"type\":\"GeometryCollection\",\"geometries\":[]}", - "row": 4 - }, - { - "copyText": "geo:{\"type\":\"GeometryCollection\",\"geometries\":[]}", - "displayText": "geo:{\"type\":\"GeometryCollection\",\"geometries\":[]}", - "row": 5 - }, - { - "copyText": "geo:{\"type\":\"GeometryCollection\",\"geometries\":[]}", - "displayText": "geo:{\"type\":\"GeometryCollection\",\"geometries\":[]}", - "row": 6 - }, - { - "copyText": "geo:{\"type\":\"GeometryCollection\",\"geometries\":[]}", - "displayText": "geo:{\"type\":\"GeometryCollection\",\"geometries\":[]}", - "row": 7 - }, - { - "copyText": "geo:{\"type\":\"GeometryCollection\",\"geometries\":[]}", - "displayText": "geo:{\"type\":\"GeometryCollection\",\"geometries\":[]}", - "row": 8 - }, - { - "copyText": "geo:{\"type\":\"GeometryCollection\",\"geometries\":[]}", - "displayText": "geo:{\"type\":\"GeometryCollection\",\"geometries\":[]}", - "row": 9 - }, - { - "copyText": "geo:{\"type\":\"GeometryCollection\",\"geometries\":[]}", - "displayText": "geo:{\"type\":\"GeometryCollection\",\"geometries\":[]}", - "row": 10 - }, - { - "copyText": "geo:{\"type\":\"GeometryCollection\",\"geometries\":[]}", - "displayText": "geo:{\"type\":\"GeometryCollection\",\"geometries\":[]}", - "row": 11 - } - ], - "field": "geometryCollection" - }, - { - "cellType": "inet", - "cells": [ - { - "copyText": "74.134.198.20", - "displayText": "74.134.198.20", - "row": 0 - }, - { - "copyText": "209.189.147.198", - "displayText": "209.189.147.198", - "row": 1 - }, - { - "copyText": "126.158.213.24", - "displayText": "126.158.213.24", - "row": 2 - }, - { - "copyText": "89.132.237.41", - "displayText": "89.132.237.41", - "row": 3 - }, - { - "copyText": "202.191.150.41", - "displayText": "202.191.150.41", - "row": 4 - }, - { - "copyText": "34.84.183.225", - "displayText": "34.84.183.225", - "row": 5 - }, - { - "copyText": "87.10.168.238", - "displayText": "87.10.168.238", - "row": 6 - }, - { - "copyText": "246.38.101.16", - "displayText": "246.38.101.16", - "row": 7 - }, - { - "copyText": "70.138.98.57", - "displayText": "70.138.98.57", - "row": 8 - }, - { - "copyText": "53.95.218.245", - "displayText": "53.95.218.245", - "row": 9 - }, - { - "copyText": "31.79.93.58", - "displayText": "31.79.93.58", - "row": 10 - }, - { - "copyText": "73.245.172.54", - "displayText": "73.245.172.54", - "row": 11 - } - ], - "field": "inet" - }, - { - "cellType": "image", - "cells": [ - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23ef4444'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EIMG%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23ef4444'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EIMG%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 0 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23f59e0b'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EIMG%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23f59e0b'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EIMG%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 1 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%233b82f6'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EIMG%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%233b82f6'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EIMG%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 2 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23f59e0b'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EIMG%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23f59e0b'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EIMG%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 3 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2306b6d4'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EIMG%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2306b6d4'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EIMG%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 4 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2322c55e'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EIMG%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2322c55e'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EIMG%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 5 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23f59e0b'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EIMG%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23f59e0b'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EIMG%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 6 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23f59e0b'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EIMG%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23f59e0b'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EIMG%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 7 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2322c55e'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EIMG%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2322c55e'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EIMG%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 8 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2322c55e'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EIMG%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2322c55e'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EIMG%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 9 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23a855f7'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EIMG%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23a855f7'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EIMG%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 10 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23ef4444'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EIMG%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23ef4444'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EIMG%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 11 - } - ], - "field": "image" - }, - { - "cellType": "file", - "cells": [ - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%233b82f6'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EFILE%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%233b82f6'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EFILE%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 0 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23ef4444'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EFILE%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23ef4444'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EFILE%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 1 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2322c55e'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EFILE%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2322c55e'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EFILE%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 2 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23f59e0b'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EFILE%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23f59e0b'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EFILE%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 3 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23a855f7'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EFILE%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23a855f7'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EFILE%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 4 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2306b6d4'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EFILE%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2306b6d4'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EFILE%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 5 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%233b82f6'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EFILE%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%233b82f6'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EFILE%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 6 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23ef4444'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EFILE%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23ef4444'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EFILE%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 7 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2322c55e'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EFILE%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2322c55e'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EFILE%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 8 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23f59e0b'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EFILE%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23f59e0b'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EFILE%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 9 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23a855f7'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EFILE%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23a855f7'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EFILE%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 10 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2306b6d4'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EFILE%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2306b6d4'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EFILE%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 11 - } - ], - "field": "file" - }, - { - "cellType": "video", - "cells": [ - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23a855f7'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EVIDEO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23a855f7'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EVIDEO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 0 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23f59e0b'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EVIDEO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23f59e0b'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EVIDEO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 1 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2306b6d4'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EVIDEO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2306b6d4'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EVIDEO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 2 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2322c55e'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EVIDEO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2322c55e'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EVIDEO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 3 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2322c55e'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EVIDEO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2322c55e'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EVIDEO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 4 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23f59e0b'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EVIDEO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23f59e0b'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EVIDEO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 5 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%233b82f6'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EVIDEO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%233b82f6'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EVIDEO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 6 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23f59e0b'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EVIDEO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23f59e0b'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EVIDEO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 7 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23a855f7'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EVIDEO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23a855f7'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EVIDEO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 8 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23ef4444'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EVIDEO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23ef4444'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EVIDEO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 9 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%233b82f6'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EVIDEO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%233b82f6'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EVIDEO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 10 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2322c55e'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EVIDEO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2322c55e'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EVIDEO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 11 - } - ], - "field": "video" - }, - { - "cellType": "audio", - "cells": [ - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23a855f7'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EAUDIO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23a855f7'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EAUDIO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 0 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23f59e0b'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EAUDIO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23f59e0b'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EAUDIO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 1 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23a855f7'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EAUDIO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23a855f7'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EAUDIO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 2 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23f59e0b'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EAUDIO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23f59e0b'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EAUDIO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 3 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23a855f7'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EAUDIO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23a855f7'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EAUDIO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 4 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%233b82f6'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EAUDIO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%233b82f6'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EAUDIO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 5 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23a855f7'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EAUDIO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23a855f7'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EAUDIO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 6 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2322c55e'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EAUDIO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2322c55e'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EAUDIO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 7 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%233b82f6'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EAUDIO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%233b82f6'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EAUDIO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 8 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2322c55e'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EAUDIO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2322c55e'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EAUDIO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 9 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23ef4444'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EAUDIO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23ef4444'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EAUDIO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 10 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23f59e0b'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EAUDIO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23f59e0b'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EAUDIO%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 11 - } - ], - "field": "audio" - }, - { - "cellType": "upload", - "cells": [ - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%233b82f6'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EUPLOAD%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%233b82f6'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EUPLOAD%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 0 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23ef4444'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EUPLOAD%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23ef4444'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EUPLOAD%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 1 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2322c55e'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EUPLOAD%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2322c55e'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EUPLOAD%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 2 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23f59e0b'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EUPLOAD%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23f59e0b'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EUPLOAD%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 3 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23a855f7'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EUPLOAD%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23a855f7'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EUPLOAD%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 4 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2306b6d4'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EUPLOAD%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2306b6d4'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EUPLOAD%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 5 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%233b82f6'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EUPLOAD%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%233b82f6'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EUPLOAD%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 6 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23ef4444'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EUPLOAD%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23ef4444'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EUPLOAD%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 7 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2322c55e'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EUPLOAD%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2322c55e'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EUPLOAD%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 8 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23f59e0b'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EUPLOAD%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23f59e0b'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EUPLOAD%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 9 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23a855f7'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EUPLOAD%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%23a855f7'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EUPLOAD%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 10 - }, - { - "copyText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2306b6d4'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EUPLOAD%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "displayText": "[\"data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'96'%20height%3D'96'%3E%3Crect%20width%3D'96'%20height%3D'96'%20fill%3D'%2306b6d4'%2F%3E%3Ctext%20x%3D'48'%20y%3D'54'%20font-family%3D'system-ui%2Csans-serif'%20font-size%3D'13'%20font-weight%3D'700'%20fill%3D'white'%20text-anchor%3D'middle'%3EUPLOAD%3C%2Ftext%3E%3C%2Fsvg%3E\"]", - "row": 11 - } - ], - "field": "upload" - }, - { - "cellType": "uuid", - "cells": [ - { - "copyText": "5e457d6f-8658-44e9-fd76-be8a2b69d641", - "displayText": "5e457d6f-8658-44e9-fd76-be8a2b69d641", - "row": 0 - }, - { - "copyText": "acbedaa2-4f3e-40b8-d425-ba514f124b0b", - "displayText": "acbedaa2-4f3e-40b8-d425-ba514f124b0b", - "row": 1 - }, - { - "copyText": "c7551731-584a-4ff1-edbd-7ced52600c13", - "displayText": "c7551731-584a-4ff1-edbd-7ced52600c13", - "row": 2 - }, - { - "copyText": "fccb7ce7-24e9-4cf7-5790-79249188229b", - "displayText": "fccb7ce7-24e9-4cf7-5790-79249188229b", - "row": 3 - }, - { - "copyText": "2539baa7-7032-4028-3189-8442c55066de", - "displayText": "2539baa7-7032-4028-3189-8442c55066de", - "row": 4 - }, - { - "copyText": "688958ec-40f7-419d-35b0-f13270a12259", - "displayText": "688958ec-40f7-419d-35b0-f13270a12259", - "row": 5 - }, - { - "copyText": "231b1faa-098b-460a-468a-4b3014b33189", - "displayText": "231b1faa-098b-460a-468a-4b3014b33189", - "row": 6 - }, - { - "copyText": "f6b115fa-16a0-4f26-63bb-9e4ba5720209", - "displayText": "f6b115fa-16a0-4f26-63bb-9e4ba5720209", - "row": 7 - }, - { - "copyText": "d9514829-15df-4345-e15e-514598069f8b", - "displayText": "d9514829-15df-4345-e15e-514598069f8b", - "row": 8 - }, - { - "copyText": "4a64b778-697a-4205-c9a2-8a00a86646e1", - "displayText": "4a64b778-697a-4205-c9a2-8a00a86646e1", - "row": 9 - }, - { - "copyText": "300b6a83-3fd7-45c8-960e-dda76485cdf7", - "displayText": "300b6a83-3fd7-45c8-960e-dda76485cdf7", - "row": 10 - }, - { - "copyText": "b7768432-6ed3-433e-ed67-1366434ee157", - "displayText": "b7768432-6ed3-433e-ed67-1366434ee157", - "row": 11 - } - ], - "field": "uuid" - }, - { - "cellType": "color", - "cells": [ - { - "copyText": "#a855f7", - "displayText": "#a855f7", - "row": 0 - }, - { - "copyText": "#3b82f6", - "displayText": "#3b82f6", - "row": 1 - }, - { - "copyText": "#22c55e", - "displayText": "#22c55e", - "row": 2 - }, - { - "copyText": "#ef4444", - "displayText": "#ef4444", - "row": 3 - }, - { - "copyText": "#06b6d4", - "displayText": "#06b6d4", - "row": 4 - }, - { - "copyText": "#f59e0b", - "displayText": "#f59e0b", - "row": 5 - }, - { - "copyText": "#ef4444", - "displayText": "#ef4444", - "row": 6 - }, - { - "copyText": "#06b6d4", - "displayText": "#06b6d4", - "row": 7 - }, - { - "copyText": "#06b6d4", - "displayText": "#06b6d4", - "row": 8 - }, - { - "copyText": "#f59e0b", - "displayText": "#f59e0b", - "row": 9 - }, - { - "copyText": "#ef4444", - "displayText": "#ef4444", - "row": 10 - }, - { - "copyText": "#06b6d4", - "displayText": "#06b6d4", - "row": 11 - } - ], - "field": "color" - }, - { - "cellType": "rating", - "cells": [ - { - "copyText": "4", - "displayText": "4", - "row": 0 - }, - { - "copyText": "5", - "displayText": "5", - "row": 1 - }, - { - "copyText": "2", - "displayText": "2", - "row": 2 - }, - { - "copyText": "3", - "displayText": "3", - "row": 3 - }, - { - "copyText": "3", - "displayText": "3", - "row": 4 - }, - { - "copyText": "5", - "displayText": "5", - "row": 5 - }, - { - "copyText": "4", - "displayText": "4", - "row": 6 - }, - { - "copyText": "1", - "displayText": "1", - "row": 7 - }, - { - "copyText": "5", - "displayText": "5", - "row": 8 - }, - { - "copyText": "5", - "displayText": "5", - "row": 9 - }, - { - "copyText": "4", - "displayText": "4", - "row": 10 - }, - { - "copyText": "5", - "displayText": "5", - "row": 11 - } - ], - "field": "rating" - }, - { - "cellType": "tags", - "cells": [ - { - "copyText": "[\"juliet\",\"delta\"]", - "displayText": "", - "row": 0 - }, - { - "copyText": "[\"foxtrot\",\"juliet\"]", - "displayText": "", - "row": 1 - }, - { - "copyText": "[\"juliet\",\"hotel\"]", - "displayText": "", - "row": 2 - }, - { - "copyText": "[\"hotel\",\"alpha\"]", - "displayText": "", - "row": 3 - }, - { - "copyText": "[\"foxtrot\",\"delta\"]", - "displayText": "", - "row": 4 - }, - { - "copyText": "[\"charlie\",\"alpha\"]", - "displayText": "", - "row": 5 - }, - { - "copyText": "[\"hotel\",\"india\"]", - "displayText": "", - "row": 6 - }, - { - "copyText": "[\"foxtrot\",\"bravo\"]", - "displayText": "", - "row": 7 - }, - { - "copyText": "[\"echo\",\"echo\"]", - "displayText": "", - "row": 8 - }, - { - "copyText": "[\"alpha\",\"charlie\"]", - "displayText": "", - "row": 9 - }, - { - "copyText": "[\"delta\",\"alpha\"]", - "displayText": "", - "row": 10 - }, - { - "copyText": "[\"echo\",\"delta\"]", - "displayText": "", - "row": 11 - } - ], - "field": "tags" - }, - { - "cellType": "tsvector", - "cells": [ - { - "copyText": "[\"charlie A\",\"alpha B\"]", - "displayText": "", - "row": 0 - }, - { - "copyText": "[\"golf A\",\"delta B\"]", - "displayText": "", - "row": 1 - }, - { - "copyText": "[\"delta A\",\"juliet B\"]", - "displayText": "", - "row": 2 - }, - { - "copyText": "[\"delta A\",\"charlie B\"]", - "displayText": "", - "row": 3 - }, - { - "copyText": "[\"hotel A\",\"charlie B\"]", - "displayText": "", - "row": 4 - }, - { - "copyText": "[\"india A\",\"alpha B\"]", - "displayText": "", - "row": 5 - }, - { - "copyText": "[\"bravo A\",\"alpha B\"]", - "displayText": "", - "row": 6 - }, - { - "copyText": "[\"india A\",\"charlie B\"]", - "displayText": "", - "row": 7 - }, - { - "copyText": "[\"golf A\",\"foxtrot B\"]", - "displayText": "", - "row": 8 - }, - { - "copyText": "[\"alpha A\",\"india B\"]", - "displayText": "", - "row": 9 - }, - { - "copyText": "[\"golf A\",\"echo B\"]", - "displayText": "", - "row": 10 - }, - { - "copyText": "[\"charlie A\",\"alpha B\"]", - "displayText": "", - "row": 11 - } - ], - "field": "tsvector" - }, - { - "cellType": "origin", - "cells": [ - { - "copyText": "manual", - "displayText": "manual", - "row": 0 - }, - { - "copyText": "api", - "displayText": "api", - "row": 1 - }, - { - "copyText": "import", - "displayText": "import", - "row": 2 - }, - { - "copyText": "manual", - "displayText": "manual", - "row": 3 - }, - { - "copyText": "import", - "displayText": "import", - "row": 4 - }, - { - "copyText": "system", - "displayText": "system", - "row": 5 - }, - { - "copyText": "import", - "displayText": "import", - "row": 6 - }, - { - "copyText": "system", - "displayText": "system", - "row": 7 - }, - { - "copyText": "manual", - "displayText": "manual", - "row": 8 - }, - { - "copyText": "api", - "displayText": "api", - "row": 9 - }, - { - "copyText": "manual", - "displayText": "manual", - "row": 10 - }, - { - "copyText": "manual", - "displayText": "manual", - "row": 11 - } - ], - "field": "origin" - }, - { - "cellType": "relation", - "cells": [ - { - "copyText": "Edsger Ritchie", - "displayText": "Edsger Ritchie", - "row": 0 - }, - { - "copyText": "Ada Liskov", - "displayText": "Ada Liskov", - "row": 1 - }, - { - "copyText": "Ken Thompson", - "displayText": "Ken Thompson", - "row": 2 - }, - { - "copyText": "Ken Hopper", - "displayText": "Ken Hopper", - "row": 3 - }, - { - "copyText": "Grace Thompson", - "displayText": "Grace Thompson", - "row": 4 - }, - { - "copyText": "Margaret Ritchie", - "displayText": "Margaret Ritchie", - "row": 5 - }, - { - "copyText": "Ken Liskov", - "displayText": "Ken Liskov", - "row": 6 - }, - { - "copyText": "Dennis Ritchie", - "displayText": "Dennis Ritchie", - "row": 7 - }, - { - "copyText": "Grace Dijkstra", - "displayText": "Grace Dijkstra", - "row": 8 - }, - { - "copyText": "Ada Ritchie", - "displayText": "Ada Ritchie", - "row": 9 - }, - { - "copyText": "Grace Hamilton", - "displayText": "Grace Hamilton", - "row": 10 - }, - { - "copyText": "Linus Torvalds", - "displayText": "Linus Torvalds", - "row": 11 - } - ], - "field": "relation" - } -] diff --git a/packages/sheets/src/grid/__tests__/cell-type-resolver.test.ts b/packages/sheets/src/grid/__tests__/cell-type-resolver.test.ts new file mode 100644 index 0000000..72e6a47 --- /dev/null +++ b/packages/sheets/src/grid/__tests__/cell-type-resolver.test.ts @@ -0,0 +1,46 @@ +import { describe, expect, it } from 'vitest'; + +import { resolveCellType, type FieldMetadata } from '../../cell-types/cell-type-resolver'; + +function field(type: FieldMetadata['type']): FieldMetadata { + return { name: 'value', type }; +} + +describe('resolveCellType', () => { + it('applies schema metadata precedence before the GraphQL fallback', () => { + const cases: Array<[FieldMetadata, string]> = [ + [field({ gqlType: 'String', isArray: true, pgType: 'uuid', pgAlias: 'image' }), 'image'], + [field({ gqlType: 'String', isArray: true, pgType: 'uuid' }), 'text-array'], + [field({ gqlType: 'String', pgType: 'uuid' }), 'uuid'], + [field({ gqlType: 'GeoJSON', subtype: 'GeometryPoint' }), 'geometry-point'], + [field({ gqlType: 'Mystery' }), 'text'], + ]; + + for (const [metadata, expected] of cases) { + expect(resolveCellType('value', metadata).cellType).toBe(expected); + } + }); + + it('makes only schema-owned identifiers and timestamps readonly', () => { + expect(resolveCellType('id', field({ gqlType: 'UUID' }))).toMatchObject({ + cellType: 'uuid', + canEdit: false, + canActivate: false, + isReadonly: true, + }); + expect(resolveCellType('id', field({ gqlType: 'Int' }))).toMatchObject({ + cellType: 'integer', + canEdit: true, + isReadonly: false, + }); + expect(resolveCellType('createdAt')).toMatchObject({ canEdit: false, isReadonly: true }); + }); + + it('keeps viewer-only search vectors activatable without making them editable', () => { + expect(resolveCellType('search', field({ gqlType: 'String', pgType: 'tsvector' }))).toMatchObject({ + cellType: 'tsvector', + canEdit: false, + canActivate: true, + }); + }); +}); diff --git a/packages/sheets/src/grid/__tests__/registry-fidelity.test.ts b/packages/sheets/src/grid/__tests__/registry-fidelity.test.ts deleted file mode 100644 index cba297f..0000000 --- a/packages/sheets/src/grid/__tests__/registry-fidelity.test.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { describe, expect, it } from 'vitest'; - -import { createCellTypeRegistry, type CellTypeBuiltins } from '../../cell-types/cell-type-registry'; -import type { SheetsCellRenderContext } from '../../cell-types/define-cell-type'; -import type { RelationInfo } from '../../store/relation-info-slice'; - -import { createSheetsCell } from '../../cell-model/create-sheets-cell'; -import type { CellCreationMetadata } from '../grid-cell-types'; - -// LOCK 1 — Built-in fidelity (roadmap §1.3). -// -// With NO consumer cell types registered, the per-instance CellTypeRegistry must -// be a TRANSPARENT FALLBACK over the built-in NATIVE rendering engine. This pins -// that `registry.toSheetsCell(typeKey, value, ctx)` produces output deep-equal to a -// direct `createSheetsCell(value, metadata)` call for identical inputs — exactly how -// useSheetsContent wires the builtins. If these ever diverge, the registry has -// stopped being a pure override layer. - -// The exact builtins wiring — toSheetsCell delegates to createSheetsCell. (The glide -// engines are gone; the native dispatcher is the only built-in display path.) -const builtins: CellTypeBuiltins = { - toSheetsCell: (value, ctx) => createSheetsCell(value, ctx.metadata), -}; - -// Build metadata the way useSheetsContent does (cellType === resolved typeKey, -// canEdit/isReadonly/activationBehavior come from the structural route). For -// fidelity we only need cellType to drive factory selection; the other fields are -// passed identically through both paths so they cannot cause a divergence. -function makeMetadata(cellType: string, overrides?: Partial): CellCreationMetadata { - return { - cellType, - fieldName: overrides?.fieldName ?? cellType, - canEdit: true, - isReadonly: false, - activationBehavior: 'double-click', - ...overrides, - }; -} - -function makeCtx(metadata: CellCreationMetadata): SheetsCellRenderContext { - return { metadata }; -} - -interface FidelityCase { - label: string; - typeKey: string; - value: unknown; - metaOverrides?: Partial; -} - -const RELATION_INFO_BELONGS_TO: RelationInfo = { - kind: 'belongsTo', - relatedTable: 'users', - relationField: 'author', - foreignKeyField: 'authorId', - displayCandidates: ['displayName', 'name'], -}; - -const RELATION_INFO_HAS_MANY: RelationInfo = { - kind: 'hasMany', - relatedTable: 'comments', - relationField: 'comments', - foreignKeyField: 'postId', - displayCandidates: ['title'], -}; - -// Representative coverage across the built-in factory families. Each case is a -// (typeKey, value) the corresponding SheetsCell factory's canHandle accepts. -const CASES: FidelityCase[] = [ - // text / string - { label: 'text — string value', typeKey: 'text', value: 'hello world' }, - { label: 'text — null (empty cell)', typeKey: 'text', value: null }, - { label: 'text — object falls back to JSON preview', typeKey: 'text', value: { a: 1, b: 2 } }, - // number / Int - { label: 'number — numeric value', typeKey: 'number', value: 42.5 }, - { label: 'integer — string coerced', typeKey: 'integer', value: '17' }, - { label: 'number — null', typeKey: 'number', value: null }, - { label: 'number — NaN string', typeKey: 'number', value: 'not-a-number' }, - // boolean - { label: 'boolean — true', typeKey: 'boolean', value: true }, - { label: 'boolean — null coerces false', typeKey: 'boolean', value: null }, - { label: 'toggle — truthy coercion', typeKey: 'toggle', value: 1 }, - // date / timestamp - { label: 'date — iso string', typeKey: 'date', value: '2026-06-11' }, - { label: 'timestamptz — value', typeKey: 'timestamptz', value: '2026-06-11T10:00:00Z' }, - { label: 'datetime — null', typeKey: 'datetime', value: null }, - // image - { label: 'image — url string', typeKey: 'image', value: 'https://example.com/a.png' }, - { label: 'image — object with url key', typeKey: 'image', value: { url: 'https://example.com/b.png' } }, - { label: 'image — null', typeKey: 'image', value: null }, - // geometry (the native dispatcher's built-in geometry fallback owns this) - { label: 'geometry — object value', typeKey: 'geometry', value: { type: 'Point', coordinates: [1, 2] } }, - { label: 'geometry — null', typeKey: 'geometry', value: null }, - // relation — single (belongsTo) - { - label: 'relation — belongsTo object', - typeKey: 'relation', - value: { id: 'u1', displayName: 'Ada Lovelace' }, - metaOverrides: { relationInfo: RELATION_INFO_BELONGS_TO, relationOptions: {} }, - }, - { - label: 'relation — belongsTo null', - typeKey: 'relation', - value: null, - metaOverrides: { relationInfo: RELATION_INFO_BELONGS_TO, relationOptions: {} }, - }, - // relation — list (hasMany) exercises the chip-limit branch - { - label: 'relation — hasMany list with chip overflow', - typeKey: 'relation', - value: [{ title: 'one' }, { title: 'two' }, { title: 'three' }, { title: 'four' }], - metaOverrides: { - relationInfo: RELATION_INFO_HAS_MANY, - relationOptions: { relationChipLimit: 2, relationLabelMaxLength: 24 }, - }, - }, -]; - -describe('LOCK 1 — registry.toSheetsCell is a transparent fallback to createSheetsCell', () => { - const registry = createCellTypeRegistry([], builtins); - - it.each(CASES)('$label', ({ typeKey, value, metaOverrides }) => { - const metadata = makeMetadata(typeKey, metaOverrides); - - // Direct built-in path (what the registry must mirror). - const direct = createSheetsCell(value, metadata); - - // Registry path with zero consumer defs — should be 1:1. - const viaRegistry = registry.toSheetsCell(typeKey, value, makeCtx(metadata)); - - expect(viaRegistry).toEqual(direct); - }); - - it('covers every built-in factory family at least once', () => { - // Guard against silent shrinkage of the representative set. - const families = new Set(CASES.map((c) => c.typeKey)); - for (const required of ['text', 'number', 'boolean', 'date', 'image', 'relation', 'geometry']) { - expect(families.has(required)).toBe(true); - } - }); -}); diff --git a/packages/sheets/src/selection/__tests__/range-set.golden.test.ts b/packages/sheets/src/selection/__tests__/range-set.golden.test.ts deleted file mode 100644 index f88e5e6..0000000 --- a/packages/sheets/src/selection/__tests__/range-set.golden.test.ts +++ /dev/null @@ -1,108 +0,0 @@ -/** - * RangeSet ⇄ golden parity (Phase 6). - * - * Drives the NATIVE {@link RangeSet} through the SAME op programs frozen in - * `src/grid/__golden__/selection.golden.json` (recorded from glide's - * `CompactSelection` by `selection.golden.test.ts`). For every committed case it - * rebuilds the SequenceResult shape and asserts it DEEP-EQUALS the recorded - * `result` — i.e. RangeSet reproduces CompactSelection byte-for-byte. This file - * is the executable RangeSet contract. - */ - -import { describe, expect, it } from 'vitest'; - -import { loadGolden } from '../../grid/__golden__/parity.harness'; -import { RangeSet } from '../range-set'; - -// ─── Op vocabulary — mirrors selection.golden.test.ts ─────────────────────── - -type Op = - | { op: 'add'; index: number } - | { op: 'addRange'; start: number; end: number } - | { op: 'remove'; index: number } - | { op: 'removeRange'; start: number; end: number } - | { op: 'offset'; amount: number } - | { op: 'fromSingleIndex'; index: number } - | { op: 'fromSingleRange'; start: number; end: number }; - -interface HasIndexProbe { - index: number; - present: boolean; -} - -interface SequenceResult { - items?: number[]; - length: number; - first: number | null; - last: number | null; - hasIndexProbes: HasIndexProbe[]; -} - -interface GoldenSequence { - name: string; - ops: Op[]; - result: SequenceResult; -} - -/** - * Reference executor's native twin: maps each op onto RangeSet. `fromSingle*` - * RESETS the accumulator; every other op threads the immutable result forward. - */ -function runOps(ops: Op[]): RangeSet { - let sel = RangeSet.empty(); - for (const o of ops) { - switch (o.op) { - case 'add': - sel = sel.add(o.index); - break; - case 'addRange': - sel = sel.add([o.start, o.end]); - break; - case 'remove': - sel = sel.remove(o.index); - break; - case 'removeRange': - sel = sel.remove([o.start, o.end]); - break; - case 'offset': - sel = sel.offset(o.amount); - break; - case 'fromSingleIndex': - sel = RangeSet.fromSingleSelection(o.index); - break; - case 'fromSingleRange': - sel = RangeSet.fromSingleSelection([o.start, o.end]); - break; - } - } - return sel; -} - -/** - * Rebuild the recorded shape from a RangeSet. `items` is OMITTED when the golden - * omits it (the `sampleItems: false` 10k cases — detected by the absence of an - * `items` key on the committed result), matching `record()` in the spec file. - */ -function record(sel: RangeSet, probeIndices: number[], sampleItems: boolean): SequenceResult { - const first = sel.first(); - const last = sel.last(); - const result: SequenceResult = { - length: sel.length, - first: first === undefined ? null : first, - last: last === undefined ? null : last, - hasIndexProbes: probeIndices.map((index) => ({ index, present: sel.hasIndex(index) })), - }; - if (sampleItems) result.items = sel.toArray(); - return result; -} - -const GOLDEN = loadGolden('selection.golden') as GoldenSequence[]; - -describe('RangeSet golden parity — reproduces CompactSelection (Phase 6)', () => { - it.each(GOLDEN.map((seq) => [seq.name, seq] as const))('%s', (_name, seq) => { - const probes = seq.result.hasIndexProbes.map((probe) => probe.index); - const sampleItems = seq.result.items !== undefined; - const actual = record(runOps(seq.ops), probes, sampleItems); - expect(actual).toEqual(seq.result); - }); -}); diff --git a/packages/sheets/src/selection/__tests__/range-set.test.ts b/packages/sheets/src/selection/__tests__/range-set.test.ts new file mode 100644 index 0000000..2faa9f1 --- /dev/null +++ b/packages/sheets/src/selection/__tests__/range-set.test.ts @@ -0,0 +1,36 @@ +import { describe, expect, it } from 'vitest'; + +import { RangeSet } from '../range-set'; + +describe('RangeSet', () => { + it('merges touching and overlapping ranges without mutating prior selections', () => { + const first = RangeSet.empty().add([2, 5]); + const merged = first.add([5, 8]).add([0, 3]); + + expect(first.toArray()).toEqual([2, 3, 4]); + expect(merged.toArray()).toEqual([0, 1, 2, 3, 4, 5, 6, 7]); + }); + + it('splits a range when removing an interior selection', () => { + const selection = RangeSet.fromSingleSelection([0, 10]).remove([3, 6]); + + expect(selection.toArray()).toEqual([0, 1, 2, 6, 7, 8, 9]); + expect(selection.hasIndex(3)).toBe(false); + expect(selection.first()).toBe(0); + expect(selection.last()).toBe(9); + }); + + it('offsets sparse ranges while preserving their selected count', () => { + const selection = RangeSet.empty().add([0, 2]).add([5, 7]).offset(3); + + expect(selection.toArray()).toEqual([3, 4, 8, 9]); + expect(selection.length).toBe(4); + }); + + it('treats empty and inverted ranges as no-ops', () => { + const selection = RangeSet.empty().add([1, 4]); + + expect(selection.add([3, 3])).toBe(selection); + expect(selection.remove([5, 2])).toBe(selection); + }); +}); diff --git a/packages/sheets/src/selection/range-set.ts b/packages/sheets/src/selection/range-set.ts index df1157c..4b424cd 100644 --- a/packages/sheets/src/selection/range-set.ts +++ b/packages/sheets/src/selection/range-set.ts @@ -8,9 +8,8 @@ * O(#ranges) instead of O(#rows). Every mutator returns a NEW RangeSet; the * receiver is never mutated. * - * Behavior is frozen by `src/grid/__golden__/selection.golden.json` (the - * `CompactSelection` reference) — this class MUST reproduce all 26 cases - * byte-identically. See `src/selection/__tests__/range-set.golden.test.ts`. + * Focused tests protect merging, splitting, offsets, and immutability without + * freezing the obsolete grid implementation this class replaced. */ type Range = readonly [start: number, end: number]; @@ -61,9 +60,8 @@ function insertRange(ranges: readonly Range[], [s, e]: Range): Range[] { * Subtract `[selMin, selMax)` from a sorted, disjoint range list, SPLITTING any * covering range (removing the middle of one yields two). * - * This is a FAITHFUL port of glide `CompactSelection.remove` — INCLUDING its two - * documented quirks (frozen by selection.golden.json), not a clean set - * difference: + * This is a faithful port of glide `CompactSelection.remove`, including its two + * documented quirks rather than a clean set difference: * * 1. Overlap test is `start <= selMax && selMin <= end` — both bounds `<=`, so * a range touching the cut is "intersected" (usually a harmless in-place diff --git a/packages/sheets/src/stories/__tests__/stories-render.test.tsx b/packages/sheets/src/stories/__tests__/stories-render.test.tsx index 72c12e1..2524299 100644 --- a/packages/sheets/src/stories/__tests__/stories-render.test.tsx +++ b/packages/sheets/src/stories/__tests__/stories-render.test.tsx @@ -1,199 +1,73 @@ /* @vitest-environment jsdom */ -// -// Portable-stories render gate (Stage 3). Imports EVERY showcase/stress story via Storybook's -// `composeStories` (the portable-stories API, re-exported by @storybook/react-vite) and renders -// each one in jsdom with react-dom/client + act — the same mount idiom as the package's other -// component tests (no @testing-library; not a dep here). -// -// Per story the gate asserts: -// • it MOUNTS without throwing and renders SOMETHING (container gains children); -// • for the grid-bearing stories, after the async mock query resolves the ported DOM grid -// POPULATES — a [data-part-id="sheets-viewport"] with [role="gridcell"] nodes appears (we -// poll for it). Edge-state stories (Loading / Empty / Error) and the custom-table Selection -// harness legitimately render NO viewport, so the viewport assertion is applied only when a -// viewport is present — never forced — and every story must still mount + render content. -// • VIRTUALIZATION: any populated grid keeps a BOUNDED number of gridcell nodes in the DOM -// (never one-per-row), and the 10k stress story specifically renders far fewer than 10000. -// -// composeStories applies each story's args/decorators/render but does NOT run its play(); we only -// mount, so the (browser-only) interaction steps never execute under vitest. import { act } from 'react'; import { createRoot, type Root } from 'react-dom/client'; -import { afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest'; import { composeStories } from '@storybook/react-vite'; +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; -import * as AllCellTypesStories from '../all-cell-types.stories'; -import * as EditingStories from '../editing.stories'; import * as StressStories from '../stress.stories'; (globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true; -// A composed story is a render-able component; `storyName` is attached by composeStories. -type ComposedStory = React.ComponentType & { storyName?: string }; - -// composeStories(module) -> { [exportName]: ComposedStory }. Flatten every module into a flat list -// of { title, name, Story } so one parametrized describe-block drives them all. -interface NamedStory { - title: string; - name: string; - Story: ComposedStory; -} - -function collect(title: string, mod: Parameters[0]): NamedStory[] { - const composed = composeStories(mod) as Record; - return Object.entries(composed).map(([exportName, Story]) => ({ - title, - name: Story.storyName ?? exportName, - Story, - })); -} - -const ALL_STORIES: NamedStory[] = [ - ...collect('Showcase/All Cell Types', AllCellTypesStories), - ...collect('Showcase/Editing', EditingStories), - ...collect('Stress/10k Rows', StressStories), -]; - -// Every kept story (All Cell Types / Editing / Stress) renders the real DOM grid, so all are -// expected to populate a viewport — no exemptions. (The former full-screen edge-state and -// custom-table-harness stories were removed in the 3-story consolidation.) -const NON_POPULATING = new Set(); -// Upper bound for "virtualized" — a populated grid must keep far fewer gridcell nodes than rows. -const MAX_GRIDCELLS = 2000; -// Per-test budget: the 100k / wide stress stories take a few seconds to build their dataset. -const STORY_TEST_TIMEOUT = 30000; -// Poll budget for a grid to populate — comfortably under STORY_TEST_TIMEOUT so the catch runs. -const POPULATE_TIMEOUT = 8000; - const proto = window.HTMLElement.prototype; -describe('Storybook stories render in jsdom (portable composeStories)', () => { +describe('Sheets virtualization', () => { let root: Root; let container: HTMLDivElement; - const origW = Object.getOwnPropertyDescriptor(proto, 'offsetWidth'); - const origH = Object.getOwnPropertyDescriptor(proto, 'offsetHeight'); - const origRect = proto.getBoundingClientRect; - - beforeAll(() => { - expect(ALL_STORIES.length).toBeGreaterThan(0); - }); + const originalWidth = Object.getOwnPropertyDescriptor(proto, 'offsetWidth'); + const originalHeight = Object.getOwnPropertyDescriptor(proto, 'offsetHeight'); + const originalRect = proto.getBoundingClientRect; beforeEach(() => { - // jsdom does NO layout. Shim element geometry so TanStack Virtual works the way it does in a - // real browser: a sized scroll element (offsetWidth/Height) AND a real per-row height - // (getBoundingClientRect, read by the row virtualizer's measureElement). Without the row - // height, 0-height rows make the virtual range unbounded — in infinite-scroll mode that - // requests every page and loops (a jsdom measurement artifact, not a grid bug); a realistic - // height bounds the window to a few dozen rows, exactly as in the browser. Object.defineProperty(proto, 'offsetWidth', { configurable: true, get: () => 1200 }); Object.defineProperty(proto, 'offsetHeight', { configurable: true, get: () => 800 }); - proto.getBoundingClientRect = function () { - return { width: 1200, height: 33, top: 0, left: 0, right: 1200, bottom: 33, x: 0, y: 0, toJSON() {} } as DOMRect; - }; + proto.getBoundingClientRect = () => ({ + width: 1200, + height: 33, + top: 0, + left: 0, + right: 1200, + bottom: 33, + x: 0, + y: 0, + toJSON() {}, + }) as DOMRect; container = document.createElement('div'); document.body.appendChild(container); root = createRoot(container); }); afterEach(async () => { - await act(async () => { - root.unmount(); - }); + await act(async () => root.unmount()); container.remove(); - // Overlay editors / portals mount onto document.body — clear any leftovers between stories. - document.body.querySelectorAll('[data-slot="overlay-manager"], #portal').forEach((node) => node.remove()); - if (origW) Object.defineProperty(proto, 'offsetWidth', origW); - else delete (proto as unknown as Record).offsetWidth; - if (origH) Object.defineProperty(proto, 'offsetHeight', origH); - else delete (proto as unknown as Record).offsetHeight; - proto.getBoundingClientRect = origRect; + if (originalWidth) Object.defineProperty(proto, 'offsetWidth', originalWidth); + if (originalHeight) Object.defineProperty(proto, 'offsetHeight', originalHeight); + proto.getBoundingClientRect = originalRect; }); - for (const { title, name, Story } of ALL_STORIES) { - it(`${title} › ${name} mounts + renders`, { timeout: STORY_TEST_TIMEOUT }, async () => { - await act(async () => { - root.render(); - }); - - // Let the async mock query resolve. Grid-bearing stories POPULATE — poll until - // [role="gridcell"] nodes appear (the viewport div mounts before data loads, so we must - // wait on the cells, not the viewport). Edge-state (Loading/Empty/Error) + the custom - // Selection harness render no gridcells; for those, settle the async path and rely on the - // universal mount+content assertions below. - const populates = !NON_POPULATING.has(`${title} › ${name}`); - if (populates) { - try { - await waitFor( - () => { - expect(container.querySelectorAll('[role="gridcell"]').length).toBeGreaterThan(0); - }, - { timeout: POPULATE_TIMEOUT }, - ); - } catch { - // Falls through: a story without a populating grid is still validated by (1). - } - } else { - // Settle the (never-resolving / empty) async path so the state UI commits. - await act(async () => { - await new Promise((resolve) => setTimeout(resolve, 50)); - }); - } + it('keeps the 10,000-row story to a bounded DOM window', { timeout: 30_000 }, async () => { + const { TenThousandRows } = composeStories(StressStories); + await act(async () => root.render()); - // (1) Universal: the story mounted and rendered real DOM content (no crash, non-empty). - expect(container.childElementCount).toBeGreaterThan(0); - expect(container.querySelectorAll('*').length).toBeGreaterThan(0); - - // (2) If a grid viewport rendered, it must have populated AND stay virtualized (bounded - // gridcell node count — never one node per data row) with zero canvas on the DOM path. - const viewport = container.querySelector('[data-part-id="sheets-viewport"]'); - if (viewport && populates) { - const gridcells = container.querySelectorAll('[role="gridcell"]'); - expect(gridcells.length).toBeGreaterThan(0); - expect(gridcells.length).toBeLessThan(MAX_GRIDCELLS); - expect(container.querySelectorAll('canvas').length).toBe(0); - } + await waitFor(() => { + expect(container.querySelectorAll('[role="gridcell"]').length).toBeGreaterThan(0); }); - } - // The headline stress story: 10k rows, infinite scroll. Assert it mounts, the virtualizer - // renders a BOUNDED window (NOT 10000 DOM nodes), and the data really is 10k-scale. - it('Stress/10k Rows › TenThousandRows virtualizes (bounded DOM nodes, not 10000)', { timeout: STORY_TEST_TIMEOUT }, async () => { - const { TenThousandRows } = composeStories(StressStories) as Record; - await act(async () => { - root.render(); - }); - await waitFor( - () => { - expect(container.querySelector('[data-part-id="sheets-viewport"]')).not.toBeNull(); - expect(container.querySelectorAll('[role="gridcell"]').length).toBeGreaterThan(0); - }, - { timeout: POPULATE_TIMEOUT }, - ); - const gridcells = container.querySelectorAll('[role="gridcell"]'); - // Bounded window: far fewer DOM gridcells than the 10000-row dataset (virtualization holds). - expect(gridcells.length).toBeLessThan(MAX_GRIDCELLS); - expect(container.querySelectorAll('canvas').length).toBe(0); + expect(container.querySelectorAll('[role="gridcell"]').length).toBeLessThan(2_000); }); }); -// ─── Minimal async poller (mirrors the smoke-test idiom; no @testing-library in this package) ───── -async function waitFor(assert: () => void, { timeout = 5000, interval = 30 } = {}): Promise { - const start = Date.now(); +async function waitFor(assertion: () => void): Promise { + const deadline = Date.now() + 8_000; let lastError: unknown; - // eslint-disable-next-line no-constant-condition - while (true) { + while (Date.now() < deadline) { try { - await act(async () => { - await Promise.resolve(); - }); - assert(); + await act(async () => Promise.resolve()); + assertion(); return; } catch (error) { lastError = error; - if (Date.now() - start > timeout) throw lastError; - await act(async () => { - await new Promise((resolve) => setTimeout(resolve, interval)); - }); + await act(async () => new Promise((resolve) => setTimeout(resolve, 30))); } } + throw lastError; } diff --git a/packages/sheets/src/stories/_support/fixtures.ts b/packages/sheets/src/stories/_support/fixtures.ts index c577401..919e018 100644 --- a/packages/sheets/src/stories/_support/fixtures.ts +++ b/packages/sheets/src/stories/_support/fixtures.ts @@ -5,10 +5,8 @@ // that CellType regardless of gqlType (see cell-types/type-mapping.ts mapAliasToCellType). // buildAllCellTypesTable emits ONE field per (forceable) CellType so the grid paints every view. // -// Row values mirror grid/__golden__/display-cases.ts patterns (strings, numbers, booleans, ISO -// dates, arrays, {id,displayName} relations, image urls, geojson, …) but are generated by a -// seeded PRNG (mulberry32) so makeRows(n) is fully reproducible — required for a stable 10k -// stress test and visual regression. +// Row values cover strings, numbers, booleans, ISO dates, relations, images, +// and GeoJSON. A seeded PRNG keeps the 10k stress fixture reproducible. import type { MetaTable, MetaField } from '../../forms/types'; import type { SheetsRow } from '../../grid/row-model'; import type { CellType } from '../../cell-types/types'; @@ -288,7 +286,7 @@ function valueForField(spec: FieldSpec, rng: () => number, rowIndex: number): un case 'url': return `https://example.com/${pick(rng, WORDS)}/${rowIndex}.html`; // `pick(rng, COLORS)` consumes one rng() — same as the old url's `pick(rng, WORDS)` — - // so non-media columns keep identical seeded values (golden stays minimal). + // so non-media columns keep identical seeded values. case 'image': return placeholderMedia(pick(rng, COLORS), 'IMG'); case 'video': diff --git a/packages/sheets/src/table/__tests__/grid-viewport-active-cell.test.tsx b/packages/sheets/src/table/__tests__/grid-viewport-active-cell.test.tsx index 8f5bd42..48ffbf2 100644 --- a/packages/sheets/src/table/__tests__/grid-viewport-active-cell.test.tsx +++ b/packages/sheets/src/table/__tests__/grid-viewport-active-cell.test.tsx @@ -146,19 +146,16 @@ describe('GridViewport active cell (focus ring + click-to-activate + arrow nav)' return container.querySelector('[data-active="true"]'); } - it('renders the active cell with aria-selected, the ring id, and the focus-ring class', async () => { + it('connects the active cell to the grid accessibility state', async () => { await act(async () => { root.render(); }); - // The active cell is [0,0] → id sheets-cell-0-0, aria-selected, ring class. + // The active cell is [0,0] and the grid points assistive technology to it. const active = activeCellNode(); expect(active).toBeTruthy(); expect(active!.id).toBe('sheets-cell-0-0'); expect(active!.getAttribute('aria-selected')).toBe('true'); - expect(active!.className).toContain('ring-2'); - - // The grid root mirrors it via aria-activedescendant. expect(gridRoot().getAttribute('aria-activedescendant')).toBe('sheets-cell-0-0'); // Exactly ONE active cell. diff --git a/packages/sheets/src/table/__tests__/grid-viewport-header.test.tsx b/packages/sheets/src/table/__tests__/grid-viewport-header.test.tsx index 9b30812..6a76396 100644 --- a/packages/sheets/src/table/__tests__/grid-viewport-header.test.tsx +++ b/packages/sheets/src/table/__tests__/grid-viewport-header.test.tsx @@ -85,24 +85,6 @@ describe('GridViewport header row (sort caret + resize handle + sticky pin)', () return Array.from(container.querySelectorAll('[role="columnheader"]')); } - it('renders a columnheader per column, each carrying its title', async () => { - await act(async () => { - root.render(); - }); - - const headers = headerCells(); - // 3 columns: sticky col0 + 2 virtualized. - expect(headers.length).toBe(COLUMNS.length); - - const titles = headers.map((h) => h.textContent ?? ''); - expect(titles.some((t) => t.includes('Name'))).toBe(true); - expect(titles.some((t) => t.includes('Count'))).toBe(true); - expect(titles.some((t) => t.includes('Active'))).toBe(true); - - // Each header is keyed to its column id for the shell to target. - expect(headers.map((h) => h.getAttribute('data-col-key')).sort()).toEqual(['active', 'count', 'name']); - }); - it('toggles sort via onHeaderClick(colKey) when a header is clicked', async () => { const clicks: string[] = []; await act(async () => { diff --git a/packages/sheets/src/table/__tests__/grid-viewport-rowmarker.test.tsx b/packages/sheets/src/table/__tests__/grid-viewport-rowmarker.test.tsx index 3313719..111bc66 100644 --- a/packages/sheets/src/table/__tests__/grid-viewport-rowmarker.test.tsx +++ b/packages/sheets/src/table/__tests__/grid-viewport-rowmarker.test.tsx @@ -147,32 +147,12 @@ describe('GridViewport row-marker column (leading checkbox + select-all → Rang function markerHeader(): HTMLElement | null { return container.querySelector('[data-slot="row-marker-header"]'); } - function gridCells(): HTMLElement[] { - return Array.from(container.querySelectorAll('[role="gridcell"]')); - } // The base-ui Checkbox renders a ); - - const button = view.container.querySelector('button'); - expect(button).not.toBeNull(); - expect(button!.className).toContain('motion-safe:active:not-disabled:scale-[0.96]'); - expect(button!.className).toContain('h-10'); - expect(button!.className).toContain('min-w-10'); - expect(button!.className).toContain('pointer-coarse:min-h-11'); - expect(button!.className).toContain('pointer-coarse:min-w-11'); - }); - - it('expands compact icon controls to an actual coarse-pointer target', async () => { - const view = createTestRoot(); - await view.render(); - - const button = view.container.querySelector('button'); - expect(button).not.toBeNull(); - expect(button!.className).not.toContain('motion-safe:active:not-disabled:scale-[0.96]'); - expect(button!.hasAttribute('static')).toBe(false); - }); }); diff --git a/packages/ui/test/field-empty.test.tsx b/packages/ui/test/field-empty.test.tsx deleted file mode 100644 index 1abda0d..0000000 --- a/packages/ui/test/field-empty.test.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import * as React from 'react'; -import { renderToString } from 'react-dom/server'; -import { describe, expect, it } from 'vitest'; - -import { Empty, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from '../src/components/empty'; -import { - Field, - FieldDescription, - FieldError, - FieldGroup, - FieldLabel, - FieldLegend, - FieldSet, -} from '../src/components/field'; -import { Input } from '../src/components/input'; - -function parse(markup: string) { - const container = document.createElement('div'); - container.innerHTML = markup; - return container; -} - -describe('field and empty composition', () => { - it('preserves the legacy Field contract', () => { - const container = parse( - renderToString( - - - , - ), - ); - - expect(container.querySelector('[data-slot="field-label"]')?.getAttribute('for')).toBe('email'); - expect(container.querySelector('[data-slot="field-description"]')?.textContent).toBe('Used for notifications.'); - expect(container.querySelector('[data-slot="field-error"]')?.textContent).toBe('Email is required.'); - expect(container.querySelector('[data-slot="field"]')?.getAttribute('data-invalid')).toBe('true'); - }); - - it('exposes the upstream composable field surface', () => { - const container = parse( - renderToString( -
- Profile - - - Name - - Public display name. - - - -
, - ), - ); - - expect(container.querySelector('[data-slot="field-set"]')).not.toBeNull(); - expect(container.querySelector('[data-slot="field-group"]')).not.toBeNull(); - expect(container.querySelector('[data-slot="field-error"]')?.textContent).toBe('Required'); - }); - - it('renders the standard empty-state slots', () => { - const container = parse( - renderToString( - - - Icon - No rows - Create the first row. - - , - ), - ); - - expect(container.querySelector('[data-slot="empty"]')).not.toBeNull(); - expect(container.querySelector('[data-slot="empty-icon"]')?.getAttribute('data-variant')).toBe('icon'); - expect(container.querySelector('[data-slot="empty-description"]')?.tagName).toBe('P'); - }); -}); diff --git a/packages/ui/test/select-transition.test.tsx b/packages/ui/test/select-transition.test.tsx deleted file mode 100644 index 5caa288..0000000 --- a/packages/ui/test/select-transition.test.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import * as React from 'react'; -import { act } from 'react'; -import { createRoot, type Root } from 'react-dom/client'; -import { afterEach, describe, expect, it } from 'vitest'; - -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from '../src/components/select'; - -globalThis.IS_REACT_ACT_ENVIRONMENT = true; - -const activeRoots = new Set(); - -afterEach(async () => { - for (const root of activeRoots) { - await act(async () => root.unmount()); - } - activeRoots.clear(); - document.body.replaceChildren(); -}); - -describe('Select transitions', () => { - it('defines both entering and exiting popup states', async () => { - const container = document.createElement('div'); - document.body.appendChild(container); - const root = createRoot(container); - activeRoots.add(root); - - await act(async () => { - root.render( - , - ); - }); - - const popup = document.querySelector('[data-slot="select-popup"]'); - expect(popup?.className).toContain('data-starting-style:opacity-0'); - expect(popup?.className).toContain('data-ending-style:opacity-0'); - expect(popup?.className).not.toContain('has-data-starting-style'); - }); -}); diff --git a/packages/ui/test/stack-animation.test.tsx b/packages/ui/test/stack-animation.test.tsx deleted file mode 100644 index 3ddb8b1..0000000 --- a/packages/ui/test/stack-animation.test.tsx +++ /dev/null @@ -1,153 +0,0 @@ -import * as React from 'react'; -import { act } from 'react'; -import { createRoot, type Root } from 'react-dom/client'; -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; - -const motionState = vi.hoisted(() => ({ - prefersReducedMotion: false, - cardProps: [] as Array>, -})); - -vi.mock('motion/react', async () => { - const React = await vi.importActual('react'); - - return { - motion: { - div: React.forwardRef>(function MotionDiv(props, ref) { - const { - initial, - animate, - exit, - transition, - onAnimationComplete, - ...elementProps - } = props; - - motionState.cardProps.push({ - initial, - animate, - exit, - transition, - onAnimationComplete, - }); - - return
; - }), - }, - useReducedMotion: () => motionState.prefersReducedMotion, - }; -}); - -import { StackCard } from '../src/components/stack/stack-card'; -import { CardStackProvider } from '../src/components/stack/stack-context'; -import type { AnimationConfig, CardSpec } from '../src/components/stack/stack.types'; - -globalThis.IS_REACT_ACT_ENVIRONMENT = true; - -const activeRoots = new Set(); - -function TestCard() { - return null; -} - -const card: CardSpec = { - id: 'settings', - title: 'Settings', - Component: TestCard, -}; - -async function renderCard(animation?: AnimationConfig) { - const container = document.createElement('div'); - document.body.appendChild(container); - const root = createRoot(container); - activeRoots.add(root); - - await act(async () => { - root.render( - - - , - ); - }); -} - -function latestMotionProps() { - const props = motionState.cardProps.at(-1); - expect(props).toBeDefined(); - return props!; -} - -beforeEach(() => { - motionState.prefersReducedMotion = false; - motionState.cardProps = []; -}); - -afterEach(async () => { - for (const root of activeRoots) { - await act(async () => root.unmount()); - } - activeRoots.clear(); - document.body.replaceChildren(); -}); - -describe('StackCard motion', () => { - it('uses configured enter and exit tweens before settling onto the offset spring', async () => { - const enterEase = [0.1, 0.2, 0.3, 1]; - const exitEase = [0.4, 0.3, 0.2, 1]; - - await renderCard({ duration: 0.12, enterEase, exitEase }); - - let props = latestMotionProps(); - expect(props.initial).toEqual({ x: '100%', opacity: 1 }); - expect(props.animate).toEqual({ x: 0, opacity: 1 }); - expect(props.transition).toEqual({ - type: 'tween', - duration: 0.12, - ease: enterEase, - }); - expect(props.exit).toEqual({ - x: '100%', - opacity: 0, - transition: { - type: 'tween', - duration: 0.12, - ease: exitEase, - }, - }); - - await act(async () => { - (props.onAnimationComplete as (() => void) | undefined)?.(); - }); - - props = latestMotionProps(); - expect(props.transition).toEqual({ - type: 'spring', - stiffness: 300, - damping: 30, - mass: 0.8, - }); - }); - - it('settles entry and exit immediately when reduced motion is requested', async () => { - motionState.prefersReducedMotion = true; - - await renderCard({ duration: 0.2 }); - - const props = latestMotionProps(); - expect(props.initial).toEqual({ x: 0, opacity: 1 }); - expect(props.animate).toEqual({ x: 0, opacity: 1 }); - expect(props.transition).toEqual({ duration: 0 }); - expect(props.exit).toEqual({ - x: 0, - opacity: 0, - transition: { duration: 0 }, - }); - }); -}); From b9d0014c2f5b95bf00997ad73c92f372f392d051 Mon Sep 17 00:00:00 2001 From: yyyyaaa Date: Mon, 17 Aug 2026 12:47:58 +0700 Subject: [PATCH 2/2] fix(registry): pin sugar-high v1 for AI installs --- packages/ui/registry.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/registry.json b/packages/ui/registry.json index 8a7311f..a2e256a 100644 --- a/packages/ui/registry.json +++ b/packages/ui/registry.json @@ -2275,7 +2275,7 @@ "docs": "Import PromptInput, Message, Tool, Reasoning, and PlanTracker from the installed ai directory under your configured shadcn ui alias.", "dependencies": [ "lucide-react", - "sugar-high" + "sugar-high@^1.2.1" ], "registryDependencies": [ "cn",