From 45fb5b62ee5038b870a38491a79678bb74e38e71 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 10:37:50 +0200 Subject: [PATCH 001/104] TML-3537: plan for value objects in storage, and the first two dispatch briefs Signed-off-by: willbot Signed-off-by: Will Madden --- .../10-value-object-storage-shape.md | 43 +++++++++++++++++++ .../dispatches/11-plans-with-no-operations.md | 43 +++++++++++++++++++ .../slices/remaining-nouns/plan-pr3.md | 36 ++++++++++++++++ 3 files changed, 122 insertions(+) create mode 100644 projects/migration-statements/slices/remaining-nouns/dispatches/10-value-object-storage-shape.md create mode 100644 projects/migration-statements/slices/remaining-nouns/dispatches/11-plans-with-no-operations.md create mode 100644 projects/migration-statements/slices/remaining-nouns/plan-pr3.md diff --git a/projects/migration-statements/slices/remaining-nouns/dispatches/10-value-object-storage-shape.md b/projects/migration-statements/slices/remaining-nouns/dispatches/10-value-object-storage-shape.md new file mode 100644 index 000000000000..549c6db9c024 --- /dev/null +++ b/projects/migration-statements/slices/remaining-nouns/dispatches/10-value-object-storage-shape.md @@ -0,0 +1,43 @@ +# Dispatch 10 — value objects in the storage plane: contract shape and validators + +**Plan:** [`../plan-pr3.md`](../plan-pr3.md) · **ADR:** `docs/architecture docs/adrs/ADR 278 - Value objects are storage entities, and a discriminator column names a value set.md` · **Grounding:** `wip/pr3-grounding.md` § A, B · **Branch:** `tml-3537-value-objects-storage` + +## Outcome + +The contract types and validators accept a value object as a storage entity, at `storage.namespaces..entries.valueObject.`, in both families, with the same definition it has in the domain plane today. A domain field that holds a value object refers to it by a storage coordinate, and so does a value object member that holds another. Validation checks the reference across the two planes. The domain plane no longer has a `valueObjects` key. Nothing writes the new shape yet; authoring and emit change in dispatches 12 and 13, so this dispatch keeps the old fixtures passing only where a test needs them and says which tests it left for dispatch 12. + +## What the grounding found + +- Nothing in the domain plane refers to storage today. An enum field's `valueSet` refers to the domain enum (`value-set-ref.ts`). This reference is the first from domain to storage, which ADR 221 allows (a domain entity may refer to a storage entity, never the reverse). +- Domain validation (`validate-domain.ts`) sees only the domain plane and needs read access to storage for this check. +- No existing type fits the reference as it is. `EntityCoordinate` lives in framework-components, which foundation cannot import. Decide the reference type from the code: a storage coordinate in foundation, consistent with the existing cross-plane references (`plane`, `namespaceId`, `entityKind`, `entityName`), reused rather than duplicated. Report the name and shape you chose, and why. + +## Do + +1. Read ADR 221, ADR 278, ADR 178, `domain-types.ts`, `value-set-ref.ts`, `validate-domain.ts`, the SQL and Mongo storage types and their validators, the JSON schema `data-contract-sql-v1.json`, and the domain accessors (`domainValueObjectsAtDefaultNamespace` and friends). +2. **Tests first, red before each change:** a contract with a value object in storage and a field referring to it validates; a reference to a missing storage value object is refused with a message naming the field and the coordinate; a nested reference resolves; a value object that refers to itself validates; the old domain `valueObjects` key is refused; both families. +3. Implement the types, the validators and the accessors. Keep every name from the existing vocabulary (`valueObject` is the entity kind, as ADR 278 says). Do not invent new nouns; if a new name is unavoidable, say so in the report with candidates. +4. Update the JSON schema. + +## Do not + +- Do not change authoring, emit, runtime or the MongoDB validator derivation; those are dispatches 12 to 14. +- Do not re-emit fixtures; that is dispatch 16. + +## Rules + +- Never run `pnpm test:integration`, `pnpm test:e2e` or `pnpm test:all`. Only named files inside `test/integration` with `pnpm test `. +- Use the shell's Node and `pnpm`. Never `npx`. +- No comments where the code can say it; no bare `as` casts in production code; no `any`; arktype, not zod; no file extensions in imports; no backwards-compatibility exports; framework packages name no family or target. +- Commit with `git commit -s --trailer "Signed-off-by: Will Madden "`. No AI attribution lines. Stage files explicitly. +- Do not push. Do not open a PR. +- Save slow command output under `wip/` and read the file. The machine may be loaded: run tests by file where you can. + +## Done when + +- Typecheck of the touched packages; lint and `test --coverage` in the contract foundation, framework-components, SQL contract and Mongo contract packages; `lint:deps`; `lint:ratchets`. +- The report lists every test outside those packages that now fails because nothing writes the new shape yet, for dispatch 12. + +## Report + +Plain English, short sentences: the reference type and why, the validation rules and messages, the test that was red first for each change, the gates, the tests left failing for dispatch 12, and the commits. diff --git a/projects/migration-statements/slices/remaining-nouns/dispatches/11-plans-with-no-operations.md b/projects/migration-statements/slices/remaining-nouns/dispatches/11-plans-with-no-operations.md new file mode 100644 index 000000000000..a883e1318004 --- /dev/null +++ b/projects/migration-statements/slices/remaining-nouns/dispatches/11-plans-with-no-operations.md @@ -0,0 +1,43 @@ +# Dispatch 11 — a plan with no operations is written and applied + +**Plan:** [`../plan-pr3.md`](../plan-pr3.md) · **ADR:** ADR 278, "Why every change is an operation" · **Grounding:** `wip/pr3-grounding.md` § F · **Branch:** `tml-3537-value-objects-storage` + +## Outcome + +When the storage hash changes but the planner produces no operation, `migration plan` writes the package with no operations instead of refusing, and says so, naming what changed. `db migrate` applies it by moving the marker to the new hash. `db update` does the same. This holds on Postgres, SQLite and MongoDB. Will ruled on 2026-10-21 that migrations are expected to have operations, but edge cases will happen, and the system must handle them, not refuse them. + +## What the grounding found + +- `migration plan` refuses at `migration-plan.ts` (around line 215, `errorPlanProducedNoOperations`, guarded by `noOperationsExpected`). +- The Postgres runner already moves the marker for a migration with no operations between two different hashes. The SQLite and MongoDB runners and `db update` were not checked. +- The integrity check refuses a migration whose start and end hash are equal unless it has a `data` operation (`check-integrity.ts`); that rule stays, since this dispatch is about two different hashes. + +## Do + +1. **Tests first, red before each change:** `migration plan` with a changed storage hash and no operations writes the package (`ops.json` `[]`, `migration.json` with both hashes) and prints that the plan has no operations and which storage entities changed; `db migrate` applies it and the marker holds the new hash, on PGlite, SQLite and mongodb-memory-server; `db update` does the same; `db verify` is clean afterwards. To make a storage change with no operation today, use a change the planner does not turn into DDL on that target (find one per target from the code, for example a SQLite value-set change); say which you used. +2. Remove the refusal for this case. Keep `errorPlanProducedNoOperations` only if another case still needs it; if none does, delete it and its error-reference entry. +3. Name what changed: compare the two contracts' storage and list the entity coordinates that differ, in the message and the `--json` output. Reuse an existing comparison if one exists. +4. Docs: Migration System (the `migration plan` emission cases table and the no-operations rule), the error reference, the CLI README. + +## Do not + +- Do not change how the planner plans anything else. +- Do not touch value objects or discriminators; that is the rest of PR 3. + +## Rules + +- Never run `pnpm test:integration`, `pnpm test:e2e` or `pnpm test:all`. Only named files inside `test/integration` with `pnpm test `; journeys with `pnpm test:journeys `. +- Use the shell's Node and `pnpm`. Never `npx`. +- No comments where the code can say it; no bare `as` casts in production code; no file extensions in imports; framework packages name no family or target. +- Commit with `git commit -s --trailer "Signed-off-by: Will Madden "`. No AI attribution lines. Stage files explicitly. +- Do not push. Do not open a PR. +- Save slow command output under `wip/` and read the file. The machine may be loaded. + +## Done when + +- Typecheck; lint and `test --coverage` in the CLI, the migration package, and the Postgres, SQLite and MongoDB targets; `lint:deps`; `lint:ratchets`; `check:error-reference`. +- Every integration file that mentions `errorPlanProducedNoOperations`, `PLAN_PRODUCED_NO_OPERATIONS` or "no operations", by name. + +## Report + +Plain English, short sentences: the change per command and target, the message and JSON shape, the storage change used per target in tests, the test that was red first for each change, the gates, and the commits. diff --git a/projects/migration-statements/slices/remaining-nouns/plan-pr3.md b/projects/migration-statements/slices/remaining-nouns/plan-pr3.md new file mode 100644 index 000000000000..c175fb0f4ed0 --- /dev/null +++ b/projects/migration-statements/slices/remaining-nouns/plan-pr3.md @@ -0,0 +1,36 @@ +# Slice plan — 3b, PR 3: value objects are storage entities, and a discriminator column names a value set + +**Spec:** [`spec.md`](./spec.md) decision 1 · **ADR:** ADR 278 (approved by Will, 2026-10-21) · **Linear:** [TML-3537](https://linear.app/prisma-company/issue/TML-3537) · **Branch:** `tml-3537-value-objects-storage`, from `origin/main` · **Grounding:** `wip/pr3-grounding.md` (gitignored, in this worktree; code references at main 9979a2d6ca) + +This PR changes the contract shape. It adds no statements; PR 4 adds the value object and variant statements on top of it. + +## Decisions taken while planning (record each in ADR 278 in this PR) + +- **The discriminator value set is SQL only.** On MongoDB the collection validator already pins each variant's discriminator value, so a change there already changes the storage hash. +- **An enum-typed discriminator keeps the enum's value set and check.** The column has one `valueSet` slot, and the enum already restricts it; a variant value outside the enum is already refused. +- **`enumType()` refuses a name containing a dot**, so `.` value-set keys cannot collide with an enum's. +- **The Postgres value object check is one strict `jsonpath` predicate**, written so a NULL result fails (`IS TRUE`). The ADR's `?&` and `-` operators are not used. +- **A value object that contains itself** is checked as the MongoDB validator does: fully down to where the cycle starts, any object below. +- **Value object fields have no separate stored names**; they are stored under their own names in both families. +- **The example migrations change** (Will, 2026-10-23): each `examples/prisma-8-demo` migration that introduces a value object or a variant gains the operations that create the new checks. Users get an upgrade script for their own projects (Will, 2026-10-23). + +## Gate for every dispatch + +As for PR 2: typecheck; lint and `test --coverage` in every touched package; `lint:deps`; `lint:ratchets`; `fixtures:check` when contracts or rendering change; `check:error-reference`; `check:upgrade-coverage --mode pr --prev $(git merge-base HEAD origin/main) --head HEAD` when a fragment is added. Integration and journeys by named file only. The reviewer runs code on PGlite and mongodb-memory-server, not only reads it. + +## Dispatches + +| # | Outcome | Builds on | +| --- | --- | --- | +| 10 | Contract shape and validators: a storage-plane value object entity in both families, a domain field referring to it by a storage coordinate (the first reference from domain to storage), validation across the two planes, the JSON schema, and the framework accessors | — | +| 11 | A plan whose storage hash changes but that has no operations is written and applied: `migration plan` writes it, `db migrate` and `db update` move the marker, on Postgres, SQLite and MongoDB | — | +| 12 | Authoring and build in both families write value objects into storage (SQL PSL, Mongo PSL, Mongo TS builder, Prisma 6 reader), and SQL PSL writes the discriminator value set and its column reference before the storage hash is computed; `enumType()` refuses dotted names; the MongoDB validator derivation reads value objects from storage | 10 | +| 13 | Emit and types: `contract.d.ts` types read value objects from the storage plane, with a defaulted type parameter so contracts without value objects emit the same text | 10 | +| 14 | Runtime and tools read value objects through the domain field's reference: the SQL and Mongo ORMs and query builders, the CLI statement resolver, `contract print`, `contract infer`, verification | 12, 13 | +| 15 | Postgres derived checks: the discriminator membership check, and the value object `jsonpath` check for every shape SQL authoring produces, starting from a test per shape on PGlite; a new SQL fixture with nested and list members; contract print and infer recognise the new checks | 12 | +| 16 | Re-emit every fixture and committed contract; the `prisma-8-demo` migrations gain the operations that create the new checks; the app and extension upgrade fragments with a script that rewrites a user's contract snapshots and keeps their manifests' hashes consistent, validated by running it on a project with a real migration history; ADR 278, ADR 172 and ADR 178 notes, Data Contract and MongoDB Family docs, the skill | 11, 14, 15 | +| 17 | Journeys, docs and the PR text | 16 | + +**Order:** 10 and 11 first (11 is independent); then 12 and 13; then 14 and 15; then 16; then 17. Then the two-reviewer `/drive-code-review`, fixes, manual QA, the PR. + +**Risk notes from grounding:** the SQL PSL interpreter resolves polymorphism after the storage hash is computed (dispatch 12 must move the discriminator facts into the build); every `contract.d.ts` may change if the field-types expression changes (dispatch 13); existing rows with extra keys fail the new check at apply (upgrade notes, dispatch 16); the snapshot rewrite in the upgrade script changes storage hashes, so the script must rewrite the snapshot directories and every manifest that names them, or leave old snapshots readable (dispatch 16 decides, from the `data-type-in-contract` fragment of the rc.14 to rc.15 release, which rewrote contracts before). From 5656914c51c1b2e455e0134e390e49508daf26ca Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 10:38:12 +0200 Subject: [PATCH 002/104] TML-3537: refuse only an actual value-set clash, not dotted enum names Signed-off-by: willbot Signed-off-by: Will Madden --- .../migration-statements/slices/remaining-nouns/plan-pr3.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/migration-statements/slices/remaining-nouns/plan-pr3.md b/projects/migration-statements/slices/remaining-nouns/plan-pr3.md index c175fb0f4ed0..4e6a2aed0961 100644 --- a/projects/migration-statements/slices/remaining-nouns/plan-pr3.md +++ b/projects/migration-statements/slices/remaining-nouns/plan-pr3.md @@ -8,7 +8,7 @@ This PR changes the contract shape. It adds no statements; PR 4 adds the value o - **The discriminator value set is SQL only.** On MongoDB the collection validator already pins each variant's discriminator value, so a change there already changes the storage hash. - **An enum-typed discriminator keeps the enum's value set and check.** The column has one `valueSet` slot, and the enum already restricts it; a variant value outside the enum is already refused. -- **`enumType()` refuses a name containing a dot**, so `.` value-set keys cannot collide with an enum's. +- **A clash between an enum's value set and a discriminator's is refused, and nothing else.** Enum value sets are keyed by the enum's name and a discriminator's by `.`. Only an enum named exactly like a discriminator's key (possible only from the TypeScript builder, since PSL names are identifiers) clashes; the build refuses that case, naming both and saying to rename the enum. Enum names and members are otherwise unrestricted (Will, 2026-10-23: do not limit enum names to avoid a rare clash). - **The Postgres value object check is one strict `jsonpath` predicate**, written so a NULL result fails (`IS TRUE`). The ADR's `?&` and `-` operators are not used. - **A value object that contains itself** is checked as the MongoDB validator does: fully down to where the cycle starts, any object below. - **Value object fields have no separate stored names**; they are stored under their own names in both families. @@ -24,7 +24,7 @@ As for PR 2: typecheck; lint and `test --coverage` in every touched package; `li | --- | --- | --- | | 10 | Contract shape and validators: a storage-plane value object entity in both families, a domain field referring to it by a storage coordinate (the first reference from domain to storage), validation across the two planes, the JSON schema, and the framework accessors | — | | 11 | A plan whose storage hash changes but that has no operations is written and applied: `migration plan` writes it, `db migrate` and `db update` move the marker, on Postgres, SQLite and MongoDB | — | -| 12 | Authoring and build in both families write value objects into storage (SQL PSL, Mongo PSL, Mongo TS builder, Prisma 6 reader), and SQL PSL writes the discriminator value set and its column reference before the storage hash is computed; `enumType()` refuses dotted names; the MongoDB validator derivation reads value objects from storage | 10 | +| 12 | Authoring and build in both families write value objects into storage (SQL PSL, Mongo PSL, Mongo TS builder, Prisma 6 reader), and SQL PSL writes the discriminator value set and its column reference before the storage hash is computed; a clash between an enum's value set and a discriminator's is refused; the MongoDB validator derivation reads value objects from storage | 10 | | 13 | Emit and types: `contract.d.ts` types read value objects from the storage plane, with a defaulted type parameter so contracts without value objects emit the same text | 10 | | 14 | Runtime and tools read value objects through the domain field's reference: the SQL and Mongo ORMs and query builders, the CLI statement resolver, `contract print`, `contract infer`, verification | 12, 13 | | 15 | Postgres derived checks: the discriminator membership check, and the value object `jsonpath` check for every shape SQL authoring produces, starting from a test per shape on PGlite; a new SQL fixture with nested and list members; contract print and infer recognise the new checks | 12 | From e4cf4d2eaedfd4f4cf9338c8d38a3e31cce5204d Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 10:59:07 +0200 Subject: [PATCH 003/104] TML-3537: list the storage entities that differ between two contracts Signed-off-by: willbot Signed-off-by: Will Madden --- .../framework-components/src/exports/ir.ts | 8 ++- .../framework-components/src/ir/storage.ts | 37 +++++++++++++ .../test/element-coordinates.test.ts | 54 +++++++++++++++++++ 3 files changed, 98 insertions(+), 1 deletion(-) diff --git a/packages/1-framework/1-core/framework-components/src/exports/ir.ts b/packages/1-framework/1-core/framework-components/src/exports/ir.ts index ff35c61b9a51..7f0a072a45d4 100644 --- a/packages/1-framework/1-core/framework-components/src/exports/ir.ts +++ b/packages/1-framework/1-core/framework-components/src/exports/ir.ts @@ -16,5 +16,11 @@ export { freezeNode, IRNodeBase } from '../ir/ir-node'; export type { Namespace } from '../ir/namespace'; export { NamespaceBase, UNBOUND_NAMESPACE_ID } from '../ir/namespace'; export type { EntityCoordinate, Storage } from '../ir/storage'; -export { coordinateKey, elementCoordinates, entityAt, isPlainRecord } from '../ir/storage'; +export { + changedElementCoordinates, + coordinateKey, + elementCoordinates, + entityAt, + isPlainRecord, +} from '../ir/storage'; export type { StorageType } from '../ir/storage-type'; diff --git a/packages/1-framework/1-core/framework-components/src/ir/storage.ts b/packages/1-framework/1-core/framework-components/src/ir/storage.ts index 0a5f1a43cad8..51408c1c533b 100644 --- a/packages/1-framework/1-core/framework-components/src/ir/storage.ts +++ b/packages/1-framework/1-core/framework-components/src/ir/storage.ts @@ -1,5 +1,6 @@ import { isPlainRecord } from '@internal/contract/is-plain-record'; import type { StorageBase } from '@internal/contract/types'; +import { canonicalStringify } from '@internal/utils/canonical-stringify'; import { blindCast } from '@internal/utils/casts'; import type { IRNode } from './ir-node'; import type { Namespace } from './namespace'; @@ -76,6 +77,42 @@ export function coordinateKey( .join('-'); } +/** + * The storage entities that differ between two `Storage`-shaped values: added, removed, or with + * different content. Content is compared as JSON, ignoring key order. Sorted by namespace id, + * entity kind and entity name. With no earlier storage, every entity counts as added. + */ +export function changedElementCoordinates( + from: Pick | null, + to: Pick, +): EntityCoordinate[] { + const contentAt = ( + storage: Pick | null, + coordinate: EntityCoordinate, + ): string | undefined => { + if (storage === null) return undefined; + const entity = entityAt(storage, coordinate); + return entity === undefined + ? undefined + : canonicalStringify(JSON.parse(JSON.stringify(entity))); + }; + const coordinates = new Map(); + for (const coordinate of [ + ...(from === null ? [] : elementCoordinates(from)), + ...elementCoordinates(to), + ]) { + coordinates.set(coordinateKey(coordinate), coordinate); + } + return [...coordinates.values()] + .filter((coordinate) => contentAt(from, coordinate) !== contentAt(to, coordinate)) + .sort( + (a, b) => + a.namespaceId.localeCompare(b.namespaceId, 'en') || + a.entityKind.localeCompare(b.entityKind, 'en') || + a.entityName.localeCompare(b.entityName, 'en'), + ); +} + /** * Looks up a single entity in a `Storage`-shaped value by its full coordinate. * Returns `undefined` if the namespace, entity kind, or entity name is absent. diff --git a/packages/1-framework/1-core/framework-components/test/element-coordinates.test.ts b/packages/1-framework/1-core/framework-components/test/element-coordinates.test.ts index 66e4748d4092..3af19220d939 100644 --- a/packages/1-framework/1-core/framework-components/test/element-coordinates.test.ts +++ b/packages/1-framework/1-core/framework-components/test/element-coordinates.test.ts @@ -2,6 +2,7 @@ import type { StorageBase } from '@internal/contract/types'; import { blindCast } from '@internal/utils/casts'; import { describe, expect, it } from 'vitest'; import { + changedElementCoordinates, coordinateKey, type EntityCoordinate, elementCoordinates, @@ -151,3 +152,56 @@ describe('entityAt', () => { ).toBeUndefined(); }); }); + +describe('changedElementCoordinates', () => { + function storageOf(namespaces: Record): Pick { + return blindCast, 'synthetic storage diff fixture'>({ + namespaces, + }); + } + + it('lists added, removed and changed entities, sorted, and leaves out equal ones', () => { + const from = storageOf({ + public: { + entries: { + table: { users: { columns: { id: 'int', name: 'text' } }, posts: { id: 1 } }, + valueSet: { Status: { values: ['open'] } }, + }, + }, + }); + const to = storageOf({ + public: { + entries: { + table: { + users: { columns: { name: 'text', id: 'int', note: 'text' } }, + posts: { id: 1 }, + }, + valueSet: { Role: { values: ['admin'] } }, + }, + }, + audit: { entries: { table: { events: {} } } }, + }); + + expect(changedElementCoordinates(from, to)).toEqual([ + { plane: 'storage', namespaceId: 'audit', entityKind: 'table', entityName: 'events' }, + { plane: 'storage', namespaceId: 'public', entityKind: 'table', entityName: 'users' }, + { plane: 'storage', namespaceId: 'public', entityKind: 'valueSet', entityName: 'Role' }, + { plane: 'storage', namespaceId: 'public', entityKind: 'valueSet', entityName: 'Status' }, + ]); + }); + + it('ignores key order inside an entity', () => { + const from = storageOf({ public: { entries: { table: { users: { a: 1, b: 2 } } } } }); + const to = storageOf({ public: { entries: { table: { users: { b: 2, a: 1 } } } } }); + + expect(changedElementCoordinates(from, to)).toEqual([]); + }); + + it('treats every entity as added when there is no earlier storage', () => { + const to = storageOf({ public: { entries: { table: { users: {} } } } }); + + expect(changedElementCoordinates(null, to)).toEqual([ + { plane: 'storage', namespaceId: 'public', entityKind: 'table', entityName: 'users' }, + ]); + }); +}); From a009fadc84c44a8b6052d53e25ccf2d00af64331 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 10:59:15 +0200 Subject: [PATCH 004/104] TML-3537: migration plan writes a migration with no operations when the storage changed When the storage hash changes but the planner plans nothing, migration plan writes the package with an empty ops.json and names the storage entities that changed (changedStorageEntities in --json). db migrate and db update move the marker. Only a first migration with no operations is still refused. Signed-off-by: willbot Signed-off-by: Will Madden --- .../subsystems/7. Migration System.md | 2 + docs/reference/error-reference.md | 2 +- packages/1-framework/3-tooling/cli/README.md | 2 +- .../control-api/operations/migration-plan.ts | 172 +++++++++-------- .../3-tooling/cli/src/orm/migration/plan.ts | 6 +- .../3-tooling/cli/src/utils/cli-errors.ts | 53 ++--- .../cli/test/orm/fixtures/offline-project.ts | 12 +- .../orm/migration-plan-statements.test.ts | 20 +- .../orm/migration-plan.no-operations.test.ts | 181 +++++++++++------- .../plans-with-no-operations.e2e.test.ts | 146 ++++++++++++++ ...plans-with-no-operations.mongo.e2e.test.ts | 163 ++++++++++++++++ ...lans-with-no-operations.sqlite.e2e.test.ts | 143 ++++++++++++++ .../contract-external-auth-users-indexed.ts | 23 +++ .../contract-external-auth-users.ts | 23 +++ 14 files changed, 742 insertions(+), 206 deletions(-) create mode 100644 test/integration/test/cli-journeys/plans-with-no-operations.e2e.test.ts create mode 100644 test/integration/test/cli-journeys/plans-with-no-operations.mongo.e2e.test.ts create mode 100644 test/integration/test/cli-journeys/plans-with-no-operations.sqlite.e2e.test.ts create mode 100644 test/integration/test/fixtures/cli/cli-e2e-test-app/fixtures/mongo-cli-journeys/contract-external-auth-users-indexed.ts create mode 100644 test/integration/test/fixtures/cli/cli-e2e-test-app/fixtures/mongo-cli-journeys/contract-external-auth-users.ts diff --git a/docs/architecture docs/subsystems/7. Migration System.md b/docs/architecture docs/subsystems/7. Migration System.md index e68852f7bef1..ec0bd1de541b 100644 --- a/docs/architecture docs/subsystems/7. Migration System.md +++ b/docs/architecture docs/subsystems/7. Migration System.md @@ -170,6 +170,8 @@ The from-contract always materialises by reading the content-addressed snapshot When `--to` is omitted, `to_contract` is the emitted contract; when `--to` is supplied, `to_contract` is the resolved destination. +**A plan with no operations.** Two different storage hashes are two different database states, so a migration between them is expected to do something. When the planner produces no operations for a changed storage hash anyway, `migration plan` still writes the package, with an empty `ops.json`, and says so: the summary, a warning in the human output, names each storage entity whose content differs between the two contracts, and `--json` lists them as `changedStorageEntities` (entity coordinates, from `changedElementCoordinates` in `@internal/framework-components/ir`). `db migrate` and `db update` apply such a migration by moving the marker to the new hash; every runner already does this for an edge between two different hashes. This holds for a baseline with no operations and for the delta after it. Only a greenfield plan (no earlier contract) with no operations is refused, with `MIGRATION.PLANNING_FAILED` of kind `nothingToCreate`, since the contract describes nothing to create. When the storage hash does not change, nothing is written, as before. + An auto-baseline dates the delta package at the current time and the baseline package one minute earlier, so the baseline sorts first. Folder names have minute resolution, so packages planned within the same minute sort by name. **Data loss.** Before it writes either package, including the baseline of an auto-baseline, `migration plan` asks what each operation that would lose data means (see [The questions](#statements)). It does not ask about access: a written package is reviewed before it is applied. diff --git a/docs/reference/error-reference.md b/docs/reference/error-reference.md index b54b033cc628..5befb37a5513 100644 --- a/docs/reference/error-reference.md +++ b/docs/reference/error-reference.md @@ -1718,7 +1718,7 @@ An apply command (`db migrate`/`db update`) cannot find a path through the on-di Migration planning (typically during `db init`/`db update`) failed because of conflicts, e.g. the live database already contains objects that clash with the plan. The envelope aggregates each conflict's summary and suggested fix. Payload: `conflicts`; a conflict's `location` and `refusedStatement` leave out `namespaceId` for the unbound namespace, whose id is internal. A conflict of kind `statementRefused` refuses a `--rename` statement the planner cannot carry out, and carries that statement (`refusedStatement`, in namespace, model and field names): moving a model into the unbound namespace, which has no fixed schema; renaming or moving a table, or renaming a column, whose control policy is not `managed`; renaming a namespace with a table whose control policy is not `managed`, a namespace the starting schema does not have or whose new name it already has, Postgres's `public`, which Prisma owns and Postgres puts on every search path (the message says to move each model with `--rename :.` instead), or a schema another contract space also declares (the message names the space); a model with no table; a table or column the starting schema does not have, or already has under the new name, or a table the destination namespace already has; a field that has a column on one side of the statement only; a field rename on a model whose table changes when no statement renames the table (the message gives the steps: change the contract so only the table name changes and emit it, create a migration with `prisma migration new --from `, add `...this.renameTable(...)` to its operations and run its `migration.ts` with `node`, then emit the final contract and plan the field rename with `--from` that migration; with `db update`: change the contract so only the table name changes and emit it, rename the table in the database yourself with the SQL the message gives, check that `db update --dry-run` plans no drop of a table or column and run `db update`, then emit the final contract and run `db update` with the field statement); or a rename that produces an operation the command's operation policy does not allow. On MongoDB the planner refuses: renaming a namespace; renaming `_id`; renaming a variant whose discriminator value changes (the message says to keep the value with `@@base(, "")`); a field whose stored name contains `.` or starts with `$` (renaming from such a name says to rename it in the database yourself; renaming to one says to choose another stored name); renaming a time-series collection (the message says to create the new collection with the same time-series options, copy the documents and leave out the statement); renaming a field of a time-series collection, since MongoDB updates its documents only through the metaField (the message says to keep the field's stored name); removing a field of a time-series collection, a conflict of kind `fieldRemovalRefused` (the message says to keep the field in the contract, optional if new documents leave it out); renaming or removing a field of one root model in a collection that stores several root models without a discriminator, when another of them stores the same field and does not rename it to the same new name in the same plan (removal is a `fieldRemovalRefused` conflict; the message says to move the models to separate collections or carry out the change in a `dataTransform` operation with a filter); a collection the schema being planned from does not have, which includes a view; a collection rename onto a name the schema already has, or onto a name MongoDB rejects (empty, containing `$` or a null character, or starting with `system.`); a field stored in documents on one side of the statement only, or an embedded relation; a collection whose control policy is not `managed`; and a field rename whose collection changes with no statement renaming the model (when the model keeps its name, the message gives the steps: change only `@@map` and emit; with `db update`, rename the collection in `mongosh` and run `db update`; with `migration plan`, plan with `--delete `, make `renameCollection(...)` the first operation in its `migration.ts` and delete the drop, and the create if there is one, run its `migration.ts` with `node`, and apply it with `db migrate`; then rename the field). `db init` applies only additive operations, so when a conflict is an operation its policy refused (the conflict carries `refusedOperationClass`), such as adding a validator to a Mongo collection that already holds documents, the error's next action depends on the refused classes. When `db update` allows all of them (`widening` and `destructive`), the next action is `db update`, which applies them, asking what each operation that would lose data means. When one is `data`, which `db update` does not apply, the next action is `migration plan`, to plan a migration that `db migrate` then applies. -`migration plan` raises it when the planner produces no operations. The conflict's kind names the case, and its `why` is the next action. With an earlier contract (`noDatabaseChange`), the contract changed in a way the database does not see, for example after a Prisma upgrade that changes how `contract.json` records values. If you deploy with migrations, run `prisma migration new --from `, with the earlier contract's hash from the message, to write a migration with no operations, then `prisma db migrate`. If you manage the database with `db init` or `db update`, run `prisma db sign` on each database instead. With no earlier contract (`nothingToCreate`), the contract describes nothing to create; if the database needs something the contract does not describe, run `prisma migration new` and write the operations in `migration.ts` by hand. `migration new` writes a migration only to the emitted contract, so with `--to` the message says to emit the target first, and for a database managed with `db update` it names `prisma db sign `. When the migrations directory is empty and the `db` ref points at a contract that describes nothing to create (`nothingToBaseline`), there is no baseline migration to plan, and no command writes one with no operations yet; keep using `db update`. +`migration plan` raises it, as a conflict of kind `nothingToCreate`, when the planner produces no operations for a first migration: the contract describes nothing to create. If the database needs something the contract does not describe, run `prisma migration new` and write the operations in `migration.ts` by hand. `migration new` writes a migration only to the emitted contract, so with `--to` the message says to emit the target first. When there is an earlier contract, a plan with no operations is not refused: `migration plan` writes the migration with no operations and names the storage entities that changed, and `db migrate` applies it by moving the marker. ### MIGRATION.PLAN_NOT_ARRAY diff --git a/packages/1-framework/3-tooling/cli/README.md b/packages/1-framework/3-tooling/cli/README.md index 630ad65e7db5..58fe4ea3fba0 100644 --- a/packages/1-framework/3-tooling/cli/README.md +++ b/packages/1-framework/3-tooling/cli/README.md @@ -1093,7 +1093,7 @@ prisma migration plan [--config ] [--name ] [--from ] [--t - `migrations//ops.json` — planned operations (empty list `[]` if placeholders blocked the planner) - `migrations/snapshots//contract.{json,d.ts}` — bookend contracts, written write-if-absent, keyed by each contract's storage hash (one entry for `from` when applicable, one for `to`) -**Rename statements:** each `--rename` names the old and the new name in contract vocabulary, never a table or a column. Each side is one of `Model`, `namespace.Model`, `Model.field` or `namespace.Model.field`; both sides name a model, or both name a field of the same model. Names match exactly, including case. A name without a namespace resolves when exactly one namespace declares the model. A field's model is named as the destination contract names it, so a model rename followed by a field rename on it is `--rename Profile:User --rename User.name:User.fullName`. The old name must exist in the starting contract and not in the destination; the new name must exist in the destination and not in the starting contract. Statements resolve before anything is written; a statement that is malformed fails with `MIGRATION.STATEMENT_INVALID`, and one that does not resolve fails with `MIGRATION.STATEMENT_UNRESOLVED`, naming what it searched and what it found. Each error's next step is the statement to type instead: the accepted forms, the statement written the right way round, the statements a model-and-field mix may have meant, or the order that works. A statement that has already been applied (the origin already has the new name and not the old one) says to leave it out. Statements cannot swap two names in one plan, and the error gives the three plans that do, through a temporary name. A plan from an empty database (`--from @empty`, or greenfield) has no models to rename, so every statement is unresolved. A statement whose namespace changes, such as `--rename public.Invoice:billing.Invoice`, moves the table to the new schema on Postgres, creating the schema first when the database lacks it; a move into the unbound namespace is refused. One name on each side of a namespace of the origin contract, `--rename billing:finance`, renames the namespace on Postgres; a statement that reads both as a namespace and as a model rename is refused, and `public` and a schema another contract space declares are not renamed. Namespace renames run before the other statements, whatever order they are given in. When a name is both a namespace and a model, name the model with its namespace, or give both statements. Renaming value objects is not supported in this release. The planner renames the table or column, and the constraints and indexes named after it, ahead of the rest of the plan. On Postgres, a check, a row-level-security policy, or an expression or partial index whose SQL names a renamed column is dropped and created again, and adding a check back reads every row of the table under an exclusive lock, so on a large table such a rename takes time; `migration.ts` contains the same `...this.renameTable(...)` and `...this.renameColumn(...)` calls you could have written by hand. On MongoDB a field is named by its name in the contract, as on SQL (`id @map("_id")` is `User.id`), and a rename that keeps the stored name plans nothing. A model rename renames the collection, which keeps its documents, indexes and validator (`renameCollection`); a field rename renames the field in every document that holds it (`renameField`), and only in that variant's documents for a variant's field; `migration.ts` calls these factories. The statements are listed under `Statements applied` after the operations, each with its number of operations, and as `appliedStatements` in `--json` output, each with its `statement`, `description` and `operationIndexes`, the positions in `operations` of the operations it accounts for. A coordinate in a contract with no namespaces has no `namespaceId` in the JSON. A statement whose storage does not change, such as a model whose table name is kept with `@@map`, is listed with no operations. When the storage did not change at all and no statement needs an operation, no migration package is written and the output says so; when the storage changed but the planner planned nothing, planning fails with `MIGRATION.PLANNING_FAILED`, statements or not. A statement the planner cannot carry out, for example on a table whose control policy is not `managed`, fails planning with `MIGRATION.PLANNING_FAILED`, and the refused statement is carried in the error's conflicts. +**Rename statements:** each `--rename` names the old and the new name in contract vocabulary, never a table or a column. Each side is one of `Model`, `namespace.Model`, `Model.field` or `namespace.Model.field`; both sides name a model, or both name a field of the same model. Names match exactly, including case. A name without a namespace resolves when exactly one namespace declares the model. A field's model is named as the destination contract names it, so a model rename followed by a field rename on it is `--rename Profile:User --rename User.name:User.fullName`. The old name must exist in the starting contract and not in the destination; the new name must exist in the destination and not in the starting contract. Statements resolve before anything is written; a statement that is malformed fails with `MIGRATION.STATEMENT_INVALID`, and one that does not resolve fails with `MIGRATION.STATEMENT_UNRESOLVED`, naming what it searched and what it found. Each error's next step is the statement to type instead: the accepted forms, the statement written the right way round, the statements a model-and-field mix may have meant, or the order that works. A statement that has already been applied (the origin already has the new name and not the old one) says to leave it out. Statements cannot swap two names in one plan, and the error gives the three plans that do, through a temporary name. A plan from an empty database (`--from @empty`, or greenfield) has no models to rename, so every statement is unresolved. A statement whose namespace changes, such as `--rename public.Invoice:billing.Invoice`, moves the table to the new schema on Postgres, creating the schema first when the database lacks it; a move into the unbound namespace is refused. One name on each side of a namespace of the origin contract, `--rename billing:finance`, renames the namespace on Postgres; a statement that reads both as a namespace and as a model rename is refused, and `public` and a schema another contract space declares are not renamed. Namespace renames run before the other statements, whatever order they are given in. When a name is both a namespace and a model, name the model with its namespace, or give both statements. Renaming value objects is not supported in this release. The planner renames the table or column, and the constraints and indexes named after it, ahead of the rest of the plan. On Postgres, a check, a row-level-security policy, or an expression or partial index whose SQL names a renamed column is dropped and created again, and adding a check back reads every row of the table under an exclusive lock, so on a large table such a rename takes time; `migration.ts` contains the same `...this.renameTable(...)` and `...this.renameColumn(...)` calls you could have written by hand. On MongoDB a field is named by its name in the contract, as on SQL (`id @map("_id")` is `User.id`), and a rename that keeps the stored name plans nothing. A model rename renames the collection, which keeps its documents, indexes and validator (`renameCollection`); a field rename renames the field in every document that holds it (`renameField`), and only in that variant's documents for a variant's field; `migration.ts` calls these factories. The statements are listed under `Statements applied` after the operations, each with its number of operations, and as `appliedStatements` in `--json` output, each with its `statement`, `description` and `operationIndexes`, the positions in `operations` of the operations it accounts for. A coordinate in a contract with no namespaces has no `namespaceId` in the JSON. A statement whose storage does not change, such as a model whose table name is kept with `@@map`, is listed with no operations. When the storage did not change at all and no statement needs an operation, no migration package is written and the output says so; when the storage changed but the planner planned nothing, the migration is written with no operations, statements or not, and the output names the storage entities that changed (`changedStorageEntities` in `--json` output). A statement the planner cannot carry out, for example on a table whose control policy is not `managed`, fails planning with `MIGRATION.PLANNING_FAILED`, and the refused statement is carried in the error's conflicts. **Data loss:** a plan that would lose data is written only once you say what each such operation means. The command asks one question per model, field or storage name whose data an operation would lose (dropping a table or a column, or a type change that can change values), before it writes anything, including the baseline package of an auto-baseline. Each question is answered with a statement: diff --git a/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts b/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts index ea56b06cbbab..83ddb9b61d7f 100644 --- a/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts +++ b/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts @@ -16,6 +16,11 @@ import { type ResolvedMigrationStatement, type SchemaOwnership, } from '@internal/framework-components/control'; +import { + changedElementCoordinates, + type EntityCoordinate, + UNBOUND_NAMESPACE_ID, +} from '@internal/framework-components/ir'; import { snapshotsImportPathFrom, writeContractSnapshot, @@ -39,7 +44,6 @@ import { errorPlanProducedNoOperations, errorTargetMigrationNotSupported, type PlanDestination, - type PlanLegOrigin, } from '../../utils/cli-errors'; import { getTargetMigrations, @@ -147,38 +151,28 @@ const EMPTY_ORIGIN: ContractWithDomain = { domain: { namespaces: {} } }; type TargetMigrationsApi = NonNullable>; -/** A plan origin whose earlier contract the planner diffs against. */ -type PlanLegOriginWithContract = - | Exclude - | (Extract & { readonly contract: Contract }); - /** - * Why a planner leg failed: the planner refused (a conflict, such as a refused statement), or it - * produced no operations for a changed contract. + * Where a `migration plan` leg plans from: an empty database, an empty database toward the `db` + * ref's contract (the automatic baseline), or an earlier contract the planner diffs against. */ -interface PlannerLegFailure { - readonly reason: 'refused' | 'noOperations'; - readonly error: CliStructuredError; -} +type PlanLegOrigin = + | { readonly kind: 'empty' } + | { readonly kind: 'baseline' } + | { readonly kind: 'contract'; readonly contract: Contract }; async function runPlannerLeg( planner: ReturnType, migrations: TargetMigrationsApi, frameworkComponents: ReturnType, contract: Contract, - origin: PlanLegOriginWithContract, + origin: PlanLegOrigin, destination: PlanDestination, statements: readonly ResolvedMigrationStatement[], - /** - * True when the storage did not change and statements were given: a plan of - * no operations then means the statements need none, not a planning failure. - */ - noOperationsExpected: boolean, spaceId: string, ownership: SchemaOwnership, snapshotsImportPath: string, resolveImportSpecifier: ImportSpecifierResolver, -): Promise> { +): Promise> { const fromContract = origin.kind === 'contract' ? origin.contract : null; const fromSchema = migrations.contractToSchema(fromContract, frameworkComponents); const plannerResult = planner.plan({ @@ -199,12 +193,11 @@ async function runPlannerLeg( snapshotsImportPath, }); if (plannerResult.kind === 'failure') { - return notOk({ - reason: 'refused', - error: errorMigrationPlanningFailed({ + return notOk( + errorMigrationPlanningFailed({ conflicts: castAs(plannerResult.conflicts), }), - }); + ); } // The operation at each position of the plan, or `undefined` where it is an unfilled placeholder. @@ -212,11 +205,8 @@ async function runPlannerLeg( let hasPlaceholders = false; try { resolved = await Promise.all(plannerResult.plan.operations); - if (resolved.length === 0 && !noOperationsExpected) { - return notOk({ - reason: 'noOperations', - error: errorPlanProducedNoOperations(origin, destination), - }); + if (resolved.length === 0 && origin.kind === 'empty') { + return notOk(errorPlanProducedNoOperations(destination)); } } catch (e) { if (CliStructuredError.is(e) && e.code === 'MIGRATION.UNFILLED_PLACEHOLDER') { @@ -450,6 +440,11 @@ export interface MigrationPlanResult { readonly appliedStatements: readonly AppliedStatementReport[]; /** The new required fields whose existing rows the plan gives a temporary value. */ readonly temporaryValues: readonly TemporaryValueReport[]; + /** + * The storage entities that differ between the origin and the destination, when the storage + * changed but the written migration has no operations. + */ + readonly changedStorageEntities?: readonly EntityCoordinate[]; readonly timings: { readonly total: number; }; @@ -620,14 +615,10 @@ async function executeMigrationPlanCommandInner( break; } const resolvedFrom = resolutionResult.value; - const fromOrigin: PlanLegOriginWithContract = + const fromOrigin: PlanLegOrigin = resolvedFrom.kind === 'greenfield' ? { kind: 'empty' } - : { - kind: 'contract', - hash: resolvedFrom.fromHash, - contract: resolvedFrom.fromContract, - }; + : { kind: 'contract', contract: resolvedFrom.fromContract }; // `--to ` swaps the planner destination to an arbitrary resolved // contract (e.g. an ancestor / rollback target). The from-side resolution @@ -777,7 +768,7 @@ async function executeMigrationPlanCommandInner( delta: PlannerSuccess | undefined, planDelta: ( resolved: readonly ResolvedMigrationStatement[], - ) => Promise>, + ) => Promise>, ) => answerPlannedDataLoss({ baseline, @@ -795,8 +786,7 @@ async function executeMigrationPlanCommandInner( destination: toContract, }); if (!resolved.ok) return resolved; - const leg = await planDelta(resolved.value); - return leg.ok ? leg : notOk(leg.failure.error); + return planDelta(resolved.value); }, }); @@ -813,17 +803,16 @@ async function executeMigrationPlanCommandInner( migrations, frameworkComponents, fromContract, - { kind: 'baseline', hash: fromHash }, + { kind: 'baseline' }, { hash: fromHash, isEmitted: false }, [], - false, aggregate.app.spaceId, aggregate, snapshotsImportPathFrom(baselinePackageDir, migrationsDir), resolveImportSpecifier, ); if (!baselineLeg.ok) { - return notOk(baselineLeg.failure.error); + return baselineLeg; } const planBaselineDelta = (resolved: readonly ResolvedMigrationStatement[]) => @@ -835,7 +824,6 @@ async function executeMigrationPlanCommandInner( fromOrigin, planDestination, resolved, - false, aggregate.app.spaceId, aggregate, snapshotsImportPathFrom(deltaPackageDir, migrationsDir), @@ -843,14 +831,8 @@ async function executeMigrationPlanCommandInner( ); const plannedDelta = fromHash === toStorageHash ? undefined : await planBaselineDelta(statements); - // A refused delta writes nothing. A delta with no operations still writes the baseline, so - // the `migration new --from` its error advises has the history to start from. - if ( - plannedDelta !== undefined && - !plannedDelta.ok && - plannedDelta.failure.reason === 'refused' - ) { - return notOk(plannedDelta.failure.error); + if (plannedDelta !== undefined && !plannedDelta.ok) { + return plannedDelta; } const answered = await answerDataLoss( @@ -862,9 +844,7 @@ async function executeMigrationPlanCommandInner( return notOk(answered.failure); } const deltaLeg = - plannedDelta === undefined || !plannedDelta.ok || answered.value.delta === undefined - ? plannedDelta - : ok(answered.value.delta); + plannedDelta === undefined ? undefined : (answered.value.delta ?? plannedDelta.value); const deletes = answered.value.deletes; await writePlannedMigrationPackage( @@ -875,10 +855,6 @@ async function executeMigrationPlanCommandInner( baselineLeg.value, ); - if (deltaLeg !== undefined && !deltaLeg.ok) { - return notOk(deltaLeg.failure.error); - } - if (deltaLeg === undefined) { const statementsWithoutOperations = reportAppliedStatements( statements.map((statement) => ({ statement, operationIndexes: [] })), @@ -939,13 +915,13 @@ async function executeMigrationPlanCommandInner( fromHash, toStorageHash, deltaTimestamp, - deltaLeg.value, + deltaLeg, ); await writeDestinationSnapshot(toStorageHash); const baselineOps = baselineLeg.value.hasPlaceholders ? [] : baselineLeg.value.plannedOps; - const deltaOps = deltaLeg.value.hasPlaceholders ? [] : deltaLeg.value.plannedOps; - if (baselineLeg.value.hasPlaceholders || deltaLeg.value.hasPlaceholders) { + const deltaOps = deltaLeg.hasPlaceholders ? [] : deltaLeg.plannedOps; + if (baselineLeg.value.hasPlaceholders || deltaLeg.hasPlaceholders) { const baselineDir = relative(cwd, baselinePackageDir); const deltaDir = relative(cwd, deltaPackageDir); const result: MigrationPlanResult = { @@ -955,17 +931,17 @@ async function executeMigrationPlanCommandInner( to: toStorageHash, dir: deltaDir, baselineDir, - operations: operationSummaries(deltaLeg.value.plannedOps), + operations: operationSummaries(deltaLeg.plannedOps), baselineOperations: operationSummaries(baselineLeg.value.plannedOps), emittedExtensionDirs, ...(warnings.length > 0 ? { warnings } : {}), pendingPlaceholders: true, placeholderDirs: [ ...(baselineLeg.value.hasPlaceholders ? [relative(cwd, baselinePackageDir)] : []), - ...(deltaLeg.value.hasPlaceholders ? [relative(cwd, deltaPackageDir)] : []), + ...(deltaLeg.hasPlaceholders ? [relative(cwd, deltaPackageDir)] : []), ], - appliedStatements: [...deltaLeg.value.appliedStatements, ...deletes], - temporaryValues: deltaLeg.value.temporaryValues, + appliedStatements: [...deltaLeg.appliedStatements, ...deletes], + temporaryValues: deltaLeg.temporaryValues, summary: placeholderSummary( 'baseline + migration', [baselinePackageDir, deltaPackageDir], @@ -1001,13 +977,14 @@ async function executeMigrationPlanCommandInner( emittedExtensionDirs, ...(preview !== undefined ? { preview } : {}), ...(warnings.length > 0 ? { warnings } : {}), - appliedStatements: [...deltaLeg.value.appliedStatements, ...deletes], - temporaryValues: deltaLeg.value.temporaryValues, + appliedStatements: [...deltaLeg.appliedStatements, ...deletes], + temporaryValues: deltaLeg.temporaryValues, summary: buildAutoBaselinePlanSummary( baselineOps.length, deltaOps.length, emittedExtensionDirs.length, ), + ...(deltaOps.length === 0 ? noOperationsReport(fromContract, toContract) : {}), timings: { total: Date.now() - startTime }, }; return ok(result); @@ -1027,7 +1004,6 @@ async function executeMigrationPlanCommandInner( fromOrigin, planDestination, resolved, - resolved.length > 0 && fromHash === toStorageHash, aggregate.app.spaceId, aggregate, snapshotsImportPathFrom(packageDir, migrationsDir), @@ -1035,16 +1011,20 @@ async function executeMigrationPlanCommandInner( ); const plannedDelta = await planDelta(statements); if (!plannedDelta.ok) { - return notOk(plannedDelta.failure.error); + return plannedDelta; } const answered = await answerDataLoss(undefined, plannedDelta.value, planDelta); if (!answered.ok) { return notOk(answered.failure); } - const deltaLeg = ok(answered.value.delta ?? plannedDelta.value); + const deltaLeg = answered.value.delta ?? plannedDelta.value; const deletes = answered.value.deletes; - if (!deltaLeg.value.hasPlaceholders && deltaLeg.value.plannedOps.length === 0) { + if ( + !deltaLeg.hasPlaceholders && + deltaLeg.plannedOps.length === 0 && + fromHash === toStorageHash + ) { const result: MigrationPlanResult = { ok: true, noOp: true, @@ -1053,24 +1033,18 @@ async function executeMigrationPlanCommandInner( operations: [], emittedExtensionDirs, ...(warnings.length > 0 ? { warnings } : {}), - appliedStatements: [...deltaLeg.value.appliedStatements, ...deletes], - temporaryValues: deltaLeg.value.temporaryValues, + appliedStatements: [...deltaLeg.appliedStatements, ...deletes], + temporaryValues: deltaLeg.temporaryValues, summary: 'No changes to plan: the statements need no operations', timings: { total: Date.now() - startTime }, }; return ok(result); } - await writePlannedMigrationPackage( - packageDir, - fromHash, - toStorageHash, - timestamp, - deltaLeg.value, - ); + await writePlannedMigrationPackage(packageDir, fromHash, toStorageHash, timestamp, deltaLeg); await writeDestinationSnapshot(toStorageHash); - if (deltaLeg.value.hasPlaceholders) { + if (deltaLeg.hasPlaceholders) { const dir = relative(cwd, packageDir); const result: MigrationPlanResult = { ok: true, @@ -1078,21 +1052,21 @@ async function executeMigrationPlanCommandInner( from: fromHash, to: toStorageHash, dir, - operations: operationSummaries(deltaLeg.value.plannedOps), + operations: operationSummaries(deltaLeg.plannedOps), emittedExtensionDirs, ...(warnings.length > 0 ? { warnings } : {}), pendingPlaceholders: true, placeholderDirs: [relative(cwd, packageDir)], ...(fromDefaulted ? { fromDefaulted } : {}), - appliedStatements: [...deltaLeg.value.appliedStatements, ...deletes], - temporaryValues: deltaLeg.value.temporaryValues, + appliedStatements: [...deltaLeg.appliedStatements, ...deletes], + temporaryValues: deltaLeg.temporaryValues, summary: placeholderSummary('migration', [packageDir], invocationOf(options)), timings: { total: Date.now() - startTime }, }; return ok(result); } - const plannedOps = deltaLeg.value.plannedOps; + const plannedOps = deltaLeg.plannedOps; const preview = hasOperationPreview(familyInstance) ? familyInstance.toOperationPreview(plannedOps) : undefined; @@ -1111,9 +1085,10 @@ async function executeMigrationPlanCommandInner( ...(preview !== undefined ? { preview } : {}), ...(fromDefaulted ? { fromDefaulted } : {}), ...(warnings.length > 0 ? { warnings } : {}), - appliedStatements: [...deltaLeg.value.appliedStatements, ...deletes], - temporaryValues: deltaLeg.value.temporaryValues, + appliedStatements: [...deltaLeg.appliedStatements, ...deletes], + temporaryValues: deltaLeg.temporaryValues, summary: buildPlanSummary(plannedOps.length, emittedExtensionDirs.length), + ...(plannedOps.length === 0 ? noOperationsReport(fromContract, toContract) : {}), timings: { total: Date.now() - startTime }, }; return ok(result); @@ -1124,6 +1099,33 @@ async function executeMigrationPlanCommandInner( } } +/** + * What a migration with no operations reports: the storage entities that changed, and a summary + * that says the database needs no operation for them. + */ +function noOperationsReport( + fromContract: Contract | null, + toContract: Contract, +): Pick { + const changed = changedElementCoordinates(fromContract?.storage ?? null, toContract.storage); + const what = + changed.length === 0 + ? 'No storage entity changed; the change is elsewhere in the storage.' + : `What changed: ${changed.map(storageEntityText).join(', ')}.`; + return { + changedStorageEntities: changed, + summary: `The storage the contract describes changed, but the database needs no operation for it, so the migration has none. ${what}`, + }; +} + +function storageEntityText(coordinate: EntityCoordinate): string { + const name = + coordinate.namespaceId === UNBOUND_NAMESPACE_ID + ? coordinate.entityName + : `${coordinate.namespaceId}.${coordinate.entityName}`; + return `${coordinate.entityKind} "${name}"`; +} + /** * Compose the success-line summary so the cross-space side effect * (extension-space migration packages materialised on disk during diff --git a/packages/1-framework/3-tooling/cli/src/orm/migration/plan.ts b/packages/1-framework/3-tooling/cli/src/orm/migration/plan.ts index f1da0a4d76ef..b3a3406a2583 100644 --- a/packages/1-framework/3-tooling/cli/src/orm/migration/plan.ts +++ b/packages/1-framework/3-tooling/cli/src/orm/migration/plan.ts @@ -199,7 +199,11 @@ function planBlocks(result: MigrationPlanResult, migrationsRelative: string): re } return [ ...warningBlocks(result), - { kind: 'summary', status: 'ok', text: result.summary }, + { + kind: 'summary', + status: result.changedStorageEntities === undefined ? 'ok' : 'warn', + text: result.summary, + }, ...originNoticeBlocks(result), ...operationBlocks(result), ...temporaryValueBlocks(result), diff --git a/packages/1-framework/3-tooling/cli/src/utils/cli-errors.ts b/packages/1-framework/3-tooling/cli/src/utils/cli-errors.ts index be4c67e4e151..72ef9d1b3cc8 100644 --- a/packages/1-framework/3-tooling/cli/src/utils/cli-errors.ts +++ b/packages/1-framework/3-tooling/cli/src/utils/cli-errors.ts @@ -6,7 +6,6 @@ export type { CliErrorConflict, CliErrorEnvelope } from '@internal/errors/control'; import { - type CliErrorConflict, CliStructuredError, errorConfigFileNotFound, errorConfigValidation, @@ -858,59 +857,29 @@ export function errorStatementOriginUnknown(origin: { ); } -/** Where a `migration plan` leg plans from: an empty database, an empty database toward the `db` ref's contract (the automatic baseline), or an earlier contract. */ -export type PlanLegOrigin = - | { readonly kind: 'empty' } - | { readonly kind: 'baseline'; readonly hash: string } - | { readonly kind: 'contract'; readonly hash: string }; - /** Where a `migration plan` leg plans to; only the emitted contract can be the destination of `migration new`. */ export interface PlanDestination { readonly hash: string; readonly isEmitted: boolean; } -const REPORT_MISSED_CHANGE = - 'If you expected the database to change, migration plan missed it: report it with the output of `prisma migration plan --json`.'; - -function noOperationsConflict( - origin: PlanLegOrigin, - destination: PlanDestination, -): CliErrorConflict { - switch (origin.kind) { - case 'empty': - return { +/** + * `migration plan` refuses a first migration whose planner produced no operations: the contract + * describes nothing to create. + */ +export function errorPlanProducedNoOperations(destination: PlanDestination): CliStructuredError { + return errorMigrationPlanningFailed({ + conflicts: [ + { kind: 'nothingToCreate', summary: 'This contract describes nothing migration plan can create, so there is no first migration to plan.', why: destination.isEmitted ? 'If the database needs something the contract does not describe, run `prisma migration new`, add its operations to the new `migration.ts`, then run that `migration.ts` with `node` to write `ops.json`.' : '`prisma migration new` writes a migration only to the emitted contract. If the database needs something this contract does not describe, emit it, run `prisma migration new`, add its operations to the new `migration.ts`, then run that `migration.ts` with `node` to write `ops.json`.', - }; - case 'baseline': - return { - kind: 'nothingToBaseline', - summary: `The migrations directory is empty, so migration plan starts the migration history with a baseline migration to the contract the db ref points at, ${origin.hash}. That contract describes nothing migration plan can create, so there is no baseline to plan.`, - why: 'No command writes a baseline migration with no operations. If you manage the database with `prisma db init` or `prisma db update`, keep using `prisma db update`. No command can start a migration history from this database yet; report it with the output of `prisma migration plan --json`.', - }; - case 'contract': - return { - kind: 'noDatabaseChange', - summary: - 'The contract changed, but migration plan found nothing to change in the database. That is expected after a Prisma upgrade that changes how contract.json records values, after you switch a field to another codec of the same type, or after you change a control policy.', - why: destination.isEmitted - ? `If you deploy with migrations, run \`prisma migration new --from ${origin.hash}\` to write a migration with no operations, then \`prisma db migrate\`. If you manage the database with \`prisma db init\` or \`prisma db update\`, run \`prisma db sign\` on each database instead. ${REPORT_MISSED_CHANGE}` - : `\`prisma migration new\` writes a migration only to the emitted contract; to write one to this target, emit it first. If you manage the database with \`prisma db init\` or \`prisma db update\`, run \`prisma db sign ${destination.hash}\` on each database. ${REPORT_MISSED_CHANGE}`, - }; - } -} - -/** `migration plan` refuses a leg whose planner produced no operations, and says how to proceed from where it planned. */ -export function errorPlanProducedNoOperations( - origin: PlanLegOrigin, - destination: PlanDestination, -): CliStructuredError { - return errorMigrationPlanningFailed({ conflicts: [noOperationsConflict(origin, destination)] }); + }, + ], + }); } export function errorMarkerMismatch( diff --git a/packages/1-framework/3-tooling/cli/test/orm/fixtures/offline-project.ts b/packages/1-framework/3-tooling/cli/test/orm/fixtures/offline-project.ts index 1d0a6bb78cb8..0fe2ae90e5ec 100644 --- a/packages/1-framework/3-tooling/cli/test/orm/fixtures/offline-project.ts +++ b/packages/1-framework/3-tooling/cli/test/orm/fixtures/offline-project.ts @@ -71,12 +71,16 @@ function modelEntry(model: FixtureModel): readonly [string, Record>; + export function contractJson( storageHash: string, models?: readonly FixtureModel[], + storageNamespaces: FixtureStorageNamespaces = {}, ): Record { return { - storage: { storageHash, namespaces: {} }, + storage: { storageHash, namespaces: storageNamespaces }, schemaVersion: '1.0.0', target: 'postgres', targetFamily: 'sql', @@ -98,6 +102,7 @@ export function contractJson( export async function createOfflineProject(options: { readonly storageHash: string; readonly models?: readonly FixtureModel[]; + readonly storageNamespaces?: FixtureStorageNamespaces; }): Promise { const dir = createTestProjectDir('orm-offline'); created.push(dir); @@ -105,7 +110,7 @@ export async function createOfflineProject(options: { await mkdir(join(dir, 'output'), { recursive: true }); await writeFile( contractPath, - JSON.stringify(contractJson(options.storageHash, options.models)), + JSON.stringify(contractJson(options.storageHash, options.models, options.storageNamespaces)), 'utf-8', ); await writeFile( @@ -173,9 +178,10 @@ export async function seedContractSnapshot(options: { readonly migrationsDir: string; readonly storageHash: string; readonly models?: readonly FixtureModel[]; + readonly storageNamespaces?: FixtureStorageNamespaces; }): Promise { await writeContractSnapshot(options.migrationsDir, options.storageHash, { - contractJson: contractJson(options.storageHash, options.models), + contractJson: contractJson(options.storageHash, options.models, options.storageNamespaces), contractDts: 'export type Contract = never;\n', }); } diff --git a/packages/1-framework/3-tooling/cli/test/orm/migration-plan-statements.test.ts b/packages/1-framework/3-tooling/cli/test/orm/migration-plan-statements.test.ts index 466189f279c9..e503f11d7854 100644 --- a/packages/1-framework/3-tooling/cli/test/orm/migration-plan-statements.test.ts +++ b/packages/1-framework/3-tooling/cli/test/orm/migration-plan-statements.test.ts @@ -211,18 +211,22 @@ describe('migration plan --rename', () => { expect(await plannedDirs(project)).toEqual(['20260101T0000_initial']); }); - it('fails planning when the storage changed but the planner planned nothing, statements or not', async () => { + it('writes a migration with no operations when the storage changed but the planner planned nothing', async () => { const project = await renamingProject(); const run = await harness(project, { operations: [] }).run( - ['migration', 'plan', '--rename', 'Profile:User', '--json'], + ['migration', 'plan', '--name', 'delta', '--rename', 'Profile:User', '--json'], { cwd: project.dir }, ); - expect(run.json.at(-1)).toMatchObject({ - kind: 'result', - envelope: { ok: false, error: { code: 'MIGRATION.PLANNING_FAILED' } }, + expect(run.presented?.data).toMatchObject({ + noOp: false, + operations: [], + appliedStatements: [{ statement: profileToUser, operationIndexes: [] }], }); - expect(await plannedDirs(project)).toEqual(['20260101T0000_initial']); + expect((await plannedDirs(project)).map((dir) => dir.replace(/^\d{8}T\d{4}_/, ''))).toEqual([ + 'initial', + 'delta', + ]); }); it('passes the statements to the delta of an auto-baseline plan only', async () => { @@ -331,7 +335,7 @@ describe('migration plan --rename', () => { }); }); - it('writes nothing when the auto-baseline delta is refused, and only the baseline when it has no operations', async () => { + it('writes nothing when the auto-baseline delta is refused, and the baseline and the delta when it has no operations', async () => { const refusedProject = await renamingProject({ history: false }); const refused = await harness(refusedProject, { refuseStatements: true }).run( ['migration', 'plan', '--name', 'delta', '--rename', 'Profile:User', '--json'], @@ -360,7 +364,7 @@ describe('migration plan --rename', () => { }, }).toEqual({ refused: { kind: 'statementRefused', dirs: [] }, - empty: { kind: 'noDatabaseChange', dirs: ['baseline'] }, + empty: { kind: undefined, dirs: ['baseline', 'delta'] }, }); }); }); diff --git a/packages/1-framework/3-tooling/cli/test/orm/migration-plan.no-operations.test.ts b/packages/1-framework/3-tooling/cli/test/orm/migration-plan.no-operations.test.ts index 05eee6b097cf..fc5738aeef7f 100644 --- a/packages/1-framework/3-tooling/cli/test/orm/migration-plan.no-operations.test.ts +++ b/packages/1-framework/3-tooling/cli/test/orm/migration-plan.no-operations.test.ts @@ -7,6 +7,7 @@ import { createOrmTestCli } from '../helpers/orm-test-cli'; import { ADDITIVE_OP, createOfflineProject, + type FixtureStorageNamespaces, OFFLINE_COMMANDS, type OfflineProject, offlineConfig, @@ -21,11 +22,6 @@ const HASH_TO = `c0ffee${'0'.repeat(58)}`; const HASH_FROM = `beef${'1'.repeat(60)}`; const HASH_OTHER = `dead${'2'.repeat(60)}`; -const NO_DATABASE_CHANGE = - 'The contract changed, but migration plan found nothing to change in the database. That is expected after a Prisma upgrade that changes how contract.json records values, after you switch a field to another codec of the same type, or after you change a control policy.'; -const REPORT_IT = - 'If you expected the database to change, migration plan missed it: report it with the output of `prisma migration plan --json`.'; - beforeEach(resetRenderContractDtsMock); afterEach(removeOfflineProjects); @@ -43,6 +39,7 @@ async function run(project: OfflineProject, args: readonly string[]) { return { exitCode: result.exitCode, envelope: terminal !== undefined && terminal.kind === 'result' ? terminal.envelope : undefined, + data: result.presented?.data, }; } @@ -66,39 +63,72 @@ async function migrationsOf(project: OfflineProject) { const dirs = (await readdir(project.appMigrationsDir)).filter((entry) => entry !== 'refs'); return Promise.all( dirs.map(async (dir) => { - const metadata = JSON.parse( - await readFile(join(project.appMigrationsDir, dir, 'migration.json'), 'utf-8'), - ); - return { from: metadata.from, to: metadata.to }; + const packageDir = join(project.appMigrationsDir, dir); + const metadata = JSON.parse(await readFile(join(packageDir, 'migration.json'), 'utf-8')); + const ops = JSON.parse(await readFile(join(packageDir, 'ops.json'), 'utf-8')); + return { from: metadata.from, to: metadata.to, ops }; }), ); } +const STORAGE_FROM: FixtureStorageNamespaces = { + public: { + entries: { + table: { posts: { columns: { id: 'int' } }, users: { columns: { id: 'int' } } }, + valueSet: { Status: { values: ['open'] } }, + }, + }, +}; + +const STORAGE_TO: FixtureStorageNamespaces = { + public: { + entries: { + table: { posts: { columns: { id: 'int' } }, users: { columns: { id: 'int', note: 'text' } } }, + valueSet: { Role: { values: ['admin'] } }, + }, + }, +}; + +const CHANGED_STORAGE_ENTITIES = [ + { plane: 'storage', namespaceId: 'public', entityKind: 'table', entityName: 'users' }, + { plane: 'storage', namespaceId: 'public', entityKind: 'valueSet', entityName: 'Role' }, + { plane: 'storage', namespaceId: 'public', entityKind: 'valueSet', entityName: 'Status' }, +]; + +const NO_OPERATIONS_SUMMARY = + 'The storage the contract describes changed, but the database needs no operation for it, so the migration has none. What changed: table "public.users", valueSet "public.Role", valueSet "public.Status".'; + /** The database sits at HASH_FROM, which a migration reaches; the emitted contract is HASH_TO. */ async function projectWithHistory(): Promise { - const project = await createOfflineProject({ storageHash: HASH_TO }); + const project = await createOfflineProject({ + storageHash: HASH_TO, + storageNamespaces: STORAGE_TO, + }); await seedMigrationPackage({ appMigrationsDir: project.appMigrationsDir, dirName: '20260101T0000_initial', from: null, to: HASH_FROM, }); - await seedContractSnapshot({ migrationsDir: project.migrationsDir, storageHash: HASH_FROM }); + await seedContractSnapshot({ + migrationsDir: project.migrationsDir, + storageHash: HASH_FROM, + storageNamespaces: STORAGE_FROM, + }); await seedDbRef({ appMigrationsDir: project.appMigrationsDir, storageHash: HASH_FROM }); return project; } const NOTHING_TO_CREATE = 'This contract describes nothing migration plan can create, so there is no first migration to plan.'; -const NOTHING_TO_BASELINE = { - kind: 'nothingToBaseline', - summary: `The migrations directory is empty, so migration plan starts the migration history with a baseline migration to the contract the db ref points at, ${HASH_FROM}. That contract describes nothing migration plan can create, so there is no baseline to plan.`, - why: 'No command writes a baseline migration with no operations. If you manage the database with `prisma db init` or `prisma db update`, keep using `prisma db update`. No command can start a migration history from this database yet; report it with the output of `prisma migration plan --json`.', -}; /** Records HASH_OTHER as the contract of a `target` ref, so `--to target` names it. */ async function seedOtherContract(project: OfflineProject): Promise { - await seedContractSnapshot({ migrationsDir: project.migrationsDir, storageHash: HASH_OTHER }); + await seedContractSnapshot({ + migrationsDir: project.migrationsDir, + storageHash: HASH_OTHER, + storageNamespaces: STORAGE_TO, + }); await writeRef(join(project.appMigrationsDir, 'refs'), 'target', { hash: HASH_OTHER, invariants: [], @@ -107,32 +137,63 @@ async function seedOtherContract(project: OfflineProject): Promise { /** An empty migrations directory whose db ref points at HASH_FROM. */ async function projectWithDbRefOnly(): Promise { - const project = await createOfflineProject({ storageHash: HASH_TO }); - await seedContractSnapshot({ migrationsDir: project.migrationsDir, storageHash: HASH_FROM }); + const project = await createOfflineProject({ + storageHash: HASH_TO, + storageNamespaces: STORAGE_TO, + }); + await seedContractSnapshot({ + migrationsDir: project.migrationsDir, + storageHash: HASH_FROM, + storageNamespaces: STORAGE_FROM, + }); await seedDbRef({ appMigrationsDir: project.appMigrationsDir, storageHash: HASH_FROM }); return project; } +function writtenWithNoOperations(to: string) { + return { + exitCode: 0, + data: { + ok: true, + noOp: false, + from: HASH_FROM, + to, + operations: [], + changedStorageEntities: CHANGED_STORAGE_ENTITIES, + summary: NO_OPERATIONS_SUMMARY, + }, + }; +} + describe('migration plan when the planner produces no operations', () => { describe('from an earlier contract', () => { - const fix = `If you deploy with migrations, run \`prisma migration new --from ${HASH_FROM}\` to write a migration with no operations, then \`prisma db migrate\`. If you manage the database with \`prisma db init\` or \`prisma db update\`, run \`prisma db sign\` on each database instead. ${REPORT_IT}`; - - it('names the earlier contract, and migration new --from accepts it', async () => { + it('writes a migration with no operations and names what changed in storage', async () => { const project = await projectWithHistory(); const plan = await run(project, ['migration', 'plan']); - const scaffold = await run(project, ['migration', 'new', '--from', HASH_FROM]); - expect(plan).toMatchObject( - planningFailed({ kind: 'noDatabaseChange', summary: NO_DATABASE_CHANGE, why: fix }), + expect(plan).toMatchObject(writtenWithNoOperations(HASH_TO)); + expect(await migrationsOf(project)).toEqual( + expect.arrayContaining([{ from: HASH_FROM, to: HASH_TO, ops: [] }]), ); - expect({ exitCode: scaffold.exitCode, migrations: await migrationsOf(project) }).toEqual({ - exitCode: 0, - migrations: expect.arrayContaining([{ from: HASH_FROM, to: HASH_TO }]), + }); + + it('says so in a warning, above the contract edge', async () => { + const project = await projectWithHistory(); + + const plan = await cli(project).run(['migration', 'plan'], { + cwd: project.dir, + isTty: { stdout: true }, + }); + + expect(plan.presented?.presentation.human).toContainEqual({ + kind: 'summary', + status: 'warn', + text: NO_OPERATIONS_SUMMARY, }); }); - it('gives the same advice when the delta after an automatic baseline has no operations', async () => { + it('writes the delta after an automatic baseline with no operations', async () => { const project = await projectWithDbRefOnly(); const harness = createOrmTestCli({ commands: OFFLINE_COMMANDS, @@ -141,26 +202,25 @@ describe('migration plan when the planner produces no operations', () => { }); const plan = await harness.run(['migration', 'plan', '--json'], { cwd: project.dir }); - const terminal = plan.json.at(-1); - const scaffold = await run(project, ['migration', 'new', '--from', HASH_FROM]); - - expect({ - exitCode: plan.exitCode, - envelope: - terminal !== undefined && terminal.kind === 'result' ? terminal.envelope : undefined, - }).toMatchObject( - planningFailed({ kind: 'noDatabaseChange', summary: NO_DATABASE_CHANGE, why: fix }), - ); - expect({ exitCode: scaffold.exitCode, migrations: await migrationsOf(project) }).toEqual({ + + expect({ exitCode: plan.exitCode, data: plan.presented?.data }).toMatchObject({ exitCode: 0, - migrations: expect.arrayContaining([ - { from: null, to: HASH_FROM }, - { from: HASH_FROM, to: HASH_TO }, - ]), + data: { + from: HASH_FROM, + to: HASH_TO, + operations: [], + changedStorageEntities: CHANGED_STORAGE_ENTITIES, + }, }); + expect(await migrationsOf(project)).toEqual( + expect.arrayContaining([ + { from: null, to: HASH_FROM, ops: [expect.objectContaining({ id: ADDITIVE_OP.id })] }, + { from: HASH_FROM, to: HASH_TO, ops: [] }, + ]), + ); }); - it('names db sign with the --to target and leaves out migration new', async () => { + it('writes a migration with no operations to the --to target', async () => { const project = await projectWithHistory(); await seedMigrationPackage({ appMigrationsDir: project.appMigrationsDir, @@ -171,11 +231,7 @@ describe('migration plan when the planner produces no operations', () => { await seedOtherContract(project); expect(await run(project, ['migration', 'plan', '--to', 'target'])).toMatchObject( - planningFailed({ - kind: 'noDatabaseChange', - summary: NO_DATABASE_CHANGE, - why: `\`prisma migration new\` writes a migration only to the emitted contract; to write one to this target, emit it first. If you manage the database with \`prisma db init\` or \`prisma db update\`, run \`prisma db sign ${HASH_OTHER}\` on each database. ${REPORT_IT}`, - }), + writtenWithNoOperations(HASH_OTHER), ); }); }); @@ -196,7 +252,7 @@ describe('migration plan when the planner produces no operations', () => { ); expect({ exitCode: scaffold.exitCode, migrations: await migrationsOf(project) }).toEqual({ exitCode: 0, - migrations: [{ from: null, to: HASH_TO }], + migrations: [{ from: null, to: HASH_TO, ops: [] }], }); }); @@ -215,25 +271,20 @@ describe('migration plan when the planner produces no operations', () => { }); describe('toward the db ref contract on an empty migrations directory', () => { - it('says there is no baseline to plan, without sending the user to migration new', async () => { + it('writes a baseline with no operations, then the delta', async () => { const project = await projectWithDbRefOnly(); const plan = await run(project, ['migration', 'plan']); - const scaffold = await run(project, ['migration', 'new']); - expect(plan).toMatchObject(planningFailed(NOTHING_TO_BASELINE)); - expect(scaffold).toMatchObject({ - exitCode: 2, - envelope: { ok: false, error: { code: 'MIGRATION.HASH_NOT_IN_GRAPH' } }, + expect(plan).toMatchObject({ + exitCode: 0, + data: { from: HASH_FROM, to: HASH_TO, baselineOperations: [] }, }); - }); - - it('gives the same advice when --to names the destination', async () => { - const project = await projectWithDbRefOnly(); - await seedOtherContract(project); - - expect(await run(project, ['migration', 'plan', '--to', 'target'])).toMatchObject( - planningFailed(NOTHING_TO_BASELINE), + expect(await migrationsOf(project)).toEqual( + expect.arrayContaining([ + { from: null, to: HASH_FROM, ops: [] }, + { from: HASH_FROM, to: HASH_TO, ops: [] }, + ]), ); }); }); diff --git a/test/integration/test/cli-journeys/plans-with-no-operations.e2e.test.ts b/test/integration/test/cli-journeys/plans-with-no-operations.e2e.test.ts new file mode 100644 index 000000000000..4d804b3122f3 --- /dev/null +++ b/test/integration/test/cli-journeys/plans-with-no-operations.e2e.test.ts @@ -0,0 +1,146 @@ +/** + * A contract change that changes the storage hash but needs no database operation (Postgres). + * + * `auth_users` is a table the application does not manage (`@@control(external)`): the planner plans nothing for it, but its columns are part of the contract's storage. Adding a column the table already has changes the storage hash and needs no operation. + * + * Journey N1 plans the change: `migration plan` writes a migration with no operations, names the table that changed, and `migrate` moves the marker to the new hash. Journey N2 applies the same change with `db update`. + */ + +import { readFileSync, writeFileSync } from 'node:fs'; +import { join } from 'pathe'; +import { describe, expect, it } from 'vitest'; +import { withTempDir } from '../utils/cli-test-helpers'; +import { + type JourneyContext, + latestMigrationDirName, + parseJsonOutput, + planMigrationAndSelfEmit, + readEmittedContractStorageHash, + runContractEmit, + runDbUpdate, + runDbVerify, + runMigrate, + runMigrationPlan, + setupJourney, + sql, + timeouts, + useDevDatabase, +} from '../utils/journey-test-helpers'; + +const AUTH_USERS_SQL = 'CREATE TABLE public.auth_users (id integer PRIMARY KEY, email text)'; + +const FROM_PSL = `// use prisma-8 + +model User { + id Int @id + email String +} + +model AuthUser { + id Int @id + + @@map("auth_users") + @@control(external) +} +`; + +const TO_PSL = FROM_PSL.replace(' id Int @id\n\n', ' id Int @id\n email String?\n\n'); + +interface NoOperationsPlan { + readonly operations: readonly unknown[]; + readonly changedStorageEntities: readonly unknown[]; + readonly summary: string; +} + +async function emitContract(ctx: JourneyContext, psl: string, label: string): Promise { + writeFileSync(join(ctx.testDir, 'contract.prisma'), psl, 'utf-8'); + const emit = await runContractEmit(ctx); + expect(emit.exitCode, `${label}: emit: ${emit.stderr}`).toBe(0); +} + +async function markerHash(connectionString: string): Promise { + const { rows } = await sql( + connectionString, + "SELECT core_hash FROM prisma_contract.marker WHERE space = 'app'", + ); + return rows[0]?.['core_hash']; +} + +withTempDir(({ createTempDir }) => { + describe('Journey N1 (Postgres): migration plan writes a migration with no operations', () => { + const db = useDevDatabase({ onReady: (cs) => sql(cs, AUTH_USERS_SQL) }); + + it( + 'names the changed table, and migrate moves the marker to the new hash', + async () => { + const ctx = setupJourney({ + connectionString: db.connectionString, + createTempDir, + contractMode: 'psl', + }); + await emitContract(ctx, FROM_PSL, 'N1.01'); + const initial = await planMigrationAndSelfEmit(ctx, ['--name', 'initial']); + expect(initial.exitCode, `N1.02: plan initial: ${initial.stderr}`).toBe(0); + expect((await runMigrate(ctx)).exitCode, 'N1.03: migrate initial').toBe(0); + const origin = latestMigrationDirName(ctx); + await emitContract(ctx, TO_PSL, 'N1.04'); + + const plan = await runMigrationPlan(ctx, ['--from', origin, '--name', 'email', '--json']); + expect(plan.exitCode, `N1.05: plan: ${plan.stderr}`).toBe(0); + expect(parseJsonOutput(plan), 'N1.05: no operations').toMatchObject({ + operations: [], + changedStorageEntities: [ + { + plane: 'storage', + namespaceId: 'public', + entityKind: 'table', + entityName: 'auth_users', + }, + ], + summary: expect.stringContaining('What changed: table "public.auth_users".'), + }); + const dir = join(ctx.testDir, 'migrations/app', latestMigrationDirName(ctx)); + expect(JSON.parse(readFileSync(join(dir, 'ops.json'), 'utf-8')), 'N1.05: ops.json').toEqual( + [], + ); + + const applied = await runMigrate(ctx); + expect(applied.exitCode, `N1.06: migrate: ${applied.stderr}`).toBe(0); + expect(await markerHash(db.connectionString), 'N1.06: the marker').toBe( + readEmittedContractStorageHash({ ...ctx, outputDir: ctx.testDir }), + ); + const verify = await runDbVerify(ctx); + expect(verify.exitCode, `N1.07: db verify: ${verify.stderr}`).toBe(0); + }, + timeouts.spinUpPpgDev, + ); + }); + + describe('Journey N2 (Postgres): db update applies a change with no operations', () => { + const db = useDevDatabase({ onReady: (cs) => sql(cs, AUTH_USERS_SQL) }); + + it( + 'moves the marker to the new hash', + async () => { + const ctx = setupJourney({ + connectionString: db.connectionString, + createTempDir, + contractMode: 'psl', + }); + await emitContract(ctx, FROM_PSL, 'N2.01'); + const initial = await runDbUpdate(ctx); + expect(initial.exitCode, `N2.02: db update initial: ${initial.stderr}`).toBe(0); + await emitContract(ctx, TO_PSL, 'N2.03'); + + const update = await runDbUpdate(ctx); + expect(update.exitCode, `N2.04: db update: ${update.stderr}`).toBe(0); + expect(await markerHash(db.connectionString), 'N2.04: the marker').toBe( + readEmittedContractStorageHash({ ...ctx, outputDir: ctx.testDir }), + ); + const verify = await runDbVerify(ctx); + expect(verify.exitCode, `N2.05: db verify: ${verify.stderr}`).toBe(0); + }, + timeouts.spinUpPpgDev, + ); + }); +}); diff --git a/test/integration/test/cli-journeys/plans-with-no-operations.mongo.e2e.test.ts b/test/integration/test/cli-journeys/plans-with-no-operations.mongo.e2e.test.ts new file mode 100644 index 000000000000..2ce979d7942c --- /dev/null +++ b/test/integration/test/cli-journeys/plans-with-no-operations.mongo.e2e.test.ts @@ -0,0 +1,163 @@ +/** + * A contract change that changes the storage hash but needs no database operation (MongoDB). + * + * `auth_users` is a collection the application does not manage (`controlPolicy: 'external'`): the planner plans nothing for it, but its indexes are part of the contract's storage. Adding an index the collection already has changes the storage hash and needs no operation. + * + * Journey N1 plans the change: `migration plan` writes a migration with no operations, names the collection that changed, and `migrate` moves the marker to the new hash. Journey N2 applies the same change with `db update`. + */ + +import { copyFileSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'; +import { rm } from 'node:fs/promises'; +import { join } from 'node:path'; +import { timeouts } from '@repo/test-utils'; +import { type Db, MongoClient } from 'mongodb'; +import { MongoMemoryReplSet } from 'mongodb-memory-server'; +import { afterAll, afterEach, beforeAll, describe, expect, it } from 'vitest'; +import { fixtureAppDir } from '../utils/cli-test-helpers'; +import { + type JourneyContext, + latestMigrationDirName, + parseJsonOutput, + readEmittedContractStorageHash, + runContractEmit, + runDbUpdate, + runDbVerify, + runMigrate, + runMigrationPlan, +} from '../utils/journey-test-helpers'; + +const FIXTURES_DIR = join(fixtureAppDir, 'fixtures/mongo-cli-journeys'); + +interface NoOperationsPlan { + readonly operations: readonly unknown[]; + readonly changedStorageEntities: readonly unknown[]; + readonly summary: string; +} + +function withDatabase(baseUri: string, dbName: string): string { + const url = new URL(baseUri); + url.pathname = `/${dbName}`; + return url.toString(); +} + +function setupProject(connectionString: string): JourneyContext { + const testDir = join( + fixtureAppDir, + `test-mongo-no-operations-${Date.now()}-${Math.random().toString(36).slice(2)}`, + ); + const outputDir = join(testDir, 'output'); + mkdirSync(outputDir, { recursive: true }); + mkdirSync(join(testDir, 'migrations'), { recursive: true }); + writeFileSync( + join(testDir, 'package.json'), + JSON.stringify({ name: 'mongo-no-operations-journey', private: true, type: 'module' }), + 'utf-8', + ); + const config = readFileSync(join(FIXTURES_DIR, 'prisma.config.with-db.ts'), 'utf-8').replace( + /\{\{DB_URL\}\}/g, + () => connectionString, + ); + const configPath = join(testDir, 'prisma.config.ts'); + writeFileSync(configPath, config, 'utf-8'); + return { testDir, configPath, outputDir }; +} + +async function emitContract(ctx: JourneyContext, fixture: string, label: string): Promise { + copyFileSync(join(FIXTURES_DIR, fixture), join(ctx.testDir, 'contract.ts')); + const emitted = await runContractEmit(ctx); + expect(emitted.exitCode, `${label}: emit: ${emitted.stderr}`).toBe(0); +} + +async function seedAuthUsers(db: Db): Promise { + await db.createCollection('auth_users'); + await db.collection('auth_users').createIndex({ email: 1 }, { unique: true }); +} + +async function markerHash(db: Db): Promise { + const marker = await db + .collection('_prisma_migrations') + .findOne({ space: 'app', storageHash: { $exists: true } }); + return marker?.['storageHash']; +} + +describe('Journeys (MongoDB): a plan with no operations', { + timeout: timeouts.spinUpMongoMemoryServer, +}, () => { + let replSet: MongoMemoryReplSet; + let client: MongoClient; + const created = new Set(); + + beforeAll(async () => { + replSet = await MongoMemoryReplSet.create({ + instanceOpts: [ + { launchTimeout: timeouts.spinUpMongoMemoryServer, storageEngine: 'wiredTiger' }, + ], + replSet: { count: 1, storageEngine: 'wiredTiger' }, + }); + client = new MongoClient(replSet.getUri()); + await client.connect(); + }, timeouts.spinUpMongoMemoryServer); + + afterEach(async () => { + for (const dir of created) { + await rm(dir, { recursive: true, force: true }).catch(() => {}); + } + created.clear(); + }); + + afterAll(async () => { + await client?.close().catch(() => {}); + await replSet?.stop().catch(() => {}); + }, timeouts.spinUpMongoMemoryServer); + + it('N1: migration plan names the changed collection, and migrate moves the marker', async () => { + const dbName = 'mongo_no_operations_plan'; + const db = client.db(dbName); + await seedAuthUsers(db); + const ctx = setupProject(withDatabase(replSet.getUri(), dbName)); + created.add(ctx.testDir); + await emitContract(ctx, 'contract-external-auth-users.ts', 'N1.01'); + const initial = await runMigrationPlan(ctx, ['--name', 'initial']); + expect(initial.exitCode, `N1.02: plan initial: ${initial.stderr}`).toBe(0); + const applyInitial = await runMigrate(ctx); + expect(applyInitial.exitCode, `N1.03: migrate initial: ${applyInitial.stderr}`).toBe(0); + const origin = latestMigrationDirName(ctx); + await emitContract(ctx, 'contract-external-auth-users-indexed.ts', 'N1.04'); + + const plan = await runMigrationPlan(ctx, ['--from', origin, '--name', 'index', '--json']); + expect(plan.exitCode, `N1.05: plan: ${plan.stderr}`).toBe(0); + expect(parseJsonOutput(plan), 'N1.05: no operations').toMatchObject({ + operations: [], + changedStorageEntities: [ + expect.objectContaining({ entityKind: 'collection', entityName: 'auth_users' }), + ], + summary: expect.stringContaining('collection "'), + }); + const dir = join(ctx.testDir, 'migrations/app', latestMigrationDirName(ctx)); + expect(JSON.parse(readFileSync(join(dir, 'ops.json'), 'utf-8')), 'N1.05: ops.json').toEqual([]); + + const applied = await runMigrate(ctx); + expect(applied.exitCode, `N1.06: migrate: ${applied.stderr}`).toBe(0); + expect(await markerHash(db), 'N1.06: the marker').toBe(readEmittedContractStorageHash(ctx)); + const verify = await runDbVerify(ctx); + expect(verify.exitCode, `N1.07: db verify: ${verify.stderr}`).toBe(0); + }); + + it('N2: db update moves the marker to the new hash', async () => { + const dbName = 'mongo_no_operations_update'; + const db = client.db(dbName); + await seedAuthUsers(db); + const ctx = setupProject(withDatabase(replSet.getUri(), dbName)); + created.add(ctx.testDir); + await emitContract(ctx, 'contract-external-auth-users.ts', 'N2.01'); + const initial = await runDbUpdate(ctx); + expect(initial.exitCode, `N2.02: db update initial: ${initial.stderr}`).toBe(0); + await emitContract(ctx, 'contract-external-auth-users-indexed.ts', 'N2.03'); + + const update = await runDbUpdate(ctx); + expect(update.exitCode, `N2.04: db update: ${update.stderr}`).toBe(0); + expect(await markerHash(db), 'N2.04: the marker').toBe(readEmittedContractStorageHash(ctx)); + const verify = await runDbVerify(ctx); + expect(verify.exitCode, `N2.05: db verify: ${verify.stderr}`).toBe(0); + }); +}); diff --git a/test/integration/test/cli-journeys/plans-with-no-operations.sqlite.e2e.test.ts b/test/integration/test/cli-journeys/plans-with-no-operations.sqlite.e2e.test.ts new file mode 100644 index 000000000000..fa5b356ade4c --- /dev/null +++ b/test/integration/test/cli-journeys/plans-with-no-operations.sqlite.e2e.test.ts @@ -0,0 +1,143 @@ +/** + * A contract change that changes the storage hash but needs no database operation (SQLite). + * + * SQLite derives no check for an enum, but the enum's value set is part of the contract's storage. Adding a member changes the storage hash and needs no operation. + * + * Journey N1 plans the change: `migration plan` writes a migration with no operations, names the value set that changed, and `migrate` moves the marker to the new hash. Journey N2 applies the same change with `db update`. + */ + +import { readFileSync, writeFileSync } from 'node:fs'; +import { DatabaseSync } from 'node:sqlite'; +import { join } from 'pathe'; +import { describe, expect, it } from 'vitest'; +import { withTempDir, writeProjectManifest } from '../utils/cli-test-helpers'; +import { + type JourneyContext, + latestMigrationDirName, + parseJsonOutput, + planMigrationAndSelfEmit, + readEmittedContractStorageHash, + runContractEmit, + runDbUpdate, + runDbVerify, + runMigrate, + runMigrationPlan, + sqlitePslConfigFixture, + timeouts, +} from '../utils/journey-test-helpers'; + +const FROM_PSL = `// use prisma-8 + +enum Role { + @@type("sqlite/text@1") + Admin = "admin" + Member = "member" +} + +model User { + id Int @id + role Role +} +`; + +const TO_PSL = FROM_PSL.replace( + ' Member = "member"\n', + ' Member = "member"\n Guest = "guest"\n', +); + +interface NoOperationsPlan { + readonly operations: readonly unknown[]; + readonly changedStorageEntities: readonly unknown[]; + readonly summary: string; +} + +type SqliteJourney = JourneyContext & { readonly dbPath: string }; + +function setupSqliteJourney(createTempDir: () => string): SqliteJourney { + const testDir = createTempDir(); + const dbPath = join(testDir, 'journey.db'); + const configPath = join(testDir, 'prisma.config.ts'); + const config = readFileSync(sqlitePslConfigFixture, 'utf-8').replace('{{DB_PATH}}', () => dbPath); + writeFileSync(configPath, config, 'utf-8'); + writeProjectManifest(testDir); + return { testDir, configPath, outputDir: testDir, dbPath }; +} + +async function emitContract(ctx: JourneyContext, psl: string, label: string): Promise { + writeFileSync(join(ctx.testDir, 'contract.prisma'), psl, 'utf-8'); + const emit = await runContractEmit(ctx); + expect(emit.exitCode, `${label}: emit: ${emit.stderr}`).toBe(0); +} + +function markerHash(dbPath: string): unknown { + const db = new DatabaseSync(dbPath); + try { + return db.prepare(`SELECT core_hash FROM "_prisma_marker" WHERE space = 'app'`).get()?.[ + 'core_hash' + ]; + } finally { + db.close(); + } +} + +withTempDir(({ createTempDir }) => { + describe('Journey N1 (SQLite): migration plan writes a migration with no operations', () => { + it( + 'names the changed value set, and migrate moves the marker to the new hash', + async () => { + const ctx = setupSqliteJourney(createTempDir); + await emitContract(ctx, FROM_PSL, 'N1.01'); + const initial = await planMigrationAndSelfEmit(ctx, ['--name', 'initial']); + expect(initial.exitCode, `N1.02: plan initial: ${initial.stderr}`).toBe(0); + expect((await runMigrate(ctx)).exitCode, 'N1.03: migrate initial').toBe(0); + const origin = latestMigrationDirName(ctx); + await emitContract(ctx, TO_PSL, 'N1.04'); + + const plan = await runMigrationPlan(ctx, ['--from', origin, '--name', 'guest', '--json']); + expect(plan.exitCode, `N1.05: plan: ${plan.stderr}`).toBe(0); + expect(parseJsonOutput(plan), 'N1.05: no operations').toMatchObject({ + operations: [], + changedStorageEntities: [ + expect.objectContaining({ entityKind: 'valueSet', entityName: 'Role' }), + ], + summary: expect.stringContaining('What changed: valueSet "Role".'), + }); + const dir = join(ctx.testDir, 'migrations/app', latestMigrationDirName(ctx)); + expect(JSON.parse(readFileSync(join(dir, 'ops.json'), 'utf-8')), 'N1.05: ops.json').toEqual( + [], + ); + + const applied = await runMigrate(ctx); + expect(applied.exitCode, `N1.06: migrate: ${applied.stderr}`).toBe(0); + expect(markerHash(ctx.dbPath), 'N1.06: the marker').toBe( + readEmittedContractStorageHash(ctx), + ); + const verify = await runDbVerify(ctx); + expect(verify.exitCode, `N1.07: db verify: ${verify.stderr}`).toBe(0); + }, + timeouts.spinUpPpgDev, + ); + }); + + describe('Journey N2 (SQLite): db update applies a change with no operations', () => { + it( + 'moves the marker to the new hash', + async () => { + const ctx = setupSqliteJourney(createTempDir); + await emitContract(ctx, FROM_PSL, 'N2.01'); + const initial = await runDbUpdate(ctx); + expect(initial.exitCode, `N2.02: db update initial: ${initial.stderr}`).toBe(0); + await emitContract(ctx, TO_PSL, 'N2.03'); + + const update = await runDbUpdate(ctx); + expect(update.exitCode, `N2.04: db update: ${update.stderr}`).toBe(0); + expect(markerHash(ctx.dbPath), 'N2.04: the marker').toBe( + readEmittedContractStorageHash(ctx), + ); + const verify = await runDbVerify(ctx); + expect(verify.exitCode, `N2.05: db verify: ${verify.stderr}`).toBe(0); + }, + timeouts.spinUpPpgDev, + ); + }); +}); diff --git a/test/integration/test/fixtures/cli/cli-e2e-test-app/fixtures/mongo-cli-journeys/contract-external-auth-users-indexed.ts b/test/integration/test/fixtures/cli/cli-e2e-test-app/fixtures/mongo-cli-journeys/contract-external-auth-users-indexed.ts new file mode 100644 index 000000000000..80cda9694fb4 --- /dev/null +++ b/test/integration/test/fixtures/cli/cli-e2e-test-app/fixtures/mongo-cli-journeys/contract-external-auth-users-indexed.ts @@ -0,0 +1,23 @@ +import { defineContract, field, index, model } from '@internal/mongo/contract-builder'; + +const User = model('User', { + collection: 'users', + fields: { + _id: field.objectId(), + email: field.string(), + }, +}); + +const AuthUser = model('AuthUser', { + collection: 'auth_users', + controlPolicy: 'external', + fields: { + _id: field.objectId(), + email: field.string(), + }, + indexes: [index({ email: 1 }, { unique: true })], +}); + +export const contract = defineContract({ + models: { User, AuthUser }, +}); diff --git a/test/integration/test/fixtures/cli/cli-e2e-test-app/fixtures/mongo-cli-journeys/contract-external-auth-users.ts b/test/integration/test/fixtures/cli/cli-e2e-test-app/fixtures/mongo-cli-journeys/contract-external-auth-users.ts new file mode 100644 index 000000000000..152950801af0 --- /dev/null +++ b/test/integration/test/fixtures/cli/cli-e2e-test-app/fixtures/mongo-cli-journeys/contract-external-auth-users.ts @@ -0,0 +1,23 @@ +import { defineContract, field, index, model } from '@internal/mongo/contract-builder'; + +const User = model('User', { + collection: 'users', + fields: { + _id: field.objectId(), + email: field.string(), + }, +}); + +const AuthUser = model('AuthUser', { + collection: 'auth_users', + controlPolicy: 'external', + fields: { + _id: field.objectId(), + email: field.string(), + }, + indexes: [], +}); + +export const contract = defineContract({ + models: { User, AuthUser }, +}); From d607bde854f9d7e7ea4b01368f213ddccafc27a1 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 11:07:40 +0200 Subject: [PATCH 005/104] TML-3537: a domain field refers to its value object by a storage coordinate EntityCoordinate moves to @internal/contract, so the foundation can use it. ValueObjectFieldType is EntityCoordinate<'storage', 'valueObject'> with kind 'valueObject'; ValueSetRef extends EntityCoordinate. The domain plane no longer has valueObjects. Domain validation checks each value object reference against storage.namespaces..entries.valueObject and refuses the old domain key. valueObjectAt and storageValueObjectsAtDefaultNamespace replace domainValueObjectsAtDefaultNamespace. Signed-off-by: willbot Signed-off-by: Will Madden --- .../contract/src/contract-types.ts | 8 +- .../contract/src/domain-envelope.ts | 8 +- .../contract/src/domain-namespace-access.ts | 12 +- .../0-foundation/contract/src/domain-types.ts | 7 +- .../contract/src/entity-coordinate.ts | 18 ++ .../contract/src/exports/types.ts | 10 +- .../contract/src/exports/validate-domain.ts | 1 + .../contract/src/storage-value-objects.ts | 50 ++++++ .../contract/src/validate-domain.ts | 134 +++++++++----- .../contract/src/value-set-ref.ts | 29 +-- .../contract/test/canonicalization.test.ts | 33 ++-- .../test/storage-value-objects.test.ts | 69 +++++++ .../test/support/application-domain-of.ts | 8 +- .../contract/test/validate-domain.test.ts | 169 ++++++++++++------ .../framework-components/src/exports/ir.ts | 2 +- .../framework-components/src/ir/domain.ts | 3 +- .../framework-components/src/ir/storage.ts | 25 +-- .../test/domain-element-coordinates.test.ts | 6 +- .../test/element-coordinates.test.ts | 3 +- .../control-api/operations/migration-plan.ts | 8 +- .../core/schema-ir/node-storage-coordinate.ts | 2 +- 21 files changed, 393 insertions(+), 212 deletions(-) create mode 100644 packages/1-framework/0-foundation/contract/src/entity-coordinate.ts create mode 100644 packages/1-framework/0-foundation/contract/src/storage-value-objects.ts create mode 100644 packages/1-framework/0-foundation/contract/test/storage-value-objects.test.ts diff --git a/packages/1-framework/0-foundation/contract/src/contract-types.ts b/packages/1-framework/0-foundation/contract/src/contract-types.ts index 9d054d5dde45..41337840f021 100644 --- a/packages/1-framework/0-foundation/contract/src/contract-types.ts +++ b/packages/1-framework/0-foundation/contract/src/contract-types.ts @@ -43,7 +43,7 @@ export interface Contract { readonly targetFamily: string; readonly roots: Record; /** - * Application plane (ADR 221): `domain.namespaces..{ models, valueObjects }`. + * Application plane (ADR 221): `domain.namespaces..{ models, enum }`. */ readonly domain: ApplicationDomain; readonly storage: TStorage; @@ -63,17 +63,17 @@ type ExactlyOneNamespace> = keyof T extends in : never; type NamespaceValueObjectsOf = TNamespace extends { - readonly valueObjects?: infer VO; + readonly entries: { readonly valueObject?: infer VO }; } ? VO extends Record ? VO : Record : Record; -/** Value-object map when the contract declares exactly one domain namespace. */ +/** Value-object map of the storage plane when the contract declares exactly one storage namespace. */ export type ContractValueObjectDefinitions = NamespaceValueObjectsOf< - TContract['domain']['namespaces'][ExactlyOneNamespace] + TContract['storage']['namespaces'][ExactlyOneNamespace] > extends infer Projected ? Projected extends Record ? Projected diff --git a/packages/1-framework/0-foundation/contract/src/domain-envelope.ts b/packages/1-framework/0-foundation/contract/src/domain-envelope.ts index 4e50e54648ef..7466d96ac4f7 100644 --- a/packages/1-framework/0-foundation/contract/src/domain-envelope.ts +++ b/packages/1-framework/0-foundation/contract/src/domain-envelope.ts @@ -1,14 +1,14 @@ -import type { ContractEnum, ContractModelBase, ContractValueObject } from './domain-types'; +import type { ContractEnum, ContractModelBase } from './domain-types'; export { UNBOUND_DOMAIN_NAMESPACE_ID } from './default-namespace'; /** - * One namespace's application-domain entities — models and optional value - * objects keyed by entity name within that namespace coordinate. + * One namespace's application-domain entities — models and optional enums + * keyed by entity name within that namespace coordinate. Value objects are + * storage entities (ADR 278). */ export interface ApplicationDomainNamespace { readonly models: Record; - readonly valueObjects?: Record; readonly enum?: Record; } diff --git a/packages/1-framework/0-foundation/contract/src/domain-namespace-access.ts b/packages/1-framework/0-foundation/contract/src/domain-namespace-access.ts index 6ca1bda9c576..9816bc47fe23 100644 --- a/packages/1-framework/0-foundation/contract/src/domain-namespace-access.ts +++ b/packages/1-framework/0-foundation/contract/src/domain-namespace-access.ts @@ -1,7 +1,7 @@ import { contractError } from './contract-errors'; import { soleDomainNamespaceId } from './default-namespace'; import type { ApplicationDomain } from './domain-envelope'; -import type { ContractModelBase, ContractValueObject } from './domain-types'; +import type { ContractModelBase } from './domain-types'; /** * Models map for the contract's single domain namespace. Throws when the @@ -22,13 +22,3 @@ export function domainModelsAtDefaultNamespace( } return domainNamespace.models; } - -/** - * Value objects for the contract's single domain namespace, when present. - * Throws when the contract does not declare exactly one namespace. - */ -export function domainValueObjectsAtDefaultNamespace( - domain: ApplicationDomain, -): Record | undefined { - return domain.namespaces[soleDomainNamespaceId(domain)]?.valueObjects; -} diff --git a/packages/1-framework/0-foundation/contract/src/domain-types.ts b/packages/1-framework/0-foundation/contract/src/domain-types.ts index 2b6be3cb200f..ec70d5b9cc1a 100644 --- a/packages/1-framework/0-foundation/contract/src/domain-types.ts +++ b/packages/1-framework/0-foundation/contract/src/domain-types.ts @@ -1,4 +1,5 @@ import type { CrossReference } from './cross-reference'; +import type { EntityCoordinate } from './entity-coordinate'; import type { JsonValue } from './types'; import type { ValueSetRef } from './value-set-ref'; @@ -8,10 +9,10 @@ export type ScalarFieldType = { readonly typeParams?: Record; }; -export type ValueObjectFieldType = { +/** A field that holds a value object: the coordinate of its storage entity (ADR 278). */ +export interface ValueObjectFieldType extends EntityCoordinate<'storage', 'valueObject'> { readonly kind: 'valueObject'; - readonly name: string; -}; +} export type UnionFieldType = { readonly kind: 'union'; diff --git a/packages/1-framework/0-foundation/contract/src/entity-coordinate.ts b/packages/1-framework/0-foundation/contract/src/entity-coordinate.ts new file mode 100644 index 000000000000..02c61e53e2c5 --- /dev/null +++ b/packages/1-framework/0-foundation/contract/src/entity-coordinate.ts @@ -0,0 +1,18 @@ +/** + * Canonical address for a named entity in a contract plane. + * + * `plane` is the top-level contract plane the entity lives on. `entityKind` equals the key of the slot the entity lives under (`'table'` for `entries.table`, `'valueObject'` for `entries.valueObject`, `'enum'` for the domain's `enum` slot), with no translation between the two (ADR 224). `entityName` is the entity's key in that slot. + * + * References between planes obey a directional invariant: domain may refer to storage; storage may never refer to domain. The coordinate carries the plane a validator checks. + * + * The type parameters narrow the plane and the kind for a reference to one kind of entity, such as a field's value object (`EntityCoordinate<'storage', 'valueObject'>`). + */ +export interface EntityCoordinate< + TPlane extends 'domain' | 'storage' = 'domain' | 'storage', + TKind extends string = string, +> { + readonly plane: TPlane; + readonly namespaceId: string; + readonly entityKind: TKind; + readonly entityName: string; +} diff --git a/packages/1-framework/0-foundation/contract/src/exports/types.ts b/packages/1-framework/0-foundation/contract/src/exports/types.ts index 4b3eb44fc734..f15564336729 100644 --- a/packages/1-framework/0-foundation/contract/src/exports/types.ts +++ b/packages/1-framework/0-foundation/contract/src/exports/types.ts @@ -14,10 +14,7 @@ export type { ContractWithDomain, } from '../domain-envelope'; export { UNBOUND_DOMAIN_NAMESPACE_ID } from '../domain-envelope'; -export { - domainModelsAtDefaultNamespace, - domainValueObjectsAtDefaultNamespace, -} from '../domain-namespace-access'; +export { domainModelsAtDefaultNamespace } from '../domain-namespace-access'; export type { ContractDiscriminator, ContractEmbedRelation, @@ -42,6 +39,7 @@ export type { UnionFieldType, ValueObjectFieldType, } from '../domain-types'; +export type { EntityCoordinate } from '../entity-coordinate'; export { ContractExecutionSectionSchema } from '../execution-section-schema'; export type { EnumMemberValueType, @@ -58,6 +56,10 @@ export type { ParameterizedOutputTypesFunction, } from '../parameterized-output-types'; export { type ResolvedDomainModel, resolveDomainModel } from '../resolve-domain-model'; +export { + storageValueObjectsAtDefaultNamespace, + valueObjectAt, +} from '../storage-value-objects'; export type { $, Brand, diff --git a/packages/1-framework/0-foundation/contract/src/exports/validate-domain.ts b/packages/1-framework/0-foundation/contract/src/exports/validate-domain.ts index eaf10f0bce0f..d87bf60b6916 100644 --- a/packages/1-framework/0-foundation/contract/src/exports/validate-domain.ts +++ b/packages/1-framework/0-foundation/contract/src/exports/validate-domain.ts @@ -1,5 +1,6 @@ export { type DomainContractShape, type DomainModelShape, + removedDomainKeyProblems, validateContractDomain, } from '../validate-domain'; diff --git a/packages/1-framework/0-foundation/contract/src/storage-value-objects.ts b/packages/1-framework/0-foundation/contract/src/storage-value-objects.ts new file mode 100644 index 000000000000..719fb95a170d --- /dev/null +++ b/packages/1-framework/0-foundation/contract/src/storage-value-objects.ts @@ -0,0 +1,50 @@ +import { blindCast } from '@internal/utils/casts'; +import { contractError } from './contract-errors'; +import type { ContractValueObject } from './domain-types'; +import type { EntityCoordinate } from './entity-coordinate'; +import { isPlainRecord } from './is-plain-record'; +import type { StorageBase } from './types'; + +type StorageNamespaces = Pick; + +function valueObjectsOf( + storage: StorageNamespaces, + namespaceId: string, +): Readonly> { + const slot = storage.namespaces[namespaceId]?.entries['valueObject']; + return isPlainRecord(slot) + ? blindCast< + Readonly>, + 'the family validators check each storage valueObject entry against the value object schema' + >(slot) + : {}; +} + +/** The storage value object a reference names, or `undefined` when storage does not have it. */ +export function valueObjectAt( + storage: StorageNamespaces, + reference: EntityCoordinate<'storage', 'valueObject'>, +): ContractValueObject | undefined { + const valueObjects = valueObjectsOf(storage, reference.namespaceId); + return Object.hasOwn(valueObjects, reference.entityName) + ? valueObjects[reference.entityName] + : undefined; +} + +/** + * The value objects of the storage's only namespace. Throws when storage does not declare exactly one namespace, since a bare value object name is then ambiguous. + */ +export function storageValueObjectsAtDefaultNamespace( + storage: StorageNamespaces, +): Readonly> { + const namespaceIds = Object.keys(storage.namespaces); + const [namespaceId] = namespaceIds; + if (namespaceId === undefined || namespaceIds.length > 1) { + throw contractError( + 'CONTRACT.NAMESPACE_INVALID', + `bare-name resolution requires exactly one storage namespace, found ${namespaceIds.length} (${namespaceIds.join(', ')}); select a namespace explicitly`, + { meta: { reason: 'multiple-storage-namespaces', namespaceIds } }, + ); + } + return valueObjectsOf(storage, namespaceId); +} diff --git a/packages/1-framework/0-foundation/contract/src/validate-domain.ts b/packages/1-framework/0-foundation/contract/src/validate-domain.ts index 704ec7605e75..55857487dab0 100644 --- a/packages/1-framework/0-foundation/contract/src/validate-domain.ts +++ b/packages/1-framework/0-foundation/contract/src/validate-domain.ts @@ -2,7 +2,11 @@ import { blindCast } from '@internal/utils/casts'; import { ContractValidationError } from './contract-validation-error'; import type { CrossReference } from './cross-reference'; import type { ContractWithDomain } from './domain-envelope'; +import type { ContractValueObject } from './domain-types'; +import { isPlainRecord } from './is-plain-record'; import { asNamespaceId, type NamespaceId } from './namespace-id'; +import { valueObjectAt } from './storage-value-objects'; +import type { StorageBase } from './types'; export interface DomainRelationShape { readonly to: CrossReference; @@ -22,6 +26,7 @@ export interface DomainModelShape { export interface DomainContractShape extends ContractWithDomain { readonly roots: Record; + readonly storage: Pick; } interface IndexedModel { @@ -60,8 +65,45 @@ function* iterateIndexedModels(index: ModelIndex): IterableIterator + isPlainRecord(namespace) && Object.hasOwn(namespace, 'valueObjects') + ? [ + `domain.namespaces.${namespaceId}.valueObjects: contracts no longer store value objects in the domain plane; they are storage entities under storage.namespaces..entries.valueObject, so re-emit the contract from its source`, + ] + : [], + ); +} + +interface StorageValueObjectEntry { + readonly namespaceId: string; + readonly name: string; + readonly valueObject: ContractValueObject; +} + +function storageValueObjects(contract: DomainContractShape): readonly StorageValueObjectEntry[] { + return Object.entries(contract.storage.namespaces).flatMap(([namespaceId, namespace]) => { + const slot = namespace.entries['valueObject']; + if (!isPlainRecord(slot)) return []; + return Object.entries(slot).map(([name, valueObject]) => ({ + namespaceId, + name, + valueObject: blindCast< + ContractValueObject, + 'the family validators check each storage valueObject entry against the value object schema' + >(valueObject), + })); + }); +} + export function validateContractDomain(contract: DomainContractShape): void { - const errors: string[] = []; + const errors: string[] = [...removedDomainKeyProblems(contract)]; const modelIndex = indexDomainModels(contract); validateRoots(contract, modelIndex, errors); @@ -260,7 +302,10 @@ function validateOwnership( interface FieldTypeLike { readonly kind?: string; - readonly name?: string; + readonly plane?: string; + readonly namespaceId?: string; + readonly entityKind?: string; + readonly entityName?: string; readonly members?: readonly FieldTypeLike[]; } @@ -271,28 +316,43 @@ interface FieldLike { readonly dict?: boolean; } -function validateValueObjectReferences(contract: DomainContractShape, errors: string[]): void { - const voNamesByNamespace = new Map>(); - for (const [namespaceKey, namespace] of Object.entries(contract.domain.namespaces)) { - const namespaceId = asNamespaceId(namespaceKey); - voNamesByNamespace.set(namespaceId, new Set(Object.keys(namespace.valueObjects ?? {}))); +function fieldLike(field: unknown): FieldLike | undefined { + return blindCast(field); +} + +function valueObjectReferenceProblem(type: FieldTypeLike): string | undefined { + if (type.plane !== 'storage' || type.entityKind !== 'valueObject') { + return `refers to a value object with plane "${type.plane}" and entityKind "${type.entityKind}"; a value object reference has plane "storage" and entityKind "valueObject"`; } + return undefined; +} - function checkType( - type: FieldTypeLike | undefined, - location: string, - namespaceId: NamespaceId, - ): void { +function validateValueObjectReferences(contract: DomainContractShape, errors: string[]): void { + function checkType(type: FieldTypeLike | undefined, location: string): void { if (!type) return; - const voNames = voNamesByNamespace.get(namespaceId) ?? new Set(); - if (type.kind === 'valueObject' && type.name && !voNames.has(type.name)) { - errors.push( - `${location} references value object "${namespaceId}:${type.name}" which does not exist in that namespace's valueObjects`, - ); + if (type.kind === 'valueObject') { + const problem = valueObjectReferenceProblem(type); + if (problem !== undefined) { + errors.push(`${location} ${problem}`); + return; + } + const namespaceId = type.namespaceId ?? ''; + const entityName = type.entityName ?? ''; + const found = valueObjectAt(contract.storage, { + plane: 'storage', + namespaceId, + entityKind: 'valueObject', + entityName, + }); + if (found === undefined) { + errors.push( + `${location} refers to value object "${entityName}" in storage namespace "${namespaceId}", which storage.namespaces.${namespaceId}.entries.valueObject does not have`, + ); + } return; } if (type.kind === 'union') { - for (const member of type.members ?? []) checkType(member, location, namespaceId); + for (const member of type.members ?? []) checkType(member, location); } } @@ -300,25 +360,21 @@ function validateValueObjectReferences(contract: DomainContractShape, errors: st const namespaceId = asNamespaceId(namespaceKey); for (const [modelName, model] of Object.entries(namespace.models)) { for (const [fieldName, field] of Object.entries(model.fields)) { - const f = blindCast( - field, - ); - checkType(f?.type, `Model "${namespaceId}:${modelName}" field "${fieldName}"`, namespaceId); - } - } - for (const [voName, vo] of Object.entries(namespace.valueObjects ?? {})) { - for (const [fieldName, field] of Object.entries(vo.fields)) { - const f = blindCast( - field, - ); checkType( - f?.type, - `Value object "${namespaceId}:${voName}" field "${fieldName}"`, - namespaceId, + fieldLike(field)?.type, + `Model "${namespaceId}:${modelName}" field "${fieldName}"`, ); } } } + for (const { namespaceId, name, valueObject } of storageValueObjects(contract)) { + for (const [fieldName, field] of Object.entries(valueObject.fields)) { + checkType( + fieldLike(field)?.type, + `Value object "${namespaceId}:${name}" field "${fieldName}"`, + ); + } + } } function validateFieldModifiers( @@ -327,10 +383,7 @@ function validateFieldModifiers( errors: string[], ): void { function validateField(location: string, field: unknown): void { - const candidate = blindCast< - FieldLike | undefined, - 'domain fields are runtime-validated shapes' - >(field); + const candidate = fieldLike(field); const many = candidate?.many; const isList = typeof many === 'object' && @@ -358,12 +411,9 @@ function validateFieldModifiers( validateField(`Model "${namespaceId}:${modelName}" field "${fieldName}"`, field); } } - for (const [namespaceKey, namespace] of Object.entries(contract.domain.namespaces)) { - const namespaceId = asNamespaceId(namespaceKey); - for (const [voName, vo] of Object.entries(namespace.valueObjects ?? {})) { - for (const [fieldName, field] of Object.entries(vo.fields)) { - validateField(`Value object "${namespaceId}:${voName}" field "${fieldName}"`, field); - } + for (const { namespaceId, name, valueObject } of storageValueObjects(contract)) { + for (const [fieldName, field] of Object.entries(valueObject.fields)) { + validateField(`Value object "${namespaceId}:${name}" field "${fieldName}"`, field); } } } diff --git a/packages/1-framework/0-foundation/contract/src/value-set-ref.ts b/packages/1-framework/0-foundation/contract/src/value-set-ref.ts index 0ac325600a71..4afb5a21afd7 100644 --- a/packages/1-framework/0-foundation/contract/src/value-set-ref.ts +++ b/packages/1-framework/0-foundation/contract/src/value-set-ref.ts @@ -1,31 +1,14 @@ +import type { EntityCoordinate } from './entity-coordinate'; + /** * Entity coordinate for a domain enum or storage value-set reference. * - * Field-name identity with the framework's `EntityCoordinate` type - * (packages/1-framework/1-core/framework-components/src/ir/storage.ts). - * Foundation-contract cannot import framework-components (dependency points - * the other way), so this is a standalone mirror of that shape plus the - * cross-space discriminator. + * Every `valueSet` reference stays in its plane: a domain field refers to a domain enum, and a storage column or check to a storage value set. * - * One-vocabulary rule (ADR 224): `entityKind` is equal to the entries slot - * key the referenced entity lives under — `'enum'` for domain's `enum` slot, - * `'valueSet'` for storage's `entries.valueSet` slot. No consumer-side - * translation between the kind string and the slot key. + * `namespaceId` admits the `UNBOUND_NAMESPACE_ID` (`__unbound__`) sentinel for single-namespace (unbound) references. * - * Every `valueSet` reference is intra-plane (domain field → domain enum; - * storage column/check → storage value-set). The directional invariant: - * domain may reference storage; storage may never reference domain. - * - * `namespaceId` admits the `UNBOUND_NAMESPACE_ID` (`__unbound__`) sentinel - * for single-namespace (unbound) references. - * - * `spaceId` is the cross-space discriminator: absent ⇒ local (same - * contract-space); present ⇒ cross-space. No separate tag field. + * `spaceId` is the cross-space discriminator: absent ⇒ local (same contract-space); present ⇒ cross-space. No separate tag field. */ -export interface ValueSetRef { - readonly plane: 'domain' | 'storage'; - readonly namespaceId: string; - readonly entityKind: 'enum' | 'valueSet'; - readonly entityName: string; +export interface ValueSetRef extends EntityCoordinate<'domain' | 'storage', 'enum' | 'valueSet'> { readonly spaceId?: string; } diff --git a/packages/1-framework/0-foundation/contract/test/canonicalization.test.ts b/packages/1-framework/0-foundation/contract/test/canonicalization.test.ts index dae0be7f0dde..7ef6e14c8e2d 100644 --- a/packages/1-framework/0-foundation/contract/test/canonicalization.test.ts +++ b/packages/1-framework/0-foundation/contract/test/canonicalization.test.ts @@ -6,8 +6,6 @@ function crossRef(model: string, namespace = 'default') { return { namespace: asNamespaceId(namespace), model }; } -import { blindCast } from '@internal/utils/casts'; -import { ifDefined } from '@internal/utils/defined'; import { type CanonicalizeContractOptions, canonicalizeContract as canonicalizeContractRaw, @@ -17,7 +15,7 @@ import { import { createPreserveEmptyPredicate, type PathPattern } from '../src/canonicalization-path-match'; import { createStorageSort, type NamedArraySortTarget } from '../src/canonicalization-storage-sort'; import type { Contract } from '../src/contract-types'; -import type { ContractModelBase, ContractValueObject } from '../src/domain-types'; +import type { ContractModelBase } from '../src/domain-types'; import { coreHash, profileHash } from '../src/types'; import { applicationDomainOf } from './support/application-domain-of'; @@ -69,6 +67,10 @@ function minimal(overrides?: Record): Contract { domain: _domain, ...rest } = overrides ?? {}; + const storageNamespaces = + valueObjects === undefined + ? {} + : { [UNBOUND]: { id: UNBOUND, entries: { valueObject: valueObjects } } }; return { targetFamily: 'sql', target: 'postgres', @@ -76,18 +78,8 @@ function minimal(overrides?: Record): Contract { domain: domainOverride !== undefined ? (domainOverride as Contract['domain']) - : applicationDomainOf({ - models: models as Record, - ...ifDefined( - 'valueObjects', - valueObjects !== undefined - ? blindCast, 'canonicalization test fixtures'>( - valueObjects, - ) - : undefined, - ), - }), - storage: { storageHash: coreHash('stub'), namespaces: {} }, + : applicationDomainOf({ models: models as Record }), + storage: { storageHash: coreHash('stub'), namespaces: storageNamespaces }, extensions: {}, capabilities: {}, meta: {}, @@ -374,10 +366,11 @@ describe('default omission', () => { expect(nameField).not.toHaveProperty('many'); const cityField = drill( result, - 'domain', + 'storage', 'namespaces', UNBOUND, - 'valueObjects', + 'entries', + 'valueObject', 'Address', 'fields', 'city', @@ -828,8 +821,8 @@ describe('orderTopLevel', () => { }); }); -describe('canonicalize with valueObjects', () => { - it('includes valueObjects under domain.namespaces when present', () => { +describe('canonicalize with value objects', () => { + it('includes value objects under storage.namespaces.*.entries.valueObject when present', () => { const contract = minimal({ valueObjects: { Address: { @@ -841,7 +834,7 @@ describe('canonicalize with valueObjects', () => { }, }); const result = canonicalizeContractToObject(contract); - const vo = drill(result, 'domain', 'namespaces', UNBOUND, 'valueObjects'); + const vo = drill(result, 'storage', 'namespaces', UNBOUND, 'entries', 'valueObject'); expect(vo).toHaveProperty('Address'); }); diff --git a/packages/1-framework/0-foundation/contract/test/storage-value-objects.test.ts b/packages/1-framework/0-foundation/contract/test/storage-value-objects.test.ts new file mode 100644 index 000000000000..d87e68a9d26d --- /dev/null +++ b/packages/1-framework/0-foundation/contract/test/storage-value-objects.test.ts @@ -0,0 +1,69 @@ +import { describe, expect, it } from 'vitest'; +import { storageValueObjectsAtDefaultNamespace, valueObjectAt } from '../src/storage-value-objects'; + +const address = { + fields: { street: { nullable: false, type: { kind: 'scalar', codecId: 'pg/text@1' } } }, +} as const; + +const storage = { + namespaces: { + public: { id: 'public', entries: { valueObject: { Address: address } } }, + }, +}; + +describe('valueObjectAt', () => { + it('reads the value object a reference names', () => { + expect( + valueObjectAt(storage, { + plane: 'storage', + namespaceId: 'public', + entityKind: 'valueObject', + entityName: 'Address', + }), + ).toEqual(address); + }); + + it('is undefined for a name or a namespace storage does not have', () => { + expect({ + name: valueObjectAt(storage, { + plane: 'storage', + namespaceId: 'public', + entityKind: 'valueObject', + entityName: 'Missing', + }), + namespace: valueObjectAt(storage, { + plane: 'storage', + namespaceId: 'billing', + entityKind: 'valueObject', + entityName: 'Address', + }), + }).toEqual({ name: undefined, namespace: undefined }); + }); +}); + +describe('storageValueObjectsAtDefaultNamespace', () => { + it('reads the value objects of the only storage namespace', () => { + expect(storageValueObjectsAtDefaultNamespace(storage)).toEqual({ Address: address }); + }); + + it('is empty when the namespace has no value objects', () => { + expect( + storageValueObjectsAtDefaultNamespace({ + namespaces: { public: { id: 'public', entries: {} } }, + }), + ).toEqual({}); + }); + + it('refuses a storage with more than one namespace', () => { + expect(() => + storageValueObjectsAtDefaultNamespace({ + namespaces: { + public: { id: 'public', entries: {} }, + billing: { id: 'billing', entries: {} }, + }, + }), + ).toThrow( + 'bare-name resolution requires exactly one storage namespace, found 2 (public, billing); select a namespace explicitly', + ); + }); +}); diff --git a/packages/1-framework/0-foundation/contract/test/support/application-domain-of.ts b/packages/1-framework/0-foundation/contract/test/support/application-domain-of.ts index 225e352f71c1..45a53b308327 100644 --- a/packages/1-framework/0-foundation/contract/test/support/application-domain-of.ts +++ b/packages/1-framework/0-foundation/contract/test/support/application-domain-of.ts @@ -1,6 +1,6 @@ import type { ApplicationDomain } from '../../src/domain-envelope'; import { UNBOUND_DOMAIN_NAMESPACE_ID } from '../../src/domain-envelope'; -import type { ContractModelBase, ContractValueObject } from '../../src/domain-types'; +import type { ContractModelBase } from '../../src/domain-types'; /** * Single-namespace application-domain authoring convenience for this package's @@ -11,17 +11,13 @@ import type { ContractModelBase, ContractValueObject } from '../../src/domain-ty */ export function applicationDomainOf(params: { readonly models?: Record; - readonly valueObjects?: Record; readonly namespaceId?: string; }): ApplicationDomain { const namespaceId = params.namespaceId ?? UNBOUND_DOMAIN_NAMESPACE_ID; const models = params.models ?? {}; return { namespaces: { - [namespaceId]: { - models, - ...(params.valueObjects !== undefined ? { valueObjects: params.valueObjects } : {}), - }, + [namespaceId]: { models }, }, }; } diff --git a/packages/1-framework/0-foundation/contract/test/validate-domain.test.ts b/packages/1-framework/0-foundation/contract/test/validate-domain.test.ts index 2dad94641224..15096c2422a7 100644 --- a/packages/1-framework/0-foundation/contract/test/validate-domain.test.ts +++ b/packages/1-framework/0-foundation/contract/test/validate-domain.test.ts @@ -6,10 +6,7 @@ function crossRef(model: string, namespace: string = UNBOUND_DOMAIN_NAMESPACE_ID return { namespace: asNamespaceId(namespace), model }; } -import { blindCast } from '@internal/utils/casts'; -import { ifDefined } from '@internal/utils/defined'; import { ContractValidationError } from '../src/contract-validation-error'; -import type { ContractValueObject } from '../src/domain-types'; import type { DomainContractShape } from '../src/validate-domain'; import { validateContractDomain } from '../src/validate-domain'; import { applicationDomainOf } from './support/application-domain-of'; @@ -47,22 +44,30 @@ function makeValidContract(overrides: Record = {}): DomainContr domain: domainOverride !== undefined ? (domainOverride as DomainContractShape['domain']) - : applicationDomainOf({ - models, - ...ifDefined( - 'valueObjects', - valueObjects !== undefined - ? blindCast< - Record, - 'validate-domain fixtures use structural value-object shapes' - >(valueObjects) - : undefined, - ), - }), + : applicationDomainOf({ models }), + storage: { + namespaces: { + [UNBOUND_DOMAIN_NAMESPACE_ID]: { + id: UNBOUND_DOMAIN_NAMESPACE_ID, + entries: valueObjects !== undefined ? { valueObject: valueObjects } : {}, + }, + }, + }, ...rest, } as DomainContractShape; } +/** A field type that refers to the value object `name` in the storage namespace `namespaceId`. */ +function valueObjectType(name: string, namespaceId: string = UNBOUND_DOMAIN_NAMESPACE_ID) { + return { + kind: 'valueObject', + plane: 'storage', + namespaceId, + entityKind: 'valueObject', + entityName: name, + }; +} + describe('validateContractDomain()', () => { describe('namespace coordinate identity', () => { it('treats the same model name in different namespaces as distinct models', () => { @@ -696,45 +701,99 @@ describe('validateContractDomain()', () => { }); describe('value object reference validation', () => { - it('accepts valid value object references from model fields', () => { + const address = { + fields: { + street: { nullable: false, type: { kind: 'scalar', codecId: 'pg/text@1' } }, + city: { nullable: false, type: { kind: 'scalar', codecId: 'pg/text@1' } }, + }, + }; + + it('accepts a model field that refers to a value object in storage', () => { const contract = makeValidContract({ roots: { users: crossRef('User') }, models: { User: makeMinimalModel({ fields: { id: { nullable: false, type: { kind: 'scalar', codecId: 'pg/int4@1' } }, - homeAddress: { nullable: false, type: { kind: 'valueObject', name: 'Address' } }, + homeAddress: { nullable: false, type: valueObjectType('Address') }, }, }), }, - valueObjects: { - Address: { + valueObjects: { Address: address }, + }); + expect(() => validateContractDomain(contract)).not.toThrow(); + }); + + it('refuses a model field that refers to a value object storage does not have, naming the field and the coordinate', () => { + const contract = makeValidContract({ + roots: { users: crossRef('User') }, + models: { + User: makeMinimalModel({ fields: { - street: { nullable: false, type: { kind: 'scalar', codecId: 'pg/text@1' } }, - city: { nullable: false, type: { kind: 'scalar', codecId: 'pg/text@1' } }, + id: { nullable: false, type: { kind: 'scalar', codecId: 'pg/int4@1' } }, + addr: { nullable: false, type: valueObjectType('Missing') }, }, - }, + }), }, + valueObjects: { Address: address }, }); - expect(() => validateContractDomain(contract)).not.toThrow(); + expect(() => validateContractDomain(contract)).toThrow( + `Model "__unbound__:User" field "addr" refers to value object "Missing" in storage namespace "__unbound__", which storage.namespaces.__unbound__.entries.valueObject does not have`, + ); }); - it('rejects nonexistent value object reference from model field', () => { + it('refuses a reference into a storage namespace the contract does not have', () => { + const contract = makeValidContract({ + roots: { users: crossRef('User') }, + models: { + User: makeMinimalModel({ + fields: { addr: { nullable: false, type: valueObjectType('Address', 'billing') } }, + }), + }, + valueObjects: { Address: address }, + }); + expect(() => validateContractDomain(contract)).toThrow( + `Model "__unbound__:User" field "addr" refers to value object "Address" in storage namespace "billing", which storage.namespaces.billing.entries.valueObject does not have`, + ); + }); + + it('refuses a value object reference that does not point at a storage value object', () => { const contract = makeValidContract({ roots: { users: crossRef('User') }, models: { User: makeMinimalModel({ fields: { - id: { nullable: false, type: { kind: 'scalar', codecId: 'pg/int4@1' } }, - addr: { nullable: false, type: { kind: 'valueObject', name: 'Missing' } }, + addr: { + nullable: false, + type: { ...valueObjectType('Address'), plane: 'domain', entityKind: 'enum' }, + }, }, }), }, + valueObjects: { Address: address }, }); - expect(() => validateContractDomain(contract)).toThrow(/Missing.*does not exist/); + expect(() => validateContractDomain(contract)).toThrow( + `Model "__unbound__:User" field "addr" refers to a value object with plane "domain" and entityKind "enum"; a value object reference has plane "storage" and entityKind "valueObject"`, + ); }); - it('accepts self-referencing value object', () => { + it('accepts a value object whose field refers to another value object', () => { + const contract = makeValidContract({ + roots: { users: crossRef('User') }, + models: { + User: makeMinimalModel({ + fields: { profile: { nullable: false, type: valueObjectType('Profile') } }, + }), + }, + valueObjects: { + Address: address, + Profile: { fields: { home: { nullable: true, type: valueObjectType('Address') } } }, + }, + }); + expect(() => validateContractDomain(contract)).not.toThrow(); + }); + + it('accepts a value object that refers to itself', () => { const contract = makeValidContract({ roots: {}, models: {}, @@ -742,7 +801,7 @@ describe('validateContractDomain()', () => { TreeNode: { fields: { label: { nullable: false, type: { kind: 'scalar', codecId: 'pg/text@1' } }, - child: { nullable: true, type: { kind: 'valueObject', name: 'TreeNode' } }, + child: { nullable: true, type: valueObjectType('TreeNode') }, }, }, }, @@ -750,74 +809,72 @@ describe('validateContractDomain()', () => { expect(() => validateContractDomain(contract)).not.toThrow(); }); - it('rejects nonexistent value object reference from another value object', () => { + it('refuses a value object field that refers to a value object storage does not have', () => { const contract = makeValidContract({ roots: {}, models: {}, valueObjects: { - Wrapper: { - fields: { - inner: { nullable: false, type: { kind: 'valueObject', name: 'Nonexistent' } }, - }, - }, + Wrapper: { fields: { inner: { nullable: false, type: valueObjectType('Nonexistent') } } }, }, }); - expect(() => validateContractDomain(contract)).toThrow(/Nonexistent.*does not exist/); + expect(() => validateContractDomain(contract)).toThrow( + `Value object "__unbound__:Wrapper" field "inner" refers to value object "Nonexistent" in storage namespace "__unbound__", which storage.namespaces.__unbound__.entries.valueObject does not have`, + ); }); - it('rejects nonexistent value object reference inside union members', () => { + it('refuses a union member that refers to a value object storage does not have', () => { const contract = makeValidContract({ roots: { users: crossRef('User') }, models: { User: makeMinimalModel({ fields: { - id: { nullable: false, type: { kind: 'scalar', codecId: 'pg/int4@1' } }, data: { nullable: false, type: { kind: 'union', - members: [ - { kind: 'scalar', codecId: 'pg/text@1' }, - { kind: 'valueObject', name: 'Ghost' }, - ], + members: [{ kind: 'scalar', codecId: 'pg/text@1' }, valueObjectType('Ghost')], }, }, }, }), }, }); - expect(() => validateContractDomain(contract)).toThrow(/Ghost.*does not exist/); + expect(() => validateContractDomain(contract)).toThrow(/value object "Ghost"/); }); - it('accepts valid value object reference inside union members', () => { + it('accepts a union member that refers to a value object in storage', () => { const contract = makeValidContract({ roots: { users: crossRef('User') }, models: { User: makeMinimalModel({ fields: { - id: { nullable: false, type: { kind: 'scalar', codecId: 'pg/int4@1' } }, data: { nullable: false, type: { kind: 'union', - members: [ - { kind: 'scalar', codecId: 'pg/text@1' }, - { kind: 'valueObject', name: 'Address' }, - ], + members: [{ kind: 'scalar', codecId: 'pg/text@1' }, valueObjectType('Address')], }, }, }, }), }, - valueObjects: { - Address: { - fields: { - street: { nullable: false, type: { kind: 'scalar', codecId: 'pg/text@1' } }, - }, + valueObjects: { Address: address }, + }); + expect(() => validateContractDomain(contract)).not.toThrow(); + }); + + it('refuses value objects in the domain plane, and says where they live now', () => { + const contract = makeValidContract({ + domain: { + namespaces: { + public: { models: {}, valueObjects: { Address: address } }, }, }, + roots: {}, }); - expect(() => validateContractDomain(contract)).not.toThrow(); + expect(() => validateContractDomain(contract)).toThrow( + 'domain.namespaces.public.valueObjects: contracts no longer store value objects in the domain plane; they are storage entities under storage.namespaces..entries.valueObject, so re-emit the contract from its source', + ); }); }); @@ -878,7 +935,7 @@ describe('validateContractDomain()', () => { expect(() => validateContractDomain(contract)).toThrow(/sibling "elementNullable"/); }); - it('rejects old many: true when type checking is bypassed', () => { + it('rejects old many: true on a storage value object field when type checking is bypassed', () => { const contract = makeValidContract({ roots: {}, models: {}, diff --git a/packages/1-framework/1-core/framework-components/src/exports/ir.ts b/packages/1-framework/1-core/framework-components/src/exports/ir.ts index 7f0a072a45d4..ed2d56146920 100644 --- a/packages/1-framework/1-core/framework-components/src/exports/ir.ts +++ b/packages/1-framework/1-core/framework-components/src/exports/ir.ts @@ -15,7 +15,7 @@ export type { IRNode } from '../ir/ir-node'; export { freezeNode, IRNodeBase } from '../ir/ir-node'; export type { Namespace } from '../ir/namespace'; export { NamespaceBase, UNBOUND_NAMESPACE_ID } from '../ir/namespace'; -export type { EntityCoordinate, Storage } from '../ir/storage'; +export type { Storage } from '../ir/storage'; export { changedElementCoordinates, coordinateKey, diff --git a/packages/1-framework/1-core/framework-components/src/ir/domain.ts b/packages/1-framework/1-core/framework-components/src/ir/domain.ts index cced223a8a32..424224cff792 100644 --- a/packages/1-framework/1-core/framework-components/src/ir/domain.ts +++ b/packages/1-framework/1-core/framework-components/src/ir/domain.ts @@ -1,5 +1,4 @@ -import type { ApplicationDomain } from '@internal/contract/types'; -import type { EntityCoordinate } from './storage'; +import type { ApplicationDomain, EntityCoordinate } from '@internal/contract/types'; /** * Lazy walk over every named domain entity in a {@link ApplicationDomain}, diff --git a/packages/1-framework/1-core/framework-components/src/ir/storage.ts b/packages/1-framework/1-core/framework-components/src/ir/storage.ts index 51408c1c533b..1d17f8fb4e80 100644 --- a/packages/1-framework/1-core/framework-components/src/ir/storage.ts +++ b/packages/1-framework/1-core/framework-components/src/ir/storage.ts @@ -1,5 +1,5 @@ import { isPlainRecord } from '@internal/contract/is-plain-record'; -import type { StorageBase } from '@internal/contract/types'; +import type { EntityCoordinate, StorageBase } from '@internal/contract/types'; import { canonicalStringify } from '@internal/utils/canonical-stringify'; import { blindCast } from '@internal/utils/casts'; import type { IRNode } from './ir-node'; @@ -7,29 +7,6 @@ import type { Namespace } from './namespace'; export { isPlainRecord }; -/** - * Canonical address for a named entity in Contract IR / Schema IR. - * - * `plane` is `'domain' | 'storage'`: which top-level contract plane the - * entity lives on. Domain-side walks yield `plane: 'domain'` via - * {@link domainElementCoordinates}; {@link elementCoordinates} over storage - * yields `plane: 'storage'`. - * - * Cross-plane references obey a directional invariant: domain → storage is - * allowed; storage → domain is forbidden. That rule is enforced by a - * separate validator, not by constraining this coordinate shape — the - * coordinate carries the axis the validator checks. - * - * Iteration order over namespace properties follows `Object.entries` order; - * consumers that depend on ordering must sort. - */ -export interface EntityCoordinate { - readonly plane: 'domain' | 'storage'; - readonly namespaceId: string; - readonly entityKind: string; - readonly entityName: string; -} - /** * Lazy walk over every named storage entity in a `Storage`-shaped * value, yielded as {@link EntityCoordinate} tuples with diff --git a/packages/1-framework/1-core/framework-components/test/domain-element-coordinates.test.ts b/packages/1-framework/1-core/framework-components/test/domain-element-coordinates.test.ts index 0c1537aa0743..9d5bd914dcef 100644 --- a/packages/1-framework/1-core/framework-components/test/domain-element-coordinates.test.ts +++ b/packages/1-framework/1-core/framework-components/test/domain-element-coordinates.test.ts @@ -3,12 +3,12 @@ import { describe, expect, it } from 'vitest'; import { domainElementCoordinates } from '../src/ir/domain'; describe('domainElementCoordinates', () => { - it('yields domain-plane coordinates for models and value objects', () => { + it('yields domain-plane coordinates for models and enums', () => { const domain: Pick = { namespaces: { auth: { models: { User: { fields: {}, relations: {}, storage: {} } }, - valueObjects: { Email: { fields: {} } }, + enum: { Role: { codecId: 'pg/text@1', members: [{ name: 'Admin', value: 'admin' }] } }, }, public: { models: { Post: { fields: {}, relations: {}, storage: {} } }, @@ -21,7 +21,7 @@ describe('domainElementCoordinates', () => { expect(coordinates).toEqual( expect.arrayContaining([ { plane: 'domain', namespaceId: 'auth', entityKind: 'models', entityName: 'User' }, - { plane: 'domain', namespaceId: 'auth', entityKind: 'valueObjects', entityName: 'Email' }, + { plane: 'domain', namespaceId: 'auth', entityKind: 'enum', entityName: 'Role' }, { plane: 'domain', namespaceId: 'public', entityKind: 'models', entityName: 'Post' }, ]), ); diff --git a/packages/1-framework/1-core/framework-components/test/element-coordinates.test.ts b/packages/1-framework/1-core/framework-components/test/element-coordinates.test.ts index 3af19220d939..540537b259e6 100644 --- a/packages/1-framework/1-core/framework-components/test/element-coordinates.test.ts +++ b/packages/1-framework/1-core/framework-components/test/element-coordinates.test.ts @@ -1,10 +1,9 @@ -import type { StorageBase } from '@internal/contract/types'; +import type { EntityCoordinate, StorageBase } from '@internal/contract/types'; import { blindCast } from '@internal/utils/casts'; import { describe, expect, it } from 'vitest'; import { changedElementCoordinates, coordinateKey, - type EntityCoordinate, elementCoordinates, entityAt, } from '../src/ir/storage'; diff --git a/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts b/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts index 83ddb9b61d7f..a297ba0323e6 100644 --- a/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts +++ b/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts @@ -4,7 +4,7 @@ import { readFile } from 'node:fs/promises'; import type { PrismaNextConfig } from '@internal/config/config-types'; -import type { Contract, ContractWithDomain } from '@internal/contract/types'; +import type { Contract, ContractWithDomain, EntityCoordinate } from '@internal/contract/types'; import { createControlStack, hasOperationPreview, @@ -16,11 +16,7 @@ import { type ResolvedMigrationStatement, type SchemaOwnership, } from '@internal/framework-components/control'; -import { - changedElementCoordinates, - type EntityCoordinate, - UNBOUND_NAMESPACE_ID, -} from '@internal/framework-components/ir'; +import { changedElementCoordinates, UNBOUND_NAMESPACE_ID } from '@internal/framework-components/ir'; import { snapshotsImportPathFrom, writeContractSnapshot, diff --git a/packages/3-targets/postgres/1-target/src/core/schema-ir/node-storage-coordinate.ts b/packages/3-targets/postgres/1-target/src/core/schema-ir/node-storage-coordinate.ts index 166be6782f01..a43a5e822f54 100644 --- a/packages/3-targets/postgres/1-target/src/core/schema-ir/node-storage-coordinate.ts +++ b/packages/3-targets/postgres/1-target/src/core/schema-ir/node-storage-coordinate.ts @@ -1,4 +1,4 @@ -import type { EntityCoordinate } from '@internal/framework-components/ir'; +import type { EntityCoordinate } from '@internal/contract/types'; import type { SqlSchemaIRNode } from '@internal/sql-schema-ir/types'; import { PostgresNativeEnumSchemaNode } from './postgres-native-enum-schema-node'; import { PostgresTableSchemaNode } from './postgres-table-schema-node'; From 1792de2a679f8253f2d906a26b4e1cac5da93992 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 11:12:56 +0200 Subject: [PATCH 006/104] TML-3537: SQL contracts store value objects under storage entries.valueObject A StorageValueObject entity kind with its schema; a member typed by an enum refers to the storage value set. A domain field type refers to the value object by its storage coordinate. The domain namespace schema drops valueObjects, and an old contract that has it is refused with a message that says to re-emit. The editor JSON schema is regenerated. Signed-off-by: willbot Signed-off-by: Will Madden --- .../1-core/contract/src/contract-view.ts | 2 +- .../1-core/contract/src/entity-kinds.ts | 19 +- .../contract/src/exports/entity-kinds.ts | 1 + .../1-core/contract/src/exports/types.ts | 2 + .../1-core/contract/src/ir/sql-storage.ts | 6 +- .../contract/src/ir/storage-entry-schemas.ts | 68 ++++++ .../contract/src/ir/storage-value-object.ts | 23 ++ .../2-sql-family/1-core/contract/src/types.ts | 1 + .../1-core/contract/src/validators.ts | 39 +--- .../test/storage-value-object.test.ts | 196 ++++++++++++++++++ .../1-core/contract/test/validators.test.ts | 75 ++++--- .../schemas/data-contract-sql-v1.json | 130 ++++++------ 12 files changed, 436 insertions(+), 126 deletions(-) create mode 100644 packages/2-sql-family/1-core/contract/src/ir/storage-value-object.ts create mode 100644 packages/2-sql-family/1-core/contract/test/storage-value-object.test.ts diff --git a/packages/2-sql-family/1-core/contract/src/contract-view.ts b/packages/2-sql-family/1-core/contract/src/contract-view.ts index 1f6ff8de375c..5d506ffa0431 100644 --- a/packages/2-sql-family/1-core/contract/src/contract-view.ts +++ b/packages/2-sql-family/1-core/contract/src/contract-view.ts @@ -13,7 +13,7 @@ import type { SqlStorage } from './ir/sql-storage'; * `valueSet` are promoted to top-level view accessors; pack-contributed kinds * (e.g. `policy`) stay under `.entries`. */ -export const SQL_BUILTIN_KINDS = ['table', 'valueSet'] as const; +export const SQL_BUILTIN_KINDS = ['table', 'valueSet', 'valueObject'] as const; export type SqlBuiltinKind = (typeof SQL_BUILTIN_KINDS)[number]; type SqlEntries> = DefaultNamespaceEntries< diff --git a/packages/2-sql-family/1-core/contract/src/entity-kinds.ts b/packages/2-sql-family/1-core/contract/src/entity-kinds.ts index 56c94fde6a8b..a87a1a9654f4 100644 --- a/packages/2-sql-family/1-core/contract/src/entity-kinds.ts +++ b/packages/2-sql-family/1-core/contract/src/entity-kinds.ts @@ -6,8 +6,13 @@ import { ifDefined } from '@internal/utils/defined'; import { contractError } from './contract-errors'; import { CheckConstraint } from './ir/check-constraint'; import { Index } from './ir/sql-index'; -import { StorageTableSchema, StorageValueSetSchema } from './ir/storage-entry-schemas'; +import { + StorageTableSchema, + StorageValueObjectSchema, + StorageValueSetSchema, +} from './ir/storage-entry-schemas'; import { StorageTable, type StorageTableInput } from './ir/storage-table'; +import { StorageValueObject, type StorageValueObjectInput } from './ir/storage-value-object'; import { StorageValueSet, type StorageValueSetInput } from './ir/storage-value-set'; import { checkConstraintInputFromSerialized, @@ -47,9 +52,18 @@ export const valueSetEntityKind: EntityKindDescriptor new StorageValueSet(input), }; +export const valueObjectEntityKind: EntityKindDescriptor< + StorageValueObjectInput, + StorageValueObject +> = { + kind: 'valueObject', + schema: StorageValueObjectSchema, + construct: (input) => new StorageValueObject(input), +}; + /** * Assembles the `kind → descriptor` registry for SQL namespaces: the built-in - * `table` and `valueSet` kinds plus any target `packKinds`. This builds the + * `table`, `valueSet` and `valueObject` kinds plus any target `packKinds`. This builds the * lookup table — it does not touch contract data. `hydrateNamespaceEntities` * later consumes this registry to turn a namespace's raw entries into IR * instances, and `createSqlContractSchema` derives validation from the same @@ -61,6 +75,7 @@ export function composeSqlEntityKinds( const kinds = new Map([ ['table', tableEntityKind], ['valueSet', valueSetEntityKind], + ['valueObject', valueObjectEntityKind], ]); for (const descriptor of packKinds) { if (kinds.has(descriptor.kind)) { diff --git a/packages/2-sql-family/1-core/contract/src/exports/entity-kinds.ts b/packages/2-sql-family/1-core/contract/src/exports/entity-kinds.ts index f20c4c0bae42..77726715bfde 100644 --- a/packages/2-sql-family/1-core/contract/src/exports/entity-kinds.ts +++ b/packages/2-sql-family/1-core/contract/src/exports/entity-kinds.ts @@ -1,5 +1,6 @@ export { composeSqlEntityKinds, tableEntityKind, + valueObjectEntityKind, valueSetEntityKind, } from '../entity-kinds'; diff --git a/packages/2-sql-family/1-core/contract/src/exports/types.ts b/packages/2-sql-family/1-core/contract/src/exports/types.ts index bd1fad6ec571..3d48e3864a75 100644 --- a/packages/2-sql-family/1-core/contract/src/exports/types.ts +++ b/packages/2-sql-family/1-core/contract/src/exports/types.ts @@ -67,6 +67,7 @@ export type { StorageTableInput, StorageTypeInstance, StorageTypeInstanceInput, + StorageValueObjectInput, StorageValueSetInput, TypeMaps, TypeMapsPhantomKey, @@ -94,6 +95,7 @@ export { SqlStorage, StorageColumn, StorageTable, + StorageValueObject, StorageValueSet, toStorageTypeInstance, UniqueConstraint, diff --git a/packages/2-sql-family/1-core/contract/src/ir/sql-storage.ts b/packages/2-sql-family/1-core/contract/src/ir/sql-storage.ts index d7343eed0012..e1cfbc266715 100644 --- a/packages/2-sql-family/1-core/contract/src/ir/sql-storage.ts +++ b/packages/2-sql-family/1-core/contract/src/ir/sql-storage.ts @@ -16,6 +16,7 @@ import { type StorageTypeInstanceInput, toStorageTypeInstance, } from './storage-type-instance'; +import type { StorageValueObject } from './storage-value-object'; import type { StorageValueSet } from './storage-value-set'; /** @@ -91,13 +92,14 @@ export interface SqlStorageInput { */ /** * The typed `entries` shape for SQL family namespaces. The open dictionary - * is intersected with optional known-kind maps so that `ns.entries.table` - * and `ns.entries.valueSet` resolve without a cast, while unknown pack- + * is intersected with optional known-kind maps so that `ns.entries.table`, + * `ns.entries.valueSet` and `ns.entries.valueObject` resolve without a cast, while unknown pack- * contributed kinds remain valid (the `Record` part allows any string key). */ export type SqlNamespaceEntries = Readonly>>> & { readonly table?: Readonly>; readonly valueSet?: Readonly>; + readonly valueObject?: Readonly>; }; /** diff --git a/packages/2-sql-family/1-core/contract/src/ir/storage-entry-schemas.ts b/packages/2-sql-family/1-core/contract/src/ir/storage-entry-schemas.ts index e23eab700984..75a82e6437ac 100644 --- a/packages/2-sql-family/1-core/contract/src/ir/storage-entry-schemas.ts +++ b/packages/2-sql-family/1-core/contract/src/ir/storage-entry-schemas.ts @@ -35,6 +35,74 @@ const StorageValueSetRefSchema = type({ 'spaceId?': 'string', }); +const VALUE_OBJECT_REFERENCE_KEYS = new Set([ + 'kind', + 'plane', + 'namespaceId', + 'entityKind', + 'entityName', +]); + +function isRecord(value: unknown): value is Record { + return typeof value === 'object' && value !== null && !Array.isArray(value); +} + +function isValueObjectReference(value: Record): boolean { + return ( + value['plane'] === 'storage' && + value['entityKind'] === 'valueObject' && + typeof value['namespaceId'] === 'string' && + typeof value['entityName'] === 'string' && + Object.keys(value).every((key) => VALUE_OBJECT_REFERENCE_KEYS.has(key)) + ); +} + +/** A field's type: a scalar with its codec, a value object by its storage coordinate, or a union of those. */ +export function isContractFieldType(value: unknown): boolean { + if (!isRecord(value)) return false; + const kind = value['kind']; + if (kind === 'scalar') { + if (typeof value['codecId'] !== 'string') return false; + const typeParams = value['typeParams']; + if (typeParams !== undefined && !isRecord(typeParams)) return false; + return true; + } + if (kind === 'valueObject') { + return isValueObjectReference(value); + } + if (kind === 'union') { + const members = value['members']; + if (!Array.isArray(members)) return false; + return members.every((m) => isContractFieldType(m)); + } + return false; +} + +export const ContractFieldTypeSchema = type('unknown').narrow((value, ctx) => + isContractFieldType(value) + ? true + : ctx.mustBe( + 'scalar, valueObject (by its storage coordinate: plane, namespaceId, entityKind, entityName), or union field type', + ), +); + +const StorageValueObjectFieldSchema = type({ + '+': 'reject', + nullable: 'boolean', + type: ContractFieldTypeSchema, + 'many?': type('false').or({ '+': 'reject', elementNullable: 'boolean' }), + 'dict?': 'true', + 'valueSet?': StorageValueSetRefSchema, +}); + +/** + * Storage value object entry under `storage.namespaces[id].entries.valueObject[name]` (ADR 278). Its fields have the shape a domain field has; a member typed by an enum refers to the storage value set, since storage never refers to the domain plane. + */ +export const StorageValueObjectSchema = type({ + '+': 'reject', + fields: type({ '[string]': StorageValueObjectFieldSchema }), +}); + const StorageColumnSchema = type({ '+': 'reject', dataType: DATA_TYPE_ID_PATTERN, diff --git a/packages/2-sql-family/1-core/contract/src/ir/storage-value-object.ts b/packages/2-sql-family/1-core/contract/src/ir/storage-value-object.ts new file mode 100644 index 000000000000..7f4de541c84e --- /dev/null +++ b/packages/2-sql-family/1-core/contract/src/ir/storage-value-object.ts @@ -0,0 +1,23 @@ +import type { ContractField } from '@internal/contract/types'; +import { freezeNode } from '@internal/framework-components/ir'; +import { SqlNode } from './sql-node'; + +/** Hydration / construction input shape for {@link StorageValueObject}; mirrors the stored JSON. */ +export interface StorageValueObjectInput { + readonly fields: Readonly>; +} + +/** + * SQL Contract IR node for a value object in a namespace's `valueObject` map (`SqlNamespace.entries.valueObject`): the keys every row holds in a column that stores it (ADR 278). A domain field refers to it by its storage coordinate. + * + * The entry's name is not on the class: value objects are keyed by name in the parent namespace's `valueObject` map. + */ +export class StorageValueObject extends SqlNode { + readonly fields: Readonly>; + + constructor(input: StorageValueObjectInput) { + super(); + this.fields = Object.freeze({ ...input.fields }); + freezeNode(this); + } +} diff --git a/packages/2-sql-family/1-core/contract/src/types.ts b/packages/2-sql-family/1-core/contract/src/types.ts index 69325e6e305c..c87765fe33a2 100644 --- a/packages/2-sql-family/1-core/contract/src/types.ts +++ b/packages/2-sql-family/1-core/contract/src/types.ts @@ -48,6 +48,7 @@ export { type StorageTypeInstanceInput, toStorageTypeInstance, } from './ir/storage-type-instance'; +export { StorageValueObject, type StorageValueObjectInput } from './ir/storage-value-object'; export { isStorageValueSet, StorageValueSet, diff --git a/packages/2-sql-family/1-core/contract/src/validators.ts b/packages/2-sql-family/1-core/contract/src/validators.ts index eb6e916b4c1e..b4e3f1c7d655 100644 --- a/packages/2-sql-family/1-core/contract/src/validators.ts +++ b/packages/2-sql-family/1-core/contract/src/validators.ts @@ -5,7 +5,10 @@ import { type ContractModel, CrossReferenceSchema, } from '@internal/contract/types'; -import { validateContractDomain } from '@internal/contract/validate-domain'; +import { + removedDomainKeyProblems, + validateContractDomain, +} from '@internal/contract/validate-domain'; import { createDataTypeLookup, DATA_TYPE_ID_PATTERN, @@ -24,6 +27,7 @@ import { type Type, type } from 'arktype'; import { contractError } from './contract-errors'; import { composeSqlEntityKinds } from './entity-kinds'; import { sameColumns, startsWithColumns } from './index-equivalence'; +import { ContractFieldTypeSchema } from './ir/storage-entry-schemas'; import { resolveSqlToOneRelationStorage } from './relation-storage'; import { sameSqlColumnType } from './sql-data-type'; @@ -419,30 +423,6 @@ function rejectDuplicateWireNamedCheckContent( } } -function isContractFieldType(value: unknown): boolean { - if (!isPlainRecord(value)) return false; - const kind = value['kind']; - if (kind === 'scalar') { - if (typeof value['codecId'] !== 'string') return false; - const typeParams = value['typeParams']; - if (typeParams !== undefined && !isPlainRecord(typeParams)) return false; - return true; - } - if (kind === 'valueObject') { - return typeof value['name'] === 'string'; - } - if (kind === 'union') { - const members = value['members']; - if (!Array.isArray(members)) return false; - return members.every((m) => isContractFieldType(m)); - } - return false; -} - -const ContractFieldTypeSchema = type('unknown').narrow((value, ctx) => - isContractFieldType(value) ? true : ctx.mustBe('scalar, valueObject, or union field type'), -); - const ModelFieldSchema = type({ '+': 'reject', nullable: 'boolean', @@ -552,9 +532,6 @@ export function createSqlContractSchema( namespaces: type({ '[string]': type({ models: type({ '[string]': ModelSchema }), - 'valueObjects?': type({ - '[string]': type({ fields: type({ '[string]': ModelFieldSchema }) }), - }), 'enum?': type({ '[string]': ContractEnumSchema }), }), }), @@ -632,7 +609,10 @@ function validateSqlContractStructure>( throw new ContractValidationError(`Unsupported target family: ${targetFamily}`, 'structural'); } - const removedKeys = removedKeyProblems(fieldOf(value, 'storage')); + const removedKeys = [ + ...removedDomainKeyProblems(value), + ...removedKeyProblems(fieldOf(value, 'storage')), + ]; if (removedKeys.length > 0) { throw new ContractValidationError( `Contract structural validation failed: ${removedKeys.join('; ')}`, @@ -1146,6 +1126,7 @@ export function validateSqlContractFully>( validateContractDomain({ roots: validated.roots, domain: validated.domain, + storage: validated.storage, }); validateSqlStorageConsistency(validated); const semanticErrors = validateStorageSemantics(validated.storage); diff --git a/packages/2-sql-family/1-core/contract/test/storage-value-object.test.ts b/packages/2-sql-family/1-core/contract/test/storage-value-object.test.ts new file mode 100644 index 000000000000..ed0b253767bf --- /dev/null +++ b/packages/2-sql-family/1-core/contract/test/storage-value-object.test.ts @@ -0,0 +1,196 @@ +import type { ContractModel } from '@internal/contract/types'; +import { hydrateNamespaceEntities, UNBOUND_NAMESPACE_ID } from '@internal/framework-components/ir'; +import { blindCast } from '@internal/utils/casts'; +import { createContract } from '@repo/test-utils'; +import { type } from 'arktype'; +import { describe, expect, it } from 'vitest'; +import { composeSqlEntityKinds } from '../src/entity-kinds'; +import { col, model, table } from '../src/factories'; +import { StorageValueObject } from '../src/ir/storage-value-object'; +import type { SqlStorage } from '../src/types'; +import { createSqlStorageSchema, validateSqlContractFully } from '../src/validators'; + +const text = { nullable: false, type: { kind: 'scalar', codecId: 'pg/text@1' } } as const; + +function valueObjectType(name: string, namespaceId: string = UNBOUND_NAMESPACE_ID) { + return { + kind: 'valueObject', + plane: 'storage', + namespaceId, + entityKind: 'valueObject', + entityName: name, + } as const; +} + +const address = { fields: { street: text, city: text } }; + +function storageWith(valueObject: Record) { + return { + namespaces: { + [UNBOUND_NAMESPACE_ID]: { + id: UNBOUND_NAMESPACE_ID, + kind: 'test-sql-namespace', + entries: { + table: { + user: table({ + id: col('pg/int4', 'pg/int4@1'), + address: col('pg/jsonb', 'pg/jsonb@1'), + }), + }, + valueObject, + }, + }, + }, + }; +} + +function contractWith( + addressType: unknown, + valueObject: Record = { Address: address }, +) { + return createContract({ + storage: blindCast( + storageWith(valueObject), + ), + models: { + User: blindCast({ + ...model('user', { id: { column: 'id' }, address: { column: 'address' } }), + fields: { + id: { nullable: false, type: { kind: 'scalar', codecId: 'pg/int4@1' } }, + address: { nullable: false, type: addressType }, + }, + }), + }, + }); +} + +describe('StorageValueObject', () => { + it('holds the fields it is given, frozen', () => { + const valueObject = new StorageValueObject(address); + expect({ fields: valueObject.fields, frozen: Object.isFrozen(valueObject) }).toEqual({ + fields: address.fields, + frozen: true, + }); + }); + + it('serializes to its fields only', () => { + expect(JSON.parse(JSON.stringify(new StorageValueObject(address)))).toEqual(address); + }); + + it('is hydrated from the valueObject slot', () => { + const hydrated = hydrateNamespaceEntities( + { valueObject: { Address: address } }, + composeSqlEntityKinds(), + 'fail', + ); + expect(hydrated['valueObject']?.['Address']).toBeInstanceOf(StorageValueObject); + }); +}); + +describe('the storage valueObject slot schema', () => { + const schema = createSqlStorageSchema(composeSqlEntityKinds()); + + function validate(valueObject: Record) { + return schema({ storageHash: 'test', ...storageWith(valueObject) }); + } + + it('accepts scalar, list, nested and self-referencing members', () => { + expect( + validate({ + Address: address, + Profile: { + fields: { + home: { nullable: true, type: valueObjectType('Address') }, + tags: { ...text, many: { elementNullable: false } }, + parent: { nullable: true, type: valueObjectType('Profile') }, + }, + }, + }), + ).not.toBeInstanceOf(type.errors); + }); + + it('refuses a member key it does not know', () => { + expect(validate({ Address: { fields: { city: { ...text, column: 'city' } } } })).toBeInstanceOf( + type.errors, + ); + }); + + it('refuses a member value set that points into the domain plane', () => { + expect( + validate({ + Address: { + fields: { + kind: { + ...text, + valueSet: { + plane: 'domain', + namespaceId: UNBOUND_NAMESPACE_ID, + entityKind: 'enum', + entityName: 'Kind', + }, + }, + }, + }, + }), + ).toBeInstanceOf(type.errors); + }); + + it('refuses a member reference that is not a storage value object coordinate', () => { + expect( + validate({ + Wrapper: { + fields: { inner: { nullable: false, type: { kind: 'valueObject', name: 'Address' } } }, + }, + }), + ).toBeInstanceOf(type.errors); + }); +}); + +describe('validateSqlContractFully with a value object in storage', () => { + it('accepts a field that refers to the value object by its storage coordinate', () => { + expect(() => validateSqlContractFully(contractWith(valueObjectType('Address')))).not.toThrow(); + }); + + it('refuses a field that refers to a value object storage does not have', () => { + expect(() => validateSqlContractFully(contractWith(valueObjectType('Missing')))).toThrow( + 'Model "__unbound__:User" field "address" refers to value object "Missing" in storage namespace "__unbound__", which storage.namespaces.__unbound__.entries.valueObject does not have', + ); + }); + + it('accepts a nested value object and one that refers to itself', () => { + const contract = contractWith(valueObjectType('Profile'), { + Address: address, + Profile: { + fields: { + home: { nullable: true, type: valueObjectType('Address') }, + parent: { nullable: true, type: valueObjectType('Profile') }, + }, + }, + }); + expect(() => validateSqlContractFully(contract)).not.toThrow(); + }); + + it('refuses a field type that names its value object the old way', () => { + expect(() => + validateSqlContractFully(contractWith({ kind: 'valueObject', name: 'Address' })), + ).toThrow(/Contract structural validation failed/); + }); + + it('refuses value objects in the domain plane, and says to re-emit', () => { + const contract = contractWith(valueObjectType('Address')); + const old = { + ...contract, + domain: { + namespaces: { + [UNBOUND_NAMESPACE_ID]: { + ...contract.domain.namespaces[UNBOUND_NAMESPACE_ID], + valueObjects: { Address: address }, + }, + }, + }, + }; + expect(() => validateSqlContractFully(old)).toThrow( + 'Contract structural validation failed: domain.namespaces.__unbound__.valueObjects: contracts no longer store value objects in the domain plane; they are storage entities under storage.namespaces..entries.valueObject, so re-emit the contract from its source', + ); + }); +}); diff --git a/packages/2-sql-family/1-core/contract/test/validators.test.ts b/packages/2-sql-family/1-core/contract/test/validators.test.ts index fed3b1e05afb..c92f5a31d756 100644 --- a/packages/2-sql-family/1-core/contract/test/validators.test.ts +++ b/packages/2-sql-family/1-core/contract/test/validators.test.ts @@ -84,41 +84,38 @@ describe('SQL contract validators', () => { }); it.each([ - { metadata: {}, valid: true, many: false }, - { metadata: { many: false }, valid: true, many: false }, - { - metadata: { many: { elementNullable: false } }, - valid: true, - many: { elementNullable: false }, - }, - { metadata: { many: { elementNullable: true } }, valid: true, many: { elementNullable: true } }, - { metadata: { many: true }, valid: false, many: undefined }, - { metadata: { many: null }, valid: false, many: undefined }, - { metadata: { many: {} }, valid: false, many: undefined }, - { metadata: { many: { elementNullable: 'false' } }, valid: false, many: undefined }, - ])('validates and normalizes value-object cardinality $metadata', ({ metadata, valid, many }) => { + { metadata: {}, valid: true }, + { metadata: { many: false }, valid: true }, + { metadata: { many: { elementNullable: false } }, valid: true }, + { metadata: { many: { elementNullable: true } }, valid: true }, + { metadata: { many: true }, valid: false }, + { metadata: { many: null }, valid: false }, + { metadata: { many: {} }, valid: false }, + { metadata: { many: { elementNullable: 'false' } }, valid: false }, + ])('validates storage value-object cardinality $metadata', ({ metadata, valid }) => { const field = { type: { kind: 'scalar', codecId: 'pg/text@1' }, nullable: false, ...metadata }; const schema = createSqlContractSchema(composeSqlEntityKinds()); const result = schema({ target: 'postgres', targetFamily: 'sql', profileHash: 'test', - domain: { + domain: { namespaces: { [UNBOUND_NAMESPACE_ID]: { models: {} } } }, + storage: { + storageHash: 'test', namespaces: { [UNBOUND_NAMESPACE_ID]: { - models: {}, - valueObjects: { Address: { fields: { city: field } } }, + id: UNBOUND_NAMESPACE_ID, + entries: { valueObject: { Address: { fields: { city: field } } } }, }, }, }, - storage: { storageHash: 'test', namespaces: {} }, }); if (valid) { expect(result).toMatchObject({ - domain: { + storage: { namespaces: { [UNBOUND_NAMESPACE_ID]: { - valueObjects: { Address: { fields: { city: { ...field, many } } } }, + entries: { valueObject: { Address: { fields: { city: field } } } }, }, }, }, @@ -1021,23 +1018,47 @@ describe('SQL contract validators', () => { it('leaves the storage type of a value-object column to the stack', () => { const c = createContract({ - storage: unboundTables({ - user: table({ id: col('pg/int4', 'pg/int4@1'), address: col('pg/text', 'pg/text@1') }), + storage: blindCast({ + namespaces: { + [UNBOUND_NAMESPACE_ID]: { + id: UNBOUND_NAMESPACE_ID, + kind: 'test-sql-namespace', + entries: { + table: { + user: table({ + id: col('pg/int4', 'pg/int4@1'), + address: col('pg/text', 'pg/text@1'), + }), + }, + valueObject: { + Address: { + fields: { + street: { nullable: false, type: { kind: 'scalar', codecId: 'pg/text@1' } }, + }, + }, + }, + }, + }, + }, }), models: { User: blindCast({ ...contractModel('user', { id: { column: 'id' }, address: { column: 'address' } }), fields: { id: { nullable: false, type: { kind: 'scalar', codecId: 'pg/int4@1' } }, - address: { nullable: false, type: { kind: 'valueObject', name: 'Address' } }, + address: { + nullable: false, + type: { + kind: 'valueObject', + plane: 'storage', + namespaceId: UNBOUND_NAMESPACE_ID, + entityKind: 'valueObject', + entityName: 'Address', + }, + }, }, }), }, - valueObjects: { - Address: { - fields: { street: { nullable: false, type: { kind: 'scalar', codecId: 'pg/text@1' } } }, - }, - }, }); expect(() => validateSqlContractFully(c)).not.toThrow(); }); diff --git a/packages/2-sql-family/2-authoring/contract-ts/schemas/data-contract-sql-v1.json b/packages/2-sql-family/2-authoring/contract-ts/schemas/data-contract-sql-v1.json index 7b151f2e5396..2cc5a0f29166 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/schemas/data-contract-sql-v1.json +++ b/packages/2-sql-family/2-authoring/contract-ts/schemas/data-contract-sql-v1.json @@ -410,71 +410,6 @@ "required": ["codecId", "members"], "additionalProperties": false } - }, - "valueObjects": { - "type": "object", - "additionalProperties": { - "type": "object", - "properties": { - "fields": { - "type": "object", - "additionalProperties": { - "type": "object", - "properties": { - "nullable": { - "type": "boolean" - }, - "type": {}, - "many": { - "anyOf": [ - { - "type": "object", - "properties": { - "elementNullable": { - "type": "boolean" - } - }, - "required": ["elementNullable"], - "additionalProperties": false - }, - { - "const": false - } - ], - "default": false - }, - "dict": { - "const": true - }, - "valueSet": { - "type": "object", - "properties": { - "entityKind": { - "const": "enum" - }, - "entityName": { - "type": "string" - }, - "namespaceId": { - "type": "string" - }, - "plane": { - "const": "domain" - }, - "spaceId": { - "type": "string" - } - }, - "required": ["entityKind", "entityName", "namespaceId", "plane"] - } - }, - "required": ["nullable", "type"], - "additionalProperties": false - } - } - }, - "required": ["fields"] - } } }, "required": ["models"] @@ -814,6 +749,71 @@ "additionalProperties": false } }, + "valueObject": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "fields": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "nullable": { + "type": "boolean" + }, + "type": {}, + "dict": { + "const": true + }, + "many": { + "anyOf": [ + { + "type": "object", + "properties": { + "elementNullable": { + "type": "boolean" + } + }, + "required": ["elementNullable"], + "additionalProperties": false + }, + { + "const": false + } + ] + }, + "valueSet": { + "type": "object", + "properties": { + "entityKind": { + "const": "valueSet" + }, + "entityName": { + "type": "string" + }, + "namespaceId": { + "type": "string" + }, + "plane": { + "const": "storage" + }, + "spaceId": { + "type": "string" + } + }, + "required": ["entityKind", "entityName", "namespaceId", "plane"] + } + }, + "required": ["nullable", "type"], + "additionalProperties": false + } + } + }, + "required": ["fields"], + "additionalProperties": false + } + }, "valueSet": { "type": "object", "additionalProperties": { From 965c0cf100ecaa9b1b1b8f4f14095b26c7daf80b Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 11:18:21 +0200 Subject: [PATCH 007/104] TML-3537: Mongo contracts store value objects under storage entries.valueObject A MongoValueObject entity kind with its schema; a member typed by an enum refers to the storage value set. A domain field type refers to the value object by its storage coordinate. The domain namespace schema and the contract serializer refuse the old domain valueObjects key with a message that says to re-emit. Readers of a field's value object name use entityName. Signed-off-by: willbot Signed-off-by: Will Madden --- .../mongo-contract/src/contract-refusals.ts | 6 +- .../mongo-contract/src/contract-schema.ts | 49 ++++- .../mongo-contract/src/derive-json-schema.ts | 9 +- .../mongo-contract/src/entity-kinds.ts | 17 +- .../src/exports/entity-kinds.ts | 1 + .../mongo-contract/src/exports/index.ts | 2 + .../mongo-contract/src/ir/mongo-storage.ts | 3 + .../src/ir/mongo-value-object.ts | 27 +++ .../mongo-contract/src/recursive-groups.ts | 2 +- .../test/contract-types.test-d.ts | 70 +++++--- .../test/derive-json-schema-helpers.ts | 14 +- .../mongo-contract/test/field-schema.test.ts | 43 +++-- .../test/validate-domain.test.ts | 1 + .../test/value-object-schema.test.ts | 170 ++++++++++++++++++ .../core/ir/mongo-contract-serializer-base.ts | 14 +- .../test/mongo-contract-json-fixture.ts | 8 +- .../mongo-contract-serializer-base.test.ts | 88 +++++++-- 17 files changed, 446 insertions(+), 78 deletions(-) create mode 100644 packages/2-mongo-family/1-foundation/mongo-contract/src/ir/mongo-value-object.ts create mode 100644 packages/2-mongo-family/1-foundation/mongo-contract/test/value-object-schema.test.ts diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/src/contract-refusals.ts b/packages/2-mongo-family/1-foundation/mongo-contract/src/contract-refusals.ts index 6be2a17995ab..04fbf22245b0 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/src/contract-refusals.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/src/contract-refusals.ts @@ -120,9 +120,9 @@ function validatedEnumNames(input: MongoContractRefusalsInput): ReadonlySet) => { for (const field of Object.values(fields)) { if (field.valueSet?.entityKind === 'enum') enums.add(field.valueSet.entityName); - if (field.type.kind === 'valueObject' && !visitedValueObjects.has(field.type.name)) { - visitedValueObjects.add(field.type.name); - visitFields(valueObjects[field.type.name]?.fields ?? {}); + if (field.type.kind === 'valueObject' && !visitedValueObjects.has(field.type.entityName)) { + visitedValueObjects.add(field.type.entityName); + visitFields(valueObjects[field.type.entityName]?.fields ?? {}); } } }; diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/src/contract-schema.ts b/packages/2-mongo-family/1-foundation/mongo-contract/src/contract-schema.ts index 476393962250..c7f0efaa7141 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/src/contract-schema.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/src/contract-schema.ts @@ -14,7 +14,10 @@ const ScalarFieldTypeSchema = type({ const ValueObjectFieldTypeSchema = type({ '+': 'reject', kind: "'valueObject'", - name: 'string', + plane: "'storage'", + namespaceId: 'string', + entityKind: "'valueObject'", + entityName: 'string', }); const UnionFieldTypeSchema = type({ @@ -62,6 +65,37 @@ const FieldSchema = RawFieldSchema.pipe((field) => ({ many: field.many ?? false, })); +const StorageValueSetRefSchema = type({ + plane: "'storage'", + namespaceId: 'string', + entityKind: "'valueSet'", + entityName: 'string', + 'spaceId?': 'string', +}); + +const RawStorageValueObjectFieldSchema = type({ + '+': 'reject', + type: FieldTypeSchema, + 'nullable?': 'boolean', + 'many?': type('false').or({ elementNullable: 'boolean' }), + 'dict?': 'boolean', + 'valueSet?': StorageValueSetRefSchema, +}); + +/** + * Storage value object entry under `storage.namespaces[id].entries.valueObject[name]` (ADR 278). Its fields have the shape a domain field has; a member typed by an enum refers to the storage value set, since storage never refers to the domain plane. + */ +export const StorageValueObjectSchema = type({ + '+': 'reject', + fields: type({ + '[string]': RawStorageValueObjectFieldSchema.pipe((field) => ({ + ...field, + nullable: field.nullable ?? false, + many: field.many ?? false, + })), + }), +}); + const RelationOnSchema = type({ '+': 'reject', localFields: 'string[]', @@ -418,6 +452,7 @@ export function createMongoNamespaceEnvelopeSchema( entries: type({ 'collection?': type({ '[string]': collectionEntrySchema(fragments) }), 'valueSet?': type({ '[string]': StorageValueSetSchema }), + 'valueObject?': type({ '[string]': StorageValueObjectSchema }), }), }).narrow((ns, ctx) => { if (typeof ns !== 'object' || ns === null || Array.isArray(ns)) { @@ -461,11 +496,15 @@ export function createMongoContractSchema( namespaces: type({ '[string]': type({ models: type({ '[string]': ModelDefinitionSchema }), - 'valueObjects?': type({ - '[string]': type({ '+': 'reject', fields: type({ '[string]': FieldSchema }) }), - }), 'enum?': type({ '[string]': ContractEnumSchema }), - }), + }).narrow((namespace, ctx) => + Object.hasOwn(namespace, 'valueObjects') + ? ctx.reject({ + expected: + 'a domain namespace without valueObjects: contracts no longer store value objects in the domain plane; they are storage entities under storage.namespaces..entries.valueObject, so re-emit the contract from its source', + }) + : true, + ), }), }), storage: type({ diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/src/derive-json-schema.ts b/packages/2-mongo-family/1-foundation/mongo-contract/src/derive-json-schema.ts index 62c7b20deab8..d24d3fea42d9 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/src/derive-json-schema.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/src/derive-json-schema.ts @@ -166,9 +166,14 @@ function fieldToBsonSchema( } if (field.type.kind === 'valueObject') { - const vo = valueObjects[field.type.name]; + const vo = valueObjects[field.type.entityName]; if (!vo) return undefined; - const voSchema = objectSchemaOf('valueObject', field.type.name, valueObjectShape(vo), sources); + const voSchema = objectSchemaOf( + 'valueObject', + field.type.entityName, + valueObjectShape(vo), + sources, + ); if (field.many) { return { bsonType: field.nullable ? ['null', 'array'] : 'array', diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/src/entity-kinds.ts b/packages/2-mongo-family/1-foundation/mongo-contract/src/entity-kinds.ts index a7b53915387a..531bcb695df0 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/src/entity-kinds.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/src/entity-kinds.ts @@ -3,8 +3,13 @@ import type { EntityKindDescriptor, } from '@internal/framework-components/ir'; import { structuredError } from '@internal/utils/structured-error'; -import { StorageCollectionSchema, StorageValueSetSchema } from './contract-schema'; +import { + StorageCollectionSchema, + StorageValueObjectSchema, + StorageValueSetSchema, +} from './contract-schema'; import { MongoCollection, type MongoCollectionInput } from './ir/mongo-collection'; +import { MongoValueObject, type MongoValueObjectInput } from './ir/mongo-value-object'; import { MongoValueSet, type MongoValueSetInput } from './ir/mongo-value-set'; export const collectionEntityKind: EntityKindDescriptor = { @@ -19,9 +24,16 @@ export const valueSetEntityKind: EntityKindDescriptor new MongoValueSet(input), }; +export const valueObjectEntityKind: EntityKindDescriptor = + { + kind: 'valueObject', + schema: StorageValueObjectSchema, + construct: (input) => new MongoValueObject(input), + }; + /** * Assembles the `kind → descriptor` registry for Mongo namespaces: the built-in - * `collection` kind plus any target `packKinds`. This builds the lookup table — + * `collection`, `valueSet` and `valueObject` kinds plus any target `packKinds`. This builds the lookup table — * it does not touch contract data. `hydrateNamespaceEntities` later consumes * this registry to turn a namespace's raw entries into IR instances. Throws on * a duplicate kind. @@ -32,6 +44,7 @@ export function composeMongoEntityKinds( const kinds = new Map([ ['collection', collectionEntityKind], ['valueSet', valueSetEntityKind], + ['valueObject', valueObjectEntityKind], ]); for (const descriptor of packKinds) { if (kinds.has(descriptor.kind)) { diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/src/exports/entity-kinds.ts b/packages/2-mongo-family/1-foundation/mongo-contract/src/exports/entity-kinds.ts index 1de79a414914..05bb30d4a848 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/src/exports/entity-kinds.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/src/exports/entity-kinds.ts @@ -1,5 +1,6 @@ export { collectionEntityKind, composeMongoEntityKinds, + valueObjectEntityKind, valueSetEntityKind, } from '../entity-kinds'; diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/src/exports/index.ts b/packages/2-mongo-family/1-foundation/mongo-contract/src/exports/index.ts index 3514c18f1d26..1690a1877619 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/src/exports/index.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/src/exports/index.ts @@ -111,6 +111,8 @@ export type { MongoValidatorValidationLevel, } from '../ir/mongo-validator'; export { MongoValidator } from '../ir/mongo-validator'; +export type { MongoValueObjectInput } from '../ir/mongo-value-object'; +export { MongoValueObject } from '../ir/mongo-value-object'; export type { MongoValueSetInput } from '../ir/mongo-value-set'; export { MongoValueSet } from '../ir/mongo-value-set'; export { mongoValueSetsOf } from '../mongo-enum'; diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/src/ir/mongo-storage.ts b/packages/2-mongo-family/1-foundation/mongo-contract/src/ir/mongo-storage.ts index de612c82e520..a38d1f972d8e 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/src/ir/mongo-storage.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/src/ir/mongo-storage.ts @@ -6,11 +6,13 @@ import { type Storage, } from '@internal/framework-components/ir'; import type { MongoCollection, MongoCollectionInput } from './mongo-collection'; +import type { MongoValueObject, MongoValueObjectInput } from './mongo-value-object'; import type { MongoValueSet, MongoValueSetInput } from './mongo-value-set'; export type MongoNamespaceEntries = Readonly>>> & { readonly collection?: Readonly>; readonly valueSet?: Readonly>; + readonly valueObject?: Readonly>; }; export interface MongoNamespaceCollectionsInput { @@ -18,6 +20,7 @@ export interface MongoNamespaceCollectionsInput { readonly entries: Readonly>>> & { readonly collection?: Readonly>; readonly valueSet?: Readonly>; + readonly valueObject?: Readonly>; }; } diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/src/ir/mongo-value-object.ts b/packages/2-mongo-family/1-foundation/mongo-contract/src/ir/mongo-value-object.ts new file mode 100644 index 000000000000..d9b48fd6811e --- /dev/null +++ b/packages/2-mongo-family/1-foundation/mongo-contract/src/ir/mongo-value-object.ts @@ -0,0 +1,27 @@ +import type { ContractField } from '@internal/contract/types'; +import { freezeNode, IRNodeBase } from '@internal/framework-components/ir'; + +/** Hydration / construction input shape for {@link MongoValueObject}; mirrors the stored JSON. */ +export interface MongoValueObjectInput { + readonly fields: Readonly>; +} + +/** + * Mongo Contract IR node for a value object in a namespace's `valueObject` map (`entries.valueObject`): the fields every subdocument that holds it has (ADR 278). Same shape as SQL's `StorageValueObject`. A domain field refers to it by its storage coordinate. The `kind` is not enumerable, so the stored JSON holds the fields only, as in SQL. Value objects are keyed by name in the parent namespace's `valueObject` map, so the entry name is not on the class. + */ +export class MongoValueObject extends IRNodeBase { + declare readonly kind: 'mongo-value-object'; + readonly fields: Readonly>; + + constructor(input: MongoValueObjectInput) { + super(); + Object.defineProperty(this, 'kind', { + value: 'mongo-value-object', + writable: false, + enumerable: false, + configurable: false, + }); + this.fields = Object.freeze({ ...input.fields }); + freezeNode(this); + } +} diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/src/recursive-groups.ts b/packages/2-mongo-family/1-foundation/mongo-contract/src/recursive-groups.ts index 29681cebcf01..20982bbd424b 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/src/recursive-groups.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/src/recursive-groups.ts @@ -8,7 +8,7 @@ export function typeKey(kind: 'valueObject' | 'model', name: string): string { function referencesOf(fields: Record): string[] { return Object.values(fields).flatMap((field) => - field.type.kind === 'valueObject' ? [typeKey('valueObject', field.type.name)] : [], + field.type.kind === 'valueObject' ? [typeKey('valueObject', field.type.entityName)] : [], ); } diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/test/contract-types.test-d.ts b/packages/2-mongo-family/1-foundation/mongo-contract/test/contract-types.test-d.ts index 233564c4c00f..546fef8d34d6 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/test/contract-types.test-d.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/test/contract-types.test-d.ts @@ -198,16 +198,34 @@ type ContractWithVO = MongoContractWithTypeMaps< readonly homeAddress: { readonly nullable: false; readonly many: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; }; readonly workAddress: { readonly nullable: true; readonly many: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; }; readonly previousAddresses: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; readonly many: { readonly elementNullable: false }; }; }; @@ -215,27 +233,6 @@ type ContractWithVO = MongoContractWithTypeMaps< readonly storage: { readonly collection: 'users' }; }; }; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly street: { - readonly nullable: false; - readonly many: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly city: { - readonly nullable: false; - readonly many: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly many: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - }; }; }; }; @@ -244,7 +241,30 @@ type ContractWithVO = MongoContractWithTypeMaps< readonly __unbound__: { readonly id: '__unbound__'; readonly kind: 'mongo-namespace'; - readonly entries: { readonly collection: { readonly users: MongoCollection } }; + readonly entries: { + readonly collection: { readonly users: MongoCollection }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly street: { + readonly nullable: false; + readonly many: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly city: { + readonly nullable: false; + readonly many: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly many: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + }; + }; }; }; readonly storageHash: StorageHashBase<'test-storage'>; diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/test/derive-json-schema-helpers.ts b/packages/2-mongo-family/1-foundation/mongo-contract/test/derive-json-schema-helpers.ts index 4c78e3f0635e..56f774d5fe42 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/test/derive-json-schema-helpers.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/test/derive-json-schema-helpers.ts @@ -45,8 +45,18 @@ export function arrayEnumField( }; } +function valueObjectType(entityName: string): ContractField['type'] { + return { + kind: 'valueObject', + plane: 'storage', + namespaceId: '__unbound__', + entityKind: 'valueObject', + entityName, + }; +} + export function voField(name: string, nullable = false): ContractField { - return { type: { kind: 'valueObject', name }, nullable, many: false }; + return { type: valueObjectType(name), nullable, many: false }; } export function voArrayField( @@ -54,5 +64,5 @@ export function voArrayField( nullable = false, elementNullable = false, ): ContractField { - return { type: { kind: 'valueObject', name }, nullable, many: { elementNullable } }; + return { type: valueObjectType(name), nullable, many: { elementNullable } }; } diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/test/field-schema.test.ts b/packages/2-mongo-family/1-foundation/mongo-contract/test/field-schema.test.ts index 73dd4755b44d..76632d7bbbda 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/test/field-schema.test.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/test/field-schema.test.ts @@ -2,24 +2,35 @@ import { type } from 'arktype'; import { describe, expect, it } from 'vitest'; import { MongoContractSchema } from '../src/contract-schema'; -function contractWithField(kind: 'models' | 'valueObjects', field: Record) { - const entry = - kind === 'models' - ? { fields: { name: field }, storage: { fields: { name: { field: 'name' } } } } - : { fields: { name: field } }; - return { - targetFamily: 'mongo', - roots: {}, - domain: { - namespaces: { - app: { models: {}, [kind]: { Item: entry } }, - }, - }, - storage: { namespaces: {} }, - }; +function contractWithField(kind: 'model' | 'storage value object', field: Record) { + return kind === 'model' + ? { + targetFamily: 'mongo', + roots: {}, + domain: { + namespaces: { + app: { + models: { + Item: { fields: { name: field }, storage: { fields: { name: { field: 'name' } } } }, + }, + }, + }, + }, + storage: { namespaces: {} }, + } + : { + targetFamily: 'mongo', + roots: {}, + domain: { namespaces: { app: { models: {} } } }, + storage: { + namespaces: { + app: { id: 'app', entries: { valueObject: { Item: { fields: { name: field } } } } }, + }, + }, + }; } -describe.each(['models', 'valueObjects'] as const)('%s field schema', (kind) => { +describe.each(['model', 'storage value object'] as const)('%s field schema', (kind) => { const fieldType = { kind: 'scalar', codecId: 'mongo/string@1' }; it('normalizes an absent many property to false', () => { diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/test/validate-domain.test.ts b/packages/2-mongo-family/1-foundation/mongo-contract/test/validate-domain.test.ts index 1375971797fc..2a8094edc54a 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/test/validate-domain.test.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/test/validate-domain.test.ts @@ -51,6 +51,7 @@ function makeValidContract(overrides: Record = {}): DomainContr domainOverride !== undefined ? (domainOverride as DomainContractShape['domain']) : applicationDomainOf({ models }), + storage: { namespaces: {} }, ...rest, } as DomainContractShape; } diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/test/value-object-schema.test.ts b/packages/2-mongo-family/1-foundation/mongo-contract/test/value-object-schema.test.ts new file mode 100644 index 000000000000..777eb8b209af --- /dev/null +++ b/packages/2-mongo-family/1-foundation/mongo-contract/test/value-object-schema.test.ts @@ -0,0 +1,170 @@ +import { + type DomainContractShape, + validateContractDomain, +} from '@internal/contract/validate-domain'; +import { hydrateNamespaceEntities } from '@internal/framework-components/ir'; +import { blindCast } from '@internal/utils/casts'; +import { type } from 'arktype'; +import { describe, expect, it } from 'vitest'; +import { MongoContractSchema } from '../src/contract-schema'; +import { composeMongoEntityKinds } from '../src/entity-kinds'; +import { MongoValueObject } from '../src/ir/mongo-value-object'; + +const NAMESPACE = '__unbound__'; +const text = { + type: { kind: 'scalar', codecId: 'mongo/string@1' }, + nullable: false, + many: false, +} as const; + +function valueObjectType(name: string) { + return { + kind: 'valueObject', + plane: 'storage', + namespaceId: NAMESPACE, + entityKind: 'valueObject', + entityName: name, + }; +} + +const address = { fields: { street: text, city: text } }; + +function contractWith( + addressField: Record, + valueObject: Record = { Address: address }, +) { + return { + targetFamily: 'mongo', + roots: { users: { namespace: NAMESPACE, model: 'User' } }, + domain: { + namespaces: { + [NAMESPACE]: { + models: { + User: { + fields: { + _id: { ...text, type: { kind: 'scalar', codecId: 'mongo/objectId@1' } }, + address: addressField, + }, + relations: {}, + storage: { collection: 'users', fields: { address: { field: 'address' } } }, + }, + }, + }, + }, + }, + storage: { + namespaces: { + [NAMESPACE]: { id: NAMESPACE, entries: { collection: { users: {} }, valueObject } }, + }, + }, + }; +} + +describe('MongoValueObject', () => { + it('holds its fields, frozen, and serializes to them', () => { + const valueObject = new MongoValueObject(address); + expect({ + json: JSON.parse(JSON.stringify(valueObject)), + frozen: Object.isFrozen(valueObject), + }).toEqual({ json: address, frozen: true }); + }); + + it('is hydrated from the valueObject slot', () => { + const hydrated = hydrateNamespaceEntities( + { valueObject: { Address: address } }, + composeMongoEntityKinds(), + 'fail', + ); + expect(hydrated['valueObject']?.['Address']).toBeInstanceOf(MongoValueObject); + }); +}); + +describe('the Mongo contract schema with value objects in storage', () => { + it('accepts a field that refers to a storage value object, and nested and self references', () => { + const contract = contractWith( + { type: valueObjectType('Profile'), nullable: false, many: false }, + { + Address: address, + Profile: { + fields: { + home: { type: valueObjectType('Address'), nullable: true, many: false }, + parent: { type: valueObjectType('Profile'), nullable: true, many: false }, + }, + }, + }, + ); + expect(MongoContractSchema(contract)).toEqual(contract); + }); + + it('normalizes an absent many on a storage value object member to false', () => { + const contract = contractWith( + { type: valueObjectType('Address'), nullable: false, many: false }, + { Address: { fields: { street: { type: text.type } } } }, + ); + expect(MongoContractSchema(contract)).toEqual( + contractWith( + { type: valueObjectType('Address'), nullable: false, many: false }, + { Address: { fields: { street: text } } }, + ), + ); + }); + + it('refuses a field type that names its value object the old way', () => { + expect( + MongoContractSchema( + contractWith({ type: { kind: 'valueObject', name: 'Address' }, nullable: false }), + ), + ).toBeInstanceOf(type.errors); + }); + + it('refuses a member value set that points into the domain plane', () => { + const contract = contractWith( + { type: valueObjectType('Address'), nullable: false }, + { + Address: { + fields: { + kind: { + ...text, + valueSet: { + plane: 'domain', + namespaceId: NAMESPACE, + entityKind: 'enum', + entityName: 'Kind', + }, + }, + }, + }, + }, + ); + expect(MongoContractSchema(contract)).toBeInstanceOf(type.errors); + }); + + it('refuses value objects in the domain plane', () => { + const contract = contractWith({ type: valueObjectType('Address'), nullable: false }); + const old = { + ...contract, + domain: { + namespaces: { + [NAMESPACE]: { + ...contract.domain.namespaces[NAMESPACE], + valueObjects: { Address: address }, + }, + }, + }, + }; + expect(MongoContractSchema(old)).toBeInstanceOf(type.errors); + }); + + it('lets domain validation refuse a reference to a value object storage does not have', () => { + const parsed = MongoContractSchema( + contractWith({ type: valueObjectType('Missing'), nullable: false }), + ); + expect(() => + validateContractDomain( + blindCast(parsed), + ), + ).toThrow( + 'Model "__unbound__:User" field "address" refers to value object "Missing" in storage namespace "__unbound__", which storage.namespaces.__unbound__.entries.valueObject does not have', + ); + }); +}); diff --git a/packages/2-mongo-family/9-family/src/core/ir/mongo-contract-serializer-base.ts b/packages/2-mongo-family/9-family/src/core/ir/mongo-contract-serializer-base.ts index 5297c7b54cf9..f5adc367b399 100644 --- a/packages/2-mongo-family/9-family/src/core/ir/mongo-contract-serializer-base.ts +++ b/packages/2-mongo-family/9-family/src/core/ir/mongo-contract-serializer-base.ts @@ -1,5 +1,8 @@ import type { ContractModel } from '@internal/contract/types'; -import { validateContractDomain } from '@internal/contract/validate-domain'; +import { + removedDomainKeyProblems, + validateContractDomain, +} from '@internal/contract/validate-domain'; import { withDerivedToOneRelationNullability } from '@internal/contract-authoring'; import type { ContractSerializer } from '@internal/framework-components/control'; import { @@ -120,11 +123,18 @@ export abstract class MongoContractSerializerBase * `storage.namespaces[namespaceId].entries.collection[collectionName]` (each value is a * `MongoCollection`, with nested `MongoIndex` / `MongoValidator` / * `MongoCollectionOptions` constructed by the `MongoCollection` constructor). - * The rest of the contract envelope (models, valueObjects, capabilities, …) + * The rest of the contract envelope (models, capabilities, …) * remains in plain-JSON form; those IR layers are handled by sibling * subsystems and don't sit behind this SPI. */ protected parseMongoContractStructure(json: unknown): MongoContract { + const removedKeys = removedDomainKeyProblems(json); + if (removedKeys.length > 0) { + throw structuredError( + 'CONTRACT.VALIDATION_FAILED', + `Contract structural validation failed: ${removedKeys.join('; ')}`, + ); + } const schema = this.contractSchema ?? MongoContractSchema; const parsed = schema(json); if (parsed instanceof arktypeType.errors) { diff --git a/packages/2-mongo-family/9-family/test/mongo-contract-json-fixture.ts b/packages/2-mongo-family/9-family/test/mongo-contract-json-fixture.ts index 25c2ef4172f5..adefc783d5f8 100644 --- a/packages/2-mongo-family/9-family/test/mongo-contract-json-fixture.ts +++ b/packages/2-mongo-family/9-family/test/mongo-contract-json-fixture.ts @@ -20,6 +20,7 @@ function normalizeModels( export function mongoContractJson(params: { readonly models?: Record; readonly storageCollections?: Record; + readonly storageValueObjects?: Record; readonly roots?: Record>; }) { const models = normalizeModels( @@ -47,7 +48,12 @@ export function mongoContractJson(params: { namespaces: { [UNBOUND_NAMESPACE_ID]: { id: UNBOUND_NAMESPACE_ID, - entries: { collection: collections }, + entries: { + collection: collections, + ...(params.storageValueObjects === undefined + ? {} + : { valueObject: params.storageValueObjects }), + }, }, }, }, diff --git a/packages/2-mongo-family/9-family/test/mongo-contract-serializer-base.test.ts b/packages/2-mongo-family/9-family/test/mongo-contract-serializer-base.test.ts index e8a68943e51a..1a506eea175e 100644 --- a/packages/2-mongo-family/9-family/test/mongo-contract-serializer-base.test.ts +++ b/packages/2-mongo-family/9-family/test/mongo-contract-serializer-base.test.ts @@ -76,33 +76,83 @@ describe('MongoContractSerializerBase', () => { expect(() => serializer.deserializeContract(json)).toThrow(/missing_collection/); }); - it('rejects when a field references a value object that does not exist', () => { - const serializer = new RecordingSerializer(); - const json = mongoContractJson({ - models: { - Item: { - fields: { - _id: { - type: { kind: 'scalar', codecId: 'mongo/objectId@1' }, - nullable: false, - many: false, + describe('given a model field that holds a value object', () => { + function contractJsonWith(storageValueObjects: Record) { + return mongoContractJson({ + models: { + Item: { + fields: { + _id: { + type: { kind: 'scalar', codecId: 'mongo/objectId@1' }, + nullable: false, + many: false, + }, + data: { + type: { + kind: 'valueObject', + plane: 'storage', + namespaceId: '__unbound__', + entityKind: 'valueObject', + entityName: 'Address', + }, + nullable: false, + many: false, + }, }, - data: { - type: { kind: 'valueObject', name: 'Missing' }, + storage: { + fields: { _id: { field: '_id' }, data: { field: 'data' } }, + collection: 'items', + }, + relations: {}, + }, + }, + storageValueObjects, + }); + } + + const address = { + fields: { + city: { type: { kind: 'scalar', codecId: 'mongo/string@1' }, nullable: false }, + }, + }; + + it('accepts it when storage has the value object', () => { + const result = new RecordingSerializer().deserializeContract( + contractJsonWith({ Address: address }), + ); + expect(result.contract.storage.namespaces['__unbound__']?.entries['valueObject']).toEqual({ + Address: { + fields: { + city: { + type: { kind: 'scalar', codecId: 'mongo/string@1' }, nullable: false, many: false, }, }, - storage: { - fields: { _id: { field: '_id' }, data: { field: 'data' } }, - collection: 'items', - }, - relations: {}, }, - }, + }); }); - expect(() => serializer.deserializeContract(json)).toThrow(); + it('refuses it when storage does not have the value object, naming the field and the coordinate', () => { + expect(() => new RecordingSerializer().deserializeContract(contractJsonWith({}))).toThrow( + 'Model "__unbound__:Item" field "data" refers to value object "Address" in storage namespace "__unbound__", which storage.namespaces.__unbound__.entries.valueObject does not have', + ); + }); + + it('refuses a contract that stores value objects in the domain plane, and says to re-emit', () => { + const json = contractJsonWith({ Address: address }); + const old = { + ...json, + domain: { + namespaces: { + __unbound__: { ...json.domain.namespaces['__unbound__'], valueObjects: {} }, + }, + }, + }; + expect(() => new RecordingSerializer().deserializeContract(old)).toThrow( + 'Contract structural validation failed: domain.namespaces.__unbound__.valueObjects: contracts no longer store value objects in the domain plane; they are storage entities under storage.namespaces..entries.valueObject, so re-emit the contract from its source', + ); + }); }); it('rejects an unknown entries kind at hydration naming the kind and the namespace id', () => { From 4f7338ecb99917f24d6bcab9dd57218a451307b4 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 11:40:57 +0200 Subject: [PATCH 008/104] TML-3537: SQL authoring writes value objects and discriminator value sets into storage The SQL build writes each value object to entries.valueObject of the default namespace before the storage hash, and fields and nested members refer to it by its storage coordinate; an enum member refers to the storage value set. A model node now carries its discriminator, variants and base, so the build writes the . value set and points the discriminator column at it. An enum-typed discriminator keeps the enum's value set, and an enum named like the discriminator's value set is refused. SQL PSL pairs variants before the build instead of patching them in afterwards. Signed-off-by: willbot Signed-off-by: Will Madden --- .../contract-psl/src/interpreter.ts | 151 ++++++++++----- .../2-authoring/contract-psl/test/fixtures.ts | 26 ++- .../test/interpreter.alias-expansion.test.ts | 9 +- ...ter.list-field-equals-single-field.test.ts | 3 +- .../interpreter.nullable-list-shapes.test.ts | 3 +- .../test/interpreter.storage-plane.test.ts | 182 ++++++++++++++++++ .../interpreter.value-object-domain.test.ts | 40 +++- .../interpreter.value-object-storage.test.ts | 25 +-- .../contract-ts/src/build-contract.ts | 108 +++++++++-- .../contract-ts/src/contract-definition.ts | 8 + .../contract-ts/src/describe-model.ts | 3 + .../contract-ts/src/domain-fields.ts | 41 +++- ...ct-builder.discriminator-value-set.test.ts | 124 ++++++++++++ ...ntract-builder.value-object-domain.test.ts | 46 +++-- .../contract-ts/test/contract-test-helpers.ts | 17 +- 15 files changed, 648 insertions(+), 138 deletions(-) create mode 100644 packages/2-sql-family/2-authoring/contract-psl/test/interpreter.storage-plane.test.ts create mode 100644 packages/2-sql-family/2-authoring/contract-ts/test/contract-builder.discriminator-value-set.test.ts diff --git a/packages/2-sql-family/2-authoring/contract-psl/src/interpreter.ts b/packages/2-sql-family/2-authoring/contract-psl/src/interpreter.ts index 858a54db429d..b6060548bb6f 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/src/interpreter.ts +++ b/packages/2-sql-family/2-authoring/contract-psl/src/interpreter.ts @@ -5,6 +5,7 @@ import type { } from '@internal/config/config-types'; import type { Contract, + ContractDiscriminator, ContractModel, ControlPolicy, CrossReference, @@ -1781,31 +1782,35 @@ function modelLabel(namespaceId: string, modelName: string, qualified: boolean): return qualified ? `${namespaceId}.${modelName}` : modelName; } -function resolvePolymorphism( - models: Record, - physicalNames: ReadonlyMap, +/** What `@@discriminator` and `@@base` declare, by model key, as the build writes it into the domain models and the storage. */ +type ResolvedPolymorphism = { + readonly discriminators: ReadonlyMap< + string, + { + readonly discriminator: ContractDiscriminator; + readonly variants: Record; + } + >; + readonly bases: ReadonlyMap; +}; + +/** + * Pairs each `@@discriminator` with the variants that declare `@@base` on it, before the build, so the build writes the discriminator's value set before it computes the storage hash (ADR 278). Reports a model with both attributes, a discriminator no variant names, a duplicate value, and a base with no discriminator. + */ +function resolveVariants( + modelKeys: ReadonlySet, discriminatorDeclarations: Map, baseDeclarations: Map, reported: ReportedPolymorphism, - syntheticPkFieldsByVariant: ReadonlyMap, - stiBaseFieldsByBase: ReadonlyMap, diagnostics: PslDiagnosticCollector, -): Record { - let patched = models; - - // STI variant columns were materialised onto the base storage table so the - // variants' `storage.fields` resolve. They are storage-only on the base — the - // domain field belongs to the variant — so strip them from the base model's - // domain + storage field maps (the table column, built upstream, stays). - for (const [baseKey, fieldNames] of stiBaseFieldsByBase) { - const baseModel = patched[baseKey]; - if (!baseModel || fieldNames.length === 0) continue; - patched = { - ...patched, - [baseKey]: stripStorageOnlyDomainFields(baseModel, fieldNames), - }; - } - +): ResolvedPolymorphism { + const discriminators = new Map< + string, + { + readonly discriminator: ContractDiscriminator; + readonly variants: Record; + } + >(); for (const [modelKey, decl] of discriminatorDeclarations) { const modelName = decl.model.name; if (baseDeclarations.has(modelKey)) { @@ -1816,16 +1821,12 @@ function resolvePolymorphism( }); continue; } - - const model = patched[modelKey]; - if (!model) continue; + if (!modelKeys.has(modelKey)) continue; const variants: Record = {}; - for (const baseDecl of baseDeclarations.values()) { if (baseDecl.base.key !== modelKey || baseDecl.value === undefined) continue; const variantName = baseDecl.model.name; - const existingVariant = variants[baseDecl.value]; if (existingVariant) { const qualified = @@ -1853,20 +1854,16 @@ function resolvePolymorphism( }); continue; } - - patched = { - ...patched, - [modelKey]: { ...model, discriminator: { field: decl.fieldName }, variants }, - }; + discriminators.set(modelKey, { discriminator: { field: decl.fieldName }, variants }); } + const bases = new Map(); for (const [variantKey, baseDecl] of baseDeclarations) { - const baseName = baseDecl.base.model.name; if (!discriminatorDeclarations.has(baseDecl.base.key)) { if (reported.discriminators.has(baseDecl.base.key)) continue; const qualified = baseDecl.namespaceId !== baseDecl.base.namespaceId; const variantLabel = modelLabel(baseDecl.namespaceId, baseDecl.model.name, qualified); - const baseLabel = modelLabel(baseDecl.base.namespaceId, baseName, qualified); + const baseLabel = modelLabel(baseDecl.base.namespaceId, baseDecl.base.model.name, qualified); diagnostics.push({ code: 'PSL_ORPHANED_BASE', message: `Model "${variantLabel}" declares @@base(${baseLabel}, ...) but "${baseLabel}" has no @@discriminator`, @@ -1874,13 +1871,56 @@ function resolvePolymorphism( }); continue; } + if (discriminatorDeclarations.has(variantKey) || !modelKeys.has(variantKey)) continue; + bases.set(variantKey, crossRef(baseDecl.base.model.name, baseDecl.base.namespaceId)); + } + return { discriminators, bases }; +} - if (discriminatorDeclarations.has(variantKey)) { - continue; - } +/** The model nodes with the discriminator, variants and base the build writes. */ +function withPolymorphism( + modelNodes: readonly ModelNode[], + polymorphism: ResolvedPolymorphism, + defaultNamespaceId: string, +): ModelNode[] { + return modelNodes.map((node) => { + const key = modelCoordinateKey(node.namespaceId ?? defaultNamespaceId, node.modelName); + const discriminator = polymorphism.discriminators.get(key); + const base = polymorphism.bases.get(key); + return { + ...node, + ...(discriminator ?? {}), + ...ifDefined('base', base), + }; + }); +} + +/** + * Points each variant without `@@map` at its base table, and strips from the domain the columns a variant's or a base's model only stores: STI columns on the base, and the link column of an MTI variant. The discriminator, the variants and the base are written by the build. + */ +function resolveVariantStorage( + models: Record, + physicalNames: ReadonlyMap, + baseDeclarations: Map, + polymorphism: ResolvedPolymorphism, + syntheticPkFieldsByVariant: ReadonlyMap, + stiBaseFieldsByBase: ReadonlyMap, +): Record { + let patched = models; + for (const [baseKey, fieldNames] of stiBaseFieldsByBase) { + const baseModel = patched[baseKey]; + if (!baseModel || fieldNames.length === 0) continue; + patched = { + ...patched, + [baseKey]: stripStorageOnlyDomainFields(baseModel, fieldNames), + }; + } + + for (const variantKey of polymorphism.bases.keys()) { + const baseDecl = baseDeclarations.get(variantKey); const variantModel = patched[variantKey]; - if (!variantModel) continue; + if (!baseDecl || !variantModel) continue; const hasExplicitMap = getAttribute(baseDecl.model.attributes, 'map') !== undefined; const resolvedTable = storageName( @@ -1890,7 +1930,6 @@ function resolvePolymorphism( const patchedVariant: ContractModel = { ...variantModel, - base: crossRef(baseName, baseDecl.base.namespaceId), ...(resolvedTable ? { storage: { ...variantModel.storage, table: resolvedTable } } : {}), }; @@ -2703,6 +2742,25 @@ export function interpretPslDocumentToSqlContract( }); } + const polyDiagnostics = createPslDiagnosticCollector(input.sources); + const polymorphism = resolveVariants( + new Set( + stiColumnModelNodes.map((node) => + modelCoordinateKey(node.namespaceId ?? defaultNamespaceId, node.modelName), + ), + ), + discriminatorDeclarations, + baseDeclarations, + reported, + polyDiagnostics, + ); + if (polyDiagnostics.length > 0) { + return notOk({ + summary: 'PSL to SQL contract interpretation failed', + diagnostics: polyDiagnostics.toExternal(), + }); + } + // Merge lowered extension-block entities into each namespace's entries. // `valueSet` is unioned rather than overwritten: a namespace may derive // value-sets from both a PSL `enum` block and an extension entity, and both @@ -2769,7 +2827,7 @@ export function interpretPslDocumentToSqlContract( : {}), createNamespace: createNamespaceWithExtensions, ...ifDefined('valueObjects', valueObjects.length > 0 ? valueObjects : undefined), - models: stiColumnModelNodes, + models: withPolymorphism(stiColumnModelNodes, polymorphism, defaultNamespaceId), }, input.codecLookup, input.dataTypes.lookup, @@ -2802,25 +2860,15 @@ export function interpretPslDocumentToSqlContract( } } - const polyDiagnostics = createPslDiagnosticCollector(input.sources); - const polymorphicModels = resolvePolymorphism( + const polymorphicModels = resolveVariantStorage( modelsByCoordinate, physicalNames, - discriminatorDeclarations, baseDeclarations, - reported, + polymorphism, syntheticPkFieldsByVariant, stiBaseFieldsByBase, - polyDiagnostics, ); - if (polyDiagnostics.length > 0) { - return notOk({ - summary: 'PSL to SQL contract interpretation failed', - diagnostics: polyDiagnostics.toExternal(), - }); - } - const filteredRoots = Object.fromEntries( Object.entries(contract.roots).filter( ([, crossReference]) => @@ -2843,7 +2891,6 @@ export function interpretPslDocumentToSqlContract( ]), ), ...ifDefined('enum', namespaceSlice.enum), - ...ifDefined('valueObjects', namespaceSlice.valueObjects), }, ]), ), diff --git a/packages/2-sql-family/2-authoring/contract-psl/test/fixtures.ts b/packages/2-sql-family/2-authoring/contract-psl/test/fixtures.ts index 59a5a3040b5f..a2e3684bf20e 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/test/fixtures.ts +++ b/packages/2-sql-family/2-authoring/contract-psl/test/fixtures.ts @@ -4,10 +4,7 @@ import type { ContractSourceDiagnostics, } from '@internal/config/config-types'; import type { Contract, JsonValue } from '@internal/contract/types'; -import { - domainModelsAtDefaultNamespace, - domainValueObjectsAtDefaultNamespace, -} from '@internal/contract/types'; +import { type ContractValueObject, domainModelsAtDefaultNamespace } from '@internal/contract/types'; import { type AuthoringContributions, type AuthoringEntityContext, @@ -598,8 +595,25 @@ export function modelsOf(contract: Contract) { return domainModelsAtDefaultNamespace(contract.domain); } -export function valueObjectsOf(contract: Contract) { - return domainValueObjectsAtDefaultNamespace(contract.domain); +/** A field type that refers to the storage value object `entityName` in `namespaceId`. */ +export function valueObjectRef(entityName: string, namespaceId = 'public') { + return { + kind: 'valueObject', + plane: 'storage', + namespaceId, + entityKind: 'valueObject', + entityName, + } as const; +} + +/** The storage value objects of every namespace, as plain JSON; `undefined` when the contract has none. */ +export function valueObjectsOf( + contract: Contract, +): Record | undefined { + const slots = Object.values(contract.storage.namespaces).flatMap((namespace) => + namespace.entries['valueObject'] === undefined ? [] : [namespace.entries['valueObject']], + ); + return slots.length === 0 ? undefined : JSON.parse(JSON.stringify(Object.assign({}, ...slots))); } export function documentScopedTypes(contract: { readonly storage?: unknown }) { diff --git a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.alias-expansion.test.ts b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.alias-expansion.test.ts index 0f3a42fc3885..dfa62f248f41 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.alias-expansion.test.ts +++ b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.alias-expansion.test.ts @@ -6,6 +6,7 @@ import { postgresNativeScalarTypeDescriptors, testEnumEntityContributions, testEnumPslBlockDescriptor, + valueObjectsOf, } from './fixtures'; import { sqlStorageFromSuccessfulSqlInterpretation } from './interpret-sql-contract-storage'; import { @@ -222,7 +223,7 @@ model Note { @@base(Post, "note") }`); expect({ - member: contract.domain.namespaces['public']?.valueObjects?.['Label']?.fields['code'], + member: valueObjectsOf(contract)?.['Label']?.fields['code'], discriminator: unboundTables(sqlStorageFromSuccessfulSqlInterpretation(contract))['Post'] ?.columns['kind'], types: documentScopedTypes(contract), @@ -238,6 +239,12 @@ model Note { nullable: false, many: false, typeParams: { length: 8 }, + valueSet: { + plane: 'storage', + entityKind: 'valueSet', + namespaceId: 'public', + entityName: 'Post.kind', + }, }, types: undefined, }); diff --git a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.list-field-equals-single-field.test.ts b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.list-field-equals-single-field.test.ts index c4aff9f34ed6..69d2c76a8b8e 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.list-field-equals-single-field.test.ts +++ b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.list-field-equals-single-field.test.ts @@ -13,6 +13,7 @@ import { postgresTarget, testEnumEntityContributions, testEnumPslBlockDescriptor, + valueObjectsOf, } from './fixtures'; const numeric = { @@ -142,7 +143,7 @@ model Product { expect(result.ok).toBe(true); if (!result.ok) return; - expect(result.value.domain.namespaces['public']?.valueObjects?.['Price']?.fields).toEqual({ + expect(valueObjectsOf(result.value)?.['Price']?.fields).toEqual({ one: { nullable: false, type: numeric, many: false }, many: { nullable: false, type: numeric, many: { elementNullable: false } }, }); diff --git a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.nullable-list-shapes.test.ts b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.nullable-list-shapes.test.ts index 5318a50a71e8..80b2c7537d91 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.nullable-list-shapes.test.ts +++ b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.nullable-list-shapes.test.ts @@ -11,6 +11,7 @@ import { postgresScalarAuthoringTypes, postgresScalarTypeDescriptors, postgresTarget, + valueObjectRef, valueObjectsOf, } from './fixtures'; @@ -221,7 +222,7 @@ model User { expect(model?.fields['addresses']).toEqual({ nullable: false, - type: { kind: 'valueObject', name: 'Address' }, + type: valueObjectRef('Address'), many: { elementNullable: true }, }); expect(addressesColumn).toEqual({ diff --git a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.storage-plane.test.ts b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.storage-plane.test.ts new file mode 100644 index 000000000000..bb08dba6ca48 --- /dev/null +++ b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.storage-plane.test.ts @@ -0,0 +1,182 @@ +import { describe, expect, it } from 'vitest'; +import { createTestSqlNamespace } from '../../../1-core/contract/test/test-support'; +import { fixtureInterpreterTypes } from './fixture-codec-descriptors'; +import { fixtureDataTypeSupport } from './fixture-data-types'; +import { + createBuiltinLikeControlMutationDefaults, + interpretSqlContract, + postgresScalarAuthoringTypes, + postgresScalarTypeDescriptors, + postgresTarget, + testEnumEntityContributions, + testEnumPslBlockDescriptor, +} from './fixtures'; + +function interpretPostgres(schema: string) { + const result = interpretSqlContract(schema, { + target: postgresTarget, + scalarColumnDescriptors: postgresScalarTypeDescriptors, + authoringContributions: { + type: postgresScalarAuthoringTypes, + entityTypes: testEnumEntityContributions, + pslBlockDescriptors: { enum: testEnumPslBlockDescriptor }, + dataTypes: fixtureDataTypeSupport.entries, + valueObjectStorageType: 'Jsonb', + }, + ...fixtureInterpreterTypes, + composedExtensionContracts: new Map(), + createNamespace: createTestSqlNamespace, + capabilities: { sql: { scalarList: true } }, + controlMutationDefaults: createBuiltinLikeControlMutationDefaults(), + }); + if (!result.ok) throw new Error(JSON.stringify(result.failure.diagnostics)); + return JSON.parse(JSON.stringify(result.value)); +} + +const text = { nullable: false, many: false, type: { kind: 'scalar', codecId: 'pg/text@1' } }; + +function valueObjectRef(name: string) { + return { + kind: 'valueObject', + plane: 'storage', + namespaceId: 'public', + entityKind: 'valueObject', + entityName: name, + }; +} + +const countryEnum = `enum Country { + @@type("pg/text@1") + DE = "DE" + FR = "FR" +} +`; + +const addressSchema = (street: string) => `${countryEnum} +type Geo { + lat Float +} + +type Address { + ${street} String + tags String[] + country Country + geo Geo? +} + +model User { + id Int @id + home Address +}`; + +describe('SQL PSL value objects in the storage plane', () => { + it('writes a composite type to storage, and the field refers to it by coordinate', () => { + const contract = interpretPostgres(addressSchema('street')); + expect({ + field: contract.domain.namespaces.public.models.User.fields.home, + valueObjects: contract.storage.namespaces.public.entries.valueObject, + domainKeys: Object.keys(contract.domain.namespaces.public).sort(), + }).toEqual({ + field: { nullable: false, many: false, type: valueObjectRef('Address') }, + valueObjects: { + Address: { + fields: { + street: text, + tags: { ...text, many: { elementNullable: false } }, + country: { + ...text, + valueSet: { + plane: 'storage', + entityKind: 'valueSet', + namespaceId: 'public', + entityName: 'Country', + }, + }, + geo: { nullable: true, many: false, type: valueObjectRef('Geo') }, + }, + }, + Geo: { + fields: { + lat: { nullable: false, many: false, type: { kind: 'scalar', codecId: 'pg/float8@1' } }, + }, + }, + }, + domainKeys: ['enum', 'models'], + }); + }); + + it('changes the storage hash when a member changes', () => { + expect(interpretPostgres(addressSchema('street')).storage.storageHash).not.toBe( + interpretPostgres(addressSchema('line1')).storage.storageHash, + ); + }); +}); + +const taskSchema = (variants: string) => `model Task { + id Int @id + type String + + @@discriminator(type) +} + +${variants}`; + +const bug = `model Bug { + severity Int + @@base(Task, "bug") +} +`; +const feature = `model Feature { + priority Int + @@base(Task, "feature") +} +`; + +describe('SQL PSL discriminator value sets', () => { + it('writes a . value set of the variant values, and the column refers to it', () => { + const contract = interpretPostgres(taskSchema(`${bug}\n${feature}`)); + const namespace = contract.storage.namespaces.public; + expect({ + valueSet: namespace.entries.valueSet?.['Task.type'], + column: namespace.entries.table.Task.columns.type.valueSet, + discriminator: contract.domain.namespaces.public.models.Task.discriminator, + }).toEqual({ + valueSet: { kind: 'valueSet', values: ['bug', 'feature'] }, + column: { + plane: 'storage', + entityKind: 'valueSet', + namespaceId: 'public', + entityName: 'Task.type', + }, + discriminator: { field: 'type' }, + }); + }); + + it("changes the storage hash when a variant's value changes", () => { + expect(interpretPostgres(taskSchema(bug)).storage.storageHash).not.toBe( + interpretPostgres(taskSchema(bug.replace('"bug"', '"defect"'))).storage.storageHash, + ); + }); + + it('keeps the enum value set of an enum-typed discriminator and writes no second one', () => { + const contract = interpretPostgres(`enum Kind { + @@type("pg/text@1") + Bug = "bug" + Feature = "feature" +} + +model Task { + id Int @id + type Kind + + @@discriminator(type) +} + +${bug}`); + const namespace = contract.storage.namespaces.public; + expect({ + valueSets: Object.keys(namespace.entries.valueSet), + column: namespace.entries.table.Task.columns.type.valueSet.entityName, + }).toEqual({ valueSets: ['Kind'], column: 'Kind' }); + }); +}); diff --git a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.value-object-domain.test.ts b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.value-object-domain.test.ts index f1dfdb0a1190..77a9099b23bc 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.value-object-domain.test.ts +++ b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.value-object-domain.test.ts @@ -10,6 +10,7 @@ import { postgresTarget, testEnumEntityContributions, testEnumPslBlockDescriptor, + valueObjectsOf, } from './fixtures'; const pslBlockDescriptors = { enum: testEnumPslBlockDescriptor }; @@ -40,8 +41,8 @@ const countryEnum = `enum Country { } `; -describe('interpretPslDocumentToSqlContract value objects in the domain', () => { - it('gives an enum-typed composite member the domain valueSet a model field of that enum has, single and list', () => { +describe('interpretPslDocumentToSqlContract value objects in the storage plane', () => { + it('gives an enum-typed composite member the storage value set of that enum, single and list', () => { const result = interpretPostgres(`${countryEnum} type Address { country Country @@ -69,9 +70,18 @@ model User { entityName: 'Country', }, }); - expect(namespace?.valueObjects?.['Address']?.fields).toEqual({ - country: modelField, - countries: { ...modelField, many: { elementNullable: false } }, + const member = { + ...modelField, + valueSet: { + plane: 'storage', + entityKind: 'valueSet', + namespaceId: 'public', + entityName: 'Country', + }, + }; + expect(valueObjectsOf(result.value)?.['Address']?.fields).toEqual({ + country: member, + countries: { ...member, many: { elementNullable: false } }, }); }); @@ -110,7 +120,7 @@ model User { inline: fields?.['inline'], email: fields?.['email'], }).toEqual({ code: short, inline: short, email }); - expect(namespace?.valueObjects?.['Label']?.fields).toEqual({ + expect(valueObjectsOf(result.value)?.['Label']?.fields).toEqual({ code: short, codes: { ...short, many: { elementNullable: false } }, email, @@ -137,7 +147,7 @@ model Order { expect(result.ok).toBe(true); if (!result.ok) return; const text = { kind: 'scalar', codecId: 'pg/text@1' }; - expect(result.value.domain.namespaces['public']?.valueObjects).toEqual({ + expect(valueObjectsOf(result.value)).toEqual({ Address: { fields: { street: { nullable: false, type: text, many: false }, @@ -147,7 +157,17 @@ model Order { }, ShippingInfo: { fields: { - address: { nullable: false, type: { kind: 'valueObject', name: 'Address' }, many: false }, + address: { + nullable: false, + type: { + kind: 'valueObject', + plane: 'storage', + namespaceId: 'public', + entityKind: 'valueObject', + entityName: 'Address', + }, + many: false, + }, notes: { nullable: false, type: text, many: false }, }, }, @@ -232,13 +252,13 @@ model User { ]); }); - it('omits valueObjects from the contract when no composite types exist', () => { + it('writes no valueObject slot when no composite types exist', () => { const result = interpretPostgres(`model User { id Int @id }`); expect(result.ok).toBe(true); if (!result.ok) return; - expect(result.value.domain.namespaces['public']?.valueObjects).toBeUndefined(); + expect(valueObjectsOf(result.value)).toBeUndefined(); }); }); diff --git a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.value-object-storage.test.ts b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.value-object-storage.test.ts index 70f71751c4cf..7670c419bab9 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.value-object-storage.test.ts +++ b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.value-object-storage.test.ts @@ -13,6 +13,7 @@ import { sqliteScalarAuthoringTypes, sqliteScalarColumnDescriptors, sqliteTarget, + valueObjectRef, } from './fixtures'; function userFieldsAndColumns(contract: Contract) { @@ -58,14 +59,16 @@ model User { const idField = { nullable: false, type: { kind: 'scalar', codecId: 'pg/int4@1' }, many: false }; const idColumn = { many: false, dataType: 'pg/int4', codecId: 'pg/int4@1', nullable: false }; -const addressFields = { - home: { nullable: true, type: { kind: 'valueObject', name: 'Address' }, many: false }, - addresses: { - nullable: false, - type: { kind: 'valueObject', name: 'Address' }, - many: { elementNullable: false }, - }, -}; +function addressFields(namespaceId = 'public') { + return { + home: { nullable: true, type: valueObjectRef('Address', namespaceId), many: false }, + addresses: { + nullable: false, + type: valueObjectRef('Address', namespaceId), + many: { elementNullable: false }, + }, + }; +} describe('interpretPslDocumentToSqlContract value-object storage', () => { describe('value-object fields keep the target-declared storage column', () => { @@ -75,7 +78,7 @@ describe('interpretPslDocumentToSqlContract value-object storage', () => { expect(result.ok).toBe(true); if (!result.ok) return; expect(userFieldsAndColumns(result.value)).toEqual({ - fields: { id: idField, ...addressFields }, + fields: { id: idField, ...addressFields() }, columns: { id: idColumn, home: { many: false, dataType: 'pg/jsonb', codecId: 'pg/jsonb@1', nullable: true }, @@ -108,7 +111,7 @@ describe('interpretPslDocumentToSqlContract value-object storage', () => { type: { kind: 'scalar', codecId: 'sqlite/integer@1' }, many: false, }, - ...addressFields, + ...addressFields('__unbound__'), }, columns: { id: { @@ -143,7 +146,7 @@ model User { expect(userFieldsAndColumns(result.value)).toEqual({ fields: { id: idField, - home: { nullable: false, type: { kind: 'valueObject', name: 'Address' }, many: false }, + home: { nullable: false, type: valueObjectRef('Address'), many: false }, }, columns: { id: idColumn, diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts b/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts index 556b5edeb419..9360b3d807a6 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts @@ -46,7 +46,7 @@ import type { ContractDefinition } from './contract-definition'; import { contractError } from './contract-errors'; import { describeModel, type ModelDescription } from './describe-model'; import { describeTableNode } from './describe-table-node'; -import { buildDomainField } from './domain-fields'; +import { buildValueObjectMember } from './domain-fields'; import { lowerTable, type TableLoweringContext } from './lower-table'; import { mergeTables } from './merge-tables'; import { modelLookupsOf, modelNamespaceId } from './model-references'; @@ -152,6 +152,87 @@ function enabledCapabilities( return enabled; } +/** The value objects of a definition as storage entities, all in the default namespace; `undefined` when it has none. */ +function storageValueObjects( + definition: ContractDefinition, + defaultNamespaceId: string, +): Record | undefined { + if (definition.valueObjects === undefined || definition.valueObjects.length === 0) { + return undefined; + } + return Object.fromEntries( + definition.valueObjects.map((valueObject) => [ + valueObject.name, + { + fields: Object.fromEntries( + valueObject.fields.map((field) => [ + field.fieldName, + buildValueObjectMember(field, defaultNamespaceId), + ]), + ), + }, + ]), + ); +} + +/** + * Writes the value set of each discriminator, keyed `.` in the base table's namespace and holding the variants' values as the column stores them, and points the discriminator column at it (ADR 278). A column an enum types keeps the enum's value set. An enum with the same name is refused, since the two value sets would share one entry. + */ +function writeDiscriminatorValueSets( + definition: ContractDefinition, + tablesByNamespace: Record>, + storageValueSetsByNs: Record>, +): void { + const defaultNamespaceId = definition.target.defaultNamespaceId; + for (const model of definition.models) { + const { discriminator, variants } = model; + if (discriminator === undefined || variants === undefined) continue; + const namespaceId = modelNamespaceId(model, defaultNamespaceId); + const table = tablesByNamespace[namespaceId]?.[model.tableName]; + const columnName = model.fields.find( + (field) => field.fieldName === discriminator.field, + )?.columnName; + const column = columnName === undefined ? undefined : table?.columns[columnName]; + if (table === undefined || columnName === undefined || column === undefined) continue; + if (column.valueSet !== undefined) continue; + const valueSetName = `${model.modelName}.${discriminator.field}`; + if (Object.hasOwn(definition.enums ?? {}, valueSetName)) { + throw contractError( + 'CONTRACT.ENUM_INVALID', + `enumType("${valueSetName}") has the name of the value set that the discriminator field "${discriminator.field}" of model "${model.modelName}" writes. Rename the enum.`, + { + meta: { + enumName: valueSetName, + model: model.modelName, + field: discriminator.field, + reason: 'discriminator-value-set-name', + }, + }, + ); + } + const namespaceValueSets = storageValueSetsByNs[namespaceId] ?? {}; + namespaceValueSets[valueSetName] = { kind: 'valueSet', values: Object.keys(variants) }; + storageValueSetsByNs[namespaceId] = namespaceValueSets; + const namespaceTables = tablesByNamespace[namespaceId] ?? {}; + namespaceTables[model.tableName] = { + ...table, + columns: { + ...table.columns, + [columnName]: { + ...column, + valueSet: { + plane: 'storage', + entityKind: 'valueSet', + namespaceId, + entityName: valueSetName, + }, + }, + }, + }; + tablesByNamespace[namespaceId] = namespaceTables; + } +} + export function buildSqlContractFromDefinition( definition: ContractDefinition, codecLookup: CodecLookupWithDescriptors, @@ -246,6 +327,8 @@ export function buildSqlContractFromDefinition( namespaceTables[table.tableName] = lowerTable(table, tableLoweringContext); tablesByNamespace[table.namespaceId] = namespaceTables; } + writeDiscriminatorValueSets(definition, tablesByNamespace, storageValueSetsByNs); + const valueObjects = storageValueObjects(definition, defaultNamespaceId); const executionDefaults: ExecutionMutationDefault[] = components.flatMap( (c) => c.domain.executionDefaults, @@ -301,6 +384,9 @@ export function buildSqlContractFromDefinition( ...(valueSetEntries !== undefined && Object.keys(valueSetEntries).length > 0 ? { valueSet: valueSetEntries } : {}), + ...(id === defaultNamespaceId && valueObjects !== undefined + ? { valueObject: valueObjects } + : {}), }, }; return [id, createNamespace(nsInput)]; @@ -362,27 +448,10 @@ export function buildSqlContractFromDefinition( defaults: executionDefaults, }); - const valueObjects: Record | undefined = - definition.valueObjects && definition.valueObjects.length > 0 - ? Object.fromEntries( - definition.valueObjects.map((vo) => [ - vo.name, - { - fields: Object.fromEntries( - vo.fields.map((f) => [f.fieldName, buildDomainField(f, defaultNamespaceId)]), - ), - }, - ]), - ) - : undefined; - const domainNamespaceIds = new Set(Object.keys(modelsByNamespace)); if (domainNamespaceIds.size === 0) { domainNamespaceIds.add(defaultNamespaceId); } - if (valueObjects !== undefined) { - domainNamespaceIds.add(defaultNamespaceId); - } for (const nsId of Object.keys(domainEnumsByNs)) { domainNamespaceIds.add(nsId); } @@ -392,9 +461,6 @@ export function buildSqlContractFromDefinition( const enumsInNs = domainEnumsByNs[namespaceId]; const namespaceSlice = { models: modelsInNs, - ...(namespaceId === defaultNamespaceId && valueObjects !== undefined - ? { valueObjects } - : {}), ...(enumsInNs !== undefined && Object.keys(enumsInNs).length > 0 ? { enum: enumsInNs } : {}), diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/contract-definition.ts b/packages/2-sql-family/2-authoring/contract-ts/src/contract-definition.ts index f90fc94de104..799c2959bfc4 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/contract-definition.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/contract-definition.ts @@ -1,6 +1,8 @@ import type { ColumnDefault, + ContractDiscriminator, ControlPolicy, + CrossReference, ExecutionMutationDefaultPhases, } from '@internal/contract/types'; import type { ForeignKeyDefaultsState } from '@internal/contract-authoring'; @@ -305,6 +307,12 @@ export interface ModelNode extends TableProperties { * table and a root for this model — the base owns both. */ readonly sharesBaseTable?: boolean; + /** The field that names a row's variant, on a model that has variants. Its value set is written into storage before the storage hash (ADR 278). */ + readonly discriminator?: ContractDiscriminator; + /** The variants of this model, keyed by the discriminator value as the column stores it. */ + readonly variants?: Readonly>; + /** The model this one is a variant of. */ + readonly base?: CrossReference; } /** diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/describe-model.ts b/packages/2-sql-family/2-authoring/contract-ts/src/describe-model.ts index 8ebe5087e5f4..eb7d66b6995e 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/describe-model.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/describe-model.ts @@ -106,6 +106,9 @@ export function describeModel( storage: { table: tableName, namespaceId, fields: storageFields }, fields: domainFields, relations: lowerRelations(model, columnToField, context), + ...ifDefined('discriminator', model.discriminator), + ...ifDefined('variants', model.variants), + ...ifDefined('base', model.base), }, executionDefaults, }, diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/domain-fields.ts b/packages/2-sql-family/2-authoring/contract-ts/src/domain-fields.ts index 8f5f9072e826..05cffe0934e3 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/domain-fields.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/domain-fields.ts @@ -1,4 +1,4 @@ -import type { ContractField } from '@internal/contract/types'; +import type { ContractField, ValueSetRef } from '@internal/contract/types'; import { nonEmptyTypeParams } from '@internal/sql-contract/types'; import { ifDefined } from '@internal/utils/defined'; import { @@ -8,18 +8,29 @@ import { } from './contract-definition'; import { enumValueSetRefs } from './enum-members'; -export function buildDomainField( +function buildField( field: ScalarMemberNode | ValueObjectMemberNode, defaultNamespaceId: string, + valueSetOf: (refs: { + readonly domain: ValueSetRef; + readonly storage: ValueSetRef; + }) => ValueSetRef, ): ContractField { + const many = field.many ? { elementNullable: field.elementNullable === true } : false; if (isValueObjectMember(field)) { return { - type: { kind: 'valueObject', name: field.valueObjectName }, + type: { + kind: 'valueObject', + plane: 'storage', + namespaceId: defaultNamespaceId, + entityKind: 'valueObject', + entityName: field.valueObjectName, + }, nullable: field.nullable, - many: field.many ? { elementNullable: field.elementNullable === true } : false, + many, }; } - + const refs = enumValueSetRefs(field.enumTypeHandle, defaultNamespaceId); return { type: { kind: 'scalar', @@ -27,7 +38,23 @@ export function buildDomainField( ...ifDefined('typeParams', nonEmptyTypeParams(field.descriptor.typeParams)), }, nullable: field.nullable, - many: field.many ? { elementNullable: field.elementNullable === true } : false, - ...ifDefined('valueSet', enumValueSetRefs(field.enumTypeHandle, defaultNamespaceId)?.domain), + many, + ...ifDefined('valueSet', refs === undefined ? undefined : valueSetOf(refs)), }; } + +/** A model field in the domain plane. A value object is referred to by its storage coordinate, an enum by the domain enum. */ +export function buildDomainField( + field: ScalarMemberNode | ValueObjectMemberNode, + defaultNamespaceId: string, +): ContractField { + return buildField(field, defaultNamespaceId, (refs) => refs.domain); +} + +/** A member of a storage value object. Storage never refers to the domain plane, so an enum member refers to the storage value set. */ +export function buildValueObjectMember( + field: ScalarMemberNode | ValueObjectMemberNode, + defaultNamespaceId: string, +): ContractField { + return buildField(field, defaultNamespaceId, (refs) => refs.storage); +} diff --git a/packages/2-sql-family/2-authoring/contract-ts/test/contract-builder.discriminator-value-set.test.ts b/packages/2-sql-family/2-authoring/contract-ts/test/contract-builder.discriminator-value-set.test.ts new file mode 100644 index 000000000000..2826143db187 --- /dev/null +++ b/packages/2-sql-family/2-authoring/contract-ts/test/contract-builder.discriminator-value-set.test.ts @@ -0,0 +1,124 @@ +import { crossRef } from '@internal/contract/types'; +import type { TargetPackRef } from '@internal/framework-components/components'; +import { describe, expect, it } from 'vitest'; +import { createTestSqlNamespace } from '../../../1-core/contract/test/test-support'; +import { testTypeLookups } from '../../../1-core/contract/test/test-type-lookups'; +import { buildSqlContractFromDefinition } from '../src/contract-builder'; +import type { ContractDefinition } from '../src/contract-definition'; +import { enumType, member } from '../src/enum-type'; + +const postgresTargetPack: TargetPackRef<'sql', 'postgres'> = { + kind: 'target', + id: 'postgres', + familyId: 'sql', + targetId: 'postgres', + version: '0.0.1', + defaultNamespaceId: 'public', +}; + +const int4 = { codecId: 'pg/int4@1' } as const; +const text = { codecId: 'pg/text@1' } as const; + +function definitionWith( + overrides: Partial> & { + readonly typeEnum?: ReturnType; + readonly variants?: Readonly>; + } = {}, +): ContractDefinition { + const variants = overrides.variants ?? { bug: 'Bug', feature: 'Feature' }; + return { + warnings: undefined, + target: postgresTargetPack, + createNamespace: createTestSqlNamespace, + ...(overrides.enums !== undefined ? { enums: overrides.enums } : {}), + models: [ + { + modelName: 'Task', + tableName: 'task', + fields: [ + { fieldName: 'id', columnName: 'id', descriptor: int4, nullable: false }, + { + fieldName: 'type', + columnName: 'type', + descriptor: text, + nullable: false, + ...(overrides.typeEnum !== undefined ? { enumTypeHandle: overrides.typeEnum } : {}), + }, + ], + id: { columns: ['id'] }, + discriminator: { field: 'type' }, + variants: Object.fromEntries( + Object.entries(variants).map(([value, model]) => [value, crossRef(model, 'public')]), + ), + }, + ...Object.values(variants).map((model) => ({ + modelName: model, + tableName: 'task', + fields: [], + sharesBaseTable: true, + base: crossRef('Task', 'public'), + })), + ], + }; +} + +function build(definition: ContractDefinition) { + return buildSqlContractFromDefinition( + definition, + testTypeLookups.codecLookup, + testTypeLookups.dataTypeLookup, + ); +} + +describe('the value set of a discriminator', () => { + it('is written as ., and the discriminator column refers to it', () => { + const contract = build(definitionWith()); + const namespace = contract.storage.namespaces['public']; + expect({ + valueSet: JSON.parse(JSON.stringify(namespace?.entries.valueSet?.['Task.type'])), + column: namespace?.entries.table?.['task']?.columns['type']?.valueSet, + task: { + discriminator: contract.domain.namespaces['public']?.models['Task']?.discriminator, + variants: contract.domain.namespaces['public']?.models['Task']?.variants, + }, + bugBase: contract.domain.namespaces['public']?.models['Bug']?.base, + }).toEqual({ + valueSet: { kind: 'valueSet', values: ['bug', 'feature'] }, + column: { + plane: 'storage', + entityKind: 'valueSet', + namespaceId: 'public', + entityName: 'Task.type', + }, + task: { + discriminator: { field: 'type' }, + variants: { bug: crossRef('Bug', 'public'), feature: crossRef('Feature', 'public') }, + }, + bugBase: crossRef('Task', 'public'), + }); + }); + + it('changes the storage hash when a variant is added', () => { + expect(build(definitionWith({ variants: { bug: 'Bug' } })).storage.storageHash).not.toBe( + build(definitionWith()).storage.storageHash, + ); + }); + + it('is not written when an enum types the discriminator field, which keeps the enum value set', () => { + const Kind = enumType('Kind', text, member('Bug', 'bug'), member('Feature', 'feature')); + const namespace = build(definitionWith({ enums: { Kind }, typeEnum: Kind })).storage.namespaces[ + 'public' + ]; + expect({ + valueSets: Object.keys(namespace?.entries.valueSet ?? {}), + column: namespace?.entries.table?.['task']?.columns['type']?.valueSet?.entityName, + }).toEqual({ valueSets: ['Kind'], column: 'Kind' }); + }); + + it('refuses an enum whose name is the value set key of a discriminator, naming both', () => { + const clash = enumType('Task.type', text, member('Bug', 'bug')); + expect(() => build(definitionWith({ enums: { 'Task.type': clash } }))).toThrow( + 'enumType("Task.type") has the name of the value set that the discriminator field "type" of model "Task" writes. Rename the enum.', + ); + }); +}); diff --git a/packages/2-sql-family/2-authoring/contract-ts/test/contract-builder.value-object-domain.test.ts b/packages/2-sql-family/2-authoring/contract-ts/test/contract-builder.value-object-domain.test.ts index c647ce3cd36a..14216a77a1e2 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/test/contract-builder.value-object-domain.test.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/test/contract-builder.value-object-domain.test.ts @@ -6,6 +6,16 @@ import { buildSqlContractFromDefinition } from '../src/contract-builder'; import { enumType, member } from '../src/enum-type'; import { valueObjectsOf } from './contract-test-helpers'; +function valueObjectRef(entityName: string) { + return { + kind: 'valueObject', + plane: 'storage', + namespaceId: 'public', + entityKind: 'valueObject', + entityName, + } as const; +} + const postgresTargetPack: TargetPackRef<'sql', 'postgres'> = { kind: 'target', id: 'postgres', @@ -23,8 +33,8 @@ const numeric = { } as const; const idField = { fieldName: 'id', columnName: 'id', descriptor: int4, nullable: false } as const; -describe('value-object members and value-object fields in the domain', () => { - it('types a model field by its value object, optional and list', () => { +describe('value objects in the storage plane, and the fields that refer to them', () => { + it('types a model field by the storage coordinate of its value object, optional and list', () => { const jsonb = { codecId: 'pg/jsonb@1' } as const; const contract = buildSqlContractFromDefinition( { @@ -67,16 +77,16 @@ describe('value-object members and value-object fields in the domain', () => { const { id: _id, ...fields } = contract.domain.namespaces['public']?.models['User']?.fields ?? {}; expect(fields).toEqual({ - home: { type: { kind: 'valueObject', name: 'Address' }, nullable: true, many: false }, + home: { type: valueObjectRef('Address'), nullable: true, many: false }, addresses: { - type: { kind: 'valueObject', name: 'Address' }, + type: valueObjectRef('Address'), nullable: false, many: { elementNullable: false }, }, }); }); - it('builds value-object members like model fields, keeping many, type parameters and the domain valueSet of an enum', () => { + it('builds value-object members like model fields, keeping many and type parameters, with the storage value set of an enum', () => { const Country = enumType('Country', text, member('DE', 'DE'), member('FR', 'FR')); const scalarFields = [ { fieldName: 'amount', descriptor: numeric, nullable: false }, @@ -149,17 +159,11 @@ describe('value-object members and value-object fields in the domain', () => { testTypeLookups.dataTypeLookup, ); - const namespace = contract.domain.namespaces['public']; - const { - id: _id, - shipping: _shipping, - ...modelFields - } = namespace?.models['Order']?.fields ?? {}; - const { stops, ...memberFields } = namespace?.valueObjects?.['Shipping']?.fields ?? {}; - expect({ memberFields, stops }).toEqual({ - memberFields: modelFields, + const { stops, ...memberFields } = valueObjectsOf(contract)?.['Shipping']?.fields ?? {}; + expect({ stops, domainKeys: Object.keys(contract.domain.namespaces['public'] ?? {}) }).toEqual({ + domainKeys: ['models', 'enum'], stops: { - type: { kind: 'valueObject', name: 'Stop' }, + type: valueObjectRef('Stop'), nullable: false, many: { elementNullable: true }, }, @@ -180,8 +184,8 @@ describe('value-object members and value-object fields in the domain', () => { nullable: false, many: false, valueSet: { - plane: 'domain', - entityKind: 'enum', + plane: 'storage', + entityKind: 'valueSet', namespaceId: 'public', entityName: 'Country', }, @@ -191,8 +195,8 @@ describe('value-object members and value-object fields in the domain', () => { nullable: true, many: { elementNullable: false }, valueSet: { - plane: 'domain', - entityKind: 'enum', + plane: 'storage', + entityKind: 'valueSet', namespaceId: 'public', entityName: 'Country', }, @@ -242,7 +246,7 @@ describe('value-object members and value-object fields in the domain', () => { fields: { street: { type: { kind: 'scalar', codecId: 'pg/text@1' }, nullable: false, many: false }, location: { - type: { kind: 'valueObject', name: 'GeoLocation' }, + type: valueObjectRef('GeoLocation'), nullable: true, many: false, }, @@ -251,7 +255,7 @@ describe('value-object members and value-object fields in the domain', () => { }); }); - it('omits valueObjects from the contract when none are defined', () => { + it('writes no valueObject slot when none are defined', () => { const contract = buildSqlContractFromDefinition( { warnings: undefined, diff --git a/packages/2-sql-family/2-authoring/contract-ts/test/contract-test-helpers.ts b/packages/2-sql-family/2-authoring/contract-ts/test/contract-test-helpers.ts index 279f6a45be19..5989db6e82bc 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/test/contract-test-helpers.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/test/contract-test-helpers.ts @@ -1,8 +1,5 @@ import type { Contract, ContractValueObjectDefinitions } from '@internal/contract/types'; -import { - domainModelsAtDefaultNamespace, - domainValueObjectsAtDefaultNamespace, -} from '@internal/contract/types'; +import { domainModelsAtDefaultNamespace } from '@internal/contract/types'; import { blindCast } from '@internal/utils/casts'; /** Models map for the contract's sole domain namespace, precise per-namespace. */ @@ -13,12 +10,18 @@ export function modelsOf(contract: T): SoleNamespaceModels; } +/** The `valueObject` slot of the contract's sole storage namespace, or `undefined` when it has none. */ export function valueObjectsOf( contract: T, ): ContractValueObjectDefinitions | undefined { - return domainValueObjectsAtDefaultNamespace(contract.domain) as - | ContractValueObjectDefinitions - | undefined; + const [namespace, ...others] = Object.values(contract.storage.namespaces); + if (namespace === undefined || others.length > 0) { + throw new Error('valueObjectsOf reads a contract with exactly one storage namespace'); + } + return blindCast< + ContractValueObjectDefinitions | undefined, + 'test helper reads the storage valueObject slot the build wrote' + >(namespace.entries['valueObject']); } /** Flat model map for runtime assertions when model types are widened by the test harness. */ From 669f4b9052391fd002ea5ded498d559b22092d4e Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 11:52:32 +0200 Subject: [PATCH 009/104] TML-3537: Mongo authoring writes value objects into storage The Mongo builder writes value objects to entries.valueObject and buildMongoStorage hashes them. field.valueObject, Mongo PSL and the Prisma 6 reader write the storage coordinate. A value object member typed by an enum refers to the storage value set, and the validator refusals count such enums. The Mongo target serializer carries every entries kind through deserialize and serialize. The derived validators do not change. Signed-off-by: willbot Signed-off-by: Will Madden --- .../mongo-contract/src/build-mongo-storage.ts | 9 +- .../mongo-contract/src/contract-refusals.ts | 4 +- .../test/contract-refusals.test.ts | 30 +++- .../test/derive-json-schema-helpers.ts | 15 ++ .../contract-prisma6/src/interpreter.ts | 8 +- .../composite-types/expected-contract.json | 140 ++++++++++-------- .../fixtures/lists/expected-contract.json | 45 +++--- .../contract-psl/src/interpreter.ts | 8 +- .../test/interpreter.aliases.test.ts | 6 +- .../test/interpreter.single-voice.test.ts | 20 ++- .../contract-psl/test/interpreter.test.ts | 43 +++++- .../contract-ts/src/build-contract.ts | 4 +- .../contract-ts/src/build-models.ts | 23 ++- .../contract-ts/src/contract-builder.ts | 12 +- .../test/contract-builder.dsl.test.ts | 6 +- ...ract-builder.storage-value-objects.test.ts | 97 ++++++++++++ .../core/mongo-target-contract-serializer.ts | 79 ++++------ .../mongo-target-contract-serializer.test.ts | 31 ++++ 18 files changed, 417 insertions(+), 163 deletions(-) create mode 100644 packages/2-mongo-family/2-authoring/contract-ts/test/contract-builder.storage-value-objects.test.ts diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/src/build-mongo-storage.ts b/packages/2-mongo-family/1-foundation/mongo-contract/src/build-mongo-storage.ts index 1815aec6036e..06f1933ffd28 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/src/build-mongo-storage.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/src/build-mongo-storage.ts @@ -3,22 +3,26 @@ import { mongoContractCanonicalizationHooks } from './canonicalization-hooks'; import { buildMongoNamespace } from './ir/build-mongo-namespace'; import type { MongoCollectionInput } from './ir/mongo-collection'; import { MongoStorage } from './ir/mongo-storage'; +import type { MongoValueObjectInput } from './ir/mongo-value-object'; import type { MongoValueSetInput } from './ir/mongo-value-set'; /** - * Builds a Mongo contract's storage: the namespace `namespaceId` holding the collections and value sets, and the storage hash that `db sign` and `db verify` compare. Every Mongo contract source calls this, so they hash storage the same way. + * Builds a Mongo contract's storage: the namespace `namespaceId` holding the collections, value sets and value objects, and the storage hash that `db sign` and `db verify` compare. Every Mongo contract source calls this, so they hash storage the same way. */ export function buildMongoStorage(input: { readonly namespaceId: string; readonly collections: Readonly>; readonly valueSets: Readonly>; + readonly valueObjects?: Readonly>; }): MongoStorage { const hasValueSets = Object.keys(input.valueSets).length > 0; + const valueObjects = input.valueObjects ?? {}; const namespace = buildMongoNamespace({ id: input.namespaceId, entries: { collection: input.collections, ...(hasValueSets ? { valueSet: input.valueSets } : {}), + ...(Object.keys(valueObjects).length > 0 ? { valueObject: valueObjects } : {}), }, }); const storageHash = computeStorageHash({ @@ -33,6 +37,9 @@ export function buildMongoStorage(input: { ...(namespace.entries.valueSet !== undefined ? { valueSet: namespace.entries.valueSet } : {}), + ...(namespace.entries.valueObject !== undefined + ? { valueObject: namespace.entries.valueObject } + : {}), }, }, }, diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/src/contract-refusals.ts b/packages/2-mongo-family/1-foundation/mongo-contract/src/contract-refusals.ts index 04fbf22245b0..00c3c5e634b6 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/src/contract-refusals.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/src/contract-refusals.ts @@ -106,7 +106,7 @@ export function lookupFreeRefusalsOf( } /** - * The enums a collection validator lists: those stored by a model in a managed collection, by one of its variants or embedded models, or by a value object any of them holds. + * The enums a collection validator lists: those stored by a model in a managed collection, by one of its variants or embedded models, or by a value object any of them holds. A model field refers to its domain enum and a value object member to the enum's storage value set; both carry the enum's name. */ function validatedEnumNames(input: MongoContractRefusalsInput): ReadonlySet { const { models, valueObjects, controlPolicies } = input; @@ -119,7 +119,7 @@ function validatedEnumNames(input: MongoContractRefusalsInput): ReadonlySet(); const visitFields = (fields: Record) => { for (const field of Object.values(fields)) { - if (field.valueSet?.entityKind === 'enum') enums.add(field.valueSet.entityName); + if (field.valueSet !== undefined) enums.add(field.valueSet.entityName); if (field.type.kind === 'valueObject' && !visitedValueObjects.has(field.type.entityName)) { visitedValueObjects.add(field.type.entityName); visitFields(valueObjects[field.type.entityName]?.fields ?? {}); diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/test/contract-refusals.test.ts b/packages/2-mongo-family/1-foundation/mongo-contract/test/contract-refusals.test.ts index 46bc6fa464a7..3b287c6b8ed5 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/test/contract-refusals.test.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/test/contract-refusals.test.ts @@ -3,7 +3,13 @@ import { describe, expect, it } from 'vitest'; import { mongoContractRefusalsOf } from '../src/contract-refusals'; import type { MongoModelStorage } from '../src/contract-types'; import { mongoTypeLookups } from '../src/fake-type-lookups'; -import { arrayField, enumField, scalarField, voField } from './derive-json-schema-helpers'; +import { + arrayField, + enumField, + memberEnumField, + scalarField, + voField, +} from './derive-json-schema-helpers'; function model( fields: ContractModel['fields'], @@ -166,6 +172,28 @@ describe('mongoContractRefusalsOf', () => { ).toEqual([expect.objectContaining({ kind: 'enumNotListable', enumName: 'Big' })]); }); + it('refuses an enum a validator cannot list, used by a value object member through its storage value set', () => { + expect( + refusals({ + enums: unlistableEnums, + valueObjects: { + Sizing: { + fields: { + size: memberEnumField('mongo/int64@1', 'Big'), + ratio: memberEnumField('mongo/double@1', 'Ratio'), + }, + }, + }, + models: { + Job: model({ sizing: voField('Sizing') }, { storage: { collection: 'jobs' } }), + }, + }), + ).toEqual([ + expect.objectContaining({ kind: 'enumNotListable', enumName: 'Big' }), + expect.objectContaining({ kind: 'enumMemberNotListable', enumName: 'Ratio' }), + ]); + }); + it('refuses an enum a validator cannot list, and a member it cannot list', () => { expect( refusals({ diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/test/derive-json-schema-helpers.ts b/packages/2-mongo-family/1-foundation/mongo-contract/test/derive-json-schema-helpers.ts index 56f774d5fe42..f80914ec5f2a 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/test/derive-json-schema-helpers.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/test/derive-json-schema-helpers.ts @@ -45,6 +45,21 @@ export function arrayEnumField( }; } +/** A value object member typed by an enum: it refers to the enum's storage value set, as a storage entity's field does. */ +export function memberEnumField(codecId: string, enumName: string): ContractField { + return { + type: { kind: 'scalar', codecId }, + nullable: false, + many: false, + valueSet: { + plane: 'storage', + entityKind: 'valueSet', + namespaceId: '__unbound__', + entityName: enumName, + }, + }; +} + function valueObjectType(entityName: string): ContractField['type'] { return { kind: 'valueObject', diff --git a/packages/2-mongo-family/2-authoring/contract-prisma6/src/interpreter.ts b/packages/2-mongo-family/2-authoring/contract-prisma6/src/interpreter.ts index a69cecdddbf8..cce9d5dc5876 100644 --- a/packages/2-mongo-family/2-authoring/contract-prisma6/src/interpreter.ts +++ b/packages/2-mongo-family/2-authoring/contract-prisma6/src/interpreter.ts @@ -803,7 +803,13 @@ function resolveFieldType( if (ctx.compositeTypeNames.has(field.typeName)) { if (nativeType !== undefined) return nativeTypeUnsupported(nativeType); return { - type: { kind: 'valueObject', name: field.typeName }, + type: { + kind: 'valueObject', + plane: 'storage', + namespaceId: ctx.namespaceId, + entityKind: 'valueObject', + entityName: field.typeName, + }, nullable: field.optional, ...many, }; diff --git a/packages/2-mongo-family/2-authoring/contract-prisma6/test/fixtures/composite-types/expected-contract.json b/packages/2-mongo-family/2-authoring/contract-prisma6/test/fixtures/composite-types/expected-contract.json index 16b15259c85a..316ec6f230c3 100644 --- a/packages/2-mongo-family/2-authoring/contract-prisma6/test/fixtures/composite-types/expected-contract.json +++ b/packages/2-mongo-family/2-authoring/contract-prisma6/test/fixtures/composite-types/expected-contract.json @@ -1,6 +1,6 @@ { - "targetFamily": "mongo", "target": "mongo", + "targetFamily": "mongo", "roots": { "Post": { "namespace": "__unbound__", @@ -24,7 +24,10 @@ "address": { "type": { "kind": "valueObject", - "name": "Address" + "plane": "storage", + "namespaceId": "__unbound__", + "entityKind": "valueObject", + "entityName": "Address" }, "nullable": false, "many": false @@ -32,7 +35,10 @@ "history": { "type": { "kind": "valueObject", - "name": "Address" + "plane": "storage", + "namespaceId": "__unbound__", + "entityKind": "valueObject", + "entityName": "Address" }, "nullable": false, "many": { @@ -42,7 +48,10 @@ "backup": { "type": { "kind": "valueObject", - "name": "Address" + "plane": "storage", + "namespaceId": "__unbound__", + "entityKind": "valueObject", + "entityName": "Address" }, "nullable": true, "many": false @@ -67,74 +76,16 @@ } } } - }, - "valueObjects": { - "Address": { - "fields": { - "street": { - "type": { - "kind": "scalar", - "codecId": "mongo/string@1" - }, - "nullable": false, - "many": false - }, - "zip": { - "type": { - "kind": "scalar", - "codecId": "mongo/string@1" - }, - "nullable": true, - "many": false - }, - "ownerId": { - "type": { - "kind": "scalar", - "codecId": "mongo/objectId@1" - }, - "nullable": false, - "many": false - }, - "geo": { - "type": { - "kind": "valueObject", - "name": "Geo" - }, - "nullable": false, - "many": false - } - } - }, - "Geo": { - "fields": { - "lat": { - "type": { - "kind": "scalar", - "codecId": "mongo/double@1" - }, - "nullable": false, - "many": false - }, - "lng": { - "type": { - "kind": "scalar", - "codecId": "mongo/double@1" - }, - "nullable": false, - "many": false - } - } - } } } } }, - "extensions": {}, "capabilities": {}, + "extensions": {}, "profileHash": "251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3", "meta": {}, "storage": { - "storageHash": "dd22a5221b155cd435b52d49dd4a066c207d083079a291c2340674f70d8e067a", + "storageHash": "ae4ed29eb19b7d02a12b90b87853eb23103557cd076483c0110cd1b4d95e11da", "namespaces": { "__unbound__": { "id": "__unbound__", @@ -144,6 +95,67 @@ "Post": { "kind": "mongo-collection" } + }, + "valueObject": { + "Address": { + "fields": { + "street": { + "type": { + "kind": "scalar", + "codecId": "mongo/string@1" + }, + "nullable": false, + "many": false + }, + "zip": { + "type": { + "kind": "scalar", + "codecId": "mongo/string@1" + }, + "nullable": true, + "many": false + }, + "ownerId": { + "type": { + "kind": "scalar", + "codecId": "mongo/objectId@1" + }, + "nullable": false, + "many": false + }, + "geo": { + "type": { + "kind": "valueObject", + "plane": "storage", + "namespaceId": "__unbound__", + "entityKind": "valueObject", + "entityName": "Geo" + }, + "nullable": false, + "many": false + } + } + }, + "Geo": { + "fields": { + "lat": { + "type": { + "kind": "scalar", + "codecId": "mongo/double@1" + }, + "nullable": false, + "many": false + }, + "lng": { + "type": { + "kind": "scalar", + "codecId": "mongo/double@1" + }, + "nullable": false, + "many": false + } + } + } } } } diff --git a/packages/2-mongo-family/2-authoring/contract-prisma6/test/fixtures/lists/expected-contract.json b/packages/2-mongo-family/2-authoring/contract-prisma6/test/fixtures/lists/expected-contract.json index 4c4abf92894c..0b9036cd1029 100644 --- a/packages/2-mongo-family/2-authoring/contract-prisma6/test/fixtures/lists/expected-contract.json +++ b/packages/2-mongo-family/2-authoring/contract-prisma6/test/fixtures/lists/expected-contract.json @@ -1,6 +1,6 @@ { - "targetFamily": "mongo", "target": "mongo", + "targetFamily": "mongo", "roots": { "Post": { "namespace": "__unbound__", @@ -57,20 +57,23 @@ "codecId": "mongo/string@1" }, "nullable": false, + "many": { + "elementNullable": false + }, "valueSet": { "plane": "domain", "entityKind": "enum", "namespaceId": "__unbound__", "entityName": "Role" - }, - "many": { - "elementNullable": false } }, "address": { "type": { "kind": "valueObject", - "name": "Address" + "plane": "storage", + "namespaceId": "__unbound__", + "entityKind": "valueObject", + "entityName": "Address" }, "nullable": false, "many": { @@ -104,20 +107,6 @@ } } }, - "valueObjects": { - "Address": { - "fields": { - "street": { - "type": { - "kind": "scalar", - "codecId": "mongo/string@1" - }, - "nullable": false, - "many": false - } - } - } - }, "enum": { "Role": { "codecId": "mongo/string@1", @@ -136,12 +125,12 @@ } } }, - "extensions": {}, "capabilities": {}, + "extensions": {}, "profileHash": "251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3", "meta": {}, "storage": { - "storageHash": "3da02d07cd29acc61bc54a1929d31fe70720137e369c44564b6ca3e8f3e46546", + "storageHash": "e6d057cae9c68e89ed7207907298557efb1fdf16c57f31373bd9cffaec8b65be", "namespaces": { "__unbound__": { "id": "__unbound__", @@ -157,6 +146,20 @@ "kind": "valueSet", "values": ["USER", "ADMIN"] } + }, + "valueObject": { + "Address": { + "fields": { + "street": { + "type": { + "kind": "scalar", + "codecId": "mongo/string@1" + }, + "nullable": false, + "many": false + } + } + } } } } diff --git a/packages/2-mongo-family/2-authoring/contract-psl/src/interpreter.ts b/packages/2-mongo-family/2-authoring/contract-psl/src/interpreter.ts index 9e55d51a5593..7e7a5d373e93 100644 --- a/packages/2-mongo-family/2-authoring/contract-psl/src/interpreter.ts +++ b/packages/2-mongo-family/2-authoring/contract-psl/src/interpreter.ts @@ -813,7 +813,13 @@ function resolveNonRelationField( if (resolution?.kind === 'compositeType') { return { field: { - type: { kind: 'valueObject', name: resolution.symbol.name }, + type: { + kind: 'valueObject', + plane: 'storage', + namespaceId: resolution.namespace.id, + entityKind: 'valueObject', + entityName: resolution.symbol.name, + }, nullable: field.optional, many: field.list ? { elementNullable: field.elementOptional } : false, }, diff --git a/packages/2-mongo-family/2-authoring/contract-psl/test/interpreter.aliases.test.ts b/packages/2-mongo-family/2-authoring/contract-psl/test/interpreter.aliases.test.ts index 77c359315c75..71e0c73ad4d2 100644 --- a/packages/2-mongo-family/2-authoring/contract-psl/test/interpreter.aliases.test.ts +++ b/packages/2-mongo-family/2-authoring/contract-psl/test/interpreter.aliases.test.ts @@ -169,7 +169,11 @@ model Item { id ObjectId @id @map("_id") ref Ref }`); - expect(contract.domain.namespaces[UNBOUND_NAMESPACE_ID]?.valueObjects?.['Ref']).toEqual({ + expect( + JSON.parse( + JSON.stringify(contract.storage.namespaces[UNBOUND_NAMESPACE_ID]?.entries['valueObject']), + )['Ref'], + ).toEqual({ fields: { target: { type: { kind: 'scalar', codecId: 'mongo/objectId@1' }, diff --git a/packages/2-mongo-family/2-authoring/contract-psl/test/interpreter.single-voice.test.ts b/packages/2-mongo-family/2-authoring/contract-psl/test/interpreter.single-voice.test.ts index 90e8039e558d..d0a80a4e0c18 100644 --- a/packages/2-mongo-family/2-authoring/contract-psl/test/interpreter.single-voice.test.ts +++ b/packages/2-mongo-family/2-authoring/contract-psl/test/interpreter.single-voice.test.ts @@ -84,9 +84,25 @@ model Item { if (!result.ok) throw new Error(JSON.stringify(result.failure)); expect(result.value.domain.namespaces[UNBOUND_NAMESPACE_ID]?.models['Item']?.fields).toEqual({ id: { type: { kind: 'scalar', codecId: 'mongo/objectId@1' }, nullable: false, many: false }, - label: { type: { kind: 'valueObject', name: 'String' }, nullable: false, many: false }, + label: { + type: { + kind: 'valueObject', + plane: 'storage', + namespaceId: '__unbound__', + entityKind: 'valueObject', + entityName: 'String', + }, + nullable: false, + many: false, + }, }); - expect(result.value.domain.namespaces[UNBOUND_NAMESPACE_ID]?.valueObjects).toEqual({ + expect( + JSON.parse( + JSON.stringify( + result.value.storage.namespaces[UNBOUND_NAMESPACE_ID]?.entries['valueObject'], + ), + ), + ).toEqual({ String: { fields: { length: { diff --git a/packages/2-mongo-family/2-authoring/contract-psl/test/interpreter.test.ts b/packages/2-mongo-family/2-authoring/contract-psl/test/interpreter.test.ts index 4b71b59000fd..e9548efdef95 100644 --- a/packages/2-mongo-family/2-authoring/contract-psl/test/interpreter.test.ts +++ b/packages/2-mongo-family/2-authoring/contract-psl/test/interpreter.test.ts @@ -92,7 +92,8 @@ function modelsOf(ir: Contract): Record { } function valueObjectsOf(ir: Contract): Record | undefined { - return ir.domain.namespaces[UNBOUND_NAMESPACE_ID]!.valueObjects; + const slot = ir.storage.namespaces[UNBOUND_NAMESPACE_ID]?.entries['valueObject']; + return slot === undefined ? undefined : JSON.parse(JSON.stringify(slot)); } function model(ir: Contract, name: string): MongoModel { @@ -1332,7 +1333,13 @@ model Item { expect(modelsOf(ir)['User']).toMatchObject({ fields: { homeAddress: { - type: { kind: 'valueObject', name: 'Address' }, + type: { + kind: 'valueObject', + plane: 'storage', + namespaceId: '__unbound__', + entityKind: 'valueObject', + entityName: 'Address', + }, nullable: true, many: false, }, @@ -1356,7 +1363,13 @@ model Item { expect(modelsOf(ir)['User']).toMatchObject({ fields: { addresses: { - type: { kind: 'valueObject', name: 'Address' }, + type: { + kind: 'valueObject', + plane: 'storage', + namespaceId: '__unbound__', + entityKind: 'valueObject', + entityName: 'Address', + }, nullable: false, many: { elementNullable: false }, }, @@ -1416,7 +1429,13 @@ model Item { many: false, }, location: { - type: { kind: 'valueObject', name: 'GeoPoint' }, + type: { + kind: 'valueObject', + plane: 'storage', + namespaceId: '__unbound__', + entityKind: 'valueObject', + entityName: 'GeoPoint', + }, nullable: false, many: false, }, @@ -2626,12 +2645,24 @@ model Item { `); expect(model(ir, 'User').fields).toMatchObject({ nullableElements: { - type: { kind: 'valueObject', name: 'Address' }, + type: { + kind: 'valueObject', + plane: 'storage', + namespaceId: '__unbound__', + entityKind: 'valueObject', + entityName: 'Address', + }, nullable: false, many: { elementNullable: true }, }, fullyNullable: { - type: { kind: 'valueObject', name: 'Address' }, + type: { + kind: 'valueObject', + plane: 'storage', + namespaceId: '__unbound__', + entityKind: 'valueObject', + entityName: 'Address', + }, nullable: true, many: { elementNullable: true }, }, diff --git a/packages/2-mongo-family/2-authoring/contract-ts/src/build-contract.ts b/packages/2-mongo-family/2-authoring/contract-ts/src/build-contract.ts index ba7f87a12618..2defeab45ed2 100644 --- a/packages/2-mongo-family/2-authoring/contract-ts/src/build-contract.ts +++ b/packages/2-mongo-family/2-authoring/contract-ts/src/build-contract.ts @@ -135,6 +135,7 @@ export function buildMongoContractFromDefinition( ]), ), valueSets: storageValueSets, + valueObjects: builtValueObjects, }); // Contracts carry no capabilities when built; emit adds the stack's. The profile hash fingerprints this empty set. const capabilities: Record> = {}; @@ -154,9 +155,6 @@ export function buildMongoContractFromDefinition( namespaces: { [namespaceId]: { models: builtModels, - ...(Object.keys(builtValueObjects).length > 0 - ? { valueObjects: builtValueObjects } - : {}), ...(Object.keys(builtEnums).length > 0 ? { enum: builtEnums } : {}), }, }, diff --git a/packages/2-mongo-family/2-authoring/contract-ts/src/build-models.ts b/packages/2-mongo-family/2-authoring/contract-ts/src/build-models.ts index 1e97dac01860..237caa771c65 100644 --- a/packages/2-mongo-family/2-authoring/contract-ts/src/build-models.ts +++ b/packages/2-mongo-family/2-authoring/contract-ts/src/build-models.ts @@ -38,11 +38,23 @@ export function storedFieldNameIn(fields: readonly FieldNode[], fieldName: strin return field === undefined ? fieldName : storedNameOf(field); } -function buildContractField(field: FieldNode, namespaceId: string): ContractField { +/** + * A field as the contract holds it. A model field refers to its enum in the domain plane; a value object member is a storage entity's field, and storage never refers to the domain, so it refers to the enum's storage value set. + */ +function buildContractField( + field: FieldNode, + namespaceId: string, + plane: 'domain' | 'storage', +): ContractField { const valueSet: ValueSetRef | undefined = field.enumName === undefined ? undefined - : { plane: 'domain', entityKind: 'enum', namespaceId, entityName: field.enumName }; + : { + plane, + entityKind: plane === 'domain' ? 'enum' : 'valueSet', + namespaceId, + entityName: field.enumName, + }; return { type: field.type, @@ -55,9 +67,10 @@ function buildContractField(field: FieldNode, namespaceId: string): ContractFiel function buildFields( fields: readonly FieldNode[], namespaceId: string, + plane: 'domain' | 'storage' = 'domain', ): Record { return Object.fromEntries( - fields.map((field) => [field.fieldName, buildContractField(field, namespaceId)]), + fields.map((field) => [field.fieldName, buildContractField(field, namespaceId, plane)]), ); } @@ -165,7 +178,9 @@ export function buildValueObjects( }); } } - builtValueObjects[valueObject.name] = { fields: buildFields(valueObject.fields, namespaceId) }; + builtValueObjects[valueObject.name] = { + fields: buildFields(valueObject.fields, namespaceId, 'storage'), + }; } return builtValueObjects; diff --git a/packages/2-mongo-family/2-authoring/contract-ts/src/contract-builder.ts b/packages/2-mongo-family/2-authoring/contract-ts/src/contract-builder.ts index ce54e05ec07a..87c3e9143b07 100644 --- a/packages/2-mongo-family/2-authoring/contract-ts/src/contract-builder.ts +++ b/packages/2-mongo-family/2-authoring/contract-ts/src/contract-builder.ts @@ -50,7 +50,7 @@ import { type TargetPackRef, } from '@internal/framework-components/components'; import { extractCodecLookup } from '@internal/framework-components/control'; -import type { UNBOUND_NAMESPACE_ID } from '@internal/framework-components/ir'; +import { UNBOUND_NAMESPACE_ID } from '@internal/framework-components/ir'; import type { MongoCollection, MongoCollectionOptionsAuthoringInput, @@ -1287,12 +1287,18 @@ export const field = { type: blindCast< { readonly kind: 'valueObject'; - readonly name: NormalizeValueObjectName; + readonly plane: 'storage'; + readonly namespaceId: typeof UNBOUND_NAMESPACE_ID; + readonly entityKind: 'valueObject'; + readonly entityName: NormalizeValueObjectName; }, 'resolveValueObjectName returns the normalized name the type computes' >({ kind: 'valueObject', - name: resolveValueObjectName(valueObjectName), + plane: 'storage', + namespaceId: UNBOUND_NAMESPACE_ID, + entityKind: 'valueObject', + entityName: resolveValueObjectName(valueObjectName), }), nullable: false, many: false, diff --git a/packages/2-mongo-family/2-authoring/contract-ts/test/contract-builder.dsl.test.ts b/packages/2-mongo-family/2-authoring/contract-ts/test/contract-builder.dsl.test.ts index 0a8f43904511..8aa9db6955a1 100644 --- a/packages/2-mongo-family/2-authoring/contract-ts/test/contract-builder.dsl.test.ts +++ b/packages/2-mongo-family/2-authoring/contract-ts/test/contract-builder.dsl.test.ts @@ -1,7 +1,7 @@ import { crossRef, domainModelsAtDefaultNamespace, - domainValueObjectsAtDefaultNamespace, + storageValueObjectsAtDefaultNamespace, } from '@internal/contract/types'; import { UNBOUND_NAMESPACE_ID } from '@internal/framework-components/ir'; import { describe, expect, it } from 'vitest'; @@ -368,7 +368,9 @@ describe('mongo contract builder', () => { }), }, }); - expect(domainValueObjectsAtDefaultNamespace(contract.domain)).toEqual({ + expect( + JSON.parse(JSON.stringify(storageValueObjectsAtDefaultNamespace(contract.storage))), + ).toEqual({ Address: { fields: { street: { diff --git a/packages/2-mongo-family/2-authoring/contract-ts/test/contract-builder.storage-value-objects.test.ts b/packages/2-mongo-family/2-authoring/contract-ts/test/contract-builder.storage-value-objects.test.ts new file mode 100644 index 000000000000..7c3e1a3c2aa4 --- /dev/null +++ b/packages/2-mongo-family/2-authoring/contract-ts/test/contract-builder.storage-value-objects.test.ts @@ -0,0 +1,97 @@ +import { UNBOUND_NAMESPACE_ID } from '@internal/framework-components/ir'; +import { describe, expect, it } from 'vitest'; +import { defineContract, field, model, valueObject } from '../src/contract-builder'; +import { enumType, member } from '../src/enum-type'; +import { mongoFamilyPack, mongoTargetPack } from './mongo-test-packs'; + +const Role = enumType('Role', { codecId: 'mongo/string@1' }, member('Admin', 'admin')); + +function contractWith(street: 'street' | 'line1') { + const Geo = valueObject('Geo', { fields: { lat: field.double() } }); + const Address = valueObject('Address', { + fields: { + [street]: field.string(), + role: field.namedType(Role), + geo: field.valueObject(Geo).optional(), + }, + }); + const User = model('User', { + collection: 'users', + fields: { _id: field.objectId(), address: field.valueObject(Address) }, + }); + return defineContract({ + family: mongoFamilyPack, + target: mongoTargetPack, + enums: { Role }, + models: { User }, + valueObjects: { Address, Geo }, + }); +} + +function json(value: unknown) { + return JSON.parse(JSON.stringify(value)); +} + +function valueObjectRef(entityName: string) { + return { + kind: 'valueObject', + plane: 'storage', + namespaceId: UNBOUND_NAMESPACE_ID, + entityKind: 'valueObject', + entityName, + }; +} + +describe('the Mongo builder writes value objects into storage', () => { + it('writes them under entries.valueObject, and fields refer to them by coordinate', () => { + const contract = contractWith('street'); + expect({ + valueObjects: json(contract.storage.namespaces[UNBOUND_NAMESPACE_ID]?.entries)['valueObject'], + field: json(contract.domain.namespaces[UNBOUND_NAMESPACE_ID]?.models['User']?.fields)[ + 'address' + ], + domainKeys: Object.keys(contract.domain.namespaces[UNBOUND_NAMESPACE_ID] ?? {}).sort(), + }).toEqual({ + valueObjects: { + Address: { + fields: { + street: { + type: { kind: 'scalar', codecId: 'mongo/string@1' }, + nullable: false, + many: false, + }, + role: { + type: { kind: 'scalar', codecId: 'mongo/string@1' }, + nullable: false, + many: false, + valueSet: { + plane: 'storage', + entityKind: 'valueSet', + namespaceId: UNBOUND_NAMESPACE_ID, + entityName: 'Role', + }, + }, + geo: { type: valueObjectRef('Geo'), nullable: true, many: false }, + }, + }, + Geo: { + fields: { + lat: { + type: { kind: 'scalar', codecId: 'mongo/double@1' }, + nullable: false, + many: false, + }, + }, + }, + }, + field: { type: valueObjectRef('Address'), nullable: false, many: false }, + domainKeys: ['enum', 'models'], + }); + }); + + it('changes the storage hash when a member changes', () => { + expect(String(contractWith('street').storage.storageHash)).not.toBe( + String(contractWith('line1').storage.storageHash), + ); + }); +}); diff --git a/packages/3-targets/mongo/1-target/src/core/mongo-target-contract-serializer.ts b/packages/3-targets/mongo/1-target/src/core/mongo-target-contract-serializer.ts index 11202ce0e773..b5ef43fe0b39 100644 --- a/packages/3-targets/mongo/1-target/src/core/mongo-target-contract-serializer.ts +++ b/packages/3-targets/mongo/1-target/src/core/mongo-target-contract-serializer.ts @@ -4,10 +4,8 @@ import { type MongoCollectionInput, type MongoContract, MongoStorage, - type MongoValueSetInput, } from '@internal/mongo-contract'; import { blindCast } from '@internal/utils/casts'; -import { ifDefined } from '@internal/utils/defined'; import type { JsonObject } from '@internal/utils/json'; import type { MongoTargetContract } from './mongo-target-contract'; import { MongoTargetDatabase, MongoTargetUnboundDatabase } from './mongo-target-database'; @@ -17,37 +15,25 @@ export class MongoTargetContractSerializer extends MongoContractSerializerBase { - const collectionCount = Object.keys(nsData.entries.collection ?? {}).length; - const valueSetCount = Object.keys(nsData.entries['valueSet'] ?? {}).length; - if (nsId === UNBOUND_NAMESPACE_ID && collectionCount === 0 && valueSetCount === 0) { + const hasEntities = Object.values(nsData.entries).some( + (slot) => Object.keys(slot).length > 0, + ); + if (nsId === UNBOUND_NAMESPACE_ID && !hasEntities) { return [nsId, MongoTargetUnboundDatabase.instance]; } - const dbInput: { - id: string; - entries?: Readonly>>> & { - readonly collection?: Readonly>; - readonly valueSet?: Readonly>; - }; - } = { id: nsData.id }; - if ( - nsData.entries['collection'] !== undefined || - nsData.entries['valueSet'] !== undefined - ) { - dbInput.entries = { - collection: blindCast< - Readonly>, - 'collection entries validated by the mongo storage schema before hydration' - >(nsData.entries['collection'] ?? {}), - ...ifDefined( - 'valueSet', - blindCast< - Readonly> | undefined, - 'valueSet entries validated by the mongo storage schema before hydration' - >(nsData.entries['valueSet']), - ), - }; - } - return [nsId, new MongoTargetDatabase(dbInput)]; + return [ + nsId, + new MongoTargetDatabase({ + id: nsData.id, + entries: { + ...nsData.entries, + collection: blindCast< + Readonly>, + 'collection entries validated by the mongo storage schema before hydration' + >(nsData.entries['collection'] ?? {}), + }, + }), + ]; }), ); const targetStorage = new MongoStorage({ @@ -61,28 +47,19 @@ export class MongoTargetContractSerializer extends MongoContractSerializerBase = {}; for (const [nsId, ns] of Object.entries(storage.namespaces)) { - const collectionsOut: Record = {}; - for (const [collName, coll] of Object.entries(ns.entries.collection ?? {})) { - collectionsOut[collName] = blindCast< - JsonObject, - 'a collection IR node serializes to a JSON-safe collection object' - >(JSON.parse(JSON.stringify(coll))); - } - const valueSetOut: Record = {}; - for (const [vsName, vs] of Object.entries(ns.entries.valueSet ?? {})) { - valueSetOut[vsName] = blindCast< + const entries: Record = { + collection: blindCast< JsonObject, - 'a value-set IR node serializes to a JSON-safe { kind, values } object' - >(JSON.parse(JSON.stringify(vs))); - } - namespacesJson[nsId] = { - id: ns.id, - kind: 'mongo-database', - entries: { - collection: collectionsOut, - ...ifDefined('valueSet', Object.keys(valueSetOut).length > 0 ? valueSetOut : undefined), - }, + 'collection IR nodes serialize to JSON-safe collection objects' + >(JSON.parse(JSON.stringify(ns.entries.collection ?? {}))), }; + for (const [kind, slot] of Object.entries(ns.entries)) { + if (kind === 'collection' || Object.keys(slot).length === 0) continue; + entries[kind] = blindCast( + JSON.parse(JSON.stringify(slot)), + ); + } + namespacesJson[nsId] = { id: ns.id, kind: 'mongo-database', entries }; } return blindCast< JsonObject, diff --git a/packages/3-targets/mongo/1-target/test/mongo-target-contract-serializer.test.ts b/packages/3-targets/mongo/1-target/test/mongo-target-contract-serializer.test.ts index 95f2011b6c85..781f92515b0c 100644 --- a/packages/3-targets/mongo/1-target/test/mongo-target-contract-serializer.test.ts +++ b/packages/3-targets/mongo/1-target/test/mongo-target-contract-serializer.test.ts @@ -7,6 +7,7 @@ import { MongoIndex, MongoStorage, MongoValidator, + MongoValueObject, MongoValueSet, } from '@internal/mongo-contract'; import { applicationDomainOf } from '@repo/test-utils'; @@ -340,4 +341,34 @@ describe('MongoTargetContractSerializer', () => { expect(roleVs?.values).toEqual(['admin', 'author', 'reader']); }); }); + + describe('entries.valueObject round-trip', () => { + const address = { + fields: { + city: { type: { kind: 'scalar', codecId: 'mongo/string@1' }, nullable: false, many: false }, + }, + }; + + function makeValueObjectContractJson() { + const base = makeValidContractJson(); + const ns = base.storage.namespaces[UNBOUND_NAMESPACE_ID] as { + entries: Record; + }; + ns.entries['valueObject'] = { Address: address }; + return base; + } + + it('hydrates entries.valueObject and serializes it back unchanged', () => { + const serializer = new MongoTargetContractSerializer(); + const contract = serializer.deserializeContract(makeValueObjectContractJson()); + const out = JSON.parse(JSON.stringify(serializer.serializeContract(contract))); + expect({ + hydrated: + contract.storage.namespaces[UNBOUND_NAMESPACE_ID]?.entries.valueObject?.[ + 'Address' + ] instanceof MongoValueObject, + serialized: out.storage.namespaces[UNBOUND_NAMESPACE_ID].entries.valueObject, + }).toEqual({ hydrated: true, serialized: { Address: address } }); + }); + }); }); From d362ce9eaa0cdb61a9fc1fd4c94fd3a6aab3e70c Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 11:52:40 +0200 Subject: [PATCH 010/104] TML-3537: test-utils createContract writes value objects into storage Signed-off-by: willbot Signed-off-by: Will Madden --- test/utils/src/application-domain-of.ts | 12 +------ test/utils/src/contract-factories.ts | 29 +++++++++++++++-- test/utils/test/contract-factories.test.ts | 37 ++++++++++++++++++++++ 3 files changed, 65 insertions(+), 13 deletions(-) create mode 100644 test/utils/test/contract-factories.test.ts diff --git a/test/utils/src/application-domain-of.ts b/test/utils/src/application-domain-of.ts index 0bdbbd4315c6..5ed235b42eca 100644 --- a/test/utils/src/application-domain-of.ts +++ b/test/utils/src/application-domain-of.ts @@ -1,23 +1,13 @@ import { type ApplicationDomain, type ContractModelBase, - type ContractValueObject, UNBOUND_DOMAIN_NAMESPACE_ID, } from '@internal/contract/types'; export function applicationDomainOf(params: { readonly models?: Record; - readonly valueObjects?: Record; readonly namespaceId?: string; }): ApplicationDomain { const namespaceId = params.namespaceId ?? UNBOUND_DOMAIN_NAMESPACE_ID; - const models = params.models ?? {}; - return { - namespaces: { - [namespaceId]: { - models, - ...(params.valueObjects !== undefined ? { valueObjects: params.valueObjects } : {}), - }, - }, - }; + return { namespaces: { [namespaceId]: { models: params.models ?? {} } } }; } diff --git a/test/utils/src/contract-factories.ts b/test/utils/src/contract-factories.ts index 8ff6d11c8ab8..d808ab9feef2 100644 --- a/test/utils/src/contract-factories.ts +++ b/test/utils/src/contract-factories.ts @@ -26,6 +26,7 @@ type ContractOverrides = { /** Named domain namespaces; replaces the single unbound namespace `models` fills. */ namespaces?: Record; storage?: Omit; + /** Value objects, written to `entries.valueObject` of the storage's unbound namespace. */ valueObjects?: Record; enum?: Record; capabilities?: Record>; @@ -52,6 +53,28 @@ const DEFAULT_SQL_STORAGE = { }, } as const; +function withValueObjects( + storage: TStorage, + valueObjects: Record | undefined, +): TStorage { + if (valueObjects === undefined) return storage; + const namespaces = storage.namespaces as Record< + string, + { readonly id: string; readonly entries: Record } + >; + const namespace = namespaces[UNBOUND_NAMESPACE_ID] ?? { id: UNBOUND_NAMESPACE_ID, entries: {} }; + return { + ...storage, + namespaces: { + ...namespaces, + [UNBOUND_NAMESPACE_ID]: { + ...namespace, + entries: { ...namespace.entries, valueObject: valueObjects }, + }, + }, + }; +} + export function createContract( overrides: ContractOverrides = {}, ): Contract { @@ -59,7 +82,10 @@ export function createContract( const targetFamily = overrides.targetFamily ?? 'sql'; const capabilities = overrides.capabilities ?? {}; - const rawStorage = overrides.storage ?? DEFAULT_FRAMEWORK_STORAGE; + const rawStorage = withValueObjects( + overrides.storage ?? DEFAULT_FRAMEWORK_STORAGE, + overrides.valueObjects, + ); const storageHash = computeStorageHash({ target, @@ -85,7 +111,6 @@ export function createContract( namespaces: overrides.namespaces ?? { [UNBOUND_DOMAIN_NAMESPACE_ID]: { models: overrides.models ?? {}, - ...ifDefined('valueObjects', overrides.valueObjects), ...ifDefined('enum', overrides.enum), }, }, diff --git a/test/utils/test/contract-factories.test.ts b/test/utils/test/contract-factories.test.ts new file mode 100644 index 000000000000..63b3db0786be --- /dev/null +++ b/test/utils/test/contract-factories.test.ts @@ -0,0 +1,37 @@ +import { describe, expect, it } from 'vitest'; +import { applicationDomainOf } from '../src/application-domain-of'; +import { createContract } from '../src/contract-factories'; + +const address = { + fields: { city: { nullable: false, type: { kind: 'scalar', codecId: 'pg/text@1' } } }, +} as const; + +describe('createContract with value objects', () => { + it('puts them in the storage plane of the unbound namespace, next to the given entries', () => { + const contract = createContract({ + storage: { namespaces: { __unbound__: { id: '__unbound__', entries: { table: {} } } } }, + valueObjects: { Address: address }, + }); + expect({ + entries: contract.storage.namespaces['__unbound__']?.entries, + domain: contract.domain.namespaces['__unbound__'], + }).toEqual({ + entries: { table: {}, valueObject: { Address: address } }, + domain: { models: {} }, + }); + }); + + it('covers them in the storage hash', () => { + expect(createContract({ valueObjects: { Address: address } }).storage.storageHash).not.toBe( + createContract({}).storage.storageHash, + ); + }); +}); + +describe('applicationDomainOf', () => { + it('holds models only', () => { + expect(applicationDomainOf({ models: {} })).toEqual({ + namespaces: { __unbound__: { models: {} } }, + }); + }); +}); From 46322bae1aeafa3cd8cc65e929475a39f7a46fbb Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 11:54:12 +0200 Subject: [PATCH 011/104] TML-3537: a plan with no operations keeps its summary and adds what changed The no-operations sentence is appended to the existing summary, so an automatic baseline keeps its baseline text and a plan keeps its extension migration directories. Signed-off-by: willbot Signed-off-by: Will Madden --- .../control-api/operations/migration-plan.ts | 33 ++++++++++++------- .../orm/migration-plan.no-operations.test.ts | 4 ++- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts b/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts index a297ba0323e6..dcf1f2f57900 100644 --- a/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts +++ b/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts @@ -975,12 +975,16 @@ async function executeMigrationPlanCommandInner( ...(warnings.length > 0 ? { warnings } : {}), appliedStatements: [...deltaLeg.appliedStatements, ...deletes], temporaryValues: deltaLeg.temporaryValues, - summary: buildAutoBaselinePlanSummary( - baselineOps.length, - deltaOps.length, - emittedExtensionDirs.length, + ...withNoOperationsReport( + buildAutoBaselinePlanSummary( + baselineOps.length, + deltaOps.length, + emittedExtensionDirs.length, + ), + deltaOps.length === 0, + fromContract, + toContract, ), - ...(deltaOps.length === 0 ? noOperationsReport(fromContract, toContract) : {}), timings: { total: Date.now() - startTime }, }; return ok(result); @@ -1083,8 +1087,12 @@ async function executeMigrationPlanCommandInner( ...(warnings.length > 0 ? { warnings } : {}), appliedStatements: [...deltaLeg.appliedStatements, ...deletes], temporaryValues: deltaLeg.temporaryValues, - summary: buildPlanSummary(plannedOps.length, emittedExtensionDirs.length), - ...(plannedOps.length === 0 ? noOperationsReport(fromContract, toContract) : {}), + ...withNoOperationsReport( + buildPlanSummary(plannedOps.length, emittedExtensionDirs.length), + plannedOps.length === 0, + fromContract, + toContract, + ), timings: { total: Date.now() - startTime }, }; return ok(result); @@ -1096,13 +1104,16 @@ async function executeMigrationPlanCommandInner( } /** - * What a migration with no operations reports: the storage entities that changed, and a summary - * that says the database needs no operation for them. + * The summary of a written migration and, when it has no operations, the storage entities that + * changed, with a sentence added to the summary that says the database needs no operation for them. */ -function noOperationsReport( +function withNoOperationsReport( + summary: string, + hasNoOperations: boolean, fromContract: Contract | null, toContract: Contract, ): Pick { + if (!hasNoOperations) return { summary }; const changed = changedElementCoordinates(fromContract?.storage ?? null, toContract.storage); const what = changed.length === 0 @@ -1110,7 +1121,7 @@ function noOperationsReport( : `What changed: ${changed.map(storageEntityText).join(', ')}.`; return { changedStorageEntities: changed, - summary: `The storage the contract describes changed, but the database needs no operation for it, so the migration has none. ${what}`, + summary: `${summary}. The storage the contract describes changed, but the database needs no operation for it, so the migration has none. ${what}`, }; } diff --git a/packages/1-framework/3-tooling/cli/test/orm/migration-plan.no-operations.test.ts b/packages/1-framework/3-tooling/cli/test/orm/migration-plan.no-operations.test.ts index fc5738aeef7f..36f3f2937703 100644 --- a/packages/1-framework/3-tooling/cli/test/orm/migration-plan.no-operations.test.ts +++ b/packages/1-framework/3-tooling/cli/test/orm/migration-plan.no-operations.test.ts @@ -95,8 +95,9 @@ const CHANGED_STORAGE_ENTITIES = [ { plane: 'storage', namespaceId: 'public', entityKind: 'valueSet', entityName: 'Status' }, ]; -const NO_OPERATIONS_SUMMARY = +const NO_OPERATIONS_TEXT = 'The storage the contract describes changed, but the database needs no operation for it, so the migration has none. What changed: table "public.users", valueSet "public.Role", valueSet "public.Status".'; +const NO_OPERATIONS_SUMMARY = `Planned 0 operation(s). ${NO_OPERATIONS_TEXT}`; /** The database sits at HASH_FROM, which a migration reaches; the emitted contract is HASH_TO. */ async function projectWithHistory(): Promise { @@ -210,6 +211,7 @@ describe('migration plan when the planner produces no operations', () => { to: HASH_TO, operations: [], changedStorageEntities: CHANGED_STORAGE_ENTITIES, + summary: `Planned baseline (1 operation(s)) + 0 operation(s). ${NO_OPERATIONS_TEXT}`, }, }); expect(await migrationsOf(project)).toEqual( From ba44c87ba92000de827e4e941a3c6d8d0e866fb7 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 11:56:05 +0200 Subject: [PATCH 012/104] TML-3537: changedStorageEntities leaves out the unbound namespace id Signed-off-by: willbot Signed-off-by: Will Madden --- .../subsystems/7. Migration System.md | 2 +- .../src/control-api/operations/migration-plan.ts | 14 +++++++++++--- .../plans-with-no-operations.sqlite.e2e.test.ts | 12 ++++++++++-- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/docs/architecture docs/subsystems/7. Migration System.md b/docs/architecture docs/subsystems/7. Migration System.md index ec0bd1de541b..5111c111917f 100644 --- a/docs/architecture docs/subsystems/7. Migration System.md +++ b/docs/architecture docs/subsystems/7. Migration System.md @@ -170,7 +170,7 @@ The from-contract always materialises by reading the content-addressed snapshot When `--to` is omitted, `to_contract` is the emitted contract; when `--to` is supplied, `to_contract` is the resolved destination. -**A plan with no operations.** Two different storage hashes are two different database states, so a migration between them is expected to do something. When the planner produces no operations for a changed storage hash anyway, `migration plan` still writes the package, with an empty `ops.json`, and says so: the summary, a warning in the human output, names each storage entity whose content differs between the two contracts, and `--json` lists them as `changedStorageEntities` (entity coordinates, from `changedElementCoordinates` in `@internal/framework-components/ir`). `db migrate` and `db update` apply such a migration by moving the marker to the new hash; every runner already does this for an edge between two different hashes. This holds for a baseline with no operations and for the delta after it. Only a greenfield plan (no earlier contract) with no operations is refused, with `MIGRATION.PLANNING_FAILED` of kind `nothingToCreate`, since the contract describes nothing to create. When the storage hash does not change, nothing is written, as before. +**A plan with no operations.** Two different storage hashes are two different database states, so a migration between them is expected to do something. When the planner produces no operations for a changed storage hash anyway, `migration plan` still writes the package, with an empty `ops.json`, and says so: the summary, a warning in the human output, names each storage entity whose content differs between the two contracts, and `--json` lists them as `changedStorageEntities` (entity coordinates, from `changedElementCoordinates` in `@internal/framework-components/ir`; a coordinate in the unbound namespace leaves out `namespaceId`, as `appliedStatements` does). `db migrate` and `db update` apply such a migration by moving the marker to the new hash; every runner already does this for an edge between two different hashes. This holds for a baseline with no operations and for the delta after it. Only a greenfield plan (no earlier contract) with no operations is refused, with `MIGRATION.PLANNING_FAILED` of kind `nothingToCreate`, since the contract describes nothing to create. When the storage hash does not change, nothing is written, as before. An auto-baseline dates the delta package at the current time and the baseline package one minute earlier, so the baseline sorts first. Folder names have minute resolution, so packages planned within the same minute sort by name. diff --git a/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts b/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts index dcf1f2f57900..1c62e9b397e3 100644 --- a/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts +++ b/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts @@ -365,6 +365,11 @@ async function answerPlannedDataLoss(input: { return ok({ delta, deletes }); } +/** A storage entity a plan with no operations names: its coordinate, without the internal unbound namespace id. */ +type ChangedStorageEntity = Omit & { + readonly namespaceId?: string; +}; + export interface MigrationPlanResult { readonly ok: boolean; readonly noOp: boolean; @@ -438,9 +443,10 @@ export interface MigrationPlanResult { readonly temporaryValues: readonly TemporaryValueReport[]; /** * The storage entities that differ between the origin and the destination, when the storage - * changed but the written migration has no operations. + * changed but the written migration has no operations. A coordinate in the unbound namespace has + * no `namespaceId`, since that id is internal. */ - readonly changedStorageEntities?: readonly EntityCoordinate[]; + readonly changedStorageEntities?: readonly ChangedStorageEntity[]; readonly timings: { readonly total: number; }; @@ -1120,7 +1126,9 @@ function withNoOperationsReport( ? 'No storage entity changed; the change is elsewhere in the storage.' : `What changed: ${changed.map(storageEntityText).join(', ')}.`; return { - changedStorageEntities: changed, + changedStorageEntities: changed.map(({ namespaceId, ...rest }) => + namespaceId === UNBOUND_NAMESPACE_ID ? rest : { namespaceId, ...rest }, + ), summary: `${summary}. The storage the contract describes changed, but the database needs no operation for it, so the migration has none. ${what}`, }; } diff --git a/test/integration/test/cli-journeys/plans-with-no-operations.sqlite.e2e.test.ts b/test/integration/test/cli-journeys/plans-with-no-operations.sqlite.e2e.test.ts index fa5b356ade4c..2ab0c00eba42 100644 --- a/test/integration/test/cli-journeys/plans-with-no-operations.sqlite.e2e.test.ts +++ b/test/integration/test/cli-journeys/plans-with-no-operations.sqlite.e2e.test.ts @@ -95,10 +95,18 @@ withTempDir(({ createTempDir }) => { const plan = await runMigrationPlan(ctx, ['--from', origin, '--name', 'guest', '--json']); expect(plan.exitCode, `N1.05: plan: ${plan.stderr}`).toBe(0); - expect(parseJsonOutput(plan), 'N1.05: no operations').toMatchObject({ + const planned = parseJsonOutput(plan); + expect( + { + operations: planned.operations, + changedStorageEntities: planned.changedStorageEntities, + summary: planned.summary, + }, + 'N1.05: no operations', + ).toEqual({ operations: [], changedStorageEntities: [ - expect.objectContaining({ entityKind: 'valueSet', entityName: 'Role' }), + { plane: 'storage', entityKind: 'valueSet', entityName: 'Role' }, ], summary: expect.stringContaining('What changed: valueSet "Role".'), }); From 2de757943a2cdc71570286a3f57b6eb1d821c395 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 11:59:24 +0200 Subject: [PATCH 013/104] TML-3537: a plan with no operations names the storage keys that changed when no entity did Signed-off-by: willbot Signed-off-by: Will Madden --- .../control-api/operations/migration-plan.ts | 22 ++++++++++++-- .../cli/test/orm/fixtures/offline-project.ts | 17 +++++++++-- .../orm/migration-plan.no-operations.test.ts | 29 +++++++++++++++++++ 3 files changed, 64 insertions(+), 4 deletions(-) diff --git a/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts b/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts index 1c62e9b397e3..064b95532b23 100644 --- a/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts +++ b/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts @@ -16,7 +16,11 @@ import { type ResolvedMigrationStatement, type SchemaOwnership, } from '@internal/framework-components/control'; -import { changedElementCoordinates, UNBOUND_NAMESPACE_ID } from '@internal/framework-components/ir'; +import { + changedElementCoordinates, + isPlainRecord, + UNBOUND_NAMESPACE_ID, +} from '@internal/framework-components/ir'; import { snapshotsImportPathFrom, writeContractSnapshot, @@ -27,6 +31,7 @@ import { formatMigrationDirName, writeMigrationPackage } from '@internal/migrati import type { MigrationMetadata } from '@internal/migration-tools/metadata'; import { writeMigrationTs } from '@internal/migration-tools/migration-ts'; import type { ImportSpecifierResolver } from '@internal/publish-surface/import-roots'; +import { canonicalStringify } from '@internal/utils/canonical-stringify'; import { castAs } from '@internal/utils/casts'; import { ifDefined } from '@internal/utils/defined'; import { notOk, ok, type Result } from '@internal/utils/result'; @@ -1123,7 +1128,7 @@ function withNoOperationsReport( const changed = changedElementCoordinates(fromContract?.storage ?? null, toContract.storage); const what = changed.length === 0 - ? 'No storage entity changed; the change is elsewhere in the storage.' + ? `No storage entity changed; what changed is ${changedStorageKeys(fromContract, toContract).join(', ') || 'outside the storage entities'}.` : `What changed: ${changed.map(storageEntityText).join(', ')}.`; return { changedStorageEntities: changed.map(({ namespaceId, ...rest }) => @@ -1133,6 +1138,19 @@ function withNoOperationsReport( }; } +/** The top-level storage keys whose content differs between two contracts, such as `storage.types`, in key order. */ +function changedStorageKeys(fromContract: Contract | null, toContract: Contract): string[] { + const plain = (storage: unknown): Record => + isPlainRecord(storage) ? JSON.parse(JSON.stringify(storage)) : {}; + const from = plain(fromContract?.storage); + const to = plain(toContract.storage); + return [...new Set([...Object.keys(from), ...Object.keys(to)])] + .filter((key) => key !== 'storageHash') + .filter((key) => canonicalStringify(from[key]) !== canonicalStringify(to[key])) + .sort() + .map((key) => `storage.${key}`); +} + function storageEntityText(coordinate: EntityCoordinate): string { const name = coordinate.namespaceId === UNBOUND_NAMESPACE_ID diff --git a/packages/1-framework/3-tooling/cli/test/orm/fixtures/offline-project.ts b/packages/1-framework/3-tooling/cli/test/orm/fixtures/offline-project.ts index 0fe2ae90e5ec..54aa7037676a 100644 --- a/packages/1-framework/3-tooling/cli/test/orm/fixtures/offline-project.ts +++ b/packages/1-framework/3-tooling/cli/test/orm/fixtures/offline-project.ts @@ -78,9 +78,14 @@ export function contractJson( storageHash: string, models?: readonly FixtureModel[], storageNamespaces: FixtureStorageNamespaces = {}, + storageTypes?: Readonly>, ): Record { return { - storage: { storageHash, namespaces: storageNamespaces }, + storage: { + storageHash, + namespaces: storageNamespaces, + ...(storageTypes === undefined ? {} : { types: storageTypes }), + }, schemaVersion: '1.0.0', target: 'postgres', targetFamily: 'sql', @@ -103,6 +108,7 @@ export async function createOfflineProject(options: { readonly storageHash: string; readonly models?: readonly FixtureModel[]; readonly storageNamespaces?: FixtureStorageNamespaces; + readonly storageTypes?: Readonly>; }): Promise { const dir = createTestProjectDir('orm-offline'); created.push(dir); @@ -110,7 +116,14 @@ export async function createOfflineProject(options: { await mkdir(join(dir, 'output'), { recursive: true }); await writeFile( contractPath, - JSON.stringify(contractJson(options.storageHash, options.models, options.storageNamespaces)), + JSON.stringify( + contractJson( + options.storageHash, + options.models, + options.storageNamespaces, + options.storageTypes, + ), + ), 'utf-8', ); await writeFile( diff --git a/packages/1-framework/3-tooling/cli/test/orm/migration-plan.no-operations.test.ts b/packages/1-framework/3-tooling/cli/test/orm/migration-plan.no-operations.test.ts index 36f3f2937703..7f8b2d517921 100644 --- a/packages/1-framework/3-tooling/cli/test/orm/migration-plan.no-operations.test.ts +++ b/packages/1-framework/3-tooling/cli/test/orm/migration-plan.no-operations.test.ts @@ -179,6 +179,35 @@ describe('migration plan when the planner produces no operations', () => { ); }); + it('names the storage keys that changed when no storage entity did', async () => { + const project = await createOfflineProject({ + storageHash: HASH_TO, + storageNamespaces: STORAGE_FROM, + storageTypes: { Short: { codecId: 'sql/varchar@1' } }, + }); + await seedMigrationPackage({ + appMigrationsDir: project.appMigrationsDir, + dirName: '20260101T0000_initial', + from: null, + to: HASH_FROM, + }); + await seedContractSnapshot({ + migrationsDir: project.migrationsDir, + storageHash: HASH_FROM, + storageNamespaces: STORAGE_FROM, + }); + await seedDbRef({ appMigrationsDir: project.appMigrationsDir, storageHash: HASH_FROM }); + + expect(await run(project, ['migration', 'plan'])).toMatchObject({ + exitCode: 0, + data: { + changedStorageEntities: [], + summary: + 'Planned 0 operation(s). The storage the contract describes changed, but the database needs no operation for it, so the migration has none. No storage entity changed; what changed is storage.types.', + }, + }); + }); + it('says so in a warning, above the contract edge', async () => { const project = await projectWithHistory(); From 9f82c816257cdadf6616f5b5abbecebfabf1af14 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 11:59:55 +0200 Subject: [PATCH 014/104] TML-3537: docs no longer describe the removed refusal of a plan with no operations Signed-off-by: willbot Signed-off-by: Will Madden --- ...attributes, and a type alias is expanded into each column.md | 2 +- skills/prisma-8/references/debug.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md b/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md index 121327f5b86a..9d0f038abb04 100644 --- a/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md +++ b/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md @@ -184,7 +184,7 @@ The Prisma 7 reader builds the contract directly and writes no aliases. It reads - **Breaking for schemas.** Every preset call and every `@default(())` must be rewritten. `@updatedAt` keeps being refused in Prisma 8 PSL, with a hint that names `@onCreate(now()) @onUpdate(now())` and `temporal.UpdatedAt`. - **Hashes.** Rewriting a preset or `@default(uuid())` as attributes changes no hash when the column and generators are the same. A contract with a `types {}` block or TypeScript `types` gets a new storage hash, because its columns lose `typeRef` and gain inline params. Supabase's space contract gets a new hash. pgvector's and PostGIS's do not. -- **Upgrade.** The upgrade instructions ship a script that rewrites stored contracts and migration snapshots into the expanded form and refreshes their hashes, as the removal of `@noCheck` did. Users then re-emit and run `db sign` against every database. `migration plan` alone cannot record the new hash, because the change has no operations. The instructions also give the PSL and TypeScript rewrites for presets. +- **Upgrade.** The upgrade instructions ship a script that rewrites stored contracts and migration snapshots into the expanded form and refreshes their hashes, as the removal of `@noCheck` did. Users then re-emit and run `db sign` against every database. The change has no operations; since ADR 278, `migration plan` writes such a migration with no operations, and `db migrate` applies it by moving the marker to the new hash. The instructions also give the PSL and TypeScript rewrites for presets. - **Breaking for extension authors.** A default function can no longer lower to a client generator: `ControlMutationDefaultEntry.lower` returns a database default only. An extension that generated a value through a default function, such as `slugid()`, contributes a generator function with a binding for each codec it fills instead. `printArguments` and `applicableCodecIds` are removed; a binding declares the codecs it fills and how `contract print` writes each generator. - **Diagnostics.** The `PSL_PRESET_*` diagnostics and the refusal of preset-only generators in `@default` are removed. `@default(uuid())` gets a refusal that names `@onCreate(uuid())`. - **Less code.** The `fieldPreset` descriptor kind, `instantiateAuthoringFieldPreset`, the `option` argument kind, the `select` template node, the temporal preset builders and the preset print search are removed. The language server loses its field-preset completion branch. diff --git a/skills/prisma-8/references/debug.md b/skills/prisma-8/references/debug.md index 1f7e48b672da..d2030fc625e5 100644 --- a/skills/prisma-8/references/debug.md +++ b/skills/prisma-8/references/debug.md @@ -84,7 +84,7 @@ The single source of truth: read the envelope, find the row by `code`, follow th | `CONTRACT.VALIDATION_FAILED` | `contract emit`, `db *` | Re-run `pnpm prisma contract emit` after fixing the contract source named in `where.path`; `meta.errors` lists the issues. See `references/contract.md`. | | `CONFIG.DB_CONNECTION_REQUIRED` | `db *`, `db migrate`, `migration status` | Pass `--db ` or set `db.connection` in `prisma.config.ts`. | | `CONFIG.MISSING_EXTENSION_PACKS` | `contract emit` (e.g. contract uses `pgvector.Vector(...)` but config does not list the pgvector descriptor) | Add the descriptors named in `meta.missingExtensionPacks` to `extensions` in `prisma.config.ts`. See `references/contract.md`. | -| `MIGRATION.PLANNING_FAILED` | `db init`, `db update`, `migration plan` | Inspect `meta.conflicts`. Recovery is per-conflict — chain to `references/migrations.md`. When `migration plan` raises it because the planner produced no operations, the conflict's `why` gives the next step (`migration new --from `, or `db sign` for a database managed with `db update`). | +| `MIGRATION.PLANNING_FAILED` | `db init`, `db update`, `migration plan` | Inspect `meta.conflicts`. Recovery is per-conflict — chain to `references/migrations.md`. When `migration plan` raises it for a first migration whose contract describes nothing to create (conflict kind `nothingToCreate`), the conflict's `why` gives the next step: `migration new`, with the operations written by hand. A plan from an earlier contract with no operations is not refused: it is written, and `db migrate` moves the marker. | | `CLI.INIT_MISSING_FLAGS` / `CLI.INIT_INVALID_FLAG_VALUE` / `CLI.INIT_REINIT_NEEDS_FORCE` / `CLI.INIT_*` | `prisma orm init` | Re-run with the flags listed in `meta.missingFlags` or `meta.allowed`; a re-init needs `--confirm `. `CLI.INIT_INSTALL_FAILED` (exit 4) and `CLI.INIT_EMIT_FAILED` (exit 5) are findings on a completed scaffold — the files are on disk; fix and re-run the named step. | | `MIGRATION.UNFILLED_PLACEHOLDER` | `node /migration.ts` (self-emit) or `db migrate` | Edit `migration.ts`, replace the `placeholder("")` named by `meta.slot` with a real query closure, self-emit. See `references/migrations.md`. | | `MIGRATION.FILE_MISSING` | Reading a migration package | Restore from version control or scaffold a fresh package with `migration plan` / `migration new`. | From 299a8ca80a6d3244a5d243a716718b0bd8d6b5d9 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 12:00:27 +0200 Subject: [PATCH 015/104] TML-3537: a value object reference by name says the contract must be re-emitted Signed-off-by: willbot Signed-off-by: Will Madden --- .../0-foundation/contract/src/validate-domain.ts | 4 ++++ .../contract/test/validate-domain.test.ts | 15 +++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/packages/1-framework/0-foundation/contract/src/validate-domain.ts b/packages/1-framework/0-foundation/contract/src/validate-domain.ts index 55857487dab0..eba04c71fcbc 100644 --- a/packages/1-framework/0-foundation/contract/src/validate-domain.ts +++ b/packages/1-framework/0-foundation/contract/src/validate-domain.ts @@ -306,6 +306,7 @@ interface FieldTypeLike { readonly namespaceId?: string; readonly entityKind?: string; readonly entityName?: string; + readonly name?: string; readonly members?: readonly FieldTypeLike[]; } @@ -321,6 +322,9 @@ function fieldLike(field: unknown): FieldLike | undefined { } function valueObjectReferenceProblem(type: FieldTypeLike): string | undefined { + if (type.plane === undefined && type.entityKind === undefined) { + return `refers to value object "${type.name}" by name; contracts no longer do: a field refers to its value object by the coordinate of a storage entity under storage.namespaces..entries.valueObject, so re-emit the contract from its source`; + } if (type.plane !== 'storage' || type.entityKind !== 'valueObject') { return `refers to a value object with plane "${type.plane}" and entityKind "${type.entityKind}"; a value object reference has plane "storage" and entityKind "valueObject"`; } diff --git a/packages/1-framework/0-foundation/contract/test/validate-domain.test.ts b/packages/1-framework/0-foundation/contract/test/validate-domain.test.ts index 15096c2422a7..3674343520a1 100644 --- a/packages/1-framework/0-foundation/contract/test/validate-domain.test.ts +++ b/packages/1-framework/0-foundation/contract/test/validate-domain.test.ts @@ -777,6 +777,21 @@ describe('validateContractDomain()', () => { ); }); + it('refuses a reference by name, the old shape, and says to re-emit', () => { + const contract = makeValidContract({ + roots: { users: crossRef('User') }, + models: { + User: makeMinimalModel({ + fields: { addr: { nullable: false, type: { kind: 'valueObject', name: 'Address' } } }, + }), + }, + valueObjects: { Address: address }, + }); + expect(() => validateContractDomain(contract)).toThrow( + 'Model "__unbound__:User" field "addr" refers to value object "Address" by name; contracts no longer do: a field refers to its value object by the coordinate of a storage entity under storage.namespaces..entries.valueObject, so re-emit the contract from its source', + ); + }); + it('accepts a value object whose field refers to another value object', () => { const contract = makeValidContract({ roots: { users: crossRef('User') }, From 6279851b3fcaec8e2be061863e8e2e4c3b8a8f50 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 12:01:10 +0200 Subject: [PATCH 016/104] TML-3537: valueObjectAt reads only own namespaces and tolerates a namespace without entries Signed-off-by: willbot Signed-off-by: Will Madden --- .../contract/src/storage-value-objects.ts | 4 ++- .../test/storage-value-objects.test.ts | 25 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/packages/1-framework/0-foundation/contract/src/storage-value-objects.ts b/packages/1-framework/0-foundation/contract/src/storage-value-objects.ts index 719fb95a170d..3aa0b7d27500 100644 --- a/packages/1-framework/0-foundation/contract/src/storage-value-objects.ts +++ b/packages/1-framework/0-foundation/contract/src/storage-value-objects.ts @@ -11,7 +11,9 @@ function valueObjectsOf( storage: StorageNamespaces, namespaceId: string, ): Readonly> { - const slot = storage.namespaces[namespaceId]?.entries['valueObject']; + const slot = Object.hasOwn(storage.namespaces, namespaceId) + ? storage.namespaces[namespaceId]?.entries?.['valueObject'] + : undefined; return isPlainRecord(slot) ? blindCast< Readonly>, diff --git a/packages/1-framework/0-foundation/contract/test/storage-value-objects.test.ts b/packages/1-framework/0-foundation/contract/test/storage-value-objects.test.ts index d87e68a9d26d..da44a664497a 100644 --- a/packages/1-framework/0-foundation/contract/test/storage-value-objects.test.ts +++ b/packages/1-framework/0-foundation/contract/test/storage-value-objects.test.ts @@ -1,5 +1,7 @@ +import { blindCast } from '@internal/utils/casts'; import { describe, expect, it } from 'vitest'; import { storageValueObjectsAtDefaultNamespace, valueObjectAt } from '../src/storage-value-objects'; +import type { StorageNamespace } from '../src/types'; const address = { fields: { street: { nullable: false, type: { kind: 'scalar', codecId: 'pg/text@1' } } }, @@ -41,6 +43,29 @@ describe('valueObjectAt', () => { }); }); +describe('valueObjectAt with an unusual namespace', () => { + it('is undefined for a namespace id that names an Object.prototype property, or a namespace with no entries', () => { + const reference = (namespaceId: string) => + ({ + plane: 'storage', + namespaceId, + entityKind: 'valueObject', + entityName: 'Address', + }) as const; + expect({ + proto: valueObjectAt(storage, reference('__proto__')), + noEntries: valueObjectAt( + { + namespaces: { + bare: blindCast({ id: 'bare' }), + }, + }, + reference('bare'), + ), + }).toEqual({ proto: undefined, noEntries: undefined }); + }); +}); + describe('storageValueObjectsAtDefaultNamespace', () => { it('reads the value objects of the only storage namespace', () => { expect(storageValueObjectsAtDefaultNamespace(storage)).toEqual({ Address: address }); From 649758ea89af96c16e711cf0d2647191e327c628 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 12:07:56 +0200 Subject: [PATCH 017/104] TML-3537: a value object member's value set is typed as a storage value set ContractValueObjectField types valueSet as EntityCoordinate<'storage', 'valueSet'>, so a value object cannot refer to the domain plane by type. The SQL and Mongo builders write members and model fields through separate functions. Signed-off-by: willbot Signed-off-by: Will Madden --- .../0-foundation/contract/src/domain-types.ts | 9 ++- .../contract/src/exports/types.ts | 1 + .../test/value-object-field.test-d.ts | 24 ++++++ .../src/ir/mongo-value-object.ts | 6 +- .../test/contract-refusals.test.ts | 12 --- .../test/derive-json-schema-helpers.ts | 15 ++-- .../contract-ts/src/build-models.ts | 76 ++++++++++++------- .../contract/src/ir/storage-value-object.ts | 6 +- .../contract-ts/src/domain-fields.ts | 43 ++++++----- .../contract-ts/src/enum-members.ts | 6 +- 10 files changed, 123 insertions(+), 75 deletions(-) create mode 100644 packages/1-framework/0-foundation/contract/test/value-object-field.test-d.ts diff --git a/packages/1-framework/0-foundation/contract/src/domain-types.ts b/packages/1-framework/0-foundation/contract/src/domain-types.ts index ec70d5b9cc1a..43e852759d85 100644 --- a/packages/1-framework/0-foundation/contract/src/domain-types.ts +++ b/packages/1-framework/0-foundation/contract/src/domain-types.ts @@ -93,8 +93,15 @@ export type ContractDiscriminator = { readonly field: string; }; +/** + * A field of a value object, which is a storage entity (ADR 278). Storage never refers to the domain plane, so a member an enum types refers to the enum's storage value set. + */ +export interface ContractValueObjectField extends Omit { + readonly valueSet?: EntityCoordinate<'storage', 'valueSet'> & { readonly spaceId?: string }; +} + export type ContractValueObject = { - readonly fields: Record; + readonly fields: Record; }; export type ModelStorageBase = Readonly>; diff --git a/packages/1-framework/0-foundation/contract/src/exports/types.ts b/packages/1-framework/0-foundation/contract/src/exports/types.ts index f15564336729..914b8e0d2026 100644 --- a/packages/1-framework/0-foundation/contract/src/exports/types.ts +++ b/packages/1-framework/0-foundation/contract/src/exports/types.ts @@ -32,6 +32,7 @@ export type { ContractToManyRelation, ContractToOneRelation, ContractValueObject, + ContractValueObjectField, EmbedRelationKeys, ModelStorageBase, ReferenceRelationKeys, diff --git a/packages/1-framework/0-foundation/contract/test/value-object-field.test-d.ts b/packages/1-framework/0-foundation/contract/test/value-object-field.test-d.ts new file mode 100644 index 000000000000..7db8801e11fb --- /dev/null +++ b/packages/1-framework/0-foundation/contract/test/value-object-field.test-d.ts @@ -0,0 +1,24 @@ +import { expectTypeOf, test } from 'vitest'; +import type { ContractValueObject } from '../src/domain-types'; + +const role = { namespaceId: 'public', entityName: 'Role' } as const; +const storageValueSet = { plane: 'storage', entityKind: 'valueSet', ...role } as const; +const domainEnum = { plane: 'domain', entityKind: 'enum', ...role } as const; +const text = { kind: 'scalar', codecId: 'pg/text@1' } as const; + +test('a value object member refers to a storage value set, never a domain enum', () => { + const storageMember: ContractValueObject = { + fields: { role: { nullable: false, type: text, valueSet: storageValueSet } }, + }; + const domainMember: ContractValueObject = { + fields: { + role: { + nullable: false, + type: text, + // @ts-expect-error a storage entity's field may not refer to the domain plane + valueSet: domainEnum, + }, + }, + }; + expectTypeOf(storageMember).toEqualTypeOf(domainMember); +}); diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/src/ir/mongo-value-object.ts b/packages/2-mongo-family/1-foundation/mongo-contract/src/ir/mongo-value-object.ts index d9b48fd6811e..4023165c3fda 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/src/ir/mongo-value-object.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/src/ir/mongo-value-object.ts @@ -1,9 +1,9 @@ -import type { ContractField } from '@internal/contract/types'; +import type { ContractValueObjectField } from '@internal/contract/types'; import { freezeNode, IRNodeBase } from '@internal/framework-components/ir'; /** Hydration / construction input shape for {@link MongoValueObject}; mirrors the stored JSON. */ export interface MongoValueObjectInput { - readonly fields: Readonly>; + readonly fields: Readonly>; } /** @@ -11,7 +11,7 @@ export interface MongoValueObjectInput { */ export class MongoValueObject extends IRNodeBase { declare readonly kind: 'mongo-value-object'; - readonly fields: Readonly>; + readonly fields: Readonly>; constructor(input: MongoValueObjectInput) { super(); diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/test/contract-refusals.test.ts b/packages/2-mongo-family/1-foundation/mongo-contract/test/contract-refusals.test.ts index 3b287c6b8ed5..a670c7dad9f3 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/test/contract-refusals.test.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/test/contract-refusals.test.ts @@ -160,18 +160,6 @@ describe('mongoContractRefusalsOf', () => { ).toEqual([]); }); - it('refuses an enum a validator cannot list, used in a value object of a validated collection', () => { - expect( - refusals({ - enums: { Big: unlistableEnums.Big }, - valueObjects: { Sizing: { fields: { size: enumField('mongo/int64@1', 'Big') } } }, - models: { - Job: model({ sizing: voField('Sizing') }, { storage: { collection: 'jobs' } }), - }, - }), - ).toEqual([expect.objectContaining({ kind: 'enumNotListable', enumName: 'Big' })]); - }); - it('refuses an enum a validator cannot list, used by a value object member through its storage value set', () => { expect( refusals({ diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/test/derive-json-schema-helpers.ts b/packages/2-mongo-family/1-foundation/mongo-contract/test/derive-json-schema-helpers.ts index f80914ec5f2a..425b875a8303 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/test/derive-json-schema-helpers.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/test/derive-json-schema-helpers.ts @@ -1,6 +1,9 @@ -import type { ContractField } from '@internal/contract/types'; +import type { ContractField, ContractValueObjectField } from '@internal/contract/types'; -export function scalarField(codecId: string, nullable = false): ContractField { +/** A field with no value set: valid both on a model and as a value object member. */ +type FieldWithoutValueSet = Omit; + +export function scalarField(codecId: string, nullable = false): FieldWithoutValueSet { return { type: { kind: 'scalar', codecId }, nullable, many: false }; } @@ -22,7 +25,7 @@ export function arrayField( codecId: string, nullable = false, elementNullable = false, -): ContractField { +): FieldWithoutValueSet { return { type: { kind: 'scalar', codecId }, nullable, many: { elementNullable } }; } @@ -46,7 +49,7 @@ export function arrayEnumField( } /** A value object member typed by an enum: it refers to the enum's storage value set, as a storage entity's field does. */ -export function memberEnumField(codecId: string, enumName: string): ContractField { +export function memberEnumField(codecId: string, enumName: string): ContractValueObjectField { return { type: { kind: 'scalar', codecId }, nullable: false, @@ -70,7 +73,7 @@ function valueObjectType(entityName: string): ContractField['type'] { }; } -export function voField(name: string, nullable = false): ContractField { +export function voField(name: string, nullable = false): FieldWithoutValueSet { return { type: valueObjectType(name), nullable, many: false }; } @@ -78,6 +81,6 @@ export function voArrayField( name: string, nullable = false, elementNullable = false, -): ContractField { +): FieldWithoutValueSet { return { type: valueObjectType(name), nullable, many: { elementNullable } }; } diff --git a/packages/2-mongo-family/2-authoring/contract-ts/src/build-models.ts b/packages/2-mongo-family/2-authoring/contract-ts/src/build-models.ts index 237caa771c65..f92b6e5529c0 100644 --- a/packages/2-mongo-family/2-authoring/contract-ts/src/build-models.ts +++ b/packages/2-mongo-family/2-authoring/contract-ts/src/build-models.ts @@ -4,9 +4,9 @@ import { type ContractModel, type ContractReferenceRelation, type ContractValueObject, + type ContractValueObjectField, type CrossReference, crossRef, - type ValueSetRef, } from '@internal/contract/types'; import { resolveToOneRelationNullable } from '@internal/contract-authoring'; import { type MongoModelStorage, unsupportedStoredNameReason } from '@internal/mongo-contract'; @@ -38,39 +38,59 @@ export function storedFieldNameIn(fields: readonly FieldNode[], fieldName: strin return field === undefined ? fieldName : storedNameOf(field); } -/** - * A field as the contract holds it. A model field refers to its enum in the domain plane; a value object member is a storage entity's field, and storage never refers to the domain, so it refers to the enum's storage value set. - */ -function buildContractField( - field: FieldNode, - namespaceId: string, - plane: 'domain' | 'storage', -): ContractField { - const valueSet: ValueSetRef | undefined = - field.enumName === undefined - ? undefined - : { - plane, - entityKind: plane === 'domain' ? 'enum' : 'valueSet', - namespaceId, - entityName: field.enumName, - }; - - return { - type: field.type, - nullable: field.nullable, - many: field.many, - ...ifDefined('valueSet', valueSet), - }; +function fieldWithoutValueSet(field: FieldNode): Omit { + return { type: field.type, nullable: field.nullable, many: field.many }; } +/** A model's fields. A field an enum types refers to the domain enum. */ function buildFields( fields: readonly FieldNode[], namespaceId: string, - plane: 'domain' | 'storage' = 'domain', ): Record { return Object.fromEntries( - fields.map((field) => [field.fieldName, buildContractField(field, namespaceId, plane)]), + fields.map((field) => [ + field.fieldName, + { + ...fieldWithoutValueSet(field), + ...ifDefined( + 'valueSet', + field.enumName === undefined + ? undefined + : ({ + plane: 'domain', + entityKind: 'enum', + namespaceId, + entityName: field.enumName, + } as const), + ), + }, + ]), + ); +} + +/** A value object's members. A value object is a storage entity, and storage never refers to the domain plane, so a member an enum types refers to the enum's storage value set. */ +function buildValueObjectMembers( + fields: readonly FieldNode[], + namespaceId: string, +): Record { + return Object.fromEntries( + fields.map((field) => [ + field.fieldName, + { + ...fieldWithoutValueSet(field), + ...ifDefined( + 'valueSet', + field.enumName === undefined + ? undefined + : ({ + plane: 'storage', + entityKind: 'valueSet', + namespaceId, + entityName: field.enumName, + } as const), + ), + }, + ]), ); } @@ -179,7 +199,7 @@ export function buildValueObjects( } } builtValueObjects[valueObject.name] = { - fields: buildFields(valueObject.fields, namespaceId, 'storage'), + fields: buildValueObjectMembers(valueObject.fields, namespaceId), }; } diff --git a/packages/2-sql-family/1-core/contract/src/ir/storage-value-object.ts b/packages/2-sql-family/1-core/contract/src/ir/storage-value-object.ts index 7f4de541c84e..4fbd23fd699e 100644 --- a/packages/2-sql-family/1-core/contract/src/ir/storage-value-object.ts +++ b/packages/2-sql-family/1-core/contract/src/ir/storage-value-object.ts @@ -1,10 +1,10 @@ -import type { ContractField } from '@internal/contract/types'; +import type { ContractValueObjectField } from '@internal/contract/types'; import { freezeNode } from '@internal/framework-components/ir'; import { SqlNode } from './sql-node'; /** Hydration / construction input shape for {@link StorageValueObject}; mirrors the stored JSON. */ export interface StorageValueObjectInput { - readonly fields: Readonly>; + readonly fields: Readonly>; } /** @@ -13,7 +13,7 @@ export interface StorageValueObjectInput { * The entry's name is not on the class: value objects are keyed by name in the parent namespace's `valueObject` map. */ export class StorageValueObject extends SqlNode { - readonly fields: Readonly>; + readonly fields: Readonly>; constructor(input: StorageValueObjectInput) { super(); diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/domain-fields.ts b/packages/2-sql-family/2-authoring/contract-ts/src/domain-fields.ts index 05cffe0934e3..85929cf0d560 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/domain-fields.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/domain-fields.ts @@ -1,4 +1,4 @@ -import type { ContractField, ValueSetRef } from '@internal/contract/types'; +import type { ContractField, ContractValueObjectField } from '@internal/contract/types'; import { nonEmptyTypeParams } from '@internal/sql-contract/types'; import { ifDefined } from '@internal/utils/defined'; import { @@ -6,16 +6,14 @@ import { type ScalarMemberNode, type ValueObjectMemberNode, } from './contract-definition'; -import { enumValueSetRefs } from './enum-members'; +import { type EnumValueSetRefs, enumValueSetRefs } from './enum-members'; -function buildField( - field: ScalarMemberNode | ValueObjectMemberNode, +type Member = ScalarMemberNode | ValueObjectMemberNode; + +function fieldWithoutValueSet( + field: Member, defaultNamespaceId: string, - valueSetOf: (refs: { - readonly domain: ValueSetRef; - readonly storage: ValueSetRef; - }) => ValueSetRef, -): ContractField { +): Omit { const many = field.many ? { elementNullable: field.elementNullable === true } : false; if (isValueObjectMember(field)) { return { @@ -30,7 +28,6 @@ function buildField( many, }; } - const refs = enumValueSetRefs(field.enumTypeHandle, defaultNamespaceId); return { type: { kind: 'scalar', @@ -39,22 +36,30 @@ function buildField( }, nullable: field.nullable, many, - ...ifDefined('valueSet', refs === undefined ? undefined : valueSetOf(refs)), }; } +function enumRefsOf(field: Member, defaultNamespaceId: string): EnumValueSetRefs | undefined { + return isValueObjectMember(field) + ? undefined + : enumValueSetRefs(field.enumTypeHandle, defaultNamespaceId); +} + /** A model field in the domain plane. A value object is referred to by its storage coordinate, an enum by the domain enum. */ -export function buildDomainField( - field: ScalarMemberNode | ValueObjectMemberNode, - defaultNamespaceId: string, -): ContractField { - return buildField(field, defaultNamespaceId, (refs) => refs.domain); +export function buildDomainField(field: Member, defaultNamespaceId: string): ContractField { + return { + ...fieldWithoutValueSet(field, defaultNamespaceId), + ...ifDefined('valueSet', enumRefsOf(field, defaultNamespaceId)?.domain), + }; } /** A member of a storage value object. Storage never refers to the domain plane, so an enum member refers to the storage value set. */ export function buildValueObjectMember( - field: ScalarMemberNode | ValueObjectMemberNode, + field: Member, defaultNamespaceId: string, -): ContractField { - return buildField(field, defaultNamespaceId, (refs) => refs.storage); +): ContractValueObjectField { + return { + ...fieldWithoutValueSet(field, defaultNamespaceId), + ...ifDefined('valueSet', enumRefsOf(field, defaultNamespaceId)?.storage), + }; } diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/enum-members.ts b/packages/2-sql-family/2-authoring/contract-ts/src/enum-members.ts index c33299c517ca..70143381d5ef 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/enum-members.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/enum-members.ts @@ -1,4 +1,4 @@ -import type { ValueSetRef } from '@internal/contract/types'; +import type { EntityCoordinate } from '@internal/contract/types'; import { type EnumTypeHandle, encodeEnumTypeMembers } from '@internal/contract-authoring'; import type { CodecLookupWithDescriptors } from '@internal/framework-components/codec'; import { contractError } from './contract-errors'; @@ -29,8 +29,8 @@ export function checkMemberValues( } export interface EnumValueSetRefs { - readonly domain: ValueSetRef; - readonly storage: ValueSetRef; + readonly domain: EntityCoordinate<'domain', 'enum'>; + readonly storage: EntityCoordinate<'storage', 'valueSet'>; } /** From b8916a3d0ba7a9058a12ac16e52a019c5c0fc21b Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 12:09:41 +0200 Subject: [PATCH 018/104] TML-3537: a value object member's value set must exist in storage Signed-off-by: willbot Signed-off-by: Will Madden --- .../contract/src/validate-domain.ts | 25 ++++++++++--- .../contract/test/validate-domain.test.ts | 36 +++++++++++++++++++ 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/packages/1-framework/0-foundation/contract/src/validate-domain.ts b/packages/1-framework/0-foundation/contract/src/validate-domain.ts index eba04c71fcbc..4c6597727a6f 100644 --- a/packages/1-framework/0-foundation/contract/src/validate-domain.ts +++ b/packages/1-framework/0-foundation/contract/src/validate-domain.ts @@ -312,6 +312,7 @@ interface FieldTypeLike { interface FieldLike { readonly type?: FieldTypeLike; + readonly valueSet?: { readonly namespaceId?: string; readonly entityName?: string }; readonly many?: unknown; readonly elementNullable?: unknown; readonly dict?: boolean; @@ -373,14 +374,30 @@ function validateValueObjectReferences(contract: DomainContractShape, errors: st } for (const { namespaceId, name, valueObject } of storageValueObjects(contract)) { for (const [fieldName, field] of Object.entries(valueObject.fields)) { - checkType( - fieldLike(field)?.type, - `Value object "${namespaceId}:${name}" field "${fieldName}"`, - ); + const location = `Value object "${namespaceId}:${name}" field "${fieldName}"`; + checkType(fieldLike(field)?.type, location); + const problem = missingValueSetProblem(contract, fieldLike(field)?.valueSet); + if (problem !== undefined) errors.push(`${location} ${problem}`); } } } +/** The problem with a value object member's storage value set when storage does not have it. */ +function missingValueSetProblem( + contract: DomainContractShape, + valueSet: FieldLike['valueSet'], +): string | undefined { + if (valueSet === undefined) return undefined; + const namespaceId = valueSet.namespaceId ?? ''; + const entityName = valueSet.entityName ?? ''; + const slot = Object.hasOwn(contract.storage.namespaces, namespaceId) + ? contract.storage.namespaces[namespaceId]?.entries?.['valueSet'] + : undefined; + return isPlainRecord(slot) && Object.hasOwn(slot, entityName) + ? undefined + : `refers to value set "${entityName}" in storage namespace "${namespaceId}", which storage.namespaces.${namespaceId}.entries.valueSet does not have`; +} + function validateFieldModifiers( modelIndex: ModelIndex, contract: DomainContractShape, diff --git a/packages/1-framework/0-foundation/contract/test/validate-domain.test.ts b/packages/1-framework/0-foundation/contract/test/validate-domain.test.ts index 3674343520a1..d577d406601f 100644 --- a/packages/1-framework/0-foundation/contract/test/validate-domain.test.ts +++ b/packages/1-framework/0-foundation/contract/test/validate-domain.test.ts @@ -878,6 +878,42 @@ describe('validateContractDomain()', () => { expect(() => validateContractDomain(contract)).not.toThrow(); }); + it('refuses a value object member whose storage value set storage does not have', () => { + const member = (entityName: string) => ({ + nullable: false, + type: { kind: 'scalar', codecId: 'pg/text@1' }, + valueSet: { + plane: 'storage', + entityKind: 'valueSet', + namespaceId: UNBOUND_DOMAIN_NAMESPACE_ID, + entityName, + }, + }); + const contract = makeValidContract({ + roots: {}, + models: {}, + valueObjects: { Tagged: { fields: { role: member('Role'), kind: member('Kind') } } }, + }); + const storage = contract.storage.namespaces[UNBOUND_DOMAIN_NAMESPACE_ID]; + const withRole = { + ...contract, + storage: { + namespaces: { + [UNBOUND_DOMAIN_NAMESPACE_ID]: { + id: UNBOUND_DOMAIN_NAMESPACE_ID, + entries: { + ...storage?.entries, + valueSet: { Role: { kind: 'valueSet', values: ['a'] } }, + }, + }, + }, + }, + }; + expect(() => validateContractDomain(withRole)).toThrow( + 'Contract domain validation failed:\n- Value object "__unbound__:Tagged" field "kind" refers to value set "Kind" in storage namespace "__unbound__", which storage.namespaces.__unbound__.entries.valueSet does not have', + ); + }); + it('refuses value objects in the domain plane, and says where they live now', () => { const contract = makeValidContract({ domain: { From 2286fe450a0e93ce8d18be54caadaf3a611b0025 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 12:27:56 +0200 Subject: [PATCH 019/104] TML-3537: dispatch briefs for authoring and emit, and the column value-set check as deferred Signed-off-by: willbot Signed-off-by: Will Madden --- projects/migration-statements/deferred.md | 4 ++ ...-authoring-writes-storage-value-objects.md | 50 +++++++++++++++++++ .../13-emit-and-types-read-storage.md | 41 +++++++++++++++ 3 files changed, 95 insertions(+) create mode 100644 projects/migration-statements/slices/remaining-nouns/dispatches/12-authoring-writes-storage-value-objects.md create mode 100644 projects/migration-statements/slices/remaining-nouns/dispatches/13-emit-and-types-read-storage.md diff --git a/projects/migration-statements/deferred.md b/projects/migration-statements/deferred.md index a8ac9b4caee9..01802bda5288 100644 --- a/projects/migration-statements/deferred.md +++ b/projects/migration-statements/deferred.md @@ -192,3 +192,7 @@ Reproduction: emit a PSL contract with a `namespace public` block and a `namespa **Found:** second whole-PR review of the namespace statements, fix round (2026-10-22). `db update --delete billing` against a database whose marker hash has no local snapshot fails with `CLI.CONSENT_UNUSED`: the planner has no origin contract, so it reports no removed namespace, and the namespace's tables stay. Nothing is lost, which is the right outcome, but the message does not say why. `--rename` in the same situation fails with `MIGRATION.STATEMENT_ORIGIN_UNKNOWN`, which names the hash and the snapshot directory. A namespace `--delete` that matches no question should say the same when the origin contract is unknown. + +## Column value-set references are not checked against storage + +A column's `valueSet: { plane: 'storage', entityKind: 'valueSet', entityName }` is not checked to name an existing `storage.namespaces..entries.valueSet` entry, in either family. A hand-edited or corrupt `contract.json` with a dangling reference passes validation and fails later. PR 3 of the remaining-nouns slice adds the check for value object members only. Found in the review of that PR, 2026-10-23. diff --git a/projects/migration-statements/slices/remaining-nouns/dispatches/12-authoring-writes-storage-value-objects.md b/projects/migration-statements/slices/remaining-nouns/dispatches/12-authoring-writes-storage-value-objects.md new file mode 100644 index 000000000000..4ca1400bec6a --- /dev/null +++ b/projects/migration-statements/slices/remaining-nouns/dispatches/12-authoring-writes-storage-value-objects.md @@ -0,0 +1,50 @@ +# Dispatch 12 — authoring writes value objects and discriminator value sets into storage + +**Plan:** [`../plan-pr3.md`](../plan-pr3.md) · **ADR:** ADR 278 · **Grounding:** `wip/pr3-grounding.md` § A (SQL family, Mongo family), C, E · **Builds on:** dispatch 10 (`d607bde854`, `1792de2a67`, `965c0cf100`) · **Branch:** `tml-3537-value-objects-storage` + +## Outcome + +Every authoring path writes the dispatch 10 shape, and the storage hash covers it: + +- SQL PSL and the SQL TypeScript build write each value object to `storage.namespaces..entries.valueObject.`, and each field that holds one refers to it by the storage coordinate. A value object member typed by an enum refers to the storage value set. +- Mongo PSL, the Mongo TypeScript builder and the Prisma 6 reader do the same through the one Mongo builder. `build-mongo-storage.ts` hashes `valueObject` entries. +- SQL PSL builds the discriminator value set before the hash. The value set is `storage.namespaces..entries.valueSet..`, holding the variant values, and the discriminator column refers to it. This needs the discriminator and the variant values in the build definition, so `resolvePolymorphism` no longer patches them in after the hash (grounding risk 2). +- An enum-typed discriminator keeps the enum's value set (plan decision 2); no second value set is written. +- An enum whose name equals a discriminator's `.` key is refused, naming both and saying to rename the enum. Nothing else about enum names changes (plan decision 3). +- The MongoDB validator derivation reads value objects from storage. Its output does not change. + +The Postgres check for the discriminator and value object columns is dispatch 15, not this one. + +## Do + +1. **Tests first, red before each change:** + - SQL PSL: a composite type lands in storage with the coordinate on the field; nested and list members; an enum member refers to the storage value set; the storage hash changes when a member changes. + - SQL PSL: a polymorphic model writes the `.` value set and the column refers to it; adding a variant changes the hash; an enum-typed discriminator keeps the enum's value set. + - SQL: the clash refusal through `enumType()`. + - Mongo: TypeScript builder, PSL and Prisma 6 each write storage value objects; the hash changes with a member; the derived collection validator is unchanged for an existing fixture. +2. Implement. Reuse the names dispatch 10 chose. Do not invent nouns; if one is unavoidable, list it in the report with candidates. +3. Move `@repo/test-utils` `createContract` and `applicationDomainOf`'s `valueObjects` option to storage. +4. Fix the source type errors dispatch 10 left in `sql-contract-ts`, `sql-contract-psl`, `mongo-contract-ts`, `mongo-contract-psl` and `mongo-contract-prisma6`. + +## Do not + +- Do not change the emitter or `contract.d.ts` (dispatch 13), runtimes, the CLI resolver or `contract print` (dispatch 14), or Postgres checks (dispatch 15). +- Do not re-emit committed fixtures or examples (dispatch 16). Tests in other packages that load committed fixtures may stay red; list them. + +## Rules + +- Never run `pnpm test:integration`, `pnpm test:e2e` or `pnpm test:all`. Only named files inside `test/integration` with `pnpm test `. +- Use `mise exec --` for node and pnpm. Never `npx`. +- No comments where the code can say it; no bare `as` casts in production code; no `any`; arktype, not zod; no file extensions in imports; no backwards-compatibility exports; framework packages name no family or target. +- Commit with `git commit -s --trailer "Signed-off-by: Will Madden "`. No AI attribution lines. Stage files explicitly. +- Do not push. Do not open a PR. +- Save slow command output under `wip/` and read the file. Run tests by file where you can. + +## Done when + +- Typecheck, lint and `test --coverage` pass in every package this dispatch touches; `lint:deps`; `lint:ratchets`. +- The report lists what is still red outside those packages and which dispatch owns it. + +## Report + +Plain English, short sentences: what each authoring path now writes, how the discriminator facts reach the build, the clash message, the test that was red first for each change, the checks, what is still red, and the commits. diff --git a/projects/migration-statements/slices/remaining-nouns/dispatches/13-emit-and-types-read-storage.md b/projects/migration-statements/slices/remaining-nouns/dispatches/13-emit-and-types-read-storage.md new file mode 100644 index 000000000000..346cfbaa078e --- /dev/null +++ b/projects/migration-statements/slices/remaining-nouns/dispatches/13-emit-and-types-read-storage.md @@ -0,0 +1,41 @@ +# Dispatch 13 — `contract.d.ts` reads value objects from storage + +**Plan:** [`../plan-pr3.md`](../plan-pr3.md) · **ADR:** ADR 278 · **Grounding:** `wip/pr3-grounding.md` § A (foundation `field-value-types.ts`, framework emitter, SQL and Mongo emitters), C · **Builds on:** dispatch 10 · **Branch:** `tml-3537-value-objects-storage` + +## Outcome + +The emitted `contract.d.ts` describes value objects in the storage plane, and field types resolve a value object through its storage coordinate: + +- The framework emitter renders a value object field type as its coordinate, not `{ kind: "valueObject"; name }`. It drops the domain `valueObjects` line and writes value objects from storage. +- The SQL and Mongo emitters write `entries.valueObject` in the storage namespaces type. +- `FieldTypesFromDomain` (foundation `field-value-types.ts`) finds value objects in the storage namespaces, matched by `namespaceId` and `entityName`. The new type parameter goes last and has a default, so a contract without value objects emits the same `contract.d.ts` text as today (grounding C: 329 committed files otherwise change). +- Whether the top-level flat `valueObjects` block in `contract.d.ts` stays: decide from what reads it, and say why in the report. + +## Do + +1. **Tests first, red before each change:** emitter golden or snapshot tests for a contract with nested, list and enum-member value objects in both families; a type test that a field holding a value object gets the member types, including a nested one and a self-referring one; a contract without value objects emits unchanged text. +2. Implement in foundation, the framework emitter and both family emitters. +3. Fix the source type errors dispatch 10 left in `emitter`. + +## Do not + +- Do not change authoring (dispatch 12), runtimes, the CLI or `contract print` (dispatch 14), or Postgres checks (dispatch 15). +- Do not re-emit committed fixtures or examples (dispatch 16). + +## Rules + +- Never run `pnpm test:integration`, `pnpm test:e2e` or `pnpm test:all`. Only named files inside `test/integration` with `pnpm test `. +- Use `mise exec --` for node and pnpm. Never `npx`. +- No comments where the code can say it; no bare `as` casts in production code; no `any`; no file extensions in imports; no backwards-compatibility exports; framework packages name no family or target. +- Commit with `git commit -s --trailer "Signed-off-by: Will Madden "`. No AI attribution lines. Stage files explicitly. +- Do not push. Do not open a PR. +- Save slow command output under `wip/` and read the file. + +## Done when + +- Typecheck, lint and `test --coverage` pass in foundation, the framework emitter and both family emitters; `lint:deps`; `lint:ratchets`. +- Emitting one SQL and one Mongo fixture without value objects gives byte-identical `contract.d.ts`. + +## Report + +Plain English, short sentences: the new type parameter and its default, the rendered field type, the decision on the flat block, the test that was red first for each change, the checks, what is still red, and the commits. From d196e4840eda8678d97d84f847136289f4c1f716 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 12:50:36 +0200 Subject: [PATCH 020/104] TML-3537: field types find a value object in the storage namespace its reference names FieldTypesFromDomain takes the storage namespaces as a last type parameter with a default, so a contract without value objects types its fields as before. ValueObjectTypesFromStorage replaces ValueObjectTypesFromDomain and is keyed by storage namespace. Signed-off-by: willbot Signed-off-by: Will Madden --- .../contract/src/exports/types.ts | 2 +- .../contract/src/field-value-types.ts | 73 +++++---- .../test/field-value-types.types.test-d.ts | 146 ++++++++++++------ 3 files changed, 147 insertions(+), 74 deletions(-) diff --git a/packages/1-framework/0-foundation/contract/src/exports/types.ts b/packages/1-framework/0-foundation/contract/src/exports/types.ts index 914b8e0d2026..49c4a6120859 100644 --- a/packages/1-framework/0-foundation/contract/src/exports/types.ts +++ b/packages/1-framework/0-foundation/contract/src/exports/types.ts @@ -46,7 +46,7 @@ export type { EnumMemberValueType, FieldTypesFromDomain, StoredValueType, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, ValueTypeSide, } from '../field-value-types'; export type { NamespaceId } from '../namespace-id'; diff --git a/packages/1-framework/0-foundation/contract/src/field-value-types.ts b/packages/1-framework/0-foundation/contract/src/field-value-types.ts index 0393113a1382..55f29588b364 100644 --- a/packages/1-framework/0-foundation/contract/src/field-value-types.ts +++ b/packages/1-framework/0-foundation/contract/src/field-value-types.ts @@ -102,24 +102,27 @@ type InputClosedWhenEmpty = Side extends 'input' ? ClosedWhenEmpty : T; -type NamespaceParts = { - readonly valueObjects: ValueObjects; - readonly overrides: NamespaceOverrides; +type ContractParts = { + readonly storage: StorageNamespaces; + readonly overrides: Overrides; }; +type ValueObjectFields = Lookup< + Lookup, Ns>, 'entries'>, 'valueObject'>, Name>, + 'fields' +>; + type ValueObjectType< + Ns, Name, Parts, CodecTypes, ParameterizedOutputs extends ParameterizedOutputTypesFunction, Side extends ValueTypeSide, > = ClosedWhenEmpty<{ - readonly [F in keyof Lookup< - Lookup, Name>, - 'fields' - >]: FieldValueType< - Lookup, Name>, 'fields'>[F], - Lookup, 'valueObjects'>, Name>, F>, + readonly [F in keyof ValueObjectFields]: FieldValueType< + ValueObjectFields[F], + Lookup, Ns>, 'valueObjects'>, Name>, F>, Parts, CodecTypes, ParameterizedOutputs, @@ -134,8 +137,12 @@ type ElementType< CodecTypes, ParameterizedOutputs extends ParameterizedOutputTypesFunction, Side extends ValueTypeSide, -> = FieldType extends { readonly kind: 'valueObject'; readonly name: infer Name } - ? ValueObjectType +> = FieldType extends { + readonly kind: 'valueObject'; + readonly namespaceId: infer Ns; + readonly entityName: infer Name; +} + ? ValueObjectType : FieldType extends { readonly kind: 'union'; readonly members: infer Members extends readonly unknown[]; @@ -215,16 +222,20 @@ type FieldValueType< ParameterizedOutputs, Side > - : // `ElementType` types value objects too; this branch keys their instantiations by name alone, which costs less. + : // `ElementType` types value objects too; this branch keys their instantiations by namespace and name alone, which costs less. Field extends { - readonly type: { readonly kind: 'valueObject'; readonly name: infer Name }; + readonly type: { + readonly kind: 'valueObject'; + readonly namespaceId: infer Ns; + readonly entityName: infer Name; + }; readonly many?: infer Many; readonly dict?: infer Dictionary; } ? WithModifiers< BaseType< Override, - ValueObjectType, + ValueObjectType, Side >, NullableFlag, @@ -248,19 +259,14 @@ type FieldValueType< > : never; -type NamespacePartsOf = NamespaceParts< - Lookup, 'valueObjects'>, - Lookup ->; - /** - * `FieldOutputTypes` (`Side` `'output'`) or `FieldInputTypes` (`'input'`) of a contract whose domain namespaces are `DomainNamespaces`, computed from the contract's literal shape. Both an emitted `contract.d.ts` and a contract written with `defineContract` type their fields through it. + * `FieldOutputTypes` (`Side` `'output'`) or `FieldInputTypes` (`'input'`) of a contract whose domain namespaces are `DomainNamespaces` and whose storage namespaces are `StorageNamespaces`, computed from the contract's literal shape. A contract without value objects may leave `StorageNamespaces` out. Both an emitted `contract.d.ts` and a contract written with `defineContract` type their fields through it. * * Each value is typed by, in order: * - * 1. its override for that side, `{ output?, input? }`, from `Overrides` at `[namespace]['models'][model][field]` for a model field, or at `[namespace]['valueObjects'][valueObject][field]` for a value object field. An override is the type of a field that the contract's shape and the codec types cannot give: the members of the value set it holds, the type its codec renders as text, or the type a family's storage entry gives it; + * 1. its override for that side, `{ output?, input? }`, from `Overrides` at `[namespace]['models'][model][field]` for a model field, or at `[storage namespace]['valueObjects'][valueObject][field]` for a value object field. An override is the type of a field that the contract's shape and the codec types cannot give: the members of the value set it holds, the type its codec renders as text, or the type a family's storage entry gives it; * 2. for a scalar field, on the output side, the type a pack's type function in `ParameterizedOutputs` gives it for its type parameters (`ParameterizedOutputType`), else its codec's `output` or `input` type, or `unknown` for a codec `CodecTypes` lacks; - * 3. for a value object field, an object of the value object's field types from the same namespace's `valueObjects`, computed by these rules; + * 3. for a value object field, an object of the field types of the value object its reference names in `StorageNamespaces`, at `[namespaceId]['entries']['valueObject'][entityName]`, computed by these rules; * 4. for a union field, the union of its members' types, each a codec's plain type or a value object's type; * * then wrapped in `ReadonlyArray` for a list and in `Readonly>` for a dictionary, with `null` for a nullable element or value. A model without fields has `{}` output types and `Record` input types; a value object without fields has `Record` on both sides. @@ -271,10 +277,12 @@ export type FieldTypesFromDomain< ParameterizedOutputs extends ParameterizedOutputTypesFunction, Overrides, Side extends ValueTypeSide, + StorageNamespaces = Record, > = { readonly [Ns in keyof DomainNamespaces]: NamespaceFieldTypes< Lookup, - NamespacePartsOf, + Lookup, + ContractParts, CodecTypes, ParameterizedOutputs, Side @@ -283,6 +291,7 @@ export type FieldTypesFromDomain< type NamespaceFieldTypes< Models, + NamespaceOverrides, Parts, CodecTypes, ParameterizedOutputs extends ParameterizedOutputTypesFunction, @@ -297,7 +306,7 @@ type NamespaceFieldTypes< { readonly [F in keyof Fields]: FieldValueType< Fields[F], - Lookup, 'models'>, M>, F>, + Lookup, M>, F>, Parts, CodecTypes, ParameterizedOutputs, @@ -309,19 +318,23 @@ type NamespaceFieldTypes< }; /** - * The output (`Side` `'output'`) or input (`'input'`) type of each value object of a contract whose domain namespaces are `DomainNamespaces`, keyed by namespace and value object name, by the rules of {@link FieldTypesFromDomain}. + * The output (`Side` `'output'`) or input (`'input'`) type of each value object of a contract whose storage namespaces are `StorageNamespaces`, keyed by storage namespace and value object name, by the rules of {@link FieldTypesFromDomain}. */ -export type ValueObjectTypesFromDomain< - DomainNamespaces, +export type ValueObjectTypesFromStorage< + StorageNamespaces, CodecTypes, ParameterizedOutputs extends ParameterizedOutputTypesFunction, Overrides, Side extends ValueTypeSide, > = { - readonly [Ns in keyof DomainNamespaces]: { - readonly [V in keyof Lookup]: ValueObjectType< + readonly [Ns in keyof StorageNamespaces]: { + readonly [V in keyof Lookup< + Lookup, + 'valueObject' + >]: ValueObjectType< + Ns, V, - NamespacePartsOf, + ContractParts, CodecTypes, ParameterizedOutputs, Side diff --git a/packages/1-framework/0-foundation/contract/test/field-value-types.types.test-d.ts b/packages/1-framework/0-foundation/contract/test/field-value-types.types.test-d.ts index 883427945d1b..c2d147b05c56 100644 --- a/packages/1-framework/0-foundation/contract/test/field-value-types.types.test-d.ts +++ b/packages/1-framework/0-foundation/contract/test/field-value-types.types.test-d.ts @@ -3,7 +3,7 @@ import type { FieldTypesFromDomain, ParameterizedOutputTypesFunction, StoredValueType, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '../src/exports/types'; type CodecTypes = { @@ -32,6 +32,14 @@ interface CharOutputTypes extends ParameterizedOutputTypesFunction { type Scalar = { readonly kind: 'scalar'; readonly codecId: CodecId }; +type ValueObjectRef = { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: Ns; + readonly entityKind: 'valueObject'; + readonly entityName: Name; +}; + type Domain = { readonly app: { readonly models: { @@ -74,59 +82,80 @@ type Domain = { readonly type: Scalar<'test/text@1'>; }; readonly homeWithoutNullable: { - readonly type: { readonly kind: 'valueObject'; readonly name: 'Point' }; + readonly type: ValueObjectRef<'Point'>; }; readonly home: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: ValueObjectRef<'Address'>; }; readonly pastHomes: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: ValueObjectRef<'Address'>; readonly many: { readonly elementNullable: false }; }; + readonly elsewhere: { + readonly nullable: false; + readonly type: ValueObjectRef<'Address', 'other'>; + }; readonly contact: { readonly nullable: false; readonly type: { readonly kind: 'union'; - readonly members: readonly [ - Scalar<'test/text@1'>, - { readonly kind: 'valueObject'; readonly name: 'Address' }, - ]; + readonly members: readonly [Scalar<'test/text@1'>, ValueObjectRef<'Address'>]; }; }; }; }; readonly Empty: { readonly fields: Record }; }; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly street: { readonly nullable: false; readonly type: Scalar<'test/text@1'> }; - readonly code: { - readonly nullable: false; - readonly type: Scalar<'test/char@1'> & { readonly typeParams: { readonly length: 5 } }; + }; +}; + +type Storage = { + readonly shared: { + readonly entries: { + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly street: { readonly nullable: false; readonly type: Scalar<'test/text@1'> }; + readonly code: { + readonly nullable: false; + readonly type: Scalar<'test/char@1'> & { + readonly typeParams: { readonly length: 5 }; + }; + }; + readonly geo: { + readonly nullable: true; + readonly type: ValueObjectRef<'Point'>; + }; + readonly kind: { readonly nullable: true; readonly type: Scalar<'test/text@1'> }; + readonly meta: { readonly nullable: false; readonly type: Scalar<'test/json@1'> }; }; - readonly geo: { - readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Point' }; + }; + readonly Point: { + readonly fields: { + readonly x: { readonly nullable: false; readonly type: Scalar<'test/int@1'> }; }; - readonly kind: { readonly nullable: true; readonly type: Scalar<'test/text@1'> }; - readonly meta: { readonly nullable: false; readonly type: Scalar<'test/json@1'> }; }; - }; - readonly Point: { - readonly fields: { - readonly x: { readonly nullable: false; readonly type: Scalar<'test/int@1'> }; + readonly Nothing: { readonly fields: Record }; + readonly Tree: { + readonly fields: { + readonly children: { + readonly nullable: false; + readonly type: ValueObjectRef<'Tree'>; + readonly many: { readonly elementNullable: false }; + }; + }; }; }; - readonly Nothing: { readonly fields: Record }; - readonly Tree: { - readonly fields: { - readonly children: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Tree' }; - readonly many: { readonly elementNullable: false }; + }; + }; + readonly other: { + readonly entries: { + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly elsewhere: { readonly nullable: false; readonly type: Scalar<'test/int@1'> }; }; }; }; @@ -142,6 +171,8 @@ type Overrides = { readonly settings: { readonly output: { readonly theme: string } }; }; }; + }; + readonly shared: { readonly valueObjects: { readonly Address: { readonly kind: { readonly output: 'home' | 'work'; readonly input: 'home' | 'work' }; @@ -151,8 +182,15 @@ type Overrides = { }; }; -type Output = FieldTypesFromDomain; -type Input = FieldTypesFromDomain; +type Output = FieldTypesFromDomain< + Domain, + CodecTypes, + CharOutputTypes, + Overrides, + 'output', + Storage +>; +type Input = FieldTypesFromDomain; type User = Output['app']['User']; type UserInput = Input['app']['User']; @@ -223,6 +261,22 @@ describe('field types from a contract domain', () => { expectTypeOf().toEqualTypeOf>(); }); + test('a value object field reads the value object of the storage namespace its reference names', () => { + expectTypeOf().toEqualTypeOf<{ readonly elsewhere: bigint }>(); + }); + + test('a contract without value objects may leave the storage namespaces out', () => { + type WithoutStorage = FieldTypesFromDomain< + Domain, + CodecTypes, + CharOutputTypes, + Overrides, + 'output' + >['app']['User']; + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf<'happy' | 'sad' | null>(); + }); + test('a union field is the union of its members, a scalar member taking its plain codec type', () => { expectTypeOf().toEqualTypeOf(); expectTypeOf().toEqualTypeOf(); @@ -234,16 +288,16 @@ describe('field types from a contract domain', () => { }); }); -describe('value object types from a contract domain', () => { - type ValueObjects = ValueObjectTypesFromDomain< - Domain, +describe('value object types from contract storage', () => { + type ValueObjects = ValueObjectTypesFromStorage< + Storage, CodecTypes, CharOutputTypes, Overrides, 'output' >; - type ValueObjectInputs = ValueObjectTypesFromDomain< - Domain, + type ValueObjectInputs = ValueObjectTypesFromStorage< + Storage, CodecTypes, CharOutputTypes, Overrides, @@ -251,17 +305,23 @@ describe('value object types from a contract domain', () => { >; test('each value object is keyed by namespace and name', () => { - expectTypeOf().toEqualTypeOf(); - expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); + }); + + test('a value object is keyed by the storage namespace that holds it', () => { + expectTypeOf().toEqualTypeOf<{ + readonly elsewhere: bigint; + }>(); }); test('a value object without fields is closed on both sides', () => { - expectTypeOf().toEqualTypeOf>(); - expectTypeOf().toEqualTypeOf>(); + expectTypeOf().toEqualTypeOf>(); + expectTypeOf().toEqualTypeOf>(); }); test('a value object that holds itself resolves one level at a time', () => { - type Tree = ValueObjects['app']['Tree']; + type Tree = ValueObjects['shared']['Tree']; expectTypeOf().toEqualTypeOf(); }); }); From 0195e97b163605701ad0f52b103ea52b8982ff9c Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 12:50:49 +0200 Subject: [PATCH 021/104] TML-3537: contract.d.ts describes value objects in the storage plane The SQL and Mongo emitters write entries.valueObject in the storage namespaces type. A value object field type is its storage coordinate. The domain valueObjects line and the top-level valueObjects block are gone. A value object member's override reads the storage value set it names. The Mongo builder types value objects in storage too. A contract without value objects emits the same text as before. Signed-off-by: willbot Signed-off-by: Will Madden --- .../emitter/src/domain-type-generation.ts | 132 +++++++++++------- .../emitter/src/generate-contract-dts.ts | 67 +++------ .../test/domain-type-generation.test.ts | 36 +++-- .../emitter/test/emitter.integration.test.ts | 23 +-- .../3-tooling/emitter/test/emitter.test.ts | 43 +----- ...generate-contract-dts.type-imports.test.ts | 84 +++++++++-- .../emitter/test/type-overrides.test.ts | 109 +++++++++------ .../3-tooling/emitter/test/utils.ts | 5 +- .../test/fixtures/orm-contract.d.ts | 50 +++---- .../test/fixtures/orm-contract.json | 49 ++++--- .../contract-ts/src/contract-builder.ts | 32 +++-- .../test/contract-builder.types.test-d.ts | 11 ++ .../3-tooling/emitter/src/index.ts | 18 ++- .../test/emitter-hook.generation.test.ts | 86 ++++++++++-- .../test/fixtures/create-mongo-contract.ts | 24 +++- .../3-tooling/emitter/src/index.ts | 7 + .../test/create-emitter-test-contract.ts | 26 +++- .../emitter-hook.generation.basic.test.ts | 87 ++++++++++-- 18 files changed, 584 insertions(+), 305 deletions(-) diff --git a/packages/1-framework/3-tooling/emitter/src/domain-type-generation.ts b/packages/1-framework/3-tooling/emitter/src/domain-type-generation.ts index 1abfc678f18d..db768890e351 100644 --- a/packages/1-framework/3-tooling/emitter/src/domain-type-generation.ts +++ b/packages/1-framework/3-tooling/emitter/src/domain-type-generation.ts @@ -4,15 +4,20 @@ import type { ContractManyToManyRelation, ContractModelBase, ContractValueObject, + ContractValueObjectField, CrossReference, JsonValue, + StorageBase, + ValueObjectFieldType, } from '@internal/contract/types'; +import { valueObjectAt } from '@internal/contract/types'; import type { CodecLookup } from '@internal/framework-components/codec'; import { type ImportSpecifierResolver, keepInternalSpecifiers, type TypesImportSpec, } from '@internal/framework-components/emission'; +import { elementCoordinates, entityAt } from '@internal/framework-components/ir'; import { type ImportRequirement, renderImports, tsStringLiteral } from '@internal/ts-render'; import { blindCast } from '@internal/utils/casts'; import { emitterError } from './emitter-errors'; @@ -78,6 +83,10 @@ export function generateRootsType(roots: Record | undefi return `{ ${entries} }`; } +function valueObjectFieldTypeText(type: ValueObjectFieldType): string { + return `{ readonly kind: "valueObject"; readonly plane: "storage"; readonly namespaceId: ${serializeValue(type.namespaceId)}; readonly entityKind: "valueObject"; readonly entityName: ${serializeValue(type.entityName)} }`; +} + function contractFieldModifierSuffix(field: ContractField): string { const many = field.many ? `; readonly many: { readonly elementNullable: ${field.many.elementNullable} }` @@ -97,7 +106,7 @@ export function generateModelFieldEntry(fieldName: string, field: ContractField) return `readonly ${serializeObjectKey(fieldName)}: { readonly nullable: ${nullable}; readonly type: { readonly kind: "scalar"; readonly codecId: ${serializeValue(type.codecId)}${typeParamsSpec} }${mods} }`; } if (type.kind === 'valueObject') { - return `readonly ${serializeObjectKey(fieldName)}: { readonly nullable: ${nullable}; readonly type: { readonly kind: "valueObject"; readonly name: ${serializeValue(type.name)} }${mods} }`; + return `readonly ${serializeObjectKey(fieldName)}: { readonly nullable: ${nullable}; readonly type: ${valueObjectFieldTypeText(type)}${mods} }`; } return `readonly ${serializeObjectKey(fieldName)}: { readonly nullable: ${nullable}; readonly type: ${serializeValue(type)}${mods} }`; } @@ -369,9 +378,13 @@ export function parameterizedOutputTypeFunctionsAlias( return `type ${PARAMETERIZED_OUTPUT_TYPE_FUNCTIONS} = ${aliases.length > 0 ? aliases.join(' | ') : 'never'};`; } -/** `FieldOutputTypes` (`side` `'output'`) or `FieldInputTypes` (`'input'`): the shared generic applied to the emitted contract's domain, with `FieldTypeOverrides`. */ -export function fieldTypesMapExpression(side: 'output' | 'input'): string { - return `FieldTypesFromDomain`; +/** `FieldOutputTypes` (`side` `'output'`) or `FieldInputTypes` (`'input'`): the shared generic applied to the emitted contract's domain, with `FieldTypeOverrides`, and to its storage namespaces when the contract has value objects. */ +export function fieldTypesMapExpression( + side: 'output' | 'input', + hasValueObjects: boolean, +): string { + const storageNamespaces = hasValueObjects ? ", ContractBase['storage']['namespaces']" : ''; + return `FieldTypesFromDomain`; } export function generateContractFieldDescriptor(fieldName: string, field: ContractField): string { @@ -385,7 +398,7 @@ export function generateContractFieldDescriptor(fieldName: string, field: Contra return `readonly ${serializeObjectKey(fieldName)}: { readonly nullable: ${field.nullable}; readonly type: { readonly kind: "scalar"; readonly codecId: ${serializeValue(type.codecId)}${typeParamsSpec} }${modStr} }`; } if (type.kind === 'valueObject') { - return `readonly ${serializeObjectKey(fieldName)}: { readonly nullable: ${field.nullable}; readonly type: { readonly kind: "valueObject"; readonly name: ${serializeValue(type.name)} }${modStr} }`; + return `readonly ${serializeObjectKey(fieldName)}: { readonly nullable: ${field.nullable}; readonly type: ${valueObjectFieldTypeText(type)}${modStr} }`; } return `readonly ${serializeObjectKey(fieldName)}: { readonly nullable: ${field.nullable}; readonly type: ${serializeValue(type)}${modStr} }`; } @@ -412,7 +425,7 @@ export function generateValueObjectsDescriptorType( } /** - * `Output` and `Input` for each value object, read from the shared generic's value object types at the namespace that declares it. + * `Output` and `Input` for each value object, read from the shared generic's value object types at the storage namespace that holds it. */ export function generateValueObjectTypeAliases( valueObjectNamespaces: ReadonlyArray, @@ -421,7 +434,7 @@ export function generateValueObjectTypeAliases( return ''; } const generic = (side: 'output' | 'input') => - `ValueObjectTypesFromDomain`; + `ValueObjectTypesFromStorage`; const aliases = [ `type ValueObjectOutputTypes = ${generic('output')};`, `type ValueObjectInputTypes = ${generic('input')};`, @@ -441,7 +454,7 @@ export type TypeOverride = { readonly output?: string; readonly input?: string } * The type of a scalar field that its codec does not give, which the shared field type generic takes as an override: the literal union of the value set it holds, or the text its codec renders for its type parameters. `undefined` when the codec gives both sides, and for a value object or union field, which the generic types itself. */ export function resolveFieldTypeOverride( - field: ContractField, + field: Pick, codecLookup: CodecLookup | undefined, resolvedValueSet: ResolvedFieldValueSet | undefined, ): TypeOverride | undefined { @@ -531,8 +544,12 @@ function renderOverrideTree(tree: OverrideTree): string { return `{ ${parts.join('; ')} }`; } +type ContractPlanes = Pick & { + readonly storage: Pick; +}; + /** - * The values a field's value set holds, with the field's codec. A reference to a domain enum is read from the contract's domain, for model and value object fields alike. Any other model field goes to the family's `resolveStorageValueSet`, which reads its storage. A value object field has no storage of its own. + * The values a model field's value set holds, with the field's codec. A reference to a domain enum is read from the contract's domain. Any other model field goes to the family's `resolveStorageValueSet`, which reads its storage. */ function resolveFieldValueSet( field: ContractField, @@ -559,48 +576,69 @@ function resolveDomainValueSet( }; } -/** `FieldTypeOverrides`: per namespace, `models` or `valueObjects`, owner and field, the type of each field its codec does not give. */ +/** The values of the storage value set a value object field names, with the field's codec. */ +function resolveValueObjectFieldValueSet( + field: ContractValueObjectField, + storage: Pick, +): ResolvedFieldValueSet | undefined { + if (field.valueSet === undefined || field.type.kind !== 'scalar') return undefined; + const valueSet = entityAt<{ readonly values: readonly JsonValue[] }>(storage, field.valueSet); + if (valueSet === undefined) return undefined; + return { encodedValues: valueSet.values, codecId: field.type.codecId }; +} + +/** Each value object in storage, with the storage namespace that holds it. */ +export function storageValueObjects( + storage: Pick, +): ReadonlyArray { + return [...elementCoordinates(storage)].flatMap(({ namespaceId, entityKind, entityName }) => { + if (entityKind !== 'valueObject') return []; + const valueObject = valueObjectAt(storage, { + plane: 'storage', + namespaceId, + entityKind, + entityName, + }); + return valueObject === undefined ? [] : [[namespaceId, entityName, valueObject] as const]; + }); +} + +/** `FieldTypeOverrides`: per namespace, `models` (domain namespace) or `valueObjects` (storage namespace), owner and field, the type of each field its codec does not give. */ export function generateFieldTypeOverridesMap( - contract: Pick, + contract: ContractPlanes, codecLookup: CodecLookup | undefined, resolveStorageValueSet: StorageValueSetResolver | undefined, ): string { - return renderTypeOverridesMap( - Object.entries(contract.domain.namespaces).flatMap(([nsId, ns]) => { - const modelFields = Object.entries(ns.models).flatMap(([modelName, model]) => - Object.entries(model.fields).map( - ([fieldName, field]) => - [ - [nsId, 'models', modelName, fieldName], - resolveFieldTypeOverride( - field, - codecLookup, - resolveFieldValueSet(field, contract, () => - resolveStorageValueSet?.(modelName, fieldName, model), - ), + const modelFields = Object.entries(contract.domain.namespaces).flatMap(([nsId, ns]) => + Object.entries(ns.models).flatMap(([modelName, model]) => + Object.entries(model.fields).map( + ([fieldName, field]) => + [ + [nsId, 'models', modelName, fieldName], + resolveFieldTypeOverride( + field, + codecLookup, + resolveFieldValueSet(field, contract, () => + resolveStorageValueSet?.(modelName, fieldName, model), ), - ] as const, - ), - ); - const valueObjects = blindCast< - Record, - 'ns.valueObjects is a ContractValueObject record in the emitted IR (default to {} when absent)' - >(ns.valueObjects ?? {}); - const valueObjectFields = Object.entries(valueObjects).flatMap( - ([valueObjectName, valueObject]) => - Object.entries(valueObject.fields).map( - ([fieldName, field]) => - [ - [nsId, 'valueObjects', valueObjectName, fieldName], - resolveFieldTypeOverride( - field, - codecLookup, - resolveFieldValueSet(field, contract, () => undefined), - ), - ] as const, - ), - ); - return [...modelFields, ...valueObjectFields]; - }), + ), + ] as const, + ), + ), + ); + const valueObjectFields = storageValueObjects(contract.storage).flatMap( + ([nsId, valueObjectName, valueObject]) => + Object.entries(valueObject.fields).map( + ([fieldName, field]) => + [ + [nsId, 'valueObjects', valueObjectName, fieldName], + resolveFieldTypeOverride( + field, + codecLookup, + resolveValueObjectFieldValueSet(field, contract.storage), + ), + ] as const, + ), ); + return renderTypeOverridesMap([...modelFields, ...valueObjectFields]); } diff --git a/packages/1-framework/3-tooling/emitter/src/generate-contract-dts.ts b/packages/1-framework/3-tooling/emitter/src/generate-contract-dts.ts index 3bbfb2ccc3c4..047f97d0e6d9 100644 --- a/packages/1-framework/3-tooling/emitter/src/generate-contract-dts.ts +++ b/packages/1-framework/3-tooling/emitter/src/generate-contract-dts.ts @@ -1,9 +1,4 @@ -import type { - Contract, - ContractEnum, - ContractModelBase, - ContractValueObject, -} from '@internal/contract/types'; +import type { Contract, ContractEnum, ContractModelBase } from '@internal/contract/types'; import type { CodecLookup } from '@internal/framework-components/codec'; import { type EmissionSpi, @@ -23,13 +18,13 @@ import { generateImportLines, generateModelsType, generateRootsType, - generateValueObjectsDescriptorType, generateValueObjectTypeAliases, parameterizedOutputTypeFunctionsAlias, renderValueLiteralType, serializeExecutionType, serializeObjectKey, serializeValue, + storageValueObjects, } from './domain-type-generation'; import { emitterError } from './emitter-errors'; import { generateModelTypesBlock } from './model-types-emission'; @@ -115,42 +110,22 @@ export function generateContractDts( const rootsType = generateRootsType(contract.roots); - // Flatten value objects across all namespaces — first-name-wins on collision. - const flatValueObjects: Record = {}; - const valueObjectNamespaces: Array = []; - for (const [nsId, ns] of namespaceEntries) { - for (const [voName, vo] of Object.entries( - blindCast< - Record, - 'ns.valueObjects is a ContractValueObject record in the emitted IR (default to {} when absent)' - >(ns.valueObjects ?? {}), - )) { - if (!(voName in flatValueObjects)) { - flatValueObjects[voName] = vo; - valueObjectNamespaces.push([voName, nsId]); - } - } + const firstNamespaceOfValueObject = new Map(); + for (const [namespaceId, name] of storageValueObjects(contract.storage)) { + if (!firstNamespaceOfValueObject.has(name)) firstNamespaceOfValueObject.set(name, namespaceId); } - const valueObjects = Object.keys(flatValueObjects).length > 0 ? flatValueObjects : undefined; + const valueObjectNamespaces = [...firstNamespaceOfValueObject]; + const hasValueObjects = valueObjectNamespaces.length > 0; const valueObjectTypeAliases = generateValueObjectTypeAliases(valueObjectNamespaces); - const valueObjectsDescriptor = generateValueObjectsDescriptorType(valueObjects); - // Per-namespace models, value objects, and enum types for the domain.namespaces section. + // Per-namespace models and enum types for the domain.namespaces section. const perNamespaceTypes: Array< - readonly [string, string, string | undefined, string | undefined, string | undefined] + readonly [string, string, string | undefined, string | undefined] > = namespaceEntries.map(([nsId, ns]) => { const nsModels = ns.models; const nsModelsType = generateModelsType(nsModels, (name, model) => emitter.generateModelStorageType(name, model), ); - const nsValueObjects = blindCast< - Record | undefined, - 'ns.valueObjects is an optional ContractValueObject record in the emitted IR' - >(ns.valueObjects); - const nsValueObjectsDescriptor = - nsValueObjects !== undefined && Object.keys(nsValueObjects).length > 0 - ? generateValueObjectsDescriptorType(nsValueObjects) - : undefined; const nsEnums = blindCast< Record | undefined, 'ns.enum is an optional ContractEnum record in the emitted IR' @@ -160,27 +135,17 @@ export function generateContractDts( const nsEnumMemberTypesBlock = hasEnums ? generateEnumMemberTypesBlock(nsEnums, codecLookup) : undefined; - return [ - nsId, - nsModelsType, - nsValueObjectsDescriptor, - nsEnumBlock, - nsEnumMemberTypesBlock, - ] as const; + return [nsId, nsModelsType, nsEnumBlock, nsEnumMemberTypesBlock] as const; }); const domainNamespacesType = perNamespaceTypes - .map(([nsId, nsModelsType, nsValueObjectsDescriptor, nsEnumBlock, nsEnumMemberTypesBlock]) => { - const voLine = - nsValueObjectsDescriptor !== undefined - ? `\n readonly valueObjects: ${nsValueObjectsDescriptor};` - : ''; + .map(([nsId, nsModelsType, nsEnumBlock, nsEnumMemberTypesBlock]) => { const enumLine = nsEnumBlock !== undefined ? `\n readonly enum: ${nsEnumBlock};` : ''; const enumMemberTypesLine = nsEnumMemberTypesBlock !== undefined ? `\n readonly enumMemberTypes?: ${nsEnumMemberTypesBlock};` : ''; - return ` readonly ${serializeObjectKey(nsId)}: {\n readonly models: ${nsModelsType};${voLine}${enumLine}${enumMemberTypesLine}\n }`; + return ` readonly ${serializeObjectKey(nsId)}: {\n readonly models: ${nsModelsType};${enumLine}${enumMemberTypesLine}\n }`; }) .join(';\n'); @@ -217,8 +182,8 @@ ${familyTypeAliases} ${parameterizedOutputTypesDeclaration} ${valueObjectTypeAliases} type FieldTypeOverrides = ${fieldTypeOverrides}; -export type FieldOutputTypes = ${fieldTypesMapExpression('output')}; -export type FieldInputTypes = ${fieldTypesMapExpression('input')}; +export type FieldOutputTypes = ${fieldTypesMapExpression('output', hasValueObjects)}; +export type FieldInputTypes = ${fieldTypesMapExpression('input', hasValueObjects)}; ${extraTypeExports ? `${extraTypeExports}\n` : ''} ${modelTypesBlock} @@ -239,7 +204,7 @@ ${domainNamespacesType}; readonly capabilities: ${serializeValue(contract.capabilities)}; readonly extensions: ${serializeValue(contract.extensions)};${executionClause} readonly meta: ${serializeValue(contract.meta)}; - ${valueObjects ? `readonly valueObjects: ${valueObjectsDescriptor};` : ''} + readonly profileHash: ProfileHash; }; @@ -263,7 +228,7 @@ import type { FieldTypesFromDomain, NamespaceId, ProfileHashBase, - StorageHashBase,${valueObjects ? '\n ValueObjectTypesFromDomain,' : ''} + StorageHashBase,${hasValueObjects ? '\n ValueObjectTypesFromStorage,' : ''} } from '${resolveImportSpecifier('@internal/contract/types')}'; ${body}`; diff --git a/packages/1-framework/3-tooling/emitter/test/domain-type-generation.test.ts b/packages/1-framework/3-tooling/emitter/test/domain-type-generation.test.ts index 70dd46fdd3aa..6883ee078750 100644 --- a/packages/1-framework/3-tooling/emitter/test/domain-type-generation.test.ts +++ b/packages/1-framework/3-tooling/emitter/test/domain-type-generation.test.ts @@ -653,22 +653,34 @@ describe('generateContractFieldDescriptor', () => { ); }); - it('generates value object field descriptor', () => { + it('renders a value object field type as the coordinate of its storage value object', () => { const field: ContractField = { nullable: true, - type: { kind: 'valueObject', name: 'Address' }, + type: { + kind: 'valueObject', + plane: 'storage', + namespaceId: 'public', + entityKind: 'valueObject', + entityName: 'Address', + }, many: false, }; const result = generateContractFieldDescriptor('homeAddress', field); expect(result).toBe( - 'readonly homeAddress: { readonly nullable: true; readonly type: { readonly kind: "valueObject"; readonly name: "Address" } }', + 'readonly homeAddress: { readonly nullable: true; readonly type: { readonly kind: "valueObject"; readonly plane: "storage"; readonly namespaceId: "public"; readonly entityKind: "valueObject"; readonly entityName: "Address" } }', ); }); it('includes the non-nullable list descriptor', () => { const field: ContractField = { nullable: false, - type: { kind: 'valueObject', name: 'Address' }, + type: { + kind: 'valueObject', + plane: 'storage', + namespaceId: 'public', + entityKind: 'valueObject', + entityName: 'Address', + }, many: { elementNullable: false }, }; const result = generateContractFieldDescriptor('addresses', field); @@ -678,11 +690,17 @@ describe('generateContractFieldDescriptor', () => { it('includes element nullability inside the list descriptor', () => { const field: ContractField = { nullable: false, - type: { kind: 'valueObject', name: 'Address' }, + type: { + kind: 'valueObject', + plane: 'storage', + namespaceId: 'public', + entityKind: 'valueObject', + entityName: 'Address', + }, many: { elementNullable: true }, }; expect(generateContractFieldDescriptor('addresses', field)).toBe( - 'readonly addresses: { readonly nullable: false; readonly type: { readonly kind: "valueObject"; readonly name: "Address" }; readonly many: { readonly elementNullable: true } }', + 'readonly addresses: { readonly nullable: false; readonly type: { readonly kind: "valueObject"; readonly plane: "storage"; readonly namespaceId: "public"; readonly entityKind: "valueObject"; readonly entityName: "Address" }; readonly many: { readonly elementNullable: true } }', ); }); @@ -741,15 +759,15 @@ describe('generateValueObjectTypeAliases', () => { expect(generateValueObjectTypeAliases([])).toBe(''); }); - it('reads each value object from the shared generic at the namespace that declares it', () => { + it('reads each value object from the shared generic at the storage namespace that holds it', () => { expect( generateValueObjectTypeAliases([ ['Address', 'public'], ['Point', '__unbound__'], ]).split('\n'), ).toEqual([ - "type ValueObjectOutputTypes = ValueObjectTypesFromDomain;", - "type ValueObjectInputTypes = ValueObjectTypesFromDomain;", + "type ValueObjectOutputTypes = ValueObjectTypesFromStorage;", + "type ValueObjectInputTypes = ValueObjectTypesFromStorage;", 'export type AddressOutput = ValueObjectOutputTypes["public"]["Address"];', 'export type AddressInput = ValueObjectInputTypes["public"]["Address"];', 'export type PointOutput = ValueObjectOutputTypes["__unbound__"]["Point"];', diff --git a/packages/1-framework/3-tooling/emitter/test/emitter.integration.test.ts b/packages/1-framework/3-tooling/emitter/test/emitter.integration.test.ts index 2f02a75334cd..af7ec956d776 100644 --- a/packages/1-framework/3-tooling/emitter/test/emitter.integration.test.ts +++ b/packages/1-framework/3-tooling/emitter/test/emitter.integration.test.ts @@ -133,15 +133,22 @@ describe('emitter integration', () => { }, }, storage: { - tables: { - user: { - columns: { - id: { codecId: 'pg/int4@1', dataType: 'pg/int4', nullable: false }, + namespaces: { + __unbound__: { + id: '__unbound__', + entries: { + table: { + user: { + columns: { + id: { codecId: 'pg/int4@1', dataType: 'pg/int4', nullable: false }, + }, + primaryKey: { columns: ['id'] }, + uniques: [], + indexes: [], + foreignKeys: [], + }, + }, }, - primaryKey: { columns: ['id'] }, - uniques: [], - indexes: [], - foreignKeys: [], }, }, }, diff --git a/packages/1-framework/3-tooling/emitter/test/emitter.test.ts b/packages/1-framework/3-tooling/emitter/test/emitter.test.ts index 204296b91322..fa5db03c4d71 100644 --- a/packages/1-framework/3-tooling/emitter/test/emitter.test.ts +++ b/packages/1-framework/3-tooling/emitter/test/emitter.test.ts @@ -298,7 +298,7 @@ describe('emitter', () => { expect(result.contractDts).toContain("from '@ext/query'"); }); - it('emits value object clauses when the domain namespace declares value objects', async () => { + it('emits value object clauses when storage declares value objects', async () => { const ir = createTestContract({ storage: emptySqlStorage, valueObjects: { @@ -320,45 +320,10 @@ describe('emitter', () => { }; const result = await emit(ir, options, mockSqlHook); - expect(result.contractDts).toContain('readonly valueObjects:'); - expect(result.contractDts).toContain('export type AddressOutput'); - }); - - it('emits per-namespace valueObjects block when a single namespace declares value objects', () => { - const addressModel = { - fields: { - street: { - type: { kind: 'scalar' as const, codecId: 'pg/text@1' }, - nullable: false, - many: false as const, - }, - }, - }; - const contract = { - ...createTestContract(), - domain: { - namespaces: { - public: { - models: {}, - valueObjects: { Address: addressModel }, - }, - }, - }, - }; - const dts = generateContractDts(contract, mockSqlHook, [], { - storageHash: '0000000000000000000000000000000000000000000000000000000000000001', - profileHash: '0000000000000000000000000000000000000000000000000000000000000002', - }); - // The per-namespace valueObjects block must appear inside the namespace block. - // A positive match on the nested structure proves it is inside domain.namespaces.public. - expect(dts).toContain('readonly public:'); - const publicNsIndex = dts.indexOf('readonly public:'); - const afterPublic = dts.slice(publicNsIndex); - // valueObjects must appear before the closing of the namespace block - expect(afterPublic.indexOf('readonly valueObjects:')).toBeGreaterThan(0); - expect(afterPublic.indexOf('readonly valueObjects:')).toBeLessThan( - afterPublic.indexOf('readonly capabilities:'), + expect(result.contractDts).toContain( + "export type AddressOutput = ValueObjectOutputTypes['__unbound__']['Address'];", ); + expect(result.contractDts).not.toContain('readonly valueObjects:'); }); it('emits successfully when domain has more than one namespace', () => { diff --git a/packages/1-framework/3-tooling/emitter/test/generate-contract-dts.type-imports.test.ts b/packages/1-framework/3-tooling/emitter/test/generate-contract-dts.type-imports.test.ts index 832c9feb81cd..caecc6ec7d54 100644 --- a/packages/1-framework/3-tooling/emitter/test/generate-contract-dts.type-imports.test.ts +++ b/packages/1-framework/3-tooling/emitter/test/generate-contract-dts.type-imports.test.ts @@ -88,7 +88,13 @@ describe('generateContractDts field type maps', () => { }, address: { nullable: true, - type: { kind: 'valueObject', name: 'Address' }, + type: { + kind: 'valueObject', + plane: 'storage', + namespaceId: 'shared', + entityKind: 'valueObject', + entityName: 'Address', + }, many: false, }, }, @@ -96,10 +102,19 @@ describe('generateContractDts field type maps', () => { storage: {}, }, }, - valueObjects: { - Address: { - fields: { - street: { nullable: false, type: { kind: 'scalar', codecId: 'demo/text@1' } }, + }, + }, + storage: { + namespaces: { + shared: { + id: 'shared', + entries: { + valueObject: { + Address: { + fields: { + street: { nullable: false, type: { kind: 'scalar', codecId: 'demo/text@1' } }, + }, + }, }, }, }, @@ -107,27 +122,74 @@ describe('generateContractDts field type maps', () => { }, }); - it('applies the shared generic to the contract domain, with no override for fields the generic types itself', () => { + it('applies the shared generic to the contract domain and storage, with no override for fields the generic types itself', () => { const dts = generateContractDts(contract, createMockSpi(), [CODEC_TYPES], HASHES); expect(dts).toContain('type FieldTypeOverrides = { };'); expect(dts).not.toContain('ValueObjectFieldTypeOverrides'); expect(dts).toContain( - "export type FieldOutputTypes = FieldTypesFromDomain;", + "export type FieldOutputTypes = FieldTypesFromDomain;", ); expect(dts).toContain( - "export type FieldInputTypes = FieldTypesFromDomain;", + "export type FieldInputTypes = FieldTypesFromDomain;", ); expect(dts).toMatch(/\n {2}FieldTypesFromDomain,\n/); }); - it('types each value object alias through the shared generic', () => { + it('leaves the storage namespaces out of the shared generic for a contract without value objects', () => { + const dts = generateContractDts(createTestContract(), createMockSpi(), [CODEC_TYPES], HASHES); + + expect(dts).toContain( + "export type FieldOutputTypes = FieldTypesFromDomain;", + ); + expect(dts).not.toContain('ValueObjectTypesFromStorage'); + }); + + it('types each value object alias through the shared generic, at the storage namespace that holds it', () => { const dts = generateContractDts(contract, createMockSpi(), [CODEC_TYPES], HASHES); expect(dts).toContain( - 'export type AddressOutput = ValueObjectOutputTypes["public"]["Address"];', + 'export type AddressOutput = ValueObjectOutputTypes["shared"]["Address"];', ); - expect(dts).toMatch(/\n {2}ValueObjectTypesFromDomain,\n/); + expect(dts).toMatch(/\n {2}ValueObjectTypesFromStorage,\n/); + }); + + it('writes one alias for a value object name that two storage namespaces hold, from the first', () => { + const address = { + fields: { street: { nullable: false, type: { kind: 'scalar', codecId: 'demo/text@1' } } }, + }; + const dts = generateContractDts( + createTestContract({ + storage: { + namespaces: { + first: { id: 'first', entries: { valueObject: { Address: address } } }, + second: { id: 'second', entries: { valueObject: { Address: address } } }, + }, + }, + }), + createMockSpi(), + [CODEC_TYPES], + HASHES, + ); + + expect(dts.match(/export type AddressOutput =/g)).toHaveLength(1); + expect(dts).toContain( + 'export type AddressOutput = ValueObjectOutputTypes["first"]["Address"];', + ); + }); + + it('renders a value object field as the coordinate of its storage value object', () => { + const dts = generateContractDts(contract, createMockSpi(), [CODEC_TYPES], HASHES); + + expect(dts).toContain( + 'readonly address: { readonly nullable: true; readonly type: { readonly kind: "valueObject"; readonly plane: "storage"; readonly namespaceId: "shared"; readonly entityKind: "valueObject"; readonly entityName: "Address" } }', + ); + }); + + it('describes value objects only in storage, not in the domain or at the top level', () => { + const dts = generateContractDts(contract, createMockSpi(), [CODEC_TYPES], HASHES); + + expect(dts).not.toContain('readonly valueObjects:'); }); it('types the fields of a Models member by indexing FieldOutputTypes', () => { diff --git a/packages/1-framework/3-tooling/emitter/test/type-overrides.test.ts b/packages/1-framework/3-tooling/emitter/test/type-overrides.test.ts index d9729f9414ad..e0cc893e083a 100644 --- a/packages/1-framework/3-tooling/emitter/test/type-overrides.test.ts +++ b/packages/1-framework/3-tooling/emitter/test/type-overrides.test.ts @@ -1,4 +1,9 @@ -import type { Contract, ContractField, ContractModelBase } from '@internal/contract/types'; +import type { + Contract, + ContractField, + ContractModelBase, + StorageBase, +} from '@internal/contract/types'; import type { CodecLookup } from '@internal/framework-components/codec'; import { describe, expect, it } from 'vitest'; import { @@ -63,6 +68,14 @@ describe('renderCodecTypeOverride', () => { }); }); +const addressRef = { + kind: 'valueObject', + plane: 'storage', + namespaceId: 'shared', + entityKind: 'valueObject', + entityName: 'Address', +} as const; + describe('resolveFieldTypeOverride', () => { const scalar = (typeParams?: Record): ContractField => ({ nullable: true, @@ -103,16 +116,13 @@ describe('resolveFieldTypeOverride', () => { const valueObject: ContractField = { nullable: true, many: false, - type: { kind: 'valueObject', name: 'Address' }, + type: addressRef, }; const union: ContractField = { nullable: false, type: { kind: 'union', - members: [ - { kind: 'scalar', codecId: 'demo/text@1' }, - { kind: 'valueObject', name: 'Address' }, - ], + members: [{ kind: 'scalar', codecId: 'demo/text@1' }, addressRef], }, }; expect([ @@ -212,7 +222,8 @@ describe('generateFieldTypeOverridesMap', () => { }, }, }, - } satisfies Pick; + storage: { namespaces: {} }, + } satisfies Pick & { storage: Pick }; const resolveValueSet = (_model: string, field: string) => field === 'mood' ? { encodedValues: ['up', 'down'], codecId: 'demo/text@1' } : undefined; @@ -240,7 +251,8 @@ describe('generateFieldTypeOverridesMap', () => { }, }, }, - } satisfies Pick; + storage: { namespaces: {} }, + } satisfies Pick & { storage: Pick }; const resolveValueSet = () => ({ encodedValues: ['storage'], codecId: 'demo/text@1' }); expect(generateFieldTypeOverridesMap(contract, literalLookup, resolveValueSet)).toBe( @@ -248,25 +260,33 @@ describe('generateFieldTypeOverridesMap', () => { ); }); - it('collects the override of each value object field, from the domain enum its value set names or its codec text', () => { + it('collects the override of each value object field, keyed by its storage namespace, from the storage value set its value set names or its codec text', () => { const contract = { - domain: { + domain: { namespaces: { public: { models: {} } } }, + storage: { namespaces: { - public: { - models: {}, - enum: { Kind: kindEnum }, - valueObjects: { - Address: { - fields: { - street: { nullable: false, type: { kind: 'scalar', codecId: 'demo/text@1' } }, - kind: { - nullable: true, - type: { kind: 'scalar', codecId: 'demo/text@1' }, - valueSet: kindRef, - }, - embedding: { - nullable: false, - type: { kind: 'scalar', codecId: 'demo/vector@1', typeParams: { length: 2 } }, + shared: { + id: 'shared', + entries: { + valueSet: { Kind: { values: ['home', 'work'] } }, + valueObject: { + Address: { + fields: { + street: { nullable: false, type: { kind: 'scalar', codecId: 'demo/text@1' } }, + kind: { + nullable: true, + type: { kind: 'scalar', codecId: 'demo/text@1' }, + valueSet: { + plane: 'storage', + entityKind: 'valueSet', + namespaceId: 'shared', + entityName: 'Kind', + }, + }, + embedding: { + nullable: false, + type: { kind: 'scalar', codecId: 'demo/vector@1', typeParams: { length: 2 } }, + }, }, }, }, @@ -274,30 +294,33 @@ describe('generateFieldTypeOverridesMap', () => { }, }, }, - } as const satisfies Pick; + } as const satisfies Pick & { storage: Pick }; expect(generateFieldTypeOverridesMap(contract, literalLookup, undefined)).toBe( - '{ readonly public: { readonly valueObjects: { readonly Address: { readonly embedding: { readonly output: Vector<2>; readonly input: VectorInput<2> }; readonly kind: { readonly output: "home" | "work"; readonly input: "home" | "work" } } } } }', + '{ readonly shared: { readonly valueObjects: { readonly Address: { readonly embedding: { readonly output: Vector<2>; readonly input: VectorInput<2> }; readonly kind: { readonly output: "home" | "work"; readonly input: "home" | "work" } } } } }', ); }); - it('gives no override for a value object field whose value set is held in storage, which it cannot name', () => { + it('gives no override for a value object field whose value set storage does not have', () => { const contract = { - domain: { + domain: { namespaces: { public: { models: {} } } }, + storage: { namespaces: { - public: { - models: {}, - valueObjects: { - Address: { - fields: { - kind: { - nullable: false, - type: { kind: 'scalar', codecId: 'demo/text@1' }, - valueSet: { - plane: 'storage', - entityKind: 'valueSet', - namespaceId: 'public', - entityName: 'Kind', + shared: { + id: 'shared', + entries: { + valueObject: { + Address: { + fields: { + kind: { + nullable: false, + type: { kind: 'scalar', codecId: 'demo/text@1' }, + valueSet: { + plane: 'storage', + entityKind: 'valueSet', + namespaceId: 'shared', + entityName: 'Kind', + }, }, }, }, @@ -306,7 +329,7 @@ describe('generateFieldTypeOverridesMap', () => { }, }, }, - } as const satisfies Pick; + } as const satisfies Pick & { storage: Pick }; const resolveValueSet = () => ({ encodedValues: ['storage'], codecId: 'demo/text@1' }); expect(generateFieldTypeOverridesMap(contract, literalLookup, resolveValueSet)).toBe('{ }'); diff --git a/packages/1-framework/3-tooling/emitter/test/utils.ts b/packages/1-framework/3-tooling/emitter/test/utils.ts index 02ed97e7f9df..cc7a06fdc93a 100644 --- a/packages/1-framework/3-tooling/emitter/test/utils.ts +++ b/packages/1-framework/3-tooling/emitter/test/utils.ts @@ -19,10 +19,7 @@ const identitySerialize = (c: Contract): JsonObject => c as unknown as JsonObjec const identityDeserialize = (json: Record): Contract => { const contract = structuredClone(json) as unknown as Contract; for (const namespace of Object.values(contract.domain.namespaces)) { - for (const entity of [ - ...Object.values(namespace.models), - ...Object.values(namespace.valueObjects ?? {}), - ]) { + for (const entity of Object.values(namespace.models)) { for (const [name, field] of Object.entries(entity.fields)) { entity.fields[name] = { ...field, many: field.many ?? false }; } diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/test/fixtures/orm-contract.d.ts b/packages/2-mongo-family/1-foundation/mongo-contract/test/fixtures/orm-contract.d.ts index 7657e10fc0b4..84f2acd997e1 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/test/fixtures/orm-contract.d.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/test/fixtures/orm-contract.d.ts @@ -175,6 +175,20 @@ type ContractBase = Omit< readonly kind: 'mongo-namespace'; readonly entries: { readonly collection: { readonly tasks: MongoCollection; readonly users: MongoCollection }; + readonly valueObject: { + readonly HomeAddress: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + }; }; }; }; @@ -340,7 +354,13 @@ type ContractBase = Omit< }; readonly homeAddress: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'HomeAddress' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'HomeAddress'; + }; }; }; readonly relations: { @@ -366,40 +386,12 @@ type ContractBase = Omit< readonly storage: { readonly collection: 'users'; readonly fields: { readonly _id: { readonly field: '_id' }; readonly name: { readonly field: 'name' }; readonly email: { readonly field: 'email' }; readonly loginCount: { readonly field: 'loginCount' }; readonly tags: { readonly field: 'tags' }; readonly homeAddress: { readonly field: 'homeAddress' } }; readonly relations: { readonly addresses: { readonly field: 'addresses' } } }; }; }; - readonly valueObjects: { - readonly HomeAddress: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - }; }; }; }; readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly HomeAddress: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/test/fixtures/orm-contract.json b/packages/2-mongo-family/1-foundation/mongo-contract/test/fixtures/orm-contract.json index 25d3202d0593..a95f3db83085 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/test/fixtures/orm-contract.json +++ b/packages/2-mongo-family/1-foundation/mongo-contract/test/fixtures/orm-contract.json @@ -19,6 +19,28 @@ "collection": { "tasks": {}, "users": {} + }, + "valueObject": { + "HomeAddress": { + "fields": { + "city": { + "type": { + "kind": "scalar", + "codecId": "mongo/string@1" + }, + "nullable": false, + "many": false + }, + "country": { + "type": { + "kind": "scalar", + "codecId": "mongo/string@1" + }, + "nullable": false, + "many": false + } + } + } } } } @@ -257,7 +279,10 @@ "homeAddress": { "type": { "kind": "valueObject", - "name": "HomeAddress" + "plane": "storage", + "namespaceId": "__unbound__", + "entityKind": "valueObject", + "entityName": "HomeAddress" }, "nullable": true, "many": false @@ -370,28 +395,6 @@ "relations": {}, "owner": "Task" } - }, - "valueObjects": { - "HomeAddress": { - "fields": { - "city": { - "type": { - "kind": "scalar", - "codecId": "mongo/string@1" - }, - "nullable": false, - "many": false - }, - "country": { - "type": { - "kind": "scalar", - "codecId": "mongo/string@1" - }, - "nullable": false, - "many": false - } - } - } } } } diff --git a/packages/2-mongo-family/2-authoring/contract-ts/src/contract-builder.ts b/packages/2-mongo-family/2-authoring/contract-ts/src/contract-builder.ts index 87c3e9143b07..99bb197ec278 100644 --- a/packages/2-mongo-family/2-authoring/contract-ts/src/contract-builder.ts +++ b/packages/2-mongo-family/2-authoring/contract-ts/src/contract-builder.ts @@ -534,20 +534,30 @@ type DefinitionTargetId = Definition extends { ? TargetId : string; -type DefinitionStorage = Simplify< - MongoStorageShape & { - readonly collections: StorageCollectionsFromModels>; - readonly storageHash: StorageHashBase; - } ->; +type StorageValueObjectNamespaces> = { + readonly [K in typeof UNBOUND_NAMESPACE_ID]: { + readonly entries: { readonly valueObject: ContractValueObjectsFromRecord }; + }; +}; -type MaybeValueObjectsSection> = +type MaybeStorageValueObjectsSection> = keyof ValueObjects extends never ? EmptyObject : { - readonly valueObjects: ContractValueObjectsFromRecord; + readonly namespaces: { + readonly [K in typeof UNBOUND_NAMESPACE_ID]: MongoStorageShape['namespaces'][string] & + StorageValueObjectNamespaces[K]; + }; }; +type DefinitionStorage = Simplify< + MongoStorageShape & { + readonly collections: StorageCollectionsFromModels>; + readonly storageHash: StorageHashBase; + } +> & + MaybeStorageValueObjectsSection>; + type HandleName = Handle extends EnumTypeHandle ? Name : never; // A member is stored in its codec's JSON form, which is the authored literal only when that literal is JSON; a bigint or Date member is stored as text. @@ -602,8 +612,7 @@ type MaybeEnumsSection< type MongoDomainNamespaceFromDefinition = Simplify< { readonly models: ContractModelsFromRecord>; - } & MaybeValueObjectsSection> & - MaybeEnumsSection, CodecTypesFromDefinition> + } & MaybeEnumsSection, CodecTypesFromDefinition> >; type MongoContractBaseFromDefinition = Simplify<{ @@ -744,7 +753,8 @@ type FieldTypesFromDefinition = Fie CodecTypesFromDefinition, ParameterizedOutputTypesFromDefinition, FieldTypeOverridesFromDefinition, - Side + Side, + StorageValueObjectNamespaces> >; export type MongoContractResult = MongoContractWithTypeMaps< diff --git a/packages/2-mongo-family/2-authoring/contract-ts/test/contract-builder.types.test-d.ts b/packages/2-mongo-family/2-authoring/contract-ts/test/contract-builder.types.test-d.ts index 0d9c6a676e4a..8077935e0d2e 100644 --- a/packages/2-mongo-family/2-authoring/contract-ts/test/contract-builder.types.test-d.ts +++ b/packages/2-mongo-family/2-authoring/contract-ts/test/contract-builder.types.test-d.ts @@ -428,6 +428,17 @@ const VOContract = defineContract({ type VORow = InferModelRow; type VOInput = MongoUnboundFieldInputTypes['VOModel']; +test('value objects are typed in the storage namespace, not the domain', () => { + type Namespace = (typeof VOContract)['storage']['namespaces']['__unbound__']; + expectTypeOf().toEqualTypeOf<'Inner' | 'Outer'>(); + expectTypeOf< + Namespace['entries']['valueObject']['Outer']['fields']['inner']['type']['entityName'] + >().toEqualTypeOf<'Inner'>(); + expectTypeOf< + keyof (typeof VOContract)['domain']['namespaces']['__unbound__'] + >().toEqualTypeOf<'models'>(); +}); + test('VO output: plain value object resolves to the full nested shape', () => { expectTypeOf().toEqualTypeOf<{ readonly inner: { readonly x: string; readonly y: number }; diff --git a/packages/2-mongo-family/3-tooling/emitter/src/index.ts b/packages/2-mongo-family/3-tooling/emitter/src/index.ts index ae66d24f0d01..fdaf2298b2ef 100644 --- a/packages/2-mongo-family/3-tooling/emitter/src/index.ts +++ b/packages/2-mongo-family/3-tooling/emitter/src/index.ts @@ -1,5 +1,9 @@ import type { Contract, ContractModelBase, JsonValue } from '@internal/contract/types'; -import { serializeObjectKey, serializeValue } from '@internal/emitter/domain-type-generation'; +import { + generateValueObjectsDescriptorType, + serializeObjectKey, + serializeValue, +} from '@internal/emitter/domain-type-generation'; import type { ImportSpecifierResolver } from '@internal/framework-components/emission'; import type { Namespace } from '@internal/framework-components/ir'; import type { MongoCollection, MongoModelStorage, MongoStorage } from '@internal/mongo-contract'; @@ -47,9 +51,17 @@ function generateMongoNamespacesType(namespaces: MongoStorage['namespaces']): st } const parts: string[] = []; for (const [name, ns] of sorted) { - const collectionsType = generateMongoNamespaceCollectionsType(ns.entries.collection ?? {}); + const entriesParts = [ + `readonly collection: ${generateMongoNamespaceCollectionsType(ns.entries.collection ?? {})}`, + ]; + const valueObjects = ns.entries.valueObject; + if (valueObjects !== undefined && Object.keys(valueObjects).length > 0) { + entriesParts.push( + `readonly valueObject: ${generateValueObjectsDescriptorType(valueObjects)}`, + ); + } parts.push( - `readonly ${serializeObjectKey(name)}: { readonly id: ${serializeValue(ns.id)}; ${mongoNamespaceSerializedKind(ns)}; readonly entries: { readonly collection: ${collectionsType} } }`, + `readonly ${serializeObjectKey(name)}: { readonly id: ${serializeValue(ns.id)}; ${mongoNamespaceSerializedKind(ns)}; readonly entries: { ${entriesParts.join('; ')} } }`, ); } return `{ ${parts.join('; ')} }`; diff --git a/packages/2-mongo-family/3-tooling/emitter/test/emitter-hook.generation.test.ts b/packages/2-mongo-family/3-tooling/emitter/test/emitter-hook.generation.test.ts index a26659d56d3a..a44e2a176fcc 100644 --- a/packages/2-mongo-family/3-tooling/emitter/test/emitter-hook.generation.test.ts +++ b/packages/2-mongo-family/3-tooling/emitter/test/emitter-hook.generation.test.ts @@ -553,7 +553,7 @@ describe('mongoEmission.generateContractTypes', () => { expect(types).not.toMatch(/export type Address =/); }); - it('emits valueObjects descriptor on ContractBase', () => { + it('emits value objects in the storage namespace entries, and nowhere else', () => { const contract = createMongoContract({ valueObjects: { Address: { @@ -568,8 +568,10 @@ describe('mongoEmission.generateContractTypes', () => { }, }); const types = generateContractDts(contract, mongoEmission, [], testHashes); - expect(types).toContain('readonly valueObjects:'); - expect(types).toContain('readonly Address: { readonly fields:'); + expect(types).toContain( + 'readonly entries: { readonly collection: Record; readonly valueObject: { readonly Address: { readonly fields: { readonly street: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } } } } } }', + ); + expect(types).not.toContain('readonly valueObjects:'); }); it('emits model field with valueObject kind', () => { @@ -585,7 +587,13 @@ describe('mongoEmission.generateContractTypes', () => { homeAddress: { nullable: true, many: false, - type: { kind: 'valueObject', name: 'Address' }, + type: { + kind: 'valueObject', + plane: 'storage', + namespaceId: '__unbound__', + entityKind: 'valueObject', + entityName: 'Address', + }, }, }, relations: {}, @@ -610,7 +618,7 @@ describe('mongoEmission.generateContractTypes', () => { }); const types = generateContractDts(contract, mongoEmission, [], testHashes); expect(types).toContain( - 'readonly homeAddress: { readonly nullable: true; readonly type: { readonly kind: "valueObject"; readonly name: "Address" } }', + 'readonly homeAddress: { readonly nullable: true; readonly type: { readonly kind: "valueObject"; readonly plane: "storage"; readonly namespaceId: "__unbound__"; readonly entityKind: "valueObject"; readonly entityName: "Address" } }', ); }); @@ -626,7 +634,13 @@ describe('mongoEmission.generateContractTypes', () => { }, previousAddresses: { nullable: false, - type: { kind: 'valueObject', name: 'Address' }, + type: { + kind: 'valueObject', + plane: 'storage', + namespaceId: '__unbound__', + entityKind: 'valueObject', + entityName: 'Address', + }, many: { elementNullable: false }, }, }, @@ -652,7 +666,7 @@ describe('mongoEmission.generateContractTypes', () => { }); const types = generateContractDts(contract, mongoEmission, [], testHashes); expect(types).toContain( - 'readonly previousAddresses: { readonly nullable: false; readonly type: { readonly kind: "valueObject"; readonly name: "Address" }; readonly many: { readonly elementNullable: false } }', + 'readonly previousAddresses: { readonly nullable: false; readonly type: { readonly kind: "valueObject"; readonly plane: "storage"; readonly namespaceId: "__unbound__"; readonly entityKind: "valueObject"; readonly entityName: "Address" }; readonly many: { readonly elementNullable: false } }', ); }); @@ -668,7 +682,13 @@ describe('mongoEmission.generateContractTypes', () => { }, children: { nullable: false, - type: { kind: 'valueObject', name: 'NavItem' }, + type: { + kind: 'valueObject', + plane: 'storage', + namespaceId: '__unbound__', + entityKind: 'valueObject', + entityName: 'NavItem', + }, many: { elementNullable: false }, }, }, @@ -682,14 +702,58 @@ describe('mongoEmission.generateContractTypes', () => { 'export type NavItemOutput = ValueObjectOutputTypes["__unbound__"]["NavItem"];', ); expect(types).toContain( - 'readonly children: { readonly nullable: false; readonly type: { readonly kind: "valueObject"; readonly name: "NavItem" }; readonly many: { readonly elementNullable: false } }', + 'readonly children: { readonly nullable: false; readonly type: { readonly kind: "valueObject"; readonly plane: "storage"; readonly namespaceId: "__unbound__"; readonly entityKind: "valueObject"; readonly entityName: "NavItem" }; readonly many: { readonly elementNullable: false } }', ); }); - it('omits valueObjects when none exist', () => { + it('omits value objects when none exist', () => { const contract = createMongoContract(); const types = generateContractDts(contract, mongoEmission, [], testHashes); - expect(types).not.toContain('valueObjects'); + expect(types).not.toMatch(/valueObject/i); + }); + + it('types an enum member of a value object by the storage value set it names', () => { + const storage = namespacedMongoStorageFromCollections({}); + const contract = createMongoContract({ + storage: { + ...storage, + namespaces: { + __unbound__: { + id: '__unbound__', + entries: { + collection: {}, + valueSet: { Kind: { kind: 'valueSet', values: ['home', 'work'] } }, + }, + }, + }, + }, + valueObjects: { + Address: { + fields: { + kind: { + nullable: false, + many: false, + type: { kind: 'scalar', codecId: 'mongo/string@1' }, + valueSet: { + plane: 'storage', + entityKind: 'valueSet', + namespaceId: '__unbound__', + entityName: 'Kind', + }, + }, + }, + }, + }, + }); + const types = generateContractDts(contract, mongoEmission, [], testHashes, undefined, { + get: () => undefined, + renderOutputTypeFor: () => undefined, + renderValueLiteralFor: (_id, value) => + typeof value === 'string' ? JSON.stringify(value) : undefined, + }); + expect(types).toContain( + 'type FieldTypeOverrides = { readonly __unbound__: { readonly valueObjects: { readonly Address: { readonly kind: { readonly output: "home" | "work"; readonly input: "home" | "work" } } } } };', + ); }); it('emits nullable value object type alias field', () => { diff --git a/packages/2-mongo-family/3-tooling/emitter/test/fixtures/create-mongo-contract.ts b/packages/2-mongo-family/3-tooling/emitter/test/fixtures/create-mongo-contract.ts index b1f71e7f555f..46885ecb76d7 100644 --- a/packages/2-mongo-family/3-tooling/emitter/test/fixtures/create-mongo-contract.ts +++ b/packages/2-mongo-family/3-tooling/emitter/test/fixtures/create-mongo-contract.ts @@ -24,13 +24,32 @@ export function namespacedMongoStorageFromCollections( } as Contract['storage']; } +function withValueObjects( + storage: Contract['storage'], + valueObjects: Record, +): Contract['storage'] { + const namespace = storage.namespaces[UNBOUND_NAMESPACE_ID]; + return { + ...storage, + namespaces: { + ...storage.namespaces, + [UNBOUND_NAMESPACE_ID]: { + id: UNBOUND_NAMESPACE_ID, + ...namespace, + entries: { ...namespace?.entries, valueObject: valueObjects }, + }, + }, + }; +} + export function createMongoContract( overrides: Partial & { models?: Record; valueObjects?: Record; } = {}, ): Contract { - const { models, domain, valueObjects, ...rest } = overrides; + const { models, domain, valueObjects, storage, ...rest } = overrides; + const baseStorage = storage ?? namespacedMongoStorageFromCollections({}); return { targetFamily: 'mongo' as const, target: 'mongo', @@ -38,10 +57,9 @@ export function createMongoContract( domain ?? applicationDomainOf({ models: models ?? {}, - ...(valueObjects !== undefined ? { valueObjects } : {}), namespaceId: UNBOUND_DOMAIN_NAMESPACE_ID, }), - storage: namespacedMongoStorageFromCollections({}) as Contract['storage'], + storage: valueObjects === undefined ? baseStorage : withValueObjects(baseStorage, valueObjects), extensions: {}, capabilities: {}, meta: {}, diff --git a/packages/2-sql-family/3-tooling/emitter/src/index.ts b/packages/2-sql-family/3-tooling/emitter/src/index.ts index ce6c8d53235c..1454a81da83d 100644 --- a/packages/2-sql-family/3-tooling/emitter/src/index.ts +++ b/packages/2-sql-family/3-tooling/emitter/src/index.ts @@ -5,6 +5,7 @@ import type { JsonValue, } from '@internal/contract/types'; import { + generateValueObjectsDescriptorType, renderCodecTypeOverride, renderTypeOverridesMap, renderValueSetOverride, @@ -524,6 +525,12 @@ function generateStorageNamespacesType(namespaces: SqlStorage['namespaces']): st if (valueSetEntries !== undefined && Object.keys(valueSetEntries).length > 0) { entriesParts.push(`readonly valueSet: ${generateValueSetBlockType(valueSetEntries)}`); } + const valueObjectEntries = ns.entries.valueObject; + if (valueObjectEntries !== undefined && Object.keys(valueObjectEntries).length > 0) { + entriesParts.push( + `readonly valueObject: ${generateValueObjectsDescriptorType(valueObjectEntries)}`, + ); + } const entriesType = `{ ${entriesParts.join('; ')} }`; parts.push( `readonly ${serializeObjectKey(name)}: { readonly id: ${serializeValue(ns.id)}${kindSuffix}; readonly entries: ${entriesType} }`, diff --git a/packages/2-sql-family/3-tooling/emitter/test/create-emitter-test-contract.ts b/packages/2-sql-family/3-tooling/emitter/test/create-emitter-test-contract.ts index 06613a9c0dbe..85145ff14c9e 100644 --- a/packages/2-sql-family/3-tooling/emitter/test/create-emitter-test-contract.ts +++ b/packages/2-sql-family/3-tooling/emitter/test/create-emitter-test-contract.ts @@ -5,9 +5,28 @@ import { profileHash, UNBOUND_DOMAIN_NAMESPACE_ID, } from '@internal/contract/types'; +import { UNBOUND_NAMESPACE_ID } from '@internal/framework-components/ir'; import { applicationDomainOf } from '@repo/test-utils'; import { normalizeRootSqlStorage } from './sql-storage-fixture'; +function withValueObjects( + storage: Contract['storage'], + valueObjects: Record, +): Contract['storage'] { + const namespace = storage.namespaces[UNBOUND_NAMESPACE_ID]; + return { + ...storage, + namespaces: { + ...storage.namespaces, + [UNBOUND_NAMESPACE_ID]: { + id: UNBOUND_NAMESPACE_ID, + ...namespace, + entries: { ...namespace?.entries, valueObject: valueObjects }, + }, + }, + }; +} + export function createEmitterTestContract( overrides: Partial> & { models?: Record | undefined; @@ -16,9 +35,13 @@ export function createEmitterTestContract( } = {}, ): Contract { const { models, domain, storage, valueObjects, ...rest } = overrides; - const resolvedStorage = Object.hasOwn(overrides, 'storage') + const normalizedStorage = Object.hasOwn(overrides, 'storage') ? normalizeRootSqlStorage(storage) : normalizeRootSqlStorage({ tables: {} }); + const resolvedStorage = + valueObjects === undefined || normalizedStorage === undefined + ? normalizedStorage + : withValueObjects(normalizedStorage, valueObjects); const contract: Contract = { targetFamily: 'sql', target: 'test-db', @@ -27,7 +50,6 @@ export function createEmitterTestContract( domain ?? applicationDomainOf({ models: models ?? {}, - ...(valueObjects !== undefined ? { valueObjects } : {}), namespaceId: UNBOUND_DOMAIN_NAMESPACE_ID, }), extensions: {}, diff --git a/packages/2-sql-family/3-tooling/emitter/test/emitter-hook.generation.basic.test.ts b/packages/2-sql-family/3-tooling/emitter/test/emitter-hook.generation.basic.test.ts index 2a346eea0e15..3dd0e664ffe3 100644 --- a/packages/2-sql-family/3-tooling/emitter/test/emitter-hook.generation.basic.test.ts +++ b/packages/2-sql-family/3-tooling/emitter/test/emitter-hook.generation.basic.test.ts @@ -10,6 +10,7 @@ import { UNBOUND_NAMESPACE_ID } from '@internal/framework-components/ir'; import { describe, expect, it } from 'vitest'; import { sqlEmission } from '../src/index'; import { createEmitterTestContract as createContract } from './create-emitter-test-contract'; +import { identityCodecLookup } from './value-set-codec-lookups'; type TestDescriptor = | ControlTargetDescriptor<'sql', string> @@ -1052,7 +1053,7 @@ describe('sql-target-family-hook', () => { ); }); - it('emits valueObjects descriptor on ContractBase', () => { + it('emits value objects in the storage namespace entries, and nowhere else', () => { const ir = createContract({ valueObjects: { Address: { @@ -1068,8 +1069,10 @@ describe('sql-target-family-hook', () => { storage: { tables: {} }, }); const types = generateContractDts(ir, sqlEmission, [], testHashes); - expect(types).toContain('readonly valueObjects:'); - expect(types).toContain('readonly Address: { readonly fields:'); + expect(types).toContain( + 'readonly entries: { readonly table: {}; readonly valueObject: { readonly Address: { readonly fields: { readonly street: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "pg/text@1" } } } } } }', + ); + expect(types).not.toContain('readonly valueObjects:'); }); it('emits model fields referencing value objects with valueObject kind', () => { @@ -1089,7 +1092,13 @@ describe('sql-target-family-hook', () => { homeAddress: { nullable: true, many: false, - type: { kind: 'valueObject', name: 'Address' }, + type: { + kind: 'valueObject', + plane: 'storage', + namespaceId: '__unbound__', + entityKind: 'valueObject', + entityName: 'Address', + }, }, }, relations: {}, @@ -1123,7 +1132,7 @@ describe('sql-target-family-hook', () => { }); const types = generateContractDts(ir, sqlEmission, [], testHashes); expect(types).toContain( - 'readonly homeAddress: { readonly nullable: true; readonly type: { readonly kind: "valueObject"; readonly name: "Address" } }', + 'readonly homeAddress: { readonly nullable: true; readonly type: { readonly kind: "valueObject"; readonly plane: "storage"; readonly namespaceId: "__unbound__"; readonly entityKind: "valueObject"; readonly entityName: "Address" } }', ); }); @@ -1143,7 +1152,13 @@ describe('sql-target-family-hook', () => { id: { nullable: false, many: false, type: { kind: 'scalar', codecId: 'pg/int4@1' } }, addresses: { nullable: false, - type: { kind: 'valueObject', name: 'Address' }, + type: { + kind: 'valueObject', + plane: 'storage', + namespaceId: '__unbound__', + entityKind: 'valueObject', + entityName: 'Address', + }, many: { elementNullable: false }, }, }, @@ -1178,7 +1193,7 @@ describe('sql-target-family-hook', () => { }); const types = generateContractDts(ir, sqlEmission, [], testHashes); expect(types).toContain( - 'readonly addresses: { readonly nullable: false; readonly type: { readonly kind: "valueObject"; readonly name: "Address" }; readonly many: { readonly elementNullable: false } }', + 'readonly addresses: { readonly nullable: false; readonly type: { readonly kind: "valueObject"; readonly plane: "storage"; readonly namespaceId: "__unbound__"; readonly entityKind: "valueObject"; readonly entityName: "Address" }; readonly many: { readonly elementNullable: false } }', ); }); @@ -1194,7 +1209,13 @@ describe('sql-target-family-hook', () => { }, children: { nullable: false, - type: { kind: 'valueObject', name: 'NavItem' }, + type: { + kind: 'valueObject', + plane: 'storage', + namespaceId: '__unbound__', + entityKind: 'valueObject', + entityName: 'NavItem', + }, many: { elementNullable: false }, }, }, @@ -1206,16 +1227,60 @@ describe('sql-target-family-hook', () => { expect(types).toContain('export type NavItemOutput ='); expect(types).toContain('export type NavItemInput ='); expect(types).toContain( - 'readonly children: { readonly nullable: false; readonly type: { readonly kind: "valueObject"; readonly name: "NavItem" }; readonly many: { readonly elementNullable: false } }', + 'readonly children: { readonly nullable: false; readonly type: { readonly kind: "valueObject"; readonly plane: "storage"; readonly namespaceId: "__unbound__"; readonly entityKind: "valueObject"; readonly entityName: "NavItem" }; readonly many: { readonly elementNullable: false } }', ); }); - it('omits valueObjects when none exist', () => { + it('omits value objects when none exist', () => { const ir = createContract({ storage: { tables: {} }, }); const types = generateContractDts(ir, sqlEmission, [], testHashes); - expect(types).not.toContain('valueObjects'); + expect(types).not.toMatch(/valueObject/i); + }); + + it('types an enum member of a value object by the storage value set it names', () => { + const ir = createContract({ + valueObjects: { + Address: { + fields: { + kind: { + nullable: false, + many: false, + type: { kind: 'scalar', codecId: 'pg/text@1' }, + valueSet: { + plane: 'storage', + entityKind: 'valueSet', + namespaceId: '__unbound__', + entityName: 'Kind', + }, + }, + }, + }, + }, + storage: { + namespaces: { + __unbound__: { + id: '__unbound__', + entries: { + table: {}, + valueSet: { Kind: { kind: 'valueSet', values: ['home', 'work'] } }, + }, + }, + }, + }, + }); + const types = generateContractDts( + ir, + sqlEmission, + [], + testHashes, + undefined, + identityCodecLookup, + ); + expect(types).toContain( + 'type FieldTypeOverrides = { readonly __unbound__: { readonly valueObjects: { readonly Address: { readonly kind: { readonly output: "home" | "work"; readonly input: "home" | "work" } } } } };', + ); }); it('emits nullable value object type alias field', () => { From bbd50ff1e72c28d488f0dcbcf2299c80a6b5c4cc Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 12:51:54 +0200 Subject: [PATCH 022/104] TML-3537: a discriminator value set holds its values sorted, so declaration order does not change the storage hash Signed-off-by: willbot Signed-off-by: Will Madden --- .../2-authoring/contract-ts/src/build-contract.ts | 2 +- ...ontract-builder.discriminator-value-set.test.ts | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts b/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts index 9360b3d807a6..d9409a24c470 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts @@ -211,7 +211,7 @@ function writeDiscriminatorValueSets( ); } const namespaceValueSets = storageValueSetsByNs[namespaceId] ?? {}; - namespaceValueSets[valueSetName] = { kind: 'valueSet', values: Object.keys(variants) }; + namespaceValueSets[valueSetName] = { kind: 'valueSet', values: Object.keys(variants).sort() }; storageValueSetsByNs[namespaceId] = namespaceValueSets; const namespaceTables = tablesByNamespace[namespaceId] ?? {}; namespaceTables[model.tableName] = { diff --git a/packages/2-sql-family/2-authoring/contract-ts/test/contract-builder.discriminator-value-set.test.ts b/packages/2-sql-family/2-authoring/contract-ts/test/contract-builder.discriminator-value-set.test.ts index 2826143db187..5bb69c3ae759 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/test/contract-builder.discriminator-value-set.test.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/test/contract-builder.discriminator-value-set.test.ts @@ -98,6 +98,20 @@ describe('the value set of a discriminator', () => { }); }); + it('holds the values in sorted order, so the order the variants are declared in does not change the storage hash', () => { + const declaredFeatureFirst = build( + definitionWith({ variants: { feature: 'Feature', '10': 'Ten', bug: 'Bug', '2': 'Two' } }), + ); + const declaredBugFirst = build( + definitionWith({ variants: { bug: 'Bug', '2': 'Two', feature: 'Feature', '10': 'Ten' } }), + ); + expect({ + values: + declaredFeatureFirst.storage.namespaces['public']?.entries.valueSet?.['Task.type']?.values, + sameHash: declaredFeatureFirst.storage.storageHash === declaredBugFirst.storage.storageHash, + }).toEqual({ values: ['10', '2', 'bug', 'feature'], sameHash: true }); + }); + it('changes the storage hash when a variant is added', () => { expect(build(definitionWith({ variants: { bug: 'Bug' } })).storage.storageHash).not.toBe( build(definitionWith()).storage.storageHash, From 983929c990c12fa0d6d625dfadf241d3b7df701b Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 12:53:03 +0200 Subject: [PATCH 023/104] TML-3537: a discriminator value set is refused only when its namespace already has a value set of that name The refusal checks the base table's storage namespace, not every enum, and uses the colon form of the other enumType messages. Signed-off-by: willbot Signed-off-by: Will Madden --- .../contract-ts/src/build-contract.ts | 8 +++---- ...ct-builder.discriminator-value-set.test.ts | 22 ++++++++++++++++--- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts b/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts index d9409a24c470..c7f6172de477 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts @@ -176,7 +176,7 @@ function storageValueObjects( } /** - * Writes the value set of each discriminator, keyed `.` in the base table's namespace and holding the variants' values as the column stores them, and points the discriminator column at it (ADR 278). A column an enum types keeps the enum's value set. An enum with the same name is refused, since the two value sets would share one entry. + * Writes the value set of each discriminator, keyed `.` in the base table's namespace and holding the variants' values as the column stores them, sorted, and points the discriminator column at it (ADR 278). A column an enum types keeps the enum's value set. An enum with the same name in the same namespace is refused, since the two value sets would share one entry. */ function writeDiscriminatorValueSets( definition: ContractDefinition, @@ -196,10 +196,11 @@ function writeDiscriminatorValueSets( if (table === undefined || columnName === undefined || column === undefined) continue; if (column.valueSet !== undefined) continue; const valueSetName = `${model.modelName}.${discriminator.field}`; - if (Object.hasOwn(definition.enums ?? {}, valueSetName)) { + const namespaceValueSets = storageValueSetsByNs[namespaceId] ?? {}; + if (Object.hasOwn(namespaceValueSets, valueSetName)) { throw contractError( 'CONTRACT.ENUM_INVALID', - `enumType("${valueSetName}") has the name of the value set that the discriminator field "${discriminator.field}" of model "${model.modelName}" writes. Rename the enum.`, + `enumType("${valueSetName}"): has the name of the value set that the discriminator field "${discriminator.field}" of model "${model.modelName}" writes. Rename the enum.`, { meta: { enumName: valueSetName, @@ -210,7 +211,6 @@ function writeDiscriminatorValueSets( }, ); } - const namespaceValueSets = storageValueSetsByNs[namespaceId] ?? {}; namespaceValueSets[valueSetName] = { kind: 'valueSet', values: Object.keys(variants).sort() }; storageValueSetsByNs[namespaceId] = namespaceValueSets; const namespaceTables = tablesByNamespace[namespaceId] ?? {}; diff --git a/packages/2-sql-family/2-authoring/contract-ts/test/contract-builder.discriminator-value-set.test.ts b/packages/2-sql-family/2-authoring/contract-ts/test/contract-builder.discriminator-value-set.test.ts index 5bb69c3ae759..86fa008c6141 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/test/contract-builder.discriminator-value-set.test.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/test/contract-builder.discriminator-value-set.test.ts @@ -23,9 +23,12 @@ function definitionWith( overrides: Partial> & { readonly typeEnum?: ReturnType; readonly variants?: Readonly>; + readonly namespaceId?: string; } = {}, ): ContractDefinition { const variants = overrides.variants ?? { bug: 'Bug', feature: 'Feature' }; + const namespaceId = overrides.namespaceId ?? 'public'; + const inNamespace = overrides.namespaceId !== undefined ? { namespaceId } : {}; return { warnings: undefined, target: postgresTargetPack, @@ -35,6 +38,7 @@ function definitionWith( { modelName: 'Task', tableName: 'task', + ...inNamespace, fields: [ { fieldName: 'id', columnName: 'id', descriptor: int4, nullable: false }, { @@ -48,15 +52,16 @@ function definitionWith( id: { columns: ['id'] }, discriminator: { field: 'type' }, variants: Object.fromEntries( - Object.entries(variants).map(([value, model]) => [value, crossRef(model, 'public')]), + Object.entries(variants).map(([value, model]) => [value, crossRef(model, namespaceId)]), ), }, ...Object.values(variants).map((model) => ({ modelName: model, tableName: 'task', + ...inNamespace, fields: [], sharesBaseTable: true, - base: crossRef('Task', 'public'), + base: crossRef('Task', namespaceId), })), ], }; @@ -132,7 +137,18 @@ describe('the value set of a discriminator', () => { it('refuses an enum whose name is the value set key of a discriminator, naming both', () => { const clash = enumType('Task.type', text, member('Bug', 'bug')); expect(() => build(definitionWith({ enums: { 'Task.type': clash } }))).toThrow( - 'enumType("Task.type") has the name of the value set that the discriminator field "type" of model "Task" writes. Rename the enum.', + 'enumType("Task.type"): has the name of the value set that the discriminator field "type" of model "Task" writes. Rename the enum.', ); }); + + it('is written beside an enum of the same name in another namespace, since the two value sets do not share an entry', () => { + const sameName = enumType('Task.type', text, member('Bug', 'bug')); + const contract = build( + definitionWith({ enums: { 'Task.type': sameName }, namespaceId: 'other' }), + ); + expect({ + public: contract.storage.namespaces['public']?.entries.valueSet?.['Task.type']?.values, + other: contract.storage.namespaces['other']?.entries.valueSet?.['Task.type']?.values, + }).toEqual({ public: ['bug'], other: ['bug', 'feature'] }); + }); }); From 3f08cceb43d53609d292b2aba5d94be91d6d4a7f Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 12:55:34 +0200 Subject: [PATCH 024/104] TML-3537: two composite types with the same name in different PSL namespaces are refused Every value object is stored in the default storage namespace and models refer to composite types by bare name, so the second one silently replaced the first. Signed-off-by: willbot Signed-off-by: Will Madden --- .../contract-psl/src/interpreter.ts | 15 ++++++- .../interpreter.value-object-storage.test.ts | 41 +++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/packages/2-sql-family/2-authoring/contract-psl/src/interpreter.ts b/packages/2-sql-family/2-authoring/contract-psl/src/interpreter.ts index b6060548bb6f..49abc5e4c0c0 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/src/interpreter.ts +++ b/packages/2-sql-family/2-authoring/contract-psl/src/interpreter.ts @@ -2238,13 +2238,26 @@ export function interpretPslDocumentToSqlContract( const models: ModelSymbol[] = []; const modelEntries: ModelNamespaceEntry[] = []; const compositeTypes: CompositeTypeSymbol[] = []; + const compositeTypeNamespaces = new Map(); for (const namespace of namespaceSymbols) { for (const model of Object.values(namespace.models)) { models.push(model); modelEntries.push({ model, namespaceId: namespace.id }); } - compositeTypes.push(...Object.values(namespace.compositeTypes)); + for (const compositeType of Object.values(namespace.compositeTypes)) { + const firstNamespaceId = compositeTypeNamespaces.get(compositeType.name); + if (firstNamespaceId !== undefined) { + diagnostics.push({ + code: 'PSL_DUPLICATE_DECLARATION', + message: `Composite type "${compositeType.name}" is declared in namespace "${firstNamespaceId}" and in namespace "${namespace.id}". Every composite type is stored in the default storage namespace, so composite type names must differ across namespaces. Rename one of them.`, + ...diagnosticSource(input.sources, compositeType.node.syntax).at(), + }); + continue; + } + compositeTypeNamespaces.set(compositeType.name, namespace.id); + compositeTypes.push(compositeType); + } } const physicalNames = new Map(); for (const model of models) { diff --git a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.value-object-storage.test.ts b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.value-object-storage.test.ts index 7670c419bab9..6f11acf0bb68 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.value-object-storage.test.ts +++ b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.value-object-storage.test.ts @@ -289,3 +289,44 @@ model Child { ]); }); }); + +describe('composite types with the same name in two namespaces', () => { + it('are refused, since every value object is stored in the default storage namespace', () => { + const result = interpretPostgres(`namespace public { + type Address { + street String + } + model User { + id Int @id + home Address + } +} + +namespace auth { + type Address { + zip Int + } + model Account { + id Int @id + home Address + } +} +`); + expect( + result.ok + ? [] + : result.failure.diagnostics.map(({ code, message, span }) => ({ + code, + message, + line: span?.start.line, + })), + ).toEqual([ + { + code: 'PSL_DUPLICATE_DECLARATION', + message: + 'Composite type "Address" is declared in namespace "public" and in namespace "auth". Every composite type is stored in the default storage namespace, so composite type names must differ across namespaces. Rename one of them.', + line: 12, + }, + ]); + }); +}); From 207b0b6dd0bf7bb65bd94a605b9a6f67219ec0a0 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 12:56:22 +0200 Subject: [PATCH 025/104] TML-3537: test-utils writes value objects into storage without a cast Signed-off-by: willbot Signed-off-by: Will Madden --- test/utils/src/contract-factories.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/test/utils/src/contract-factories.ts b/test/utils/src/contract-factories.ts index d808ab9feef2..b3ea22d862f9 100644 --- a/test/utils/src/contract-factories.ts +++ b/test/utils/src/contract-factories.ts @@ -53,23 +53,22 @@ const DEFAULT_SQL_STORAGE = { }, } as const; -function withValueObjects( - storage: TStorage, +type StorageWithoutHash = Omit; + +function withValueObjects( + storage: StorageWithoutHash, valueObjects: Record | undefined, -): TStorage { +): StorageWithoutHash { if (valueObjects === undefined) return storage; - const namespaces = storage.namespaces as Record< - string, - { readonly id: string; readonly entries: Record } - >; - const namespace = namespaces[UNBOUND_NAMESPACE_ID] ?? { id: UNBOUND_NAMESPACE_ID, entries: {} }; + const namespace = storage.namespaces[UNBOUND_NAMESPACE_ID]; return { ...storage, namespaces: { - ...namespaces, + ...storage.namespaces, [UNBOUND_NAMESPACE_ID]: { + id: UNBOUND_NAMESPACE_ID, ...namespace, - entries: { ...namespace.entries, valueObject: valueObjects }, + entries: { ...namespace?.entries, valueObject: valueObjects }, }, }, }; From 9fe305dcca04ea1cb98a7d7bb4c9ff162927c899 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 12:58:07 +0200 Subject: [PATCH 026/104] TML-3537: a plan with no operations names the changed storage keys when storage is a class instance Real families hydrate storage into a class, which isPlainRecord rejects, so every such plan said the change was outside the storage entities. Signed-off-by: willbot Signed-off-by: Will Madden --- .../control-api/operations/migration-plan.ts | 8 +- .../cli/test/orm/fixtures/offline-project.ts | 23 +++++- .../orm/migration-plan.no-operations.test.ts | 76 +++++++++++-------- 3 files changed, 68 insertions(+), 39 deletions(-) diff --git a/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts b/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts index 064b95532b23..5b616980f3d0 100644 --- a/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts +++ b/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts @@ -16,11 +16,7 @@ import { type ResolvedMigrationStatement, type SchemaOwnership, } from '@internal/framework-components/control'; -import { - changedElementCoordinates, - isPlainRecord, - UNBOUND_NAMESPACE_ID, -} from '@internal/framework-components/ir'; +import { changedElementCoordinates, UNBOUND_NAMESPACE_ID } from '@internal/framework-components/ir'; import { snapshotsImportPathFrom, writeContractSnapshot, @@ -1141,7 +1137,7 @@ function withNoOperationsReport( /** The top-level storage keys whose content differs between two contracts, such as `storage.types`, in key order. */ function changedStorageKeys(fromContract: Contract | null, toContract: Contract): string[] { const plain = (storage: unknown): Record => - isPlainRecord(storage) ? JSON.parse(JSON.stringify(storage)) : {}; + typeof storage === 'object' && storage !== null ? JSON.parse(JSON.stringify(storage)) : {}; const from = plain(fromContract?.storage); const to = plain(toContract.storage); return [...new Set([...Object.keys(from), ...Object.keys(to)])] diff --git a/packages/1-framework/3-tooling/cli/test/orm/fixtures/offline-project.ts b/packages/1-framework/3-tooling/cli/test/orm/fixtures/offline-project.ts index 54aa7037676a..a7f617750a57 100644 --- a/packages/1-framework/3-tooling/cli/test/orm/fixtures/offline-project.ts +++ b/packages/1-framework/3-tooling/cli/test/orm/fixtures/offline-project.ts @@ -329,10 +329,27 @@ function fakePlanner(script: FakePlannerScript): Record { const SQL_POSTGRES = { familyId: 'sql', targetId: 'postgres', version: '1.0.0' }; +/** Storage hydrated into a class instance, as a real family's `deserializeContract` returns it. */ +class HydratedFixtureStorage { + constructor(json: object) { + Object.assign(this, json); + } +} + +function deserializeIntoStorageInstance(json: unknown): unknown { + if (json === null || typeof json !== 'object' || !('storage' in json)) return json; + const { storage } = json; + return storage === null || typeof storage !== 'object' + ? json + : { ...json, storage: new HydratedFixtureStorage(storage) }; +} + export function offlineConfig(options: { readonly project: OfflineProject; readonly script?: FakePlannerScript; readonly targetSupportsMigrations?: boolean; + /** Hydrate storage into a class instance, as real families do, instead of returning the JSON. */ + readonly storageAsClassInstance?: boolean; }): Record { const migrations = { contractToSchema: () => ({}), @@ -344,7 +361,11 @@ export function offlineConfig(options: { kind: 'family', id: 'sql', emission: {}, - create: () => ({ deserializeContract: (json: unknown) => json }), + create: () => ({ + deserializeContract: options.storageAsClassInstance + ? deserializeIntoStorageInstance + : (json: unknown) => json, + }), }, target: { ...SQL_POSTGRES, diff --git a/packages/1-framework/3-tooling/cli/test/orm/migration-plan.no-operations.test.ts b/packages/1-framework/3-tooling/cli/test/orm/migration-plan.no-operations.test.ts index 7f8b2d517921..cd631dd4ba6a 100644 --- a/packages/1-framework/3-tooling/cli/test/orm/migration-plan.no-operations.test.ts +++ b/packages/1-framework/3-tooling/cli/test/orm/migration-plan.no-operations.test.ts @@ -25,16 +25,20 @@ const HASH_OTHER = `dead${'2'.repeat(60)}`; beforeEach(resetRenderContractDtsMock); afterEach(removeOfflineProjects); -function cli(project: OfflineProject) { +function cli(project: OfflineProject, options: { readonly storageAsClassInstance?: boolean } = {}) { return createOrmTestCli({ commands: OFFLINE_COMMANDS, groups: BIN_GROUPS, - orm: offlineConfig({ project, script: { operations: [] } }), + orm: offlineConfig({ project, script: { operations: [] }, ...options }), }); } -async function run(project: OfflineProject, args: readonly string[]) { - const result = await cli(project).run([...args, '--json'], { cwd: project.dir }); +async function run( + project: OfflineProject, + args: readonly string[], + options: { readonly storageAsClassInstance?: boolean } = {}, +) { + const result = await cli(project, options).run([...args, '--json'], { cwd: project.dir }); const terminal = result.json.at(-1); return { exitCode: result.exitCode, @@ -179,34 +183,42 @@ describe('migration plan when the planner produces no operations', () => { ); }); - it('names the storage keys that changed when no storage entity did', async () => { - const project = await createOfflineProject({ - storageHash: HASH_TO, - storageNamespaces: STORAGE_FROM, - storageTypes: { Short: { codecId: 'sql/varchar@1' } }, - }); - await seedMigrationPackage({ - appMigrationsDir: project.appMigrationsDir, - dirName: '20260101T0000_initial', - from: null, - to: HASH_FROM, - }); - await seedContractSnapshot({ - migrationsDir: project.migrationsDir, - storageHash: HASH_FROM, - storageNamespaces: STORAGE_FROM, - }); - await seedDbRef({ appMigrationsDir: project.appMigrationsDir, storageHash: HASH_FROM }); - - expect(await run(project, ['migration', 'plan'])).toMatchObject({ - exitCode: 0, - data: { - changedStorageEntities: [], - summary: - 'Planned 0 operation(s). The storage the contract describes changed, but the database needs no operation for it, so the migration has none. No storage entity changed; what changed is storage.types.', - }, - }); - }); + it.each([ + { storage: 'plain JSON', storageAsClassInstance: false }, + { storage: 'a class instance, as real families hydrate it', storageAsClassInstance: true }, + ])( + 'names the storage keys that changed when no storage entity did, with storage as $storage', + async ({ storageAsClassInstance }) => { + const project = await createOfflineProject({ + storageHash: HASH_TO, + storageNamespaces: STORAGE_FROM, + storageTypes: { Short: { codecId: 'sql/varchar@1' } }, + }); + await seedMigrationPackage({ + appMigrationsDir: project.appMigrationsDir, + dirName: '20260101T0000_initial', + from: null, + to: HASH_FROM, + }); + await seedContractSnapshot({ + migrationsDir: project.migrationsDir, + storageHash: HASH_FROM, + storageNamespaces: STORAGE_FROM, + }); + await seedDbRef({ appMigrationsDir: project.appMigrationsDir, storageHash: HASH_FROM }); + + expect(await run(project, ['migration', 'plan'], { storageAsClassInstance })).toMatchObject( + { + exitCode: 0, + data: { + changedStorageEntities: [], + summary: + 'Planned 0 operation(s). The storage the contract describes changed, but the database needs no operation for it, so the migration has none. No storage entity changed; what changed is storage.types.', + }, + }, + ); + }, + ); it('says so in a warning, above the contract edge', async () => { const project = await projectWithHistory(); From 90611d50bbc2f25fd18e1f967f1cad3bfd98ba75 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 13:00:58 +0200 Subject: [PATCH 027/104] TML-3537: dispatch briefs for runtimes and Postgres checks, and the corrected reason for the clash refusal Signed-off-by: willbot Signed-off-by: Will Madden --- projects/migration-statements/deferred.md | 4 ++ .../14-runtime-and-tools-read-storage.md | 40 +++++++++++++++ .../dispatches/15-postgres-derived-checks.md | 49 +++++++++++++++++++ .../slices/remaining-nouns/plan-pr3.md | 2 +- 4 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 projects/migration-statements/slices/remaining-nouns/dispatches/14-runtime-and-tools-read-storage.md create mode 100644 projects/migration-statements/slices/remaining-nouns/dispatches/15-postgres-derived-checks.md diff --git a/projects/migration-statements/deferred.md b/projects/migration-statements/deferred.md index 01802bda5288..425a254b33b8 100644 --- a/projects/migration-statements/deferred.md +++ b/projects/migration-statements/deferred.md @@ -196,3 +196,7 @@ Reproduction: emit a PSL contract with a `namespace public` block and a `namespa ## Column value-set references are not checked against storage A column's `valueSet: { plane: 'storage', entityKind: 'valueSet', entityName }` is not checked to name an existing `storage.namespaces..entries.valueSet` entry, in either family. A hand-edited or corrupt `contract.json` with a dangling reference passes validation and fails later. PR 3 of the remaining-nouns slice adds the check for value object members only. Found in the review of that PR, 2026-10-23. + +## Composite types are not namespaced in SQL PSL + +SQL PSL resolves a composite type by its bare name across all namespaces and writes every value object to the default storage namespace. Two composite types with the same name in different PSL namespaces are refused with `PSL_DUPLICATE_DECLARATION` (before PR 3 of the remaining-nouns slice they silently overwrote each other). Letting each namespace declare its own needs namespace-qualified type references in PSL and writing each value object to its namespace's storage. Found in the review of that PR, 2026-10-23. diff --git a/projects/migration-statements/slices/remaining-nouns/dispatches/14-runtime-and-tools-read-storage.md b/projects/migration-statements/slices/remaining-nouns/dispatches/14-runtime-and-tools-read-storage.md new file mode 100644 index 000000000000..621d44a4bab3 --- /dev/null +++ b/projects/migration-statements/slices/remaining-nouns/dispatches/14-runtime-and-tools-read-storage.md @@ -0,0 +1,40 @@ +# Dispatch 14 — runtimes and tools read value objects through the field's storage reference + +**Plan:** [`../plan-pr3.md`](../plan-pr3.md) · **ADR:** ADR 278 · **Grounding:** `wip/pr3-grounding.md` § A (CLI, Postgres target, Mongo query builders) · **Builds on:** dispatches 12 and 13 · **Branch:** `tml-3537-value-objects-storage` + +## Outcome + +Nothing reads `domain.namespaces..valueObjects`, the removed accessor `domainValueObjectsAtDefaultNamespace`, or the old field type `{ kind: 'valueObject', name }`. Each reader finds a value object through the field's storage coordinate, with `valueObjectAt` from `@internal/contract`: + +- The Mongo ORM (`mongo-orm`: `collection.ts`, `compile.ts`, `field-accessor.ts`) and the Mongo query builder (`builder.ts`, `resolve-path.ts`, `result-shape.ts`), at run time and at type level. Remove the dead `MergedContractValueObjects` fallback that read the dropped flat `valueObjects` block of `contract.d.ts`. +- The CLI statement resolver (`resolve-statements.ts`, `hasValueObject`): it still refuses a statement that names a value object (PR 4 replaces that), but finds value objects in storage. Fix the test type error at `test/control-api/statements/statement-fixtures.ts:68`. +- `contract print` in the Postgres target (`psl-print/domain-types.ts`, `scalar-fields.ts`, `refusals.ts`): prints `type` blocks from storage value objects. The print round trip of a contract with nested, list and enum-member value objects must be lossless. +- `contract infer` produces no value objects; no change, unless a type error says otherwise. +- `db verify` compares tables and checks by name; confirm it needs no change. +- The SQL ORM has no run-time value object reader; confirm, and fix only what its type tests need. + +## Do + +1. **Tests first, red before each change:** a Mongo ORM read and write of a nested and a list value object through `mongodb-memory-server`; a query builder path into a value object member; the CLI refusal of a statement naming a value object, now found in storage; the Postgres print round trip. +2. Implement. Grep the whole repo, both families, for `valueObjects`, `domainValueObjectsAtDefaultNamespace` and `kind: 'valueObject'` with `name`, and leave none outside committed fixtures and snapshots (dispatch 16). + +## Do not + +- Do not change authoring, emit or derived checks (dispatch 15). Do not re-emit fixtures or examples (dispatch 16). Leave the derived-check parts of `psl-print/model-attributes.ts` and `psl-infer` to dispatch 15. + +## Rules + +- Never run `pnpm test:integration`, `pnpm test:e2e` or `pnpm test:all`. Only named files inside `test/integration` with `pnpm test `. +- Use `mise exec --` for node and pnpm. Never `npx`. Do not write to `/tmp` or any system temp directory; save slow output under `wip/` and read the file. +- No comments where the code can say it; no bare `as` casts in production code; no `any`; no file extensions in imports; no backwards-compatibility exports; framework packages name no family or target. No invented nouns. +- Commit with `git commit -s --trailer "Signed-off-by: Will Madden "`. No AI attribution lines. Stage files explicitly. +- Do not push. Do not open a PR. + +## Done when + +- Typecheck, lint and `test --coverage` pass in `mongo-orm`, `mongo-query-builder`, `target-mongo`, `target-postgres`, the CLI, and every other package this dispatch touches; `lint:deps`; `lint:ratchets`. +- The report lists what is still red and why (committed fixtures for dispatch 16). + +## Report + +Plain English, short sentences: each reader and how it now finds value objects, the grep result, the test that was red first for each change, the checks, what is still red, and the commits. diff --git a/projects/migration-statements/slices/remaining-nouns/dispatches/15-postgres-derived-checks.md b/projects/migration-statements/slices/remaining-nouns/dispatches/15-postgres-derived-checks.md new file mode 100644 index 000000000000..90580841ac43 --- /dev/null +++ b/projects/migration-statements/slices/remaining-nouns/dispatches/15-postgres-derived-checks.md @@ -0,0 +1,49 @@ +# Dispatch 15 — Postgres derives a check for the discriminator column and for each value object column + +**Plan:** [`../plan-pr3.md`](../plan-pr3.md) · **ADR:** ADR 278 decisions 2 and 3, and the plan's decisions on the `jsonpath` predicate and on value objects that contain themselves · **Grounding:** `wip/pr3-grounding.md` § D · **Builds on:** dispatch 12 · **Branch:** `tml-3537-value-objects-storage` + +## Outcome + +On a managed Postgres table: + +- **Discriminator column.** A `membership` check with the values of the `.` value set, rendered and named as an enum's membership check is. An enum-typed discriminator keeps only the enum's check (plan decision 2). +- **Value object column.** A new check kind whose name differs from the membership check on the same column (add it to `CHECK_KIND_SUFFIX` in `schema-ir/src/naming.ts`; pick the suffix from existing vocabulary and say why). Its expression is one strict `jsonpath` predicate that must hold, written so a NULL result fails: `"col" IS NULL OR jsonb_path_match("col", 'strict …') IS TRUE` for a nullable column, without the `IS NULL` arm for a required one. The predicate states, for each member, what the SQL ORM's JSON can hold: + - the column is an object (or, for a list column, an array of objects); + - each required member is present; a nullable member may be absent or JSON `null`; + - no other key; + - each scalar member's JSON type, from a codec-to-JSON-type table in the Postgres target (text, timestamps and UUIDs are `string`; integers and floats are `number`; booleans are `boolean`; JSON members are anything). For a codec the table does not know, check presence only, and list those codecs in the report; + - lists, with element nullability; + - enum members: the storage value set's values; + - nested value objects, inlined. A value object that contains itself is checked in full down to where the cycle starts, and any object is accepted below, as the MongoDB validator does. +- The family resolves nested value objects from storage and hands the target a resolved definition, so the target needs no contract access. Extend the renderer input type in both places it is declared (`target-authoring-hooks.ts` and `check-expressions.ts`), or better, declare it once where both can import it. +- Non-managed tables get none of these checks, as for enum checks today (`stripDerivedChecksFromNonManagedTables` picks up a new kind through `CHECK_KIND_SUFFIX`). +- `contract print` does not print the new checks as `@@check` (`psl-print/model-attributes.ts`), so the print round trip stays lossless. `contract infer` on a live table with such a check prints it as an authored `@@check`, since infer produces no value objects; say so in the Data Contract docs or the infer docs. +- Check whether the SQL ORM writes SQL `NULL` or JSON `null` for an absent nullable value object column, and make the predicate accept both for a nullable column and neither for a required one. + +## Do + +1. **Tests first, red before each change, on PGlite:** for each shape (flat, nullable member, list member with and without nullable elements, enum member, nested, list of nested, nullable column, list column, a value object that contains itself), a row the ORM writes is accepted and a row with a missing key, an extra key, a wrong JSON type or a value outside an enum is refused. The discriminator check refuses a value no variant has. Unit tests for the rendered SQL text and the check names. +2. A new SQL fixture with nested and list members for these tests (`test/integration/test/sql-orm-client/fixtures/` or next to the Postgres target tests, whichever the existing derived-check tests use). +3. Implement. +4. Planner and verify: confirm `db update` from an empty database creates the checks, and `db verify` is clean afterwards, on PGlite. + +## Do not + +- Do not re-emit committed fixtures or examples, and do not change the example migrations (dispatch 16). +- Do not change MySQL or SQLite: they derive no checks today. + +## Rules + +- Never run `pnpm test:integration`, `pnpm test:e2e` or `pnpm test:all`. Only named files inside `test/integration` with `pnpm test `. +- Use `mise exec --` for node and pnpm. Never `npx`. Do not write to `/tmp` or any system temp directory; save slow output under `wip/` and read the file. +- No comments where the code can say it; no bare `as` casts in production code; no `any`; no file extensions in imports; no backwards-compatibility exports; framework packages name no family or target; do not branch on target in family code. No invented nouns; a new name goes in the report with candidates. +- Commit with `git commit -s --trailer "Signed-off-by: Will Madden "`. No AI attribution lines. Stage files explicitly. +- Do not push. Do not open a PR. + +## Done when + +- Typecheck, lint and `test --coverage` pass in `target-postgres`, `sql-contract-ts`, `schema-ir`, and every other package this dispatch touches; `lint:deps`; `lint:ratchets`; `fixtures:check` is expected to fail until dispatch 16, so list what it would change. + +## Report + +Plain English, short sentences: the check kind and suffix, an example predicate for a nested value object, the codec table and the codecs it does not know, the NULL finding, the test that was red first for each change, the checks, what is still red, and the commits. diff --git a/projects/migration-statements/slices/remaining-nouns/plan-pr3.md b/projects/migration-statements/slices/remaining-nouns/plan-pr3.md index 4e6a2aed0961..7b5f9d2da0dd 100644 --- a/projects/migration-statements/slices/remaining-nouns/plan-pr3.md +++ b/projects/migration-statements/slices/remaining-nouns/plan-pr3.md @@ -8,7 +8,7 @@ This PR changes the contract shape. It adds no statements; PR 4 adds the value o - **The discriminator value set is SQL only.** On MongoDB the collection validator already pins each variant's discriminator value, so a change there already changes the storage hash. - **An enum-typed discriminator keeps the enum's value set and check.** The column has one `valueSet` slot, and the enum already restricts it; a variant value outside the enum is already refused. -- **A clash between an enum's value set and a discriminator's is refused, and nothing else.** Enum value sets are keyed by the enum's name and a discriminator's by `.`. Only an enum named exactly like a discriminator's key (possible only from the TypeScript builder, since PSL names are identifiers) clashes; the build refuses that case, naming both and saying to rename the enum. Enum names and members are otherwise unrestricted (Will, 2026-10-23: do not limit enum names to avoid a rare clash). +- **A clash between an enum's value set and a discriminator's is refused, and nothing else.** Enum value sets are keyed by the enum's name and a discriminator's by `.`. Only an enum named exactly like a discriminator's key, in the same storage namespace, clashes. No authoring surface produces that today: PSL names are identifiers, and the SQL TypeScript builder has no polymorphism, so only a hand-written contract definition reaches it. The build still refuses it as a safeguard, naming both and saying to rename the enum. Enum names and members are otherwise unrestricted (Will, 2026-10-23: do not limit enum names to avoid a rare clash). - **The Postgres value object check is one strict `jsonpath` predicate**, written so a NULL result fails (`IS TRUE`). The ADR's `?&` and `-` operators are not used. - **A value object that contains itself** is checked as the MongoDB validator does: fully down to where the cycle starts, any object below. - **Value object fields have no separate stored names**; they are stored under their own names in both families. From 7f4890ebbdda8582ee59a88b79d407f557799dc3 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 13:14:00 +0200 Subject: [PATCH 028/104] TML-3537: the Mongo ORM and query builder find a value object through the field's storage reference Run time reads go through valueObjectAt; type-level reads go through the new ContractValueObjectAt. The dead fallback to the flat valueObjects block of contract.d.ts is gone. Signed-off-by: willbot Signed-off-by: Will Madden --- .../contract/src/contract-types.ts | 16 ++ .../contract/src/exports/types.ts | 1 + .../contract/test/contract-types.test-d.ts | 21 +- .../5-query-builders/orm/src/collection.ts | 19 +- .../5-query-builders/orm/src/compile.ts | 20 +- .../orm/src/field-accessor.ts | 42 ++-- .../5-query-builders/orm/test/compile.test.ts | 64 ++++-- .../test/integration/value-objects.test.ts | 198 ++++++++++++++++++ .../orm/test/orm-types.test-d.ts | 1 - .../orm/test/value-object-inputs.test-d.ts | 88 ++++---- .../orm/test/where-equality.test.ts | 8 +- .../orm/test/where-list-fields.test-d.ts | 22 +- .../query-builder/src/builder.ts | 6 +- .../query-builder/src/exports/index.ts | 1 - .../query-builder/src/resolve-path.ts | 40 ++-- .../query-builder/src/result-shape.ts | 46 ++-- .../test/fixtures/test-contract.ts | 148 +++++++------ .../query-builder/test/result-shape.test.ts | 91 ++++++-- .../value-objects.integration.test.ts | 49 +++-- 19 files changed, 628 insertions(+), 253 deletions(-) create mode 100644 packages/2-mongo-family/5-query-builders/orm/test/integration/value-objects.test.ts diff --git a/packages/1-framework/0-foundation/contract/src/contract-types.ts b/packages/1-framework/0-foundation/contract/src/contract-types.ts index 41337840f021..421fbe1972ae 100644 --- a/packages/1-framework/0-foundation/contract/src/contract-types.ts +++ b/packages/1-framework/0-foundation/contract/src/contract-types.ts @@ -70,6 +70,22 @@ type NamespaceValueObjectsOf = TNamespace extends { : Record : Record; +/** The value object of `TStorage` that `TReference` names, at `namespaces[namespaceId].entries.valueObject[entityName]`, or `never` when storage does not have it. */ +export type ContractValueObjectAt< + TStorage, + TReference extends { readonly namespaceId: string; readonly entityName: string }, +> = TStorage extends { readonly namespaces: infer Namespaces } + ? TReference['namespaceId'] extends keyof Namespaces + ? Namespaces[TReference['namespaceId']] extends { + readonly entries: { readonly valueObject: infer VOs }; + } + ? TReference['entityName'] extends keyof VOs + ? VOs[TReference['entityName']] + : never + : never + : never + : never; + /** Value-object map of the storage plane when the contract declares exactly one storage namespace. */ export type ContractValueObjectDefinitions = NamespaceValueObjectsOf< diff --git a/packages/1-framework/0-foundation/contract/src/exports/types.ts b/packages/1-framework/0-foundation/contract/src/exports/types.ts index 49c4a6120859..c0f9cee0db0a 100644 --- a/packages/1-framework/0-foundation/contract/src/exports/types.ts +++ b/packages/1-framework/0-foundation/contract/src/exports/types.ts @@ -1,6 +1,7 @@ export type { Contract, ContractExecutionSection, + ContractValueObjectAt, ContractValueObjectDefinitions, } from '../contract-types'; export type { ControlPolicy } from '../control-policy'; diff --git a/packages/1-framework/0-foundation/contract/test/contract-types.test-d.ts b/packages/1-framework/0-foundation/contract/test/contract-types.test-d.ts index a348affe6fb2..241e76d1c8f7 100644 --- a/packages/1-framework/0-foundation/contract/test/contract-types.test-d.ts +++ b/packages/1-framework/0-foundation/contract/test/contract-types.test-d.ts @@ -1,5 +1,5 @@ import { expectTypeOf, test } from 'vitest'; -import type { Contract } from '../src/contract-types'; +import type { Contract, ContractValueObjectAt } from '../src/contract-types'; import type { CrossReference } from '../src/cross-reference'; import type { ContractField, ContractModel, ModelStorageBase } from '../src/domain-types'; import type { NamespaceId } from '../src/namespace-id'; @@ -115,3 +115,22 @@ test('old many: true does not satisfy ContractField', () => { test('ContractField has no sibling elementNullable property', () => { expectTypeOf().not.toHaveProperty('elementNullable'); }); + +test('ContractValueObjectAt finds the value object a reference names in its storage namespace', () => { + type Point = { readonly fields: { readonly x: ContractField } }; + type Geo = { readonly fields: { readonly lat: ContractField } }; + type Storage = { + readonly namespaces: { + readonly public: { readonly entries: { readonly valueObject: { readonly Point: Point } } }; + readonly geo: { readonly entries: { readonly valueObject: { readonly Point: Geo } } }; + }; + }; + type Ref = { + readonly namespaceId: Ns; + readonly entityName: Name; + }; + expectTypeOf>>().toEqualTypeOf(); + expectTypeOf>>().toEqualTypeOf(); + expectTypeOf>>().toBeNever(); + expectTypeOf>>().toBeNever(); +}); diff --git a/packages/2-mongo-family/5-query-builders/orm/src/collection.ts b/packages/2-mongo-family/5-query-builders/orm/src/collection.ts index 4a61bc762f49..58438ea0e9af 100644 --- a/packages/2-mongo-family/5-query-builders/orm/src/collection.ts +++ b/packages/2-mongo-family/5-query-builders/orm/src/collection.ts @@ -4,8 +4,9 @@ import { type ContractModel, type ContractReferenceRelation, type ContractValueObject, - domainValueObjectsAtDefaultNamespace, type PlanMeta, + type ValueObjectFieldType, + valueObjectAt, } from '@internal/contract/types'; import { UNBOUND_NAMESPACE_ID } from '@internal/framework-components/ir'; import { @@ -909,7 +910,7 @@ class MongoCollectionImpl< this.#state, this.#contract.storage.storageHash, model, - this.#valueObjects(), + this.#contract.storage, ); } @@ -996,11 +997,11 @@ class MongoCollectionImpl< if (!model) { return Object.freeze({ kind: 'unknown' as const }); } - return contractModelToMongoResultShape(model, { valueObjects: this.#valueObjects() }); + return contractModelToMongoResultShape(model, { storage: this.#contract.storage }); } - #valueObjects(): Readonly> { - return domainValueObjectsAtDefaultNamespace(this.#contract.domain) ?? {}; + #valueObjectAt(reference: ValueObjectFieldType): ContractValueObject | undefined { + return valueObjectAt(this.#contract.storage, reference); } #compileWhereObject(data: Record): MongoFilterExpr[] { @@ -1059,8 +1060,7 @@ class MongoCollectionImpl< head === undefined ? undefined : this.#modelFields()[head]; for (const segment of rest) { if (field?.type.kind !== 'valueObject') return undefined; - field = domainValueObjectsAtDefaultNamespace(this.#contract.domain)?.[field.type.name] - ?.fields[segment]; + field = this.#valueObjectAt(field.type)?.fields[segment]; } return field; } @@ -1083,8 +1083,7 @@ class MongoCollectionImpl< } if (field.type.kind === 'valueObject') { - const voName = field.type.name; - const voDef = domainValueObjectsAtDefaultNamespace(this.#contract.domain)?.[voName]; + const voDef = this.#valueObjectAt(field.type); if (!voDef) return new MongoParamRef(value); if (field.many && Array.isArray(value)) { @@ -1371,7 +1370,7 @@ class MongoCollectionImpl< return this.#fieldParam(value.value, field.type.codecId, path); } if (field.type.kind === 'valueObject' && isUnknownRecord(value.value)) { - const voDef = domainValueObjectsAtDefaultNamespace(this.#contract.domain)?.[field.type.name]; + const voDef = this.#valueObjectAt(field.type); if (voDef) return this.#wrapValueObject(value.value, voDef, path, 'write'); } return value; diff --git a/packages/2-mongo-family/5-query-builders/orm/src/compile.ts b/packages/2-mongo-family/5-query-builders/orm/src/compile.ts index ce81a721af5a..4509f8f71d6c 100644 --- a/packages/2-mongo-family/5-query-builders/orm/src/compile.ts +++ b/packages/2-mongo-family/5-query-builders/orm/src/compile.ts @@ -1,4 +1,4 @@ -import type { ContractModel, PlanMeta } from '@internal/contract/types'; +import type { ContractModel, PlanMeta, StorageBase } from '@internal/contract/types'; import type { MongoModelStorage } from '@internal/mongo-contract'; import type { MongoFieldShape, @@ -16,10 +16,7 @@ import { MongoSortStage, MongoUnwindStage, } from '@internal/mongo-query-ast/execution'; -import { - contractModelToMongoResultShape, - type MongoValueObjects, -} from '@internal/mongo-query-builder'; +import { contractModelToMongoResultShape } from '@internal/mongo-query-builder'; import { ifDefined } from '@internal/utils/defined'; import type { MongoCollectionState, MongoIncludeExpr } from './collection-state'; @@ -30,8 +27,11 @@ function isToOne(include: MongoIncludeExpr): boolean { /** * An included document is decoded through the related model's codecs, like the root document. A to-one include with no related document reads as `null`. */ -function includeShape(include: MongoIncludeExpr, valueObjects: MongoValueObjects): MongoFieldShape { - const target = contractModelToMongoResultShape(include.targetModel, { valueObjects }); +function includeShape( + include: MongoIncludeExpr, + storage: Pick, +): MongoFieldShape { + const target = contractModelToMongoResultShape(include.targetModel, { storage }); const fields = target.kind === 'document' ? target.fields : {}; return isToOne(include) ? { kind: 'document', nullable: true, fields } @@ -64,7 +64,7 @@ export function compileMongoQuery( state: MongoCollectionState, storageHash: string, model: ContractModel, - valueObjects: MongoValueObjects, + storage: Pick, ): MongoQueryPlan { const stages: MongoPipelineStage[] = []; @@ -119,9 +119,9 @@ export function compileMongoQuery( const resultShape = contractModelToMongoResultShape(model, { ...ifDefined('selection', selection), includes: Object.fromEntries( - state.includes.map((inc) => [inc.relationName, includeShape(inc, valueObjects)]), + state.includes.map((inc) => [inc.relationName, includeShape(inc, storage)]), ), - valueObjects, + storage, }); return { collection, command, meta, resultShape }; diff --git a/packages/2-mongo-family/5-query-builders/orm/src/field-accessor.ts b/packages/2-mongo-family/5-query-builders/orm/src/field-accessor.ts index f4e027245c5f..4efc780b1572 100644 --- a/packages/2-mongo-family/5-query-builders/orm/src/field-accessor.ts +++ b/packages/2-mongo-family/5-query-builders/orm/src/field-accessor.ts @@ -1,7 +1,7 @@ import type { ContractField, - ContractValueObject, - ContractValueObjectDefinitions, + ContractValueObjectAt, + ValueObjectFieldType, } from '@internal/contract/types'; import type { AnyMongoTypeMaps, @@ -52,7 +52,7 @@ type ValueObjectFieldKeys< > = { [K in keyof MongoModelsMap[ModelName]['fields'] & string]: MongoModelsMap[ModelName]['fields'][K] extends { - readonly type: { readonly kind: 'valueObject'; readonly name: string }; + readonly type: ValueObjectFieldType; } ? K : never; @@ -93,26 +93,12 @@ export type FieldExpression> = pop(end: 1 | -1): FieldOperation; } & ([NumericOperand] extends [never] ? unknown : NumericOps); -type HasValueObjects = MongoContract; - -type MergedContractValueObjects = - ContractValueObjectDefinitions & - (TContract extends { readonly valueObjects?: infer VOs } - ? VOs extends Record - ? VOs - : Record - : Record); - -type VOFields< - TContract extends HasValueObjects, - VOName extends string, -> = VOName extends keyof MergedContractValueObjects - ? MergedContractValueObjects[VOName] extends { - readonly fields: infer F extends Record; - } +type VOFields = + ContractValueObjectAt extends { + readonly fields: infer F extends Record; + } ? F - : never - : never; + : never; type VOScalarFieldKeys> = { [K in keyof Fields & string]: Fields[K] extends { readonly type: { readonly kind: 'scalar' } } @@ -122,23 +108,23 @@ type VOScalarFieldKeys> = { type VOValueObjectFieldKeys> = { [K in keyof Fields & string]: Fields[K] extends { - readonly type: { readonly kind: 'valueObject'; readonly name: string }; + readonly type: ValueObjectFieldType; } ? K : never; }[keyof Fields & string]; type VODotPaths< - TContract extends HasValueObjects, + TContract extends MongoContract, Fields extends Record, Prefix extends string, > = | { [K in VOScalarFieldKeys]: `${Prefix}${K}` }[VOScalarFieldKeys] | { [K in VOValueObjectFieldKeys]: Fields[K] extends { - readonly type: { readonly kind: 'valueObject'; readonly name: infer N extends string }; + readonly type: infer Reference extends ValueObjectFieldType; } - ? VODotPaths, `${Prefix}${K}.`> + ? VODotPaths, `${Prefix}${K}.`> : never; }[VOValueObjectFieldKeys]; @@ -150,9 +136,9 @@ export type DotPath< TContract, ModelName >]: MongoModelsMap[ModelName]['fields'][K] extends { - readonly type: { readonly kind: 'valueObject'; readonly name: infer N extends string }; + readonly type: infer Reference extends ValueObjectFieldType; } - ? VODotPaths, `${K}.`> + ? VODotPaths, `${K}.`> : never; }[ValueObjectFieldKeys]; diff --git a/packages/2-mongo-family/5-query-builders/orm/test/compile.test.ts b/packages/2-mongo-family/5-query-builders/orm/test/compile.test.ts index 1cc730079fd3..4e0673d7f434 100644 --- a/packages/2-mongo-family/5-query-builders/orm/test/compile.test.ts +++ b/packages/2-mongo-family/5-query-builders/orm/test/compile.test.ts @@ -66,7 +66,13 @@ const testUserModel: ContractModel = { address: { nullable: false, many: false, - type: { kind: 'valueObject', name: 'Address' }, + type: { + kind: 'valueObject', + plane: 'storage', + namespaceId: '__unbound__', + entityKind: 'valueObject', + entityName: 'Address', + }, }, }, }; @@ -98,6 +104,8 @@ const testPostModel: ContractModel = { const testHash = 'test-hash'; +const noStorage = { namespaces: {} }; + function stages(plan: MongoQueryPlan): ReadonlyArray { if (plan.command.kind !== 'aggregate') throw new Error(`Expected aggregate, got ${plan.command.kind}`); @@ -106,7 +114,13 @@ function stages(plan: MongoQueryPlan): ReadonlyArray { describe('compileMongoQuery', () => { it('produces empty pipeline from empty state', () => { - const plan = compileMongoQuery('users', emptyCollectionState(), testHash, testUserModel, {}); + const plan = compileMongoQuery( + 'users', + emptyCollectionState(), + testHash, + testUserModel, + noStorage, + ); expect(plan.collection).toBe('users'); expect(plan.command.kind).toBe('aggregate'); expect(stages(plan)).toEqual([]); @@ -119,7 +133,7 @@ describe('compileMongoQuery', () => { ...emptyCollectionState(), filters: [MongoFieldFilter.eq('name', 'Alice')], }; - const plan = compileMongoQuery('users', state, testHash, testUserModel, {}); + const plan = compileMongoQuery('users', state, testHash, testUserModel, noStorage); expect(stages(plan)).toHaveLength(1); const match = stages(plan)[0] as MongoMatchStage; expect(match).toBeInstanceOf(MongoMatchStage); @@ -131,7 +145,7 @@ describe('compileMongoQuery', () => { ...emptyCollectionState(), filters: [MongoFieldFilter.eq('name', 'Alice'), MongoFieldFilter.gte('age', 18)], }; - const plan = compileMongoQuery('users', state, testHash, testUserModel, {}); + const plan = compileMongoQuery('users', state, testHash, testUserModel, noStorage); expect(stages(plan)).toHaveLength(1); const match = stages(plan)[0] as MongoMatchStage; expect(match.filter.kind).toBe('and'); @@ -143,7 +157,7 @@ describe('compileMongoQuery', () => { ...emptyCollectionState(), selectedFields: ['name', 'email'], }; - const plan = compileMongoQuery('users', state, testHash, testUserModel, {}); + const plan = compileMongoQuery('users', state, testHash, testUserModel, noStorage); expect(stages(plan)).toHaveLength(1); const project = stages(plan)[0] as MongoProjectStage; expect(project).toBeInstanceOf(MongoProjectStage); @@ -155,7 +169,7 @@ describe('compileMongoQuery', () => { ...emptyCollectionState(), selectedFields: ['_id', 'name'], }; - const plan = compileMongoQuery('users', state, testHash, testUserModel, {}); + const plan = compileMongoQuery('users', state, testHash, testUserModel, noStorage); const project = stages(plan)[0] as MongoProjectStage; expect(project.projection).toEqual({ _id: 1, name: 1 }); }); @@ -165,7 +179,7 @@ describe('compileMongoQuery', () => { ...emptyCollectionState(), selectedFields: [], }; - const plan = compileMongoQuery('users', state, testHash, testUserModel, {}); + const plan = compileMongoQuery('users', state, testHash, testUserModel, noStorage); expect(stages(plan)).toEqual([]); }); @@ -174,7 +188,7 @@ describe('compileMongoQuery', () => { ...emptyCollectionState(), orderBy: { age: -1, name: 1 }, }; - const plan = compileMongoQuery('users', state, testHash, testUserModel, {}); + const plan = compileMongoQuery('users', state, testHash, testUserModel, noStorage); expect(stages(plan)).toHaveLength(1); const sort = stages(plan)[0] as MongoSortStage; expect(sort).toBeInstanceOf(MongoSortStage); @@ -186,7 +200,7 @@ describe('compileMongoQuery', () => { ...emptyCollectionState(), limit: 10, }; - const plan = compileMongoQuery('users', state, testHash, testUserModel, {}); + const plan = compileMongoQuery('users', state, testHash, testUserModel, noStorage); expect(stages(plan)).toHaveLength(1); const limit = stages(plan)[0] as MongoLimitStage; expect(limit).toBeInstanceOf(MongoLimitStage); @@ -198,7 +212,7 @@ describe('compileMongoQuery', () => { ...emptyCollectionState(), offset: 5, }; - const plan = compileMongoQuery('users', state, testHash, testUserModel, {}); + const plan = compileMongoQuery('users', state, testHash, testUserModel, noStorage); expect(stages(plan)).toHaveLength(1); const skip = stages(plan)[0] as MongoSkipStage; expect(skip).toBeInstanceOf(MongoSkipStage); @@ -220,7 +234,7 @@ describe('compileMongoQuery', () => { }, ], }; - const plan = compileMongoQuery('posts', state, testHash, testPostModel, {}); + const plan = compileMongoQuery('posts', state, testHash, testPostModel, noStorage); expect(stages(plan)).toHaveLength(2); const lookup = stages(plan)[0] as MongoLookupStage; expect(lookup).toBeInstanceOf(MongoLookupStage); @@ -249,7 +263,7 @@ describe('compileMongoQuery', () => { }, ], }; - const plan = compileMongoQuery('users', state, testHash, testUserModel, {}); + const plan = compileMongoQuery('users', state, testHash, testUserModel, noStorage); expect(stages(plan)).toHaveLength(1); expect(stages(plan)[0]).toBeInstanceOf(MongoLookupStage); }); @@ -273,7 +287,7 @@ describe('compileMongoQuery', () => { limit: 5, selectedFields: ['_id', 'name', 'email'], }; - const plan = compileMongoQuery('users', state, testHash, testUserModel, {}); + const plan = compileMongoQuery('users', state, testHash, testUserModel, noStorage); const stageKinds = stages(plan).map((s) => s.kind); expect(stageKinds).toEqual(['match', 'lookup', 'sort', 'skip', 'limit', 'project']); @@ -281,7 +295,13 @@ describe('compileMongoQuery', () => { describe('resultShape from contract', () => { it('full model maps scalar fields to leaf codec shapes', () => { - const plan = compileMongoQuery('users', emptyCollectionState(), testHash, testUserModel, {}); + const plan = compileMongoQuery( + 'users', + emptyCollectionState(), + testHash, + testUserModel, + noStorage, + ); expect(plan.resultShape?.kind).toBe('document'); if (plan.resultShape?.kind !== 'document') return; expect(plan.resultShape.fields['name']).toEqual({ @@ -293,7 +313,13 @@ describe('compileMongoQuery', () => { }); it('scalar many field is array with leaf element', () => { - const plan = compileMongoQuery('users', emptyCollectionState(), testHash, testUserModel, {}); + const plan = compileMongoQuery( + 'users', + emptyCollectionState(), + testHash, + testUserModel, + noStorage, + ); if (plan.resultShape?.kind !== 'document') throw new Error('expected document'); expect(plan.resultShape.fields['tags']).toEqual({ kind: 'array', @@ -307,7 +333,7 @@ describe('compileMongoQuery', () => { ...emptyCollectionState(), selectedFields: ['name', 'email'], }; - const plan = compileMongoQuery('users', state, testHash, testUserModel, {}); + const plan = compileMongoQuery('users', state, testHash, testUserModel, noStorage); if (plan.resultShape?.kind !== 'document') throw new Error('expected document'); expect(Object.keys(plan.resultShape.fields).sort()).toEqual(['email', 'name']); }); @@ -327,7 +353,7 @@ describe('compileMongoQuery', () => { }, ], }; - const plan = compileMongoQuery('posts', state, testHash, testPostModel, {}); + const plan = compileMongoQuery('posts', state, testHash, testPostModel, noStorage); if (plan.resultShape?.kind !== 'document') throw new Error('expected document'); expect(plan.resultShape.fields['author']).toMatchObject({ kind: 'document', @@ -357,7 +383,7 @@ describe('compileMongoQuery', () => { }, ], }; - const plan = compileMongoQuery('users', state, testHash, testUserModel, {}); + const plan = compileMongoQuery('users', state, testHash, testUserModel, noStorage); const projectStage = stages(plan).find((s) => s.kind === 'project') as | MongoProjectStage | undefined; @@ -381,7 +407,7 @@ describe('compileMongoQuery', () => { }, ], }; - const plan = compileMongoQuery('posts', state, testHash, testPostModel, {}); + const plan = compileMongoQuery('posts', state, testHash, testPostModel, noStorage); expect(stages(plan).map((s) => s.kind)).toEqual(['lookup', 'unwind', 'project']); const projectStage = stages(plan).find((s) => s.kind === 'project') as | MongoProjectStage diff --git a/packages/2-mongo-family/5-query-builders/orm/test/integration/value-objects.test.ts b/packages/2-mongo-family/5-query-builders/orm/test/integration/value-objects.test.ts new file mode 100644 index 000000000000..2559a099ed60 --- /dev/null +++ b/packages/2-mongo-family/5-query-builders/orm/test/integration/value-objects.test.ts @@ -0,0 +1,198 @@ +import mongoRuntimeAdapter from '@internal/adapter-mongo/runtime'; +import { createMongoDriver } from '@internal/driver-mongo'; +import type { + AnyMongoTypeMaps, + MongoContract, + MongoContractWithTypeMaps, +} from '@internal/mongo-contract'; +import { MongoFieldFilter, type MongoFilterExpr } from '@internal/mongo-query-ast/execution'; +import { + buildMongoEnums, + createMongoExecutionContext, + createMongoExecutionStack, + createMongoRuntime, + type MongoRuntime, +} from '@internal/mongo-runtime'; +import mongoRuntimeTarget from '@internal/target-mongo/runtime'; +import { timeouts } from '@repo/test-utils'; +import { MongoClient, ObjectId } from 'mongodb'; +import { MongoMemoryReplSet } from 'mongodb-memory-server'; +import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'vitest'; +import type { MongoOrmEnums } from '../../src/collection'; +import type { FieldExpression, FieldOperation } from '../../src/field-accessor'; +import { mongoOrm } from '../../src/mongo-orm'; + +const objectId = { kind: 'scalar', codecId: 'mongo/objectId@1' } as const; +const text = { kind: 'scalar', codecId: 'mongo/string@1' } as const; + +function valueObjectRef(entityName: string) { + return { + kind: 'valueObject', + plane: 'storage', + namespaceId: '__unbound__', + entityKind: 'valueObject', + entityName, + }; +} + +const contractJson = { + target: 'mongo', + targetFamily: 'mongo', + storageHash: 'value-objects-hash', + roots: { places: { model: 'Place', namespace: '__unbound__' } }, + storage: { + namespaces: { + __unbound__: { + id: '__unbound__', + entries: { + collection: { places: {} }, + valueObject: { + Stop: { + fields: { + label: { type: text, nullable: false, many: false }, + placeId: { type: objectId, nullable: false, many: false }, + }, + }, + Address: { + fields: { + city: { type: text, nullable: false, many: false }, + landmark: { type: valueObjectRef('Stop'), nullable: false, many: false }, + }, + }, + }, + }, + }, + }, + }, + domain: { + namespaces: { + __unbound__: { + models: { + Place: { + storage: { + collection: 'places', + fields: { + _id: { field: '_id' }, + address: { field: 'address' }, + stops: { field: 'stops' }, + }, + }, + fields: { + _id: { type: objectId, nullable: false, many: false }, + address: { type: valueObjectRef('Address'), nullable: false, many: false }, + stops: { + type: valueObjectRef('Stop'), + nullable: false, + many: { elementNullable: false }, + }, + }, + relations: {}, + }, + }, + }, + }, + }, + capabilities: {}, + extensions: {}, + profileHash: 'value-objects-profile', + meta: {}, +}; + +interface Places { + create(row: Record): Promise<{ readonly _id: unknown }>; + where(filter: MongoFilterExpr | Record): { + first(): Promise; + update( + operations: (field: (path: string) => FieldExpression) => FieldOperation[], + ): Promise; + }; +} + +function placesOf(orm: unknown): Places { + return (orm as { readonly places: Places }).places; +} + +const contract = contractJson as unknown as MongoContractWithTypeMaps< + MongoContract, + AnyMongoTypeMaps +>; + +describe('value objects in the Mongo ORM', { timeout: timeouts.spinUpMongoMemoryServer }, () => { + let replSet: MongoMemoryReplSet; + let client: MongoClient; + let runtime: MongoRuntime; + let enums: MongoOrmEnums; + const dbName = 'orm_value_objects_test'; + + beforeAll(async () => { + replSet = await MongoMemoryReplSet.create({ + replSet: { count: 1, storageEngine: 'wiredTiger' }, + }); + client = new MongoClient(replSet.getUri()); + await client.connect(); + const stack = createMongoExecutionStack({ + target: mongoRuntimeTarget, + adapter: mongoRuntimeAdapter, + }); + const context = createMongoExecutionContext({ contract: {}, stack }); + const driver = await createMongoDriver(replSet.getUri(), dbName); + runtime = createMongoRuntime({ context, driver }); + enums = buildMongoEnums(contract, context.codecs); + }, timeouts.spinUpMongoMemoryServer); + + beforeEach(async () => { + await client.db(dbName).dropDatabase(); + }); + + afterAll(async () => { + await Promise.allSettled([runtime?.close(), client?.close(), replSet?.stop()]); + }, timeouts.spinUpMongoMemoryServer); + + it('writes and reads a nested and a list value object through the codecs of their members', async () => { + const places = placesOf(mongoOrm({ contract, executor: runtime, enums })); + const landmarkId = new ObjectId().toHexString(); + const stopId = new ObjectId().toHexString(); + const created = await places.create({ + address: { city: 'Lyon', landmark: { label: 'Fourvière', placeId: landmarkId } }, + stops: [{ label: 'Bellecour', placeId: stopId }], + }); + + const stored = await client.db(dbName).collection('places').findOne({}); + const read = await places.where({ _id: created._id }).first(); + + expect({ stored, read }).toEqual({ + stored: { + _id: expect.any(ObjectId), + address: { + city: 'Lyon', + landmark: { label: 'Fourvière', placeId: new ObjectId(landmarkId) }, + }, + stops: [{ label: 'Bellecour', placeId: new ObjectId(stopId) }], + }, + read: { + _id: created._id, + address: { city: 'Lyon', landmark: { label: 'Fourvière', placeId: landmarkId } }, + stops: [{ label: 'Bellecour', placeId: stopId }], + }, + }); + }); + + it('filters and updates by a dot path into a nested value object', async () => { + const places = placesOf(mongoOrm({ contract, executor: runtime, enums })); + const landmarkId = new ObjectId().toHexString(); + await places.create({ + address: { city: 'Lyon', landmark: { label: 'Fourvière', placeId: landmarkId } }, + stops: [], + }); + + await places + .where(MongoFieldFilter.eq('address.landmark.placeId', landmarkId)) + .update((p) => [p('address.landmark.label').set('Basilique')]); + + const stored = await client.db(dbName).collection('places').findOne({}); + expect(stored?.['address']).toEqual({ + city: 'Lyon', + landmark: { label: 'Basilique', placeId: new ObjectId(landmarkId) }, + }); + }); +}); diff --git a/packages/2-mongo-family/5-query-builders/orm/test/orm-types.test-d.ts b/packages/2-mongo-family/5-query-builders/orm/test/orm-types.test-d.ts index 92927ad69dc2..106ccbb44c88 100644 --- a/packages/2-mongo-family/5-query-builders/orm/test/orm-types.test-d.ts +++ b/packages/2-mongo-family/5-query-builders/orm/test/orm-types.test-d.ts @@ -289,7 +289,6 @@ type ContractWithAssignee = Omit & 'relations' > & { readonly relations: { readonly assignee: AssigneeRelation } }; }; - readonly valueObjects: Contract['domain']['namespaces']['__unbound__']['valueObjects']; }; }; }; diff --git a/packages/2-mongo-family/5-query-builders/orm/test/value-object-inputs.test-d.ts b/packages/2-mongo-family/5-query-builders/orm/test/value-object-inputs.test-d.ts index 9e36b2a2c9df..25bebdc09162 100644 --- a/packages/2-mongo-family/5-query-builders/orm/test/value-object-inputs.test-d.ts +++ b/packages/2-mongo-family/5-query-builders/orm/test/value-object-inputs.test-d.ts @@ -61,7 +61,13 @@ type VOContract = MongoContractWithTypeMaps< readonly contactInfo: { readonly nullable: true; readonly many: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'ContactInfo' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'ContactInfo'; + }; }; readonly tags: { readonly nullable: false; @@ -81,22 +87,6 @@ type VOContract = MongoContractWithTypeMaps< }; }; }; - readonly valueObjects: { - readonly ContactInfo: { - readonly fields: { - readonly phone: { - readonly nullable: false; - readonly many: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly website: { - readonly nullable: true; - readonly many: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - }; }; }; }; @@ -106,7 +96,25 @@ type VOContract = MongoContractWithTypeMaps< readonly __unbound__: { readonly id: '__unbound__'; readonly kind: 'mongo-namespace'; - readonly entries: { readonly collection: { readonly users: MongoCollection } }; + readonly entries: { + readonly collection: { readonly users: MongoCollection }; + readonly valueObject: { + readonly ContactInfo: { + readonly fields: { + readonly phone: { + readonly nullable: false; + readonly many: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly website: { + readonly nullable: true; + readonly many: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + }; + }; }; }; }; @@ -232,7 +240,13 @@ type VOContractWithFieldTypes = MongoContractWithTypeMaps< readonly contactInfo: { readonly nullable: true; readonly many: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'ContactInfo' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'ContactInfo'; + }; }; readonly tags: { readonly nullable: false; @@ -252,22 +266,6 @@ type VOContractWithFieldTypes = MongoContractWithTypeMaps< }; }; }; - readonly valueObjects: { - readonly ContactInfo: { - readonly fields: { - readonly phone: { - readonly nullable: false; - readonly many: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly website: { - readonly nullable: true; - readonly many: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - }; }; }; }; @@ -277,7 +275,25 @@ type VOContractWithFieldTypes = MongoContractWithTypeMaps< readonly __unbound__: { readonly id: '__unbound__'; readonly kind: 'mongo-namespace'; - readonly entries: { readonly collection: { readonly users: MongoCollection } }; + readonly entries: { + readonly collection: { readonly users: MongoCollection }; + readonly valueObject: { + readonly ContactInfo: { + readonly fields: { + readonly phone: { + readonly nullable: false; + readonly many: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly website: { + readonly nullable: true; + readonly many: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + }; + }; }; }; }; diff --git a/packages/2-mongo-family/5-query-builders/orm/test/where-equality.test.ts b/packages/2-mongo-family/5-query-builders/orm/test/where-equality.test.ts index 245116a929f4..58ef00b80559 100644 --- a/packages/2-mongo-family/5-query-builders/orm/test/where-equality.test.ts +++ b/packages/2-mongo-family/5-query-builders/orm/test/where-equality.test.ts @@ -8,7 +8,13 @@ const stringType = { kind: 'scalar', codecId: 'mongo/string@1' } as const; const entryList: ContractField = { nullable: true, - type: { kind: 'valueObject', name: 'Entry' }, + type: { + kind: 'valueObject', + plane: 'storage', + namespaceId: '__unbound__', + entityKind: 'valueObject', + entityName: 'Entry', + }, many: { elementNullable: true }, }; diff --git a/packages/2-mongo-family/5-query-builders/orm/test/where-list-fields.test-d.ts b/packages/2-mongo-family/5-query-builders/orm/test/where-list-fields.test-d.ts index f1a1bf599261..050cfb47c9f1 100644 --- a/packages/2-mongo-family/5-query-builders/orm/test/where-list-fields.test-d.ts +++ b/packages/2-mongo-family/5-query-builders/orm/test/where-list-fields.test-d.ts @@ -69,18 +69,19 @@ type ListsContract = MongoContractWithTypeMaps< readonly entries: { readonly nullable: false; readonly many: { readonly elementNullable: false }; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Entry' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Entry'; + }; }; }; readonly relations: Record; readonly storage: { readonly collection: 'lists' }; }; }; - readonly valueObjects: { - readonly Entry: { - readonly fields: { readonly value: ScalarField }; - }; - }; }; }; }; @@ -90,7 +91,14 @@ type ListsContract = MongoContractWithTypeMaps< readonly __unbound__: { readonly id: '__unbound__'; readonly kind: 'mongo-namespace'; - readonly entries: { readonly collection: { readonly lists: MongoStorageCollection } }; + readonly entries: { + readonly collection: { readonly lists: MongoStorageCollection }; + readonly valueObject: { + readonly Entry: { + readonly fields: { readonly value: ScalarField }; + }; + }; + }; }; }; }; diff --git a/packages/2-mongo-family/5-query-builders/query-builder/src/builder.ts b/packages/2-mongo-family/5-query-builders/query-builder/src/builder.ts index 40c0b9fa6369..7e8764451256 100644 --- a/packages/2-mongo-family/5-query-builders/query-builder/src/builder.ts +++ b/packages/2-mongo-family/5-query-builders/query-builder/src/builder.ts @@ -1,4 +1,4 @@ -import { domainValueObjectsAtDefaultNamespace, type PlanMeta } from '@internal/contract/types'; +import type { PlanMeta } from '@internal/contract/types'; import { type AnyMongoTypeMaps, defaultMongoDomainNamespaceId, @@ -832,9 +832,7 @@ export class PipelineChain< resultShape = model ? computePipelineResultShape( this.#state.stages, - contractModelToMongoResultShape(model, { - valueObjects: domainValueObjectsAtDefaultNamespace(contractNarrow.domain) ?? {}, - }), + contractModelToMongoResultShape(model, { storage: contractNarrow.storage }), ) : { kind: 'unknown' as const }; } diff --git a/packages/2-mongo-family/5-query-builders/query-builder/src/exports/index.ts b/packages/2-mongo-family/5-query-builders/query-builder/src/exports/index.ts index ac02cf05bcdd..3188bc38d6c8 100644 --- a/packages/2-mongo-family/5-query-builders/query-builder/src/exports/index.ts +++ b/packages/2-mongo-family/5-query-builders/query-builder/src/exports/index.ts @@ -37,7 +37,6 @@ export type { export { contractFieldToMongoFieldShape, contractModelToMongoResultShape, - type MongoValueObjects, } from '../result-shape'; export { CollectionHandle, FilteredCollection } from '../state-classes'; export type { diff --git a/packages/2-mongo-family/5-query-builders/query-builder/src/resolve-path.ts b/packages/2-mongo-family/5-query-builders/query-builder/src/resolve-path.ts index 36261796f0b1..018164b74630 100644 --- a/packages/2-mongo-family/5-query-builders/query-builder/src/resolve-path.ts +++ b/packages/2-mongo-family/5-query-builders/query-builder/src/resolve-path.ts @@ -1,4 +1,4 @@ -import type { ContractValueObjectDefinitions } from '@internal/contract/types'; +import type { ContractValueObjectAt, ValueObjectFieldType } from '@internal/contract/types'; import type { MongoContract, MongoModelsMap, OwnStoredNameOf } from '@internal/mongo-contract'; import type { DocField } from './types'; @@ -48,7 +48,7 @@ export interface ModelArrayField extends DocField { * Phantom-symbol brand placed on `ModelToDocShape`'s output (and inherited * through shape-extending stages like `addFields`, `match`, `lookup`) so * `ResolveRow` can route value-object resolution through `InferModelRow` - * — which walks the contract's `valueObjects` registry and resolves + * — which reads the storage value objects fields refer to and resolves * nested types — instead of falling through the codec-lookup branch to * `unknown`. * @@ -127,13 +127,10 @@ type TranslateField = F extends { } ? FieldToLeaf : F extends { - readonly type: { - readonly kind: 'valueObject'; - readonly name: infer VOName extends string; - }; + readonly type: infer Reference extends ValueObjectFieldType; readonly nullable: infer Null extends boolean; } - ? ObjectField> & { readonly nullable: Null } + ? ObjectField> & { readonly nullable: Null } : F extends { readonly type: { readonly kind: 'scalar'; readonly codecId: string }; } @@ -141,29 +138,24 @@ type TranslateField = F extends { : DocField; /** - * Resolve a named value object from the contract into its own + * Resolve the storage value object a field's reference names into its own * `NestedDocShape`. The mapped iteration is inlined here (not delegated - * to a generic helper) so that the homomorphism over - * `VOs[VOName]['fields']` is preserved and the hover / indexed-access - * surface stays concrete at instantiation time. + * to a generic helper) so that the homomorphism over the value object's + * `fields` is preserved and the hover / indexed-access surface stays + * concrete at instantiation time. */ -type VONestedShape = [ - ContractValueObjectDefinitions, -] extends [infer VOs extends Record }>] - ? VOName extends keyof VOs & string - ? { - readonly [K in keyof VOs[VOName]['fields'] & string]: TranslateField< - TContract, - VOs[VOName]['fields'][K] - >; - } - : never +type VONestedShape = [ + ContractValueObjectAt, +] extends [infer VO extends { readonly fields: Record }] + ? { + readonly [K in keyof VO['fields'] & string]: TranslateField; + } : never; /** * Build the `NestedDocShape` for a model, keyed by stored name. Scalar leaves resolve to their - * concrete codec id; value-object fields recurse into the referenced - * `valueObjects[VOName].fields` table, producing a tree that + * concrete codec id; value-object fields recurse into the fields of the + * storage value object their reference names, producing a tree that * `ResolvePath` / `ValidPaths` can walk. * * The mapped iteration is inlined (not hidden behind a helper type that diff --git a/packages/2-mongo-family/5-query-builders/query-builder/src/result-shape.ts b/packages/2-mongo-family/5-query-builders/query-builder/src/result-shape.ts index ea7fc6a80e6a..7b610f4a0fb5 100644 --- a/packages/2-mongo-family/5-query-builders/query-builder/src/result-shape.ts +++ b/packages/2-mongo-family/5-query-builders/query-builder/src/result-shape.ts @@ -1,52 +1,60 @@ -import type { ContractField, ContractModel, ContractValueObject } from '@internal/contract/types'; +import { + type ContractField, + type ContractModel, + type ContractValueObject, + type StorageBase, + valueObjectAt, +} from '@internal/contract/types'; import { type MongoModelStorage, ownStoredNamesOf } from '@internal/mongo-contract'; import type { MongoFieldShape, MongoResultShape } from '@internal/mongo-query-ast/execution'; import { freezeMongoFieldShape, freezeMongoResultShape } from '@internal/mongo-query-ast/execution'; -export type MongoValueObjects = Readonly>; +type StorageNamespaces = Pick; const UNKNOWN: MongoFieldShape = Object.freeze({ kind: 'unknown' as const }); +const NO_STORAGE: StorageNamespaces = { namespaces: {} }; + function valueObjectFields( valueObject: ContractValueObject, - valueObjects: MongoValueObjects, - enclosing: ReadonlySet, + storage: StorageNamespaces, + enclosing: ReadonlySet, ): Record { return Object.fromEntries( Object.entries(valueObject.fields).map(([name, field]) => [ name, - fieldShape(field, valueObjects, enclosing), + fieldShape(field, storage, enclosing), ]), ); } function elementShape( field: ContractField, - valueObjects: MongoValueObjects, - enclosing: ReadonlySet, + storage: StorageNamespaces, + enclosing: ReadonlySet, ): MongoFieldShape { const { type } = field; const nullable = !!field.many && field.many.elementNullable; if (type.kind === 'scalar') { return { kind: 'leaf', codecId: type.codecId, nullable }; } - if (type.kind !== 'valueObject' || enclosing.has(type.name)) return UNKNOWN; - const valueObject = valueObjects[type.name]; - if (valueObject === undefined) return UNKNOWN; + if (type.kind !== 'valueObject') return UNKNOWN; + const valueObject = valueObjectAt(storage, type); + if (valueObject === undefined || enclosing.has(valueObject)) return UNKNOWN; return { kind: 'document', nullable, - fields: valueObjectFields(valueObject, valueObjects, new Set([...enclosing, type.name])), + fields: valueObjectFields(valueObject, storage, new Set([...enclosing, valueObject])), }; } function fieldShape( field: ContractField, - valueObjects: MongoValueObjects, - enclosing: ReadonlySet, + storage: StorageNamespaces, + enclosing: ReadonlySet, ): MongoFieldShape { if (field.dict === true) return UNKNOWN; - const element = elementShape(field, valueObjects, enclosing); + const element = elementShape(field, storage, enclosing); if (element.kind === 'unknown') return UNKNOWN; if (field.many) { return { kind: 'array', nullable: field.nullable, element }; @@ -55,13 +63,13 @@ function fieldShape( } /** - * The shape the runtime decodes one field against. A value-object field becomes a document of its own fields when `valueObjects` holds its definition; a value object nested inside itself is left undecoded. + * The shape the runtime decodes one field against. A value-object field becomes a document of its own fields when `storage` holds the value object its reference names; a value object nested inside itself is left undecoded. */ export function contractFieldToMongoFieldShape( field: ContractField, - valueObjects: MongoValueObjects = {}, + storage: StorageNamespaces = NO_STORAGE, ): MongoFieldShape { - return freezeMongoFieldShape(fieldShape(field, valueObjects, new Set())); + return freezeMongoFieldShape(fieldShape(field, storage, new Set())); } /** @@ -73,7 +81,7 @@ export function contractModelToMongoResultShape( readonly selection?: readonly string[]; /** The shape of each included relation, keyed by relation name. */ readonly includes?: Readonly>; - readonly valueObjects?: MongoValueObjects; + readonly storage?: StorageNamespaces; }, ): MongoResultShape { const fields: Record = { ...options?.includes }; @@ -96,7 +104,7 @@ export function contractModelToMongoResultShape( fields[key] = UNKNOWN; continue; } - fields[key] = contractFieldToMongoFieldShape(cf, options?.valueObjects); + fields[key] = contractFieldToMongoFieldShape(cf, options?.storage); } return freezeMongoResultShape({ kind: 'document', fields }); } diff --git a/packages/2-mongo-family/5-query-builders/query-builder/test/fixtures/test-contract.ts b/packages/2-mongo-family/5-query-builders/query-builder/test/fixtures/test-contract.ts index d6a946de1646..f6e607e57a97 100644 --- a/packages/2-mongo-family/5-query-builders/query-builder/test/fixtures/test-contract.ts +++ b/packages/2-mongo-family/5-query-builders/query-builder/test/fixtures/test-contract.ts @@ -11,6 +11,24 @@ import type { } from '@internal/mongo-contract'; import { blindCast } from '@internal/utils/casts'; +type ValueObjectRef = { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: Name; +}; + +function valueObjectRef(entityName: Name): ValueObjectRef { + return { + kind: 'valueObject', + plane: 'storage', + namespaceId: '__unbound__', + entityKind: 'valueObject', + entityName, + }; +} + type TestModels = { readonly Order: { readonly fields: { @@ -111,17 +129,17 @@ type TestModels = { readonly many: false; }; readonly address: { - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: ValueObjectRef<'Address'>; readonly nullable: false; readonly many: false; }; readonly workAddress: { - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: ValueObjectRef<'Address'>; readonly nullable: true; readonly many: false; }; readonly stats: { - readonly type: { readonly kind: 'valueObject'; readonly name: 'Stats' }; + readonly type: ValueObjectRef<'Stats'>; readonly nullable: false; readonly many: false; }; @@ -151,7 +169,7 @@ type TestModels = { readonly many: false; }; readonly home: { - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: ValueObjectRef<'Address'>; readonly nullable: true; readonly many: false; }; @@ -187,7 +205,7 @@ type TestValueObjects = { readonly many: false; }; readonly geo: { - readonly type: { readonly kind: 'valueObject'; readonly name: 'GeoPoint' }; + readonly type: ValueObjectRef<'GeoPoint'>; readonly nullable: false; readonly many: false; }; @@ -236,6 +254,7 @@ type TestStorage = { readonly customers: { readonly kind: 'mongo-collection' }; readonly accounts: { readonly kind: 'mongo-collection' }; }; + readonly valueObject: TestValueObjects; }; }; }; @@ -254,7 +273,6 @@ export type TestContract = Omit, 'domain'> & { readonly namespaces: { readonly __unbound__: { readonly models: TestModels; - readonly valueObjects: TestValueObjects; }; }; }; @@ -431,16 +449,16 @@ export const testContractJson = { many: false, }, address: { - type: { kind: 'valueObject', name: 'Address' }, + type: valueObjectRef('Address'), nullable: false, many: false, }, workAddress: { - type: { kind: 'valueObject', name: 'Address' }, + type: valueObjectRef('Address'), nullable: true, many: false, }, - stats: { type: { kind: 'valueObject', name: 'Stats' }, nullable: false, many: false }, + stats: { type: valueObjectRef('Stats'), nullable: false, many: false }, }, relations: {}, storage: { @@ -466,7 +484,7 @@ export const testContractJson = { nullable: false, many: false, }, - home: { type: { kind: 'valueObject', name: 'Address' }, nullable: true, many: false }, + home: { type: valueObjectRef('Address'), nullable: true, many: false }, }, relations: {}, storage: { @@ -479,60 +497,6 @@ export const testContractJson = { }, }, }, - valueObjects: { - Address: { - fields: { - street: { - type: { kind: 'scalar', codecId: 'mongo/string@1' }, - nullable: false, - many: false, - }, - city: { - type: { kind: 'scalar', codecId: 'mongo/string@1' }, - nullable: false, - many: false, - }, - zip: { - type: { kind: 'scalar', codecId: 'mongo/string@1' }, - nullable: true, - many: false, - }, - geo: { - type: { kind: 'valueObject', name: 'GeoPoint' }, - nullable: false, - many: false, - }, - }, - }, - GeoPoint: { - fields: { - lat: { - type: { kind: 'scalar', codecId: 'mongo/double@1' }, - nullable: false, - many: false, - }, - lng: { - type: { kind: 'scalar', codecId: 'mongo/double@1' }, - nullable: false, - many: false, - }, - }, - }, - Stats: { - fields: { - visits: { - type: { kind: 'scalar', codecId: 'mongo/double@1' }, - nullable: false, - many: false, - }, - lastSeen: { - type: { kind: 'scalar', codecId: 'mongo/date@1' }, - nullable: true, - many: false, - }, - }, - }, - }, }, }, }, @@ -548,6 +512,62 @@ export const testContractJson = { customers: { kind: 'mongo-collection' }, accounts: { kind: 'mongo-collection' }, }, + entries: { + valueObject: { + Address: { + fields: { + street: { + type: { kind: 'scalar', codecId: 'mongo/string@1' }, + nullable: false, + many: false, + }, + city: { + type: { kind: 'scalar', codecId: 'mongo/string@1' }, + nullable: false, + many: false, + }, + zip: { + type: { kind: 'scalar', codecId: 'mongo/string@1' }, + nullable: true, + many: false, + }, + geo: { + type: valueObjectRef('GeoPoint'), + nullable: false, + many: false, + }, + }, + }, + GeoPoint: { + fields: { + lat: { + type: { kind: 'scalar', codecId: 'mongo/double@1' }, + nullable: false, + many: false, + }, + lng: { + type: { kind: 'scalar', codecId: 'mongo/double@1' }, + nullable: false, + many: false, + }, + }, + }, + Stats: { + fields: { + visits: { + type: { kind: 'scalar', codecId: 'mongo/double@1' }, + nullable: false, + many: false, + }, + lastSeen: { + type: { kind: 'scalar', codecId: 'mongo/date@1' }, + nullable: true, + many: false, + }, + }, + }, + }, + }, }, }, }, diff --git a/packages/2-mongo-family/5-query-builders/query-builder/test/result-shape.test.ts b/packages/2-mongo-family/5-query-builders/query-builder/test/result-shape.test.ts index e6bb51c324e6..0f9d145340f9 100644 --- a/packages/2-mongo-family/5-query-builders/query-builder/test/result-shape.test.ts +++ b/packages/2-mongo-family/5-query-builders/query-builder/test/result-shape.test.ts @@ -1,3 +1,4 @@ +import type { ContractValueObject } from '@internal/contract/types'; import { MongoFieldFilter, MongoProjectStage } from '@internal/mongo-query-ast/execution'; import { describe, expect, it } from 'vitest'; import { mongoQuery } from '../src/query'; @@ -7,6 +8,25 @@ import { } from '../src/result-shape'; import { testContract } from './fixtures/test-contract'; +function valueObjectRef(entityName: string, namespaceId = '__unbound__') { + return { + kind: 'valueObject', + plane: 'storage', + namespaceId, + entityKind: 'valueObject', + entityName, + } as const; +} + +function storageWith( + valueObjects: Readonly>, + namespaceId = '__unbound__', +) { + return { + namespaces: { [namespaceId]: { id: namespaceId, entries: { valueObject: valueObjects } } }, + }; +} + describe('contractModelToMongoResultShape', () => { it('treats omitted cardinality as scalar', () => { expect( @@ -34,10 +54,10 @@ describe('contractModelToMongoResultShape', () => { }); it('maps a value-object field to a document of its own fields, nested value objects too', () => { - const namespace = testContract.domain.namespaces.__unbound__!; - const shape = contractModelToMongoResultShape(namespace.models['Customer'], { - valueObjects: namespace.valueObjects, - }); + const shape = contractModelToMongoResultShape( + testContract.domain.namespaces.__unbound__!.models['Customer'], + { storage: testContract.storage }, + ); if (shape.kind !== 'document') throw new Error('expected document'); const address = { street: { kind: 'leaf', codecId: 'mongo/string@1', nullable: false }, @@ -60,12 +80,12 @@ describe('contractModelToMongoResultShape', () => { it.each([false, true])('maps value-object lists with elementNullable=%s', (elementNullable) => { const shape = contractFieldToMongoFieldShape( - { type: { kind: 'valueObject', name: 'Point' }, nullable: false, many: { elementNullable } }, - { + { type: valueObjectRef('Point'), nullable: false, many: { elementNullable } }, + storageWith({ Point: { fields: { x: { type: { kind: 'scalar', codecId: 'mongo/double@1' }, nullable: false } }, }, - }, + }), ); expect(shape).toEqual({ kind: 'array', @@ -80,15 +100,15 @@ describe('contractModelToMongoResultShape', () => { it('stops at a value object that contains itself', () => { const shape = contractFieldToMongoFieldShape( - { type: { kind: 'valueObject', name: 'Node' }, nullable: false }, - { + { type: valueObjectRef('Node'), nullable: false }, + storageWith({ Node: { fields: { label: { type: { kind: 'scalar', codecId: 'mongo/string@1' }, nullable: false }, - child: { type: { kind: 'valueObject', name: 'Node' }, nullable: true }, + child: { type: valueObjectRef('Node'), nullable: true }, }, }, - }, + }), ); expect(shape).toEqual({ kind: 'document', @@ -100,7 +120,41 @@ describe('contractModelToMongoResultShape', () => { }); }); - it('maps a value-object field to unknown when no value objects are given', () => { + it('finds a value object in the storage namespace its reference names', () => { + const shape = contractFieldToMongoFieldShape( + { type: valueObjectRef('Point', 'geo'), nullable: false }, + { + namespaces: { + ...storageWith({ Point: { fields: {} } }).namespaces, + ...storageWith( + { + Point: { + fields: { + x: { type: { kind: 'scalar', codecId: 'mongo/double@1' }, nullable: false }, + }, + }, + }, + 'geo', + ).namespaces, + }, + }, + ); + expect(shape).toEqual({ + kind: 'document', + nullable: false, + fields: { x: { kind: 'leaf', codecId: 'mongo/double@1', nullable: false } }, + }); + }); + + it('maps a value-object field to unknown when storage has no such value object', () => { + const shape = contractFieldToMongoFieldShape( + { type: valueObjectRef('Missing'), nullable: false }, + storageWith({}), + ); + expect(shape).toEqual({ kind: 'unknown' }); + }); + + it('maps a value-object field to unknown when no storage is given', () => { const model = testContract.domain.namespaces.__unbound__!.models['Customer']; const shape = contractModelToMongoResultShape(model); if (shape.kind !== 'document') throw new Error('expected document'); @@ -201,6 +255,19 @@ describe('PipelineChain build resultShape', () => { expect(plan.resultShape?.kind).toBe('document'); }); + it('decodes a value-object field through the value object storage holds', () => { + const plan = mongoQuery({ contract: testContract }).from('customers').build(); + if (plan.resultShape?.kind !== 'document') throw new Error('expected document'); + expect(plan.resultShape.fields['stats']).toEqual({ + kind: 'document', + nullable: false, + fields: { + visits: { kind: 'leaf', codecId: 'mongo/double@1', nullable: false }, + lastSeen: { kind: 'leaf', codecId: 'mongo/date@1', nullable: true }, + }, + }); + }); + it('project stage reifies a document shape retaining _id and the projected field', () => { const plan = mongoQuery({ contract: testContract }) .from('orders') diff --git a/test/integration/test/value-objects/value-objects.integration.test.ts b/test/integration/test/value-objects/value-objects.integration.test.ts index 0c67b3416d0e..37345f472c9e 100644 --- a/test/integration/test/value-objects/value-objects.integration.test.ts +++ b/test/integration/test/value-objects/value-objects.integration.test.ts @@ -3,7 +3,7 @@ import { type ContractField, type ContractValueObject, domainModelsAtDefaultNamespace, - domainValueObjectsAtDefaultNamespace, + storageValueObjectsAtDefaultNamespace, UNBOUND_DOMAIN_NAMESPACE_ID, } from '@internal/contract/types'; import { MongoContractSerializer } from '@internal/family-mongo/ir'; @@ -196,7 +196,21 @@ function modelsAtDefaultNamespace(contract: Contract) { } function valueObjectsAtDefaultNamespace(contract: Contract) { - return domainValueObjectsAtDefaultNamespace(contract.domain); + return storageValueObjectsAtDefaultNamespace(contract.storage); +} + +function asJson(contract: Contract): unknown { + return JSON.parse(JSON.stringify(contract)); +} + +function valueObjectRef(namespaceId: string, entityName: string) { + return { + kind: 'valueObject', + plane: 'storage', + namespaceId, + entityKind: 'valueObject', + entityName, + }; } describeWithMongoDB('value objects: end-to-end Mongo', (ctx) => { @@ -207,13 +221,14 @@ describeWithMongoDB('value objects: end-to-end Mongo', (ctx) => { const contract = result.value; const unboundDomain = contract.domain.namespaces[UNBOUND_DOMAIN_NAMESPACE_ID]!; - expect(unboundDomain.valueObjects).toBeDefined(); - expect(unboundDomain.valueObjects!['Address']).toBeDefined(); + expect(valueObjectsAtDefaultNamespace(contract)['Address']).toBeDefined(); const userFields = unboundDomain.models['User']!.fields as Record; expect(userFields['homeAddress']!.type.kind).toBe('valueObject'); - const validated = { contract: new MongoContractSerializer().deserializeContract(contract) }; + const validated = { + contract: new MongoContractSerializer().deserializeContract(asJson(contract)), + }; const orm = mongoOrm({ contract: validated.contract, @@ -249,7 +264,9 @@ describeWithMongoDB('value objects: end-to-end Mongo', (ctx) => { const result = interpretMongoPsl(mongoPsl); if (!result.ok) throw new Error(`Interpretation failed: ${result.failure.summary}`); - const validated = { contract: new MongoContractSerializer().deserializeContract(result.value) }; + const validated = { + contract: new MongoContractSerializer().deserializeContract(asJson(result.value)), + }; const orm = mongoOrm({ contract: validated.contract, executor: ctx.runtime, @@ -292,7 +309,9 @@ type Address { const result = interpretMongoPsl(schema); if (!result.ok) throw new Error(`Interpretation failed: ${result.failure.summary}`); - const validated = { contract: new MongoContractSerializer().deserializeContract(result.value) }; + const validated = { + contract: new MongoContractSerializer().deserializeContract(asJson(result.value)), + }; const orm = mongoOrm({ contract: validated.contract, executor: ctx.runtime, @@ -317,10 +336,8 @@ describe('value objects: end-to-end SQL pipeline', () => { const contract = result.value; const publicDomain = contract.domain.namespaces['public']!; - expect(publicDomain.valueObjects).toBeDefined(); - expect(publicDomain.valueObjects!['Address']).toBeDefined(); - - const addressVo = publicDomain.valueObjects!['Address'] as ContractValueObject; + const addressVo = valueObjectsAtDefaultNamespace(contract)['Address'] as ContractValueObject; + expect(addressVo).toBeDefined(); const voFields = addressVo.fields; expect(voFields['street']).toBeDefined(); expect(voFields['city']).toBeDefined(); @@ -328,7 +345,7 @@ describe('value objects: end-to-end SQL pipeline', () => { const userFields = publicDomain.models['User']!.fields as Record; const homeAddressField = userFields['homeAddress']!; - expect(homeAddressField.type).toEqual({ kind: 'valueObject', name: 'Address' }); + expect(homeAddressField.type).toEqual(valueObjectRef('public', 'Address')); expect(homeAddressField.nullable).toBe(false); const storage = contract.storage as unknown as { @@ -398,8 +415,8 @@ type Metadata { ContractField >; - expect(mongoItemFields['meta']!.type).toEqual({ kind: 'valueObject', name: 'Metadata' }); - expect(sqlItemFields['meta']!.type).toEqual({ kind: 'valueObject', name: 'Metadata' }); + expect(mongoItemFields['meta']!.type).toEqual(valueObjectRef('__unbound__', 'Metadata')); + expect(sqlItemFields['meta']!.type).toEqual(valueObjectRef('public', 'Metadata')); }); it('nested value objects produce consistent definitions across families', () => { @@ -449,7 +466,7 @@ type Address { const sqlShipping = valueObjectsAtDefaultNamespace(sqlResult.value)!['ShippingInfo']! .fields as Record; - expect(mongoShipping['address']!.type).toEqual({ kind: 'valueObject', name: 'Address' }); - expect(sqlShipping['address']!.type).toEqual({ kind: 'valueObject', name: 'Address' }); + expect(mongoShipping['address']!.type).toEqual(valueObjectRef('__unbound__', 'Address')); + expect(sqlShipping['address']!.type).toEqual(valueObjectRef('public', 'Address')); }); }); From 7d40131d5a5cffd2262494ca55e5dec936a18d27 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 13:15:02 +0200 Subject: [PATCH 029/104] TML-3537: contract.d.ts has no blank line between meta and profileHash Signed-off-by: willbot Signed-off-by: Will Madden --- .../3-tooling/emitter/src/generate-contract-dts.ts | 1 - .../test/generate-contract-dts.spi-branches.test.ts | 7 +++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/1-framework/3-tooling/emitter/src/generate-contract-dts.ts b/packages/1-framework/3-tooling/emitter/src/generate-contract-dts.ts index 047f97d0e6d9..1b6a063f7086 100644 --- a/packages/1-framework/3-tooling/emitter/src/generate-contract-dts.ts +++ b/packages/1-framework/3-tooling/emitter/src/generate-contract-dts.ts @@ -204,7 +204,6 @@ ${domainNamespacesType}; readonly capabilities: ${serializeValue(contract.capabilities)}; readonly extensions: ${serializeValue(contract.extensions)};${executionClause} readonly meta: ${serializeValue(contract.meta)}; - readonly profileHash: ProfileHash; }; diff --git a/packages/1-framework/3-tooling/emitter/test/generate-contract-dts.spi-branches.test.ts b/packages/1-framework/3-tooling/emitter/test/generate-contract-dts.spi-branches.test.ts index 1314e34533a7..11774a5e7d56 100644 --- a/packages/1-framework/3-tooling/emitter/test/generate-contract-dts.spi-branches.test.ts +++ b/packages/1-framework/3-tooling/emitter/test/generate-contract-dts.spi-branches.test.ts @@ -60,6 +60,13 @@ function makeEnumContract(opts: { return createTestContract({ namespaces: { public: publicNs } }); } +describe('generateContractDts contract type', () => { + it('writes profileHash on the line after meta, with no blank line between them', () => { + const dts = generateContractDts(createTestContract(), createMockSpi(), [], HASHES); + expect(dts).toMatch(/readonly meta: [^\n]*;\n {2}readonly profileHash: ProfileHash;/); + }); +}); + describe('generateContractDts SPI hook plumbing', () => { it('omits extra storage exports when the SPI has no getStorageTypeExports hook', () => { const spi: EmissionSpi = createMockSpi(); From 4b69faf4ca06770ec89243a9df39121272d776d1 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 13:19:16 +0200 Subject: [PATCH 030/104] TML-3537: rename statements find value objects in storage The statement resolver still refuses a statement that names a value object, but looks for it under storage entries.valueObject. A statement contract now carries its storage namespaces. Signed-off-by: willbot Signed-off-by: Will Madden --- .../cli/src/control-api/operations/db-run.ts | 2 +- .../control-api/operations/migration-plan.ts | 13 ++-- .../control-api/statements/plan-questions.ts | 21 +++--- .../statements/resolve-statements.ts | 33 +++++++--- ...esolve-convert-backfill-statements.test.ts | 20 ++++++ .../statements/statement-fixtures.ts | 65 ++++++++++++++----- .../cli/test/orm/db-update-consent.test.ts | 2 + 7 files changed, 115 insertions(+), 41 deletions(-) diff --git a/packages/1-framework/3-tooling/cli/src/control-api/operations/db-run.ts b/packages/1-framework/3-tooling/cli/src/control-api/operations/db-run.ts index 7407fb2ee19d..eeb985355fe7 100644 --- a/packages/1-framework/3-tooling/cli/src/control-api/operations/db-run.ts +++ b/packages/1-framework/3-tooling/cli/src/control-api/operations/db-run.ts @@ -521,7 +521,7 @@ export async function executeRun>; @@ -313,9 +316,9 @@ async function answerPlannedDataLoss(input: { readonly delta: PlannerSuccess | undefined; readonly statements: readonly StatementText[]; readonly consents: readonly StatementText[]; - readonly origin: ContractWithDomain; + readonly origin: StatementContract; readonly originKnown: boolean; - readonly destination: ContractWithDomain; + readonly destination: StatementContract; readonly answer: AnswerPlanQuestions; readonly planDelta: ( statements: readonly StatementText[], diff --git a/packages/1-framework/3-tooling/cli/src/control-api/statements/plan-questions.ts b/packages/1-framework/3-tooling/cli/src/control-api/statements/plan-questions.ts index 390afe53277d..d0b7edfcf36c 100644 --- a/packages/1-framework/3-tooling/cli/src/control-api/statements/plan-questions.ts +++ b/packages/1-framework/3-tooling/cli/src/control-api/statements/plan-questions.ts @@ -1,4 +1,3 @@ -import type { ContractWithDomain } from '@internal/contract/types'; import type { CliStructuredError } from '@internal/errors/control'; import { type FieldCoordinate, @@ -21,7 +20,7 @@ import { type UnusedConsent, } from '../../utils/cli-errors'; import type { ConsentVerb } from './parse-consent'; -import { resolveStatements } from './resolve-statements'; +import { resolveStatements, type StatementContract } from './resolve-statements'; import { statementFlag } from './statement-flag'; import type { StatementText } from './statement-text'; @@ -99,9 +98,9 @@ export interface PlannedQuestions { /** The contracts a statement names things in, and the statements the plan carries out so far. */ export interface StatementContracts { - readonly origin: ContractWithDomain; + readonly origin: StatementContract; /** The destination contract; needed only to name a field of a renamed model. */ - readonly destination?: ContractWithDomain; + readonly destination?: StatementContract; /** The rename, convert and backfill statements the plan carries out, in the order given. */ readonly statements: readonly StatementText[]; } @@ -285,7 +284,7 @@ function renamedByPlan(subject: MigrationSubject, contracts: StatementContracts) /** Whether the destination still has the subject's model or field, under the name a rename gives it. */ function inDestination( subject: MigrationSubject, - contracts: StatementContracts & { readonly destination: ContractWithDomain }, + contracts: StatementContracts & { readonly destination: StatementContract }, ): boolean { if (subject.kind === 'storage') return false; const model = renamedModel(subject, contracts) ?? subject; @@ -298,7 +297,7 @@ function inDestination( /** Checks a statement typed at a question against the plan's other statements. */ function resolvesWith( statement: StatementText, - contracts: StatementContracts & { readonly destination: ContractWithDomain }, + contracts: StatementContracts & { readonly destination: StatementContract }, ): Result { const resolved = resolveStatements({ statements: [...contracts.statements, statement], @@ -311,7 +310,7 @@ function resolvesWith( } type DataLossContracts = StatementContracts & { - readonly destination: ContractWithDomain; + readonly destination: StatementContract; readonly originKnown: boolean; /** Whether the command writes a migration whose conversion the user fills in. */ readonly canConvert: boolean; @@ -324,7 +323,7 @@ type DataLossContracts = StatementContracts & { */ function missingFieldModel( subject: MigrationSubject, - contracts: StatementContracts & { readonly destination: ContractWithDomain }, + contracts: StatementContracts & { readonly destination: StatementContract }, ): ModelCoordinate | undefined { if (subject.kind !== 'field') return undefined; const model = renamedModel(subject, contracts) ?? subject; @@ -544,7 +543,7 @@ function withNamespaceDeleteOnFirstQuestion( function acceptingNamespaceAnswers( question: PlanQuestion, removedNamespace: string | undefined, - contracts: StatementContracts & { readonly destination: ContractWithDomain }, + contracts: StatementContracts & { readonly destination: StatementContract }, ): PlanQuestion { if (removedNamespace === undefined) return question; const namespaceAnswer = (verb: PlanQuestionVerb, text: string): NamespaceAnswer | undefined => { @@ -599,9 +598,9 @@ export async function askPlanQuestions readonly statements: readonly StatementText[]; readonly preAnswers: readonly StatementText[]; readonly consentAll: { readonly [verb in ConsentVerb]: boolean }; - readonly origin: ContractWithDomain; + readonly origin: StatementContract; readonly originKnown: boolean; - readonly destination: ContractWithDomain; + readonly destination: StatementContract; readonly answer: AnswerPlanQuestions; readonly replan: (statements: readonly StatementText[]) => Promise>; }): Promise< diff --git a/packages/1-framework/3-tooling/cli/src/control-api/statements/resolve-statements.ts b/packages/1-framework/3-tooling/cli/src/control-api/statements/resolve-statements.ts index 4f4393e32250..a3631bcad920 100644 --- a/packages/1-framework/3-tooling/cli/src/control-api/statements/resolve-statements.ts +++ b/packages/1-framework/3-tooling/cli/src/control-api/statements/resolve-statements.ts @@ -6,6 +6,8 @@ import { type ContractFieldType, type ContractModelBase, type ContractWithDomain, + type StorageBase, + valueObjectAt, } from '@internal/contract/types'; import type { CliStructuredError } from '@internal/errors/control'; import { @@ -35,12 +37,17 @@ import { import { statementFlag } from './statement-flag'; import type { StatementText } from './statement-text'; +/** A contract statements resolve against: its domain models, and the value objects its storage holds. */ +export type StatementContract = ContractWithDomain & { + readonly storage: Pick; +}; + /** * The origin contract statements resolve against, or where the command looked * for it without finding it. */ export type StatementOrigin = - | { readonly kind: 'contract'; readonly contract: ContractWithDomain } + | { readonly kind: 'contract'; readonly contract: StatementContract } | { readonly kind: 'missing'; readonly hash: string | null; @@ -57,14 +64,14 @@ export interface ResolveStatementsInput { /** The statements as the user wrote them, in the order given. */ readonly statements: readonly StatementText[]; readonly origin: StatementOrigin; - readonly destination: ContractWithDomain; + readonly destination: StatementContract; } type Side = 'old' | 'new'; interface NamedContract { readonly name: 'origin' | 'destination'; - readonly contract: ContractWithDomain; + readonly contract: StatementContract; } interface FoundModel { @@ -159,8 +166,15 @@ function modelIn( return Object.hasOwn(namespace.models, name) ? namespace.models[name] : undefined; } -function hasValueObject(namespace: ApplicationDomainNamespace, name: string): boolean { - return namespace.valueObjects !== undefined && Object.hasOwn(namespace.valueObjects, name); +function hasValueObject(contract: StatementContract, namespaceId: string, name: string): boolean { + return ( + valueObjectAt(contract.storage, { + plane: 'storage', + namespaceId, + entityKind: 'valueObject', + entityName: name, + }) !== undefined + ); } function modelAt( @@ -208,7 +222,7 @@ function lookupModelInNamespace( model, }; } - if (hasValueObject(namespace, name)) { + if (hasValueObject(contract.contract, namespaceId, name)) { return { kind: 'refused', ...valueObjectRefusal(displayName(namespaceId, name)) }; } return { @@ -262,7 +276,10 @@ function lookupModel( fix: `Name the model with its namespace, for example ${candidates[0]}.`, }; } - if (Object.values(namespaces).some((namespace) => hasValueObject(namespace, name))) { + const storageNamespaceIds = Object.keys(contract.contract.storage.namespaces); + if ( + storageNamespaceIds.some((namespaceId) => hasValueObject(contract.contract, namespaceId, name)) + ) { return { kind: 'refused', ...valueObjectRefusal(name) }; } return { @@ -904,7 +921,7 @@ function typeText(type: ContractFieldType): string { ? type.codecId : `${type.codecId}(${JSON.stringify(type.typeParams)})`; case 'valueObject': - return type.name; + return type.entityName; case 'union': return type.members.map(typeText).join(' | '); } diff --git a/packages/1-framework/3-tooling/cli/test/control-api/statements/resolve-convert-backfill-statements.test.ts b/packages/1-framework/3-tooling/cli/test/control-api/statements/resolve-convert-backfill-statements.test.ts index 257aa81dda79..bbfdeeda55f1 100644 --- a/packages/1-framework/3-tooling/cli/test/control-api/statements/resolve-convert-backfill-statements.test.ts +++ b/packages/1-framework/3-tooling/cli/test/control-api/statements/resolve-convert-backfill-statements.test.ts @@ -119,6 +119,26 @@ describe('resolveStatements, convert statements', () => { ); }); + it('refuses a value-object field whose type does not change, naming the value object', () => { + const withAddress = contractOf({ + app: { + models: { + User: { fields: ['id', 'home'], fieldTypes: { home: { valueObject: 'Address' } } }, + }, + valueObjects: { Address: ['city'] }, + }, + }); + expectFailure( + resolveStatements({ + statements: [convert('User.home')], + origin: { kind: 'contract', contract: withAddress }, + destination: withAddress, + }), + UNRESOLVED, + 'has the same type, Address, in the origin and destination contracts', + ); + }); + it('refuses a field the origin does not have and no rename gives it', () => { expectFailure( resolve([convert('User.handle')]), diff --git a/packages/1-framework/3-tooling/cli/test/control-api/statements/statement-fixtures.ts b/packages/1-framework/3-tooling/cli/test/control-api/statements/statement-fixtures.ts index f29df8f88fa8..0b8e66897e8b 100644 --- a/packages/1-framework/3-tooling/cli/test/control-api/statements/statement-fixtures.ts +++ b/packages/1-framework/3-tooling/cli/test/control-api/statements/statement-fixtures.ts @@ -3,20 +3,23 @@ import type { ContractModelBase, ContractRelation, ContractValueObject, - ContractWithDomain, } from '@internal/contract/types'; import { crossRef } from '@internal/contract/types'; import type { CliStructuredError } from '@internal/errors/control'; import type { Result } from '@internal/utils/result'; import { expect } from 'vitest'; +import type { StatementContract } from '../../../src/control-api/statements/resolve-statements'; interface ModelSpec { readonly fields?: readonly string[]; readonly relations?: readonly string[]; readonly base?: string; - /** The field's codec and whether it is optional, for a field `fields` lists; text and required otherwise. */ + /** The field's codec or the value object of its namespace it holds, and whether it is optional, for a field `fields` lists; text and required otherwise. */ readonly fieldTypes?: Readonly< - Record + Record< + string, + { readonly codecId?: string; readonly valueObject?: string; readonly nullable?: boolean } + > >; } @@ -31,6 +34,7 @@ const textField: ContractField = { }; function fieldsOf( + namespaceId: string, names: readonly string[], types: ModelSpec['fieldTypes'] = {}, ): Record { @@ -43,21 +47,30 @@ function fieldsOf( ? textField : { nullable: type.nullable ?? false, - type: { kind: 'scalar', codecId: type.codecId ?? 'test/text@1' }, + type: + type.valueObject === undefined + ? { kind: 'scalar', codecId: type.codecId ?? 'test/text@1' } + : { + kind: 'valueObject', + plane: 'storage', + namespaceId, + entityKind: 'valueObject', + entityName: type.valueObject, + }, }, ]; }), ); } -function modelOf(spec: ModelSpec): ContractModelBase { +function modelOf(namespaceId: string, spec: ModelSpec): ContractModelBase { const relation: ContractRelation = { to: crossRef('Other'), cardinality: '1:N', on: { localFields: ['id'], targetFields: ['ownerId'] }, }; return { - fields: fieldsOf(spec.fields ?? [], spec.fieldTypes), + fields: fieldsOf(namespaceId, spec.fields ?? [], spec.fieldTypes), relations: Object.fromEntries((spec.relations ?? []).map((name) => [name, relation])), storage: {}, ...(spec.base !== undefined ? { base: crossRef(spec.base) } : {}), @@ -65,29 +78,49 @@ function modelOf(spec: ModelSpec): ContractModelBase { } function valueObjectOf(fields: readonly string[]): ContractValueObject { - return { fields: fieldsOf(fields) }; + return { + fields: Object.fromEntries( + fields.map((name) => [ + name, + { nullable: false, type: { kind: 'scalar', codecId: 'test/text@1' } }, + ]), + ), + }; } -export function contractOf(namespaces: Record): ContractWithDomain { +export function contractOf(namespaces: Record): StatementContract { + const entries = Object.entries(namespaces); return { domain: { namespaces: Object.fromEntries( - Object.entries(namespaces).map(([id, ns]) => [ + entries.map(([id, ns]) => [ id, { models: Object.fromEntries( - Object.entries(ns.models ?? {}).map(([name, spec]) => [name, modelOf(spec)]), - ), - valueObjects: Object.fromEntries( - Object.entries(ns.valueObjects ?? {}).map(([name, fields]) => [ - name, - valueObjectOf(fields), - ]), + Object.entries(ns.models ?? {}).map(([name, spec]) => [name, modelOf(id, spec)]), ), }, ]), ), }, + storage: { + namespaces: Object.fromEntries( + entries.map(([id, ns]) => [ + id, + { + id, + entries: { + valueObject: Object.fromEntries( + Object.entries(ns.valueObjects ?? {}).map(([name, fields]) => [ + name, + valueObjectOf(fields), + ]), + ), + }, + }, + ]), + ), + }, }; } diff --git a/packages/1-framework/3-tooling/cli/test/orm/db-update-consent.test.ts b/packages/1-framework/3-tooling/cli/test/orm/db-update-consent.test.ts index 853d39bcc6e3..ed4c7aca09c5 100644 --- a/packages/1-framework/3-tooling/cli/test/orm/db-update-consent.test.ts +++ b/packages/1-framework/3-tooling/cli/test/orm/db-update-consent.test.ts @@ -63,9 +63,11 @@ const origin = { }, }, }, + storage: { namespaces: {} }, }; const destination = { domain: { namespaces: { app: { models: { User: { fields: {}, relations: {}, storage: {} } } } } }, + storage: { namespaces: {} }, }; const contracts = { origin, From a2ef81b981ae6fe1739e2d3b276a62a6c9c87356 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 13:27:51 +0200 Subject: [PATCH 031/104] TML-3537: contract print writes type blocks from the value objects in storage A value object field prints the name its storage reference names. A value-object member's value set must be the storage value set of a default-namespace enum. Signed-off-by: willbot Signed-off-by: Will Madden --- .../src/core/psl-print/domain-types.ts | 7 +-- .../1-target/src/core/psl-print/refusals.ts | 17 +++---- .../src/core/psl-print/scalar-fields.ts | 2 +- .../1-target/test/psl-print/member-support.ts | 46 +++++++++++++++---- .../1-target/test/psl-print/members.test.ts | 17 +++++-- .../test/psl-print/refusals-entries.test.ts | 4 +- .../test/psl-print/refusals-storage.test.ts | 33 +++++-------- ...red-contract-roundtrip.integration.test.ts | 27 +++++++++++ 8 files changed, 103 insertions(+), 50 deletions(-) diff --git a/packages/3-targets/postgres/1-target/src/core/psl-print/domain-types.ts b/packages/3-targets/postgres/1-target/src/core/psl-print/domain-types.ts index 8615b0e6b51a..202c8f7a384c 100644 --- a/packages/3-targets/postgres/1-target/src/core/psl-print/domain-types.ts +++ b/packages/3-targets/postgres/1-target/src/core/psl-print/domain-types.ts @@ -68,7 +68,7 @@ function buildMemberType(input: { refuseUnwritableFieldShape(field, coordinate); const { type } = field; if (type.kind === 'valueObject') { - return { kind: 'valueObject', typeName: type.name }; + return { kind: 'valueObject', typeName: type.entityName }; } refuseUnderivedMemberValueSet({ field, @@ -95,7 +95,7 @@ function buildMemberType(input: { }); } -/** The `type` blocks of one namespace, one per value object the domain declares there. */ +/** The `type` blocks of one namespace, one per value object its storage holds. */ export function buildCompositeTypes(input: { readonly contract: Contract; readonly namespaceId: string; @@ -105,7 +105,8 @@ export function buildCompositeTypes(input: { readonly domainEnums: Readonly>; }): readonly PslCompositeType[] { refuseValueObjectsOutsideDefaultNamespace(input.contract, input.namespaceId); - const valueObjects = input.contract.domain.namespaces[input.namespaceId]?.valueObjects ?? {}; + const valueObjects = + input.contract.storage.namespaces[input.namespaceId]?.entries.valueObject ?? {}; return Object.entries(valueObjects).map(([name, valueObject]) => { refuseUnwritableName('value object', name); return { diff --git a/packages/3-targets/postgres/1-target/src/core/psl-print/refusals.ts b/packages/3-targets/postgres/1-target/src/core/psl-print/refusals.ts index 55db80bfb3b2..d461a7d3e5eb 100644 --- a/packages/3-targets/postgres/1-target/src/core/psl-print/refusals.ts +++ b/packages/3-targets/postgres/1-target/src/core/psl-print/refusals.ts @@ -334,15 +334,15 @@ export function refuseUnderivedMemberValueSet(input: { if (valueSet === undefined) return; const domainEnum = input.domainEnums[valueSet.entityName]; if ( - sameJson(valueSet, derivedValueSetRefs(valueSet.entityName).domain) && + sameJson(valueSet, derivedValueSetRefs(valueSet.entityName).storage) && domainEnum?.codecId === type.codecId && type.typeParams === undefined ) { return; } throw unsupported( - `value-object member ${coordinate} carries a value set other than an enum of the default namespace with that enum's codec and no type parameters, which cannot be written in Prisma 8 PSL.`, - "PSL types the member by the enum name, and the PSL source then points the member at that enum in the default namespace and gives it the enum's codec.", + `value-object member ${coordinate} carries a value set other than the value set of an enum of the default namespace, with that enum's codec and no type parameters, which cannot be written in Prisma 8 PSL.`, + "PSL types the member by the enum name, and the PSL source then points the member at that enum's value set in the default namespace and gives it the enum's codec.", KEEP_SOURCE, { coordinate }, ); @@ -526,7 +526,7 @@ export function refuseValueObjectsOutsideDefaultNamespace( contract: Contract, namespaceId: string, ): void { - const names = Object.keys(contract.domain.namespaces[namespaceId]?.valueObjects ?? {}); + const names = Object.keys(contract.storage.namespaces[namespaceId]?.entries.valueObject ?? {}); if (namespaceId === DEFAULT_NAMESPACE_ID || names.length === 0) return; throw unsupported( `namespace "${namespaceId}" declares the value object${names.length === 1 ? '' : 's'} ${names.join(', ')}, which cannot be written in Prisma 8 PSL: the PSL source reads every value object into the default namespace.`, @@ -821,11 +821,7 @@ export function refuseUnwrittenNamespaces(input: { const storageNamespaces = new Set([DEFAULT_NAMESPACE_ID, ...input.writtenStorageNamespaces]); const defaultDomain = contract.domain.namespaces[DEFAULT_NAMESPACE_ID]; const domainNamespaces = new Set(input.models.map((entry) => entry.namespaceId)); - if ( - domainNamespaces.size === 0 || - Object.keys(defaultDomain?.valueObjects ?? {}).length > 0 || - Object.keys(defaultDomain?.enum ?? {}).length > 0 - ) { + if (domainNamespaces.size === 0 || Object.keys(defaultDomain?.enum ?? {}).length > 0) { domainNamespaces.add(DEFAULT_NAMESPACE_ID); } const planes = [ @@ -1078,6 +1074,7 @@ export function refuseUnwritableName(kind: string, name: string): void { const PRINTED_ENTRY_KINDS: ReadonlySet = new Set([ 'table', 'valueSet', + 'valueObject', 'native_enum', 'rls', 'policy', @@ -1094,7 +1091,7 @@ export function refuseUnprintedEntryKinds( if (names.length === 0) continue; throw unsupported( `namespace "${namespaceId}" declares ${names.length} "${kind}" ${names.length === 1 ? 'entity' : 'entities'} (${names.join(', ')}), which the Postgres printer cannot write.`, - 'The printer writes tables, value sets, native enums, row-level security settings, policies and roles, and no other entity kind.', + 'The printer writes tables, value sets, value objects, native enums, row-level security settings, policies and roles, and no other entity kind.', KEEP_SOURCE, { namespaceId, kind, names }, ); diff --git a/packages/3-targets/postgres/1-target/src/core/psl-print/scalar-fields.ts b/packages/3-targets/postgres/1-target/src/core/psl-print/scalar-fields.ts index 787c5c389d31..9770ffa8d844 100644 --- a/packages/3-targets/postgres/1-target/src/core/psl-print/scalar-fields.ts +++ b/packages/3-targets/postgres/1-target/src/core/psl-print/scalar-fields.ts @@ -129,7 +129,7 @@ export function buildScalarFields(input: { }); const columnType: PslColumnType = field.type.kind === 'valueObject' - ? { kind: 'valueObject', typeName: field.type.name } + ? { kind: 'valueObject', typeName: field.type.entityName } : buildColumnType({ column, inferredConstructors, diff --git a/packages/3-targets/postgres/1-target/test/psl-print/member-support.ts b/packages/3-targets/postgres/1-target/test/psl-print/member-support.ts index 29597cd446f9..b0cbb26579a9 100644 --- a/packages/3-targets/postgres/1-target/test/psl-print/member-support.ts +++ b/packages/3-targets/postgres/1-target/test/psl-print/member-support.ts @@ -6,8 +6,19 @@ export function addressContract(field: ContractField) { return deserialize( widgetContract({ columns: { address: { dataType: 'pg/jsonb', codecId: 'pg/jsonb@1', nullable: false } }, - fields: { address: { nullable: false, type: { kind: 'valueObject', name: 'Address' } } }, - domain: { valueObjects: { Address: { fields: { street: field } } } }, + fields: { + address: { + nullable: false, + type: { + kind: 'valueObject', + plane: 'storage', + namespaceId: 'public', + entityKind: 'valueObject', + entityName: 'Address', + }, + }, + }, + entries: { valueObject: { Address: { fields: { street: field } } } }, }), ); } @@ -16,20 +27,37 @@ const countryEnumParts = { enum: { Country: { codecId: 'pg/text@1', members: [{ name: 'DE', value: 'DE' }] } }, } as const; export const countryValueSet = { - plane: 'domain', - entityKind: 'enum', + plane: 'storage', + entityKind: 'valueSet', namespaceId: 'public', entityName: 'Country', } as const; -/** A contract whose value object `Address` has the given members, beside the domain enum `Country`. */ -export function withCountryMembers(fields: Record) { +/** A contract whose value object `Address` has the given members, beside the domain enum `Country` and its value set, and the given other value sets. */ +export function withCountryMembers( + fields: Record, + valueSets: Record = {}, +) { return deserialize( widgetContract({ columns: { address: { dataType: 'pg/jsonb', codecId: 'pg/jsonb@1', nullable: false } }, - fields: { address: { nullable: false, type: { kind: 'valueObject', name: 'Address' } } }, - domain: { ...countryEnumParts, valueObjects: { Address: { fields } } }, - entries: { valueSet: { Country: { kind: 'valueSet', values: ['DE'] } } }, + fields: { + address: { + nullable: false, + type: { + kind: 'valueObject', + plane: 'storage', + namespaceId: 'public', + entityKind: 'valueObject', + entityName: 'Address', + }, + }, + }, + domain: countryEnumParts, + entries: { + valueSet: { Country: { kind: 'valueSet', values: ['DE'] }, ...valueSets }, + valueObject: { Address: { fields } }, + }, }), ); } diff --git a/packages/3-targets/postgres/1-target/test/psl-print/members.test.ts b/packages/3-targets/postgres/1-target/test/psl-print/members.test.ts index 0bb97775c644..c2c8778b2daf 100644 --- a/packages/3-targets/postgres/1-target/test/psl-print/members.test.ts +++ b/packages/3-targets/postgres/1-target/test/psl-print/members.test.ts @@ -45,9 +45,20 @@ describe('value-object members are written as the type their domain type reads b deserialize( widgetContract({ columns: { price: { dataType: 'pg/jsonb', codecId: 'pg/jsonb@1', nullable: false } }, - fields: { price: { nullable: false, type: { kind: 'valueObject', name: 'Price' } } }, - domain: { - valueObjects: { + fields: { + price: { + nullable: false, + type: { + kind: 'valueObject', + plane: 'storage', + namespaceId: 'public', + entityKind: 'valueObject', + entityName: 'Price', + }, + }, + }, + entries: { + valueObject: { Price: { fields: { amount: { nullable: false, type: numeric }, diff --git a/packages/3-targets/postgres/1-target/test/psl-print/refusals-entries.test.ts b/packages/3-targets/postgres/1-target/test/psl-print/refusals-entries.test.ts index e36df50b1036..167901206c3a 100644 --- a/packages/3-targets/postgres/1-target/test/psl-print/refusals-entries.test.ts +++ b/packages/3-targets/postgres/1-target/test/psl-print/refusals-entries.test.ts @@ -5,7 +5,7 @@ describe('names PSL writes as identifiers', () => { it('refuses a value object name that is not an identifier', () => { expect( printingWidget({ - domain: { valueObjects: { 'Postal Address': { fields: { street: TEXT_FIELD } } } }, + entries: { valueObject: { 'Postal Address': { fields: { street: TEXT_FIELD } } } }, }), ).toThrow(refusal({ kind: 'value object', name: 'Postal Address' })); }); @@ -19,7 +19,7 @@ describe('names PSL writes as identifiers', () => { it('refuses a value-object field name that is not an identifier', () => { expect( printingWidget({ - domain: { valueObjects: { Address: { fields: { 'street line': TEXT_FIELD } } } }, + entries: { valueObject: { Address: { fields: { 'street line': TEXT_FIELD } } } }, }), ).toThrow(refusal({ kind: 'field', name: 'street line' })); }); diff --git a/packages/3-targets/postgres/1-target/test/psl-print/refusals-storage.test.ts b/packages/3-targets/postgres/1-target/test/psl-print/refusals-storage.test.ts index fe365df2b102..74db2f7849bf 100644 --- a/packages/3-targets/postgres/1-target/test/psl-print/refusals-storage.test.ts +++ b/packages/3-targets/postgres/1-target/test/psl-print/refusals-storage.test.ts @@ -268,10 +268,12 @@ describe('value objects', () => { it('refuses a value object outside the default namespace, which the PSL source would move', () => { expect( printingWidget({ - domainNamespaces: { - auth: { models: {}, valueObjects: { Address: { fields: { street: TEXT_FIELD } } } }, + storageNamespaces: { + auth: { + id: 'auth', + entries: { table: {}, valueObject: { Address: { fields: { street: TEXT_FIELD } } } }, + }, }, - storageNamespaces: { auth: { id: 'auth', entries: { table: {} } } }, }), ).toThrow(refusal({ namespaceId: 'auth', names: ['Address'] })); }); @@ -328,28 +330,15 @@ describe('value objects', () => { ); }); - it('refuses a value-object member whose value set is not a domain enum of the default namespace', () => { - expect( - withAddress({ - ...TEXT_FIELD, - valueSet: { - plane: 'domain', - entityKind: 'enum', - namespaceId: 'public', - entityName: 'Label', - }, - }), - ).toThrow(refusal({ coordinate: '"public".Address.street' })); - }); - - it('refuses a value-object member whose value set names an enum the domain does not declare', () => { + it('refuses a value-object member whose value set no enum derives', () => { expect( printing( - withCountryMembers({ - country: { ...TEXT_FIELD, valueSet: { ...countryValueSet, entityName: 'Missing' } }, - }), + withCountryMembers( + { label: { ...TEXT_FIELD, valueSet: { ...countryValueSet, entityName: 'Label' } } }, + { Label: { kind: 'valueSet', values: ['x'] } }, + ), ), - ).toThrow(refusal({ coordinate: '"public".Address.country' })); + ).toThrow(refusal({ namespaceId: 'public', name: 'Label' })); }); it('refuses a value-object member typed by a domain enum that also has type parameters', () => { diff --git a/test/integration/test/psl-print/authored-contract-roundtrip.integration.test.ts b/test/integration/test/psl-print/authored-contract-roundtrip.integration.test.ts index d35221b6c3eb..378c187a27a8 100644 --- a/test/integration/test/psl-print/authored-contract-roundtrip.integration.test.ts +++ b/test/integration/test/psl-print/authored-contract-roundtrip.integration.test.ts @@ -209,6 +209,33 @@ policy_select p_read { @@check(expression: sql\`length(email) > 0\`, name: "widget_email_not_blank") @@check(expression: sql\`id > 0\`, map: "widget_id_positive") } +`, + }, + { + name: 'a value object nested in another, a list of nested value objects, and an enum member', + schema: `enum Country { + @@type("pg/text@1") + DE = "DE" + FR = "FR" +} + +type Geo { + lat Float + lng Float +} + +type Address { + street String + country Country + geo Geo + stops Geo[] +} + +model Person { + id Int @id + home Address + homes Address[] +} `, }, { From ac7d924b0ce20629a6617df72f5fb9de88532e08 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 13:30:33 +0200 Subject: [PATCH 032/104] TML-3537: the SQL ORM type test reads its value object from storage Signed-off-by: willbot Signed-off-by: Will Madden --- .../test/generated-contract-types.test-d.ts | 58 +++++++++++-------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/packages/3-extensions/sql-orm-client/test/generated-contract-types.test-d.ts b/packages/3-extensions/sql-orm-client/test/generated-contract-types.test-d.ts index 8efc98d6d7bc..dbb764477396 100644 --- a/packages/3-extensions/sql-orm-client/test/generated-contract-types.test-d.ts +++ b/packages/3-extensions/sql-orm-client/test/generated-contract-types.test-d.ts @@ -515,6 +515,27 @@ type VOContractBase = Omit< id: '__unbound__'; kind: 'schema'; entries: { + valueObject: { + readonly Address: { + readonly fields: { + readonly street: { + readonly many: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + readonly nullable: false; + }; + readonly city: { + readonly many: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + readonly nullable: false; + }; + readonly zip: { + readonly many: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + readonly nullable: false; + }; + }; + }; + }; table: { users: { columns: { @@ -586,39 +607,30 @@ type VOContractBase = Omit< }; readonly homeAddress: { readonly many: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; readonly nullable: true; }; readonly workAddress: { readonly many: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; readonly nullable: false; }; }; readonly relations: Record; }; }; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly street: { - readonly many: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - readonly nullable: false; - }; - readonly city: { - readonly many: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - readonly nullable: false; - }; - readonly zip: { - readonly many: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - readonly nullable: false; - }; - }; - }; - }; }; }; }; From ab83996abde6b950c808189bf4d84256fbf57a66 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 13:36:31 +0200 Subject: [PATCH 033/104] TML-3537: deferred note on one value object alias per name Signed-off-by: willbot Signed-off-by: Will Madden --- projects/migration-statements/deferred.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/migration-statements/deferred.md b/projects/migration-statements/deferred.md index 425a254b33b8..38c58a6b0553 100644 --- a/projects/migration-statements/deferred.md +++ b/projects/migration-statements/deferred.md @@ -199,4 +199,4 @@ A column's `valueSet: { plane: 'storage', entityKind: 'valueSet', entityName }` ## Composite types are not namespaced in SQL PSL -SQL PSL resolves a composite type by its bare name across all namespaces and writes every value object to the default storage namespace. Two composite types with the same name in different PSL namespaces are refused with `PSL_DUPLICATE_DECLARATION` (before PR 3 of the remaining-nouns slice they silently overwrote each other). Letting each namespace declare its own needs namespace-qualified type references in PSL and writing each value object to its namespace's storage. Found in the review of that PR, 2026-10-23. +SQL PSL resolves a composite type by its bare name across all namespaces and writes every value object to the default storage namespace. Two composite types with the same name in different PSL namespaces are refused with `PSL_DUPLICATE_DECLARATION` (before PR 3 of the remaining-nouns slice they silently overwrote each other). Letting each namespace declare its own needs namespace-qualified type references in PSL and writing each value object to its namespace's storage. When that changes, `generate-contract-dts.ts` must also emit one `Output` alias per storage namespace; today it emits one per name, from the first namespace. Found in the review of that PR, 2026-10-23. From cbf398e117fc0fdb6a72dcc29f42fc8988f3adcb Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 14:15:56 +0200 Subject: [PATCH 034/104] TML-3537: Postgres derives a check for each value object column and each discriminator column A column that holds a value object gets a check named __value_object: one strict jsonpath predicate over the members of the value object, resolved from storage by the family. A discriminator column with no enum gets a membership check holding its value set. The renderer input is declared once in @internal/sql-contract/check-expressions. Signed-off-by: willbot Signed-off-by: Will Madden --- .../2-sql-family/1-core/contract/package.json | 1 + .../1-core/contract/src/check-expressions.ts | 164 ++++++++++ .../contract/src/exports/check-expressions.ts | 9 + .../contract/test/check-expressions.test.ts | 154 ++++++++++ .../1-core/contract/tsdown.config.ts | 1 + .../1-core/schema-ir/src/exports/naming.ts | 1 + .../1-core/schema-ir/src/naming.ts | 5 +- .../1-core/schema-ir/test/naming.test.ts | 7 +- .../2-authoring/contract-psl/test/fixtures.ts | 11 +- .../contract-ts/src/build-contract.ts | 73 ++++- .../contract-ts/src/describe-column.ts | 1 + .../contract-ts/src/describe-model.ts | 37 ++- .../contract-ts/src/domain-fields.ts | 24 +- .../contract-ts/src/lower-column.ts | 46 ++- .../contract-ts/src/lower-table.ts | 10 +- .../contract-ts/src/storage-description.ts | 9 +- .../contract-ts/src/target-authoring-hooks.ts | 29 +- .../test/check-constraint.authoring.test.ts | 17 +- .../contract-builder.derived-checks.test.ts | 260 ++++++++++++++++ .../test/fixtures/managed-user-contract.ts | 11 +- .../contract-ts/test/merge-tables.test.ts | 1 + .../contract-ts/test/table-node-helpers.ts | 11 +- .../1-target/src/core/check-expressions.ts | 150 ++++++--- .../src/core/psl-infer/infer-model-blocks.ts | 6 +- .../src/core/value-object-json-types.ts | 66 ++++ .../postgres/1-target/src/exports/types.ts | 7 +- .../check-expressions.integration.test.ts | 5 +- .../1-target/test/check-expressions.test.ts | 5 +- .../check-expressions.value-object.test.ts | 160 ++++++++++ .../inferred-psl/inferred-psl.check.test.ts | 3 +- ...erred-psl.list-element-nullability.test.ts | 3 +- .../alter-column-type.integration.test.ts | 1 + .../check-lifecycle-e2e.integration.test.ts | 1 + .../native-array-columns.integration.test.ts | 1 + .../@prisma/orm-family-sql/package.json | 1 + .../@prisma/orm-postgres/package.json | 1 + .../value-object-checks/contract.prisma | 62 ++++ .../value-object-checks.test.ts | 288 ++++++++++++++++++ 38 files changed, 1481 insertions(+), 161 deletions(-) create mode 100644 packages/2-sql-family/1-core/contract/src/check-expressions.ts create mode 100644 packages/2-sql-family/1-core/contract/src/exports/check-expressions.ts create mode 100644 packages/2-sql-family/1-core/contract/test/check-expressions.test.ts create mode 100644 packages/2-sql-family/2-authoring/contract-ts/test/contract-builder.derived-checks.test.ts create mode 100644 packages/3-targets/postgres/1-target/src/core/value-object-json-types.ts create mode 100644 packages/3-targets/postgres/1-target/test/check-expressions.value-object.test.ts create mode 100644 test/integration/test/sql-orm-client/fixtures/value-object-checks/contract.prisma create mode 100644 test/integration/test/sql-orm-client/value-object-checks.test.ts diff --git a/packages/2-sql-family/1-core/contract/package.json b/packages/2-sql-family/1-core/contract/package.json index 440c690290c1..f6732f2c3e9b 100644 --- a/packages/2-sql-family/1-core/contract/package.json +++ b/packages/2-sql-family/1-core/contract/package.json @@ -45,6 +45,7 @@ "exports": { "./authored-check-naming": "./dist/authored-check-naming.mjs", "./canonicalization-hooks": "./dist/canonicalization-hooks.mjs", + "./check-expressions": "./dist/check-expressions.mjs", "./contract-view": "./dist/contract-view.mjs", "./data-type": "./dist/data-type.mjs", "./data-type-support": "./dist/data-type-support.mjs", diff --git a/packages/2-sql-family/1-core/contract/src/check-expressions.ts b/packages/2-sql-family/1-core/contract/src/check-expressions.ts new file mode 100644 index 000000000000..fd51eb54f4e9 --- /dev/null +++ b/packages/2-sql-family/1-core/contract/src/check-expressions.ts @@ -0,0 +1,164 @@ +import type { + ContractValueObject, + ContractValueObjectField, + EntityCoordinate, + StorageBase, +} from '@internal/contract/types'; +import { valueObjectAt } from '@internal/contract/types'; +import type { CheckKind } from '@internal/sql-schema-ir/naming'; +import { invariant } from '@internal/utils/assertions'; +import { isStorageValueSet } from './ir/storage-value-set'; + +/** A value object with each nested value object inlined, as the target's check renderer reads it. */ +export interface ResolvedValueObject { + readonly fields: Readonly>; +} + +export interface ResolvedValueObjectField { + readonly nullable: boolean; + readonly many: false | { readonly elementNullable: boolean }; + readonly type: ResolvedValueObjectFieldType; +} + +/** + * `values` holds the stored values of the value set an enum member refers to. `openObject` stands where a value object that contains itself refers back into itself: any object is accepted there, as the MongoDB validator does. + */ +export type ResolvedValueObjectFieldType = + | { + readonly kind: 'scalar'; + readonly codecId: string; + readonly values: readonly (string | number)[] | undefined; + } + | { readonly kind: 'valueObject'; readonly valueObject: ResolvedValueObject } + | { readonly kind: 'openObject' }; + +/** One column's shape, as the contract builder hands it to a target's check renderer. */ +export interface CheckExpressionInput { + readonly tableName: string; + readonly columnName: string; + readonly many: false | { readonly elementNullable: boolean }; + /** The values a column typed by a domain enum, or a discriminator column, may hold. */ + readonly memberValues: readonly (string | number)[] | undefined; + /** The value object field a column holds: its nullability, whether it is a list, and the value object. */ + readonly valueObjectField: ResolvedValueObjectField | undefined; +} + +/** A check the target wants written: what it enforces, the column it constrains, and the predicate body without the surrounding `CHECK (…)`. The family names it. */ +export interface CheckExpressionCandidate { + readonly kind: CheckKind; + readonly columnName: string; + readonly expression: string; +} + +export type CheckExpressionRenderer = ( + input: CheckExpressionInput, +) => ReadonlyArray; + +type StorageNamespaces = Pick; +type ValueObjectReference = EntityCoordinate<'storage', 'valueObject'>; + +/** + * The value object a reference names, with nested value objects inlined and enum members' values read from their storage value sets. + */ +export function resolveValueObject( + storage: StorageNamespaces, + reference: ValueObjectReference, +): ResolvedValueObject { + return resolveEntered(storage, reference, [referenceKey(reference)]); +} + +function resolveEntered( + storage: StorageNamespaces, + reference: ValueObjectReference, + path: readonly string[], +): ResolvedValueObject { + const valueObject = requiredValueObject(storage, reference); + return { + fields: Object.fromEntries( + Object.entries(valueObject.fields).map(([name, field]) => [ + name, + { + nullable: field.nullable, + many: field.many ?? false, + type: resolveFieldType(storage, field, path), + }, + ]), + ), + }; +} + +function resolveFieldType( + storage: StorageNamespaces, + field: ContractValueObjectField, + path: readonly string[], +): ResolvedValueObjectFieldType { + const { type } = field; + invariant( + type.kind !== 'union' && field.dict !== true, + 'a SQL value object member is never a union or a dictionary', + ); + if (type.kind === 'scalar') { + return { + kind: 'scalar', + codecId: type.codecId, + values: field.valueSet === undefined ? undefined : valueSetValues(storage, field.valueSet), + }; + } + const key = referenceKey(type); + if (reachesAny(storage, type, new Set(path))) return { kind: 'openObject' }; + return { kind: 'valueObject', valueObject: resolveEntered(storage, type, [...path, key]) }; +} + +/** + * Whether `from` refers, through its own fields or nested ones, to a value object on the path being resolved. Each value object on the path refers to `from`, so this holds exactly when `from` is in a group of value objects that contain each other which the path has already entered. + */ +function reachesAny( + storage: StorageNamespaces, + from: ValueObjectReference, + targets: ReadonlySet, +): boolean { + const seen = new Set(); + const pending: ValueObjectReference[] = [from]; + for (let next = pending.pop(); next !== undefined; next = pending.pop()) { + const key = referenceKey(next); + if (targets.has(key)) return true; + if (seen.has(key)) continue; + seen.add(key); + for (const field of Object.values(requiredValueObject(storage, next).fields)) { + if (field.type.kind === 'valueObject') pending.push(field.type); + } + } + return false; +} + +function requiredValueObject( + storage: StorageNamespaces, + reference: ValueObjectReference, +): ContractValueObject { + const valueObject = valueObjectAt(storage, reference); + invariant( + valueObject !== undefined, + `value object "${reference.entityName}" in namespace "${reference.namespaceId}" is not in storage; the contract validators refuse a reference to a missing value object`, + ); + return valueObject; +} + +function valueSetValues( + storage: StorageNamespaces, + reference: EntityCoordinate<'storage', 'valueSet'>, +): readonly (string | number)[] { + const valueSet = Object.hasOwn(storage.namespaces, reference.namespaceId) + ? storage.namespaces[reference.namespaceId]?.entries?.['valueSet']?.[reference.entityName] + : undefined; + invariant( + isStorageValueSet(valueSet), + `value set "${reference.entityName}" in namespace "${reference.namespaceId}" is not in storage; the contract validators refuse a value object member whose value set is missing`, + ); + return valueSet.values.filter( + (value): value is string | number => typeof value === 'string' || typeof value === 'number', + ); +} + +function referenceKey(reference: ValueObjectReference): string { + return JSON.stringify([reference.namespaceId, reference.entityName]); +} diff --git a/packages/2-sql-family/1-core/contract/src/exports/check-expressions.ts b/packages/2-sql-family/1-core/contract/src/exports/check-expressions.ts new file mode 100644 index 000000000000..cbf707e89a4e --- /dev/null +++ b/packages/2-sql-family/1-core/contract/src/exports/check-expressions.ts @@ -0,0 +1,9 @@ +export { + type CheckExpressionCandidate, + type CheckExpressionInput, + type CheckExpressionRenderer, + type ResolvedValueObject, + type ResolvedValueObjectField, + type ResolvedValueObjectFieldType, + resolveValueObject, +} from '../check-expressions'; diff --git a/packages/2-sql-family/1-core/contract/test/check-expressions.test.ts b/packages/2-sql-family/1-core/contract/test/check-expressions.test.ts new file mode 100644 index 000000000000..5456b352bf6f --- /dev/null +++ b/packages/2-sql-family/1-core/contract/test/check-expressions.test.ts @@ -0,0 +1,154 @@ +import { describe, expect, it } from 'vitest'; +import { resolveValueObject } from '../src/check-expressions'; + +function valueObjectRef(name: string) { + return { + plane: 'storage', + namespaceId: 'public', + entityKind: 'valueObject', + entityName: name, + } as const; +} + +const text = { nullable: false, many: false, type: { kind: 'scalar', codecId: 'pg/text@1' } }; + +function nested(name: string, extra: Record = {}) { + return { + nullable: false, + many: false, + type: { kind: 'valueObject', ...valueObjectRef(name) }, + ...extra, + }; +} + +function storageWith(entries: Record>) { + return { namespaces: { public: { id: 'public', entries } } }; +} + +describe('resolveValueObject', () => { + it('inlines nested value objects and reads enum members from their value sets', () => { + const storage = storageWith({ + valueObject: { + Address: { + fields: { + street: text, + kind: { + nullable: true, + many: false, + type: { kind: 'scalar', codecId: 'pg/text@1' }, + valueSet: { + plane: 'storage', + namespaceId: 'public', + entityKind: 'valueSet', + entityName: 'AddressKind', + }, + }, + geo: nested('Geo', { many: { elementNullable: true } }), + }, + }, + Geo: { fields: { lat: text } }, + }, + valueSet: { AddressKind: { kind: 'valueSet', values: ['home', 'work'] } }, + }); + + expect(resolveValueObject(storage, valueObjectRef('Address'))).toEqual({ + fields: { + street: { + nullable: false, + many: false, + type: { kind: 'scalar', codecId: 'pg/text@1', values: undefined }, + }, + kind: { + nullable: true, + many: false, + type: { kind: 'scalar', codecId: 'pg/text@1', values: ['home', 'work'] }, + }, + geo: { + nullable: false, + many: { elementNullable: true }, + type: { + kind: 'valueObject', + valueObject: { + fields: { + lat: { + nullable: false, + many: false, + type: { kind: 'scalar', codecId: 'pg/text@1', values: undefined }, + }, + }, + }, + }, + }, + }, + }); + }); + + it('accepts any object where a value object that contains itself refers back to itself', () => { + const storage = storageWith({ + valueObject: { + NavItem: { + fields: { + label: text, + children: nested('NavItem', { many: { elementNullable: false } }), + }, + }, + }, + }); + + expect(resolveValueObject(storage, valueObjectRef('NavItem'))).toEqual({ + fields: { + label: { + nullable: false, + many: false, + type: { kind: 'scalar', codecId: 'pg/text@1', values: undefined }, + }, + children: { + nullable: false, + many: { elementNullable: false }, + type: { kind: 'openObject' }, + }, + }, + }); + }); + + it('accepts any object at the first reference back into a group of value objects that contain each other', () => { + const storage = storageWith({ + valueObject: { + Folder: { fields: { name: text, entry: nested('Entry'), meta: nested('Meta') } }, + Entry: { fields: { parent: nested('Folder') } }, + Meta: { fields: { owner: text } }, + }, + }); + + expect(resolveValueObject(storage, valueObjectRef('Folder'))).toEqual({ + fields: { + name: { + nullable: false, + many: false, + type: { kind: 'scalar', codecId: 'pg/text@1', values: undefined }, + }, + entry: { nullable: false, many: false, type: { kind: 'openObject' } }, + meta: { + nullable: false, + many: false, + type: { + kind: 'valueObject', + valueObject: { + fields: { + owner: { + nullable: false, + many: false, + type: { kind: 'scalar', codecId: 'pg/text@1', values: undefined }, + }, + }, + }, + }, + }, + }, + }); + }); + + it('refuses a reference storage does not hold', () => { + expect(() => resolveValueObject(storageWith({}), valueObjectRef('Missing'))).toThrow(/Missing/); + }); +}); diff --git a/packages/2-sql-family/1-core/contract/tsdown.config.ts b/packages/2-sql-family/1-core/contract/tsdown.config.ts index 23255964971e..6d0adefd1619 100644 --- a/packages/2-sql-family/1-core/contract/tsdown.config.ts +++ b/packages/2-sql-family/1-core/contract/tsdown.config.ts @@ -3,6 +3,7 @@ import { defineConfig } from '@repo/tsdown'; export default defineConfig({ entry: [ 'src/exports/authored-check-naming.ts', + 'src/exports/check-expressions.ts', 'src/exports/contract-view.ts', 'src/exports/data-type.ts', 'src/exports/data-type-support.ts', diff --git a/packages/2-sql-family/1-core/schema-ir/src/exports/naming.ts b/packages/2-sql-family/1-core/schema-ir/src/exports/naming.ts index df5d615cad41..0a9871c6cfd5 100644 --- a/packages/2-sql-family/1-core/schema-ir/src/exports/naming.ts +++ b/packages/2-sql-family/1-core/schema-ir/src/exports/naming.ts @@ -1,5 +1,6 @@ export { assertWireNamePrefixLength, + CHECK_KINDS, type CheckKind, composeCheckWirePrefix, computeCheckContentHash, diff --git a/packages/2-sql-family/1-core/schema-ir/src/naming.ts b/packages/2-sql-family/1-core/schema-ir/src/naming.ts index d16bfe124a44..5a05a0ff5518 100644 --- a/packages/2-sql-family/1-core/schema-ir/src/naming.ts +++ b/packages/2-sql-family/1-core/schema-ir/src/naming.ts @@ -158,12 +158,13 @@ export function computeCheckContentHash(expression: string): string { * The canonical spelling of this union — every layer that can depend on this * module imports it rather than re-spelling the literals. */ -export type CheckKind = 'membership' | 'elementNotNull'; +export type CheckKind = 'membership' | 'elementNotNull' | 'valueObject'; /** The trailing segment a check's wire-name prefix carries, per kind. */ const CHECK_KIND_SUFFIX = { membership: 'check', elementNotNull: 'elem_not_null', + valueObject: 'value_object', } as const; /** @@ -185,7 +186,7 @@ export function composeCheckWirePrefix( * Every {@link CheckKind} value, read off {@link CHECK_KIND_SUFFIX}'s own keys * so the enumeration can never drift out of step with it. */ -const CHECK_KINDS: readonly CheckKind[] = Object.keys(CHECK_KIND_SUFFIX).filter( +export const CHECK_KINDS: readonly CheckKind[] = Object.keys(CHECK_KIND_SUFFIX).filter( (kind): kind is CheckKind => kind in CHECK_KIND_SUFFIX, ); diff --git a/packages/2-sql-family/1-core/schema-ir/test/naming.test.ts b/packages/2-sql-family/1-core/schema-ir/test/naming.test.ts index 564de288daeb..56741d932bfc 100644 --- a/packages/2-sql-family/1-core/schema-ir/test/naming.test.ts +++ b/packages/2-sql-family/1-core/schema-ir/test/naming.test.ts @@ -92,13 +92,15 @@ describe('the naming union', () => { }); describe('composeCheckWirePrefix', () => { - it('spells the membership and element-not-null kinds distinctly', () => { + it('spells each kind distinctly', () => { expect({ membership: composeCheckWirePrefix('User', 'role', 'membership'), elementNotNull: composeCheckWirePrefix('User', 'tags', 'elementNotNull'), + valueObject: composeCheckWirePrefix('User', 'address', 'valueObject'), }).toEqual({ membership: 'User_role_check', elementNotNull: 'User_tags_elem_not_null', + valueObject: 'User_address_value_object', }); }); @@ -314,8 +316,10 @@ describe('derivedCheckPrefixes', () => { new Set([ composeCheckWirePrefix('User', 'role', 'membership'), composeCheckWirePrefix('User', 'role', 'elementNotNull'), + composeCheckWirePrefix('User', 'role', 'valueObject'), composeCheckWirePrefix('User', 'tags', 'membership'), composeCheckWirePrefix('User', 'tags', 'elementNotNull'), + composeCheckWirePrefix('User', 'tags', 'valueObject'), ]), ); }); @@ -339,6 +343,7 @@ describe('derivedCheckPrefixes', () => { new Set([ 'custom_oauth_providers_acceptable_client_ids_check', 'custom_oauth_providers_acceptable_client_ids_elem_not_', + 'custom_oauth_providers_acceptable_client_ids_value_obj', ]), ); }); diff --git a/packages/2-sql-family/2-authoring/contract-psl/test/fixtures.ts b/packages/2-sql-family/2-authoring/contract-psl/test/fixtures.ts index a2e3684bf20e..5f9be49918a3 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/test/fixtures.ts +++ b/packages/2-sql-family/2-authoring/contract-psl/test/fixtures.ts @@ -35,6 +35,7 @@ import { withSeedDiagnostics } from '@internal/psl-parser/interpret'; import type { DocumentAst, PslSources, SourceFile } from '@internal/psl-parser/syntax'; import { parse } from '@internal/psl-parser/syntax'; import { bindPslSchema } from '@internal/psl-parser/test'; +import type { CheckExpressionInput } from '@internal/sql-contract/check-expressions'; import type { SqlNamespaceBase, SqlNamespaceInput } from '@internal/sql-contract/types'; import { type EnumTypeHandle, enumType } from '@internal/sql-contract-ts/contract-builder'; import type { Result } from '@internal/utils/result'; @@ -128,13 +129,7 @@ export const sqliteEnumInferenceCodecs = { * fixture without one emits no checks at all and every assertion about checks * would hold vacuously — the PSL/TS parity test in particular. */ -export function testRenderCheckExpressions(input: { - readonly tableName: string; - readonly columnName: string; - readonly many: boolean; - readonly elementNullable: boolean; - readonly memberValues: readonly (string | number)[] | undefined; -}): ReadonlyArray<{ +export function testRenderCheckExpressions(input: CheckExpressionInput): ReadonlyArray<{ readonly kind: 'membership' | 'elementNotNull'; readonly columnName: string; readonly expression: string; @@ -160,7 +155,7 @@ export function testRenderCheckExpressions(input: { : `${column} IN (${members})`, }); } - if (input.many && !input.elementNullable) { + if (input.many !== false && !input.many.elementNullable) { candidates.push({ kind: 'elementNotNull', columnName: input.columnName, diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts b/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts index c7f6172de477..12f4d0b77d41 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts @@ -12,6 +12,8 @@ import { coreHash, crossRef, type ExecutionMutationDefault, + effectiveControlPolicy, + type StorageBase, type StorageHashBase, } from '@internal/contract/types'; import { encodeEnumTypeMembers } from '@internal/contract-authoring'; @@ -27,10 +29,12 @@ import type { import { mergeCapabilityMatrices } from '@internal/framework-components/components'; import { UNBOUND_NAMESPACE_ID } from '@internal/framework-components/ir'; import { sqlContractCanonicalizationHooks } from '@internal/sql-contract/canonicalization-hooks'; +import type { CheckExpressionRenderer } from '@internal/sql-contract/check-expressions'; import { sqlDataTypeOfCodec } from '@internal/sql-contract/data-type'; import { validateIndexTypes } from '@internal/sql-contract/index-type-validation'; import { type IndexTypeRegistry, indexTypeRegistryOf } from '@internal/sql-contract/index-types'; import { + CheckConstraint, type SqlNamespaceInput, SqlStorage, type SqlStorageInput, @@ -47,7 +51,8 @@ import { contractError } from './contract-errors'; import { describeModel, type ModelDescription } from './describe-model'; import { describeTableNode } from './describe-table-node'; import { buildValueObjectMember } from './domain-fields'; -import { lowerTable, type TableLoweringContext } from './lower-table'; +import { lowerRenderedChecks } from './lower-column'; +import { inCanonicalOrder, lowerTable, type TableLoweringContext } from './lower-table'; import { mergeTables } from './merge-tables'; import { modelLookupsOf, modelNamespaceId } from './model-references'; import { namespaceIdOrDefault } from './namespace-id'; @@ -175,6 +180,29 @@ function storageValueObjects( ); } +/** The value objects and value sets storage will hold, in the shape storage readers take. */ +function valueObjectStorageOf( + defaultNamespaceId: string, + valueObjects: Record | undefined, + storageValueSetsByNs: Readonly>>, +): Pick { + const namespaceIds = new Set([defaultNamespaceId, ...Object.keys(storageValueSetsByNs)]); + return { + namespaces: Object.fromEntries( + [...namespaceIds].map((id) => [ + id, + { + id, + entries: { + valueSet: storageValueSetsByNs[id] ?? {}, + ...(id === defaultNamespaceId ? { valueObject: valueObjects ?? {} } : {}), + }, + }, + ]), + ), + }; +} + /** * Writes the value set of each discriminator, keyed `.` in the base table's namespace and holding the variants' values as the column stores them, sorted, and points the discriminator column at it (ADR 278). A column an enum types keeps the enum's value set. An enum with the same name in the same namespace is refused, since the two value sets would share one entry. */ @@ -182,6 +210,7 @@ function writeDiscriminatorValueSets( definition: ContractDefinition, tablesByNamespace: Record>, storageValueSetsByNs: Record>, + checkRendererFor: (table: StorageTableInput) => CheckExpressionRenderer | undefined, ): void { const defaultNamespaceId = definition.target.defaultNamespaceId; for (const model of definition.models) { @@ -211,11 +240,34 @@ function writeDiscriminatorValueSets( }, ); } - namespaceValueSets[valueSetName] = { kind: 'valueSet', values: Object.keys(variants).sort() }; + const values = Object.keys(variants).sort(); + namespaceValueSets[valueSetName] = { kind: 'valueSet', values }; storageValueSetsByNs[namespaceId] = namespaceValueSets; + const render = checkRendererFor(table); + const derivedChecks = + render === undefined + ? [] + : lowerRenderedChecks( + model.tableName, + render({ + tableName: model.tableName, + columnName, + many: false, + memberValues: values, + valueObjectField: undefined, + }), + ); const namespaceTables = tablesByNamespace[namespaceId] ?? {}; namespaceTables[model.tableName] = { ...table, + ...(derivedChecks.length > 0 + ? { + checks: inCanonicalOrder([ + ...(table.checks ?? []).map((check) => CheckConstraint.from(check)), + ...derivedChecks, + ]), + } + : {}), columns: { ...table.columns, [columnName]: { @@ -308,11 +360,18 @@ export function buildSqlContractFromDefinition( // producer's) and flush once, threshold-batched by code. const authoringWarnings: AuthoringWarning[] = [...(definition.warnings ?? [])]; const collectedColumnEntities: CollectedColumnEntities = {}; + const valueObjects = storageValueObjects(definition, defaultNamespaceId); + const renderCheckExpressions = resolveCheckExpressionRenderer(definition.target); const tableLoweringContext: TableLoweringContext = { lookups, defaultNamespaceId, qualifyColumnType, - renderCheckExpressions: resolveCheckExpressionRenderer(definition.target), + renderCheckExpressions, + valueObjectStorage: valueObjectStorageOf( + defaultNamespaceId, + valueObjects, + storageValueSetsByNs, + ), collectedColumnEntities, defaultControlPolicy: definition.defaultControlPolicy, indexTypeRegistry, @@ -327,8 +386,12 @@ export function buildSqlContractFromDefinition( namespaceTables[table.tableName] = lowerTable(table, tableLoweringContext); tablesByNamespace[table.namespaceId] = namespaceTables; } - writeDiscriminatorValueSets(definition, tablesByNamespace, storageValueSetsByNs); - const valueObjects = storageValueObjects(definition, defaultNamespaceId); + writeDiscriminatorValueSets(definition, tablesByNamespace, storageValueSetsByNs, (table) => + renderCheckExpressions !== undefined && + effectiveControlPolicy(table.control, definition.defaultControlPolicy) === 'managed' + ? renderCheckExpressions + : undefined, + ); const executionDefaults: ExecutionMutationDefault[] = components.flatMap( (c) => c.domain.executionDefaults, diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/describe-column.ts b/packages/2-sql-family/2-authoring/contract-ts/src/describe-column.ts index ec2567faacf9..7c430a02558b 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/describe-column.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/describe-column.ts @@ -11,6 +11,7 @@ export function describeColumn(column: ColumnNode, site: ColumnSite): ColumnDesc many: column.many === true ? { elementNullable: column.elementNullable === true } : false, default: column.default, enumTypeHandle: column.enumTypeHandle, + valueObject: undefined, site, }; } diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/describe-model.ts b/packages/2-sql-family/2-authoring/contract-ts/src/describe-model.ts index eb7d66b6995e..14348b001770 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/describe-model.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/describe-model.ts @@ -13,7 +13,7 @@ import { } from './contract-definition'; import { contractError } from './contract-errors'; import { describeColumn } from './describe-column'; -import { buildDomainField } from './domain-fields'; +import { buildDomainField, valueObjectReference } from './domain-fields'; import { modelNamespaceId } from './model-references'; import { lowerRelations } from './model-relations'; import { type ForeignKeyResolutionContext, resolveForeignKey } from './resolve-foreign-key'; @@ -89,7 +89,7 @@ export function describeModel( context, ), ); - const storage = describeModelStorage(model, namespaceId, foreignKeys); + const storage = describeModelStorage(model, namespaceId, foreignKeys, context.defaultNamespaceId); const storageFields: Record = {}; for (const [fieldName, columnName] of Object.entries(fieldToColumn)) { @@ -122,9 +122,12 @@ function describeModelStorage( model: ModelNode, namespaceId: string, foreignKeys: readonly ForeignKeyAuthoringInput[], + defaultNamespaceId: string, ): ModelStorage { const { tableName } = model; - const columns = model.fields.map((field) => describeFieldColumn(model.modelName, field)); + const columns = model.fields.map((field) => + describeFieldColumn(model.modelName, field, defaultNamespaceId), + ); if (model.sharesBaseTable) { if (model.checks && model.checks.length > 0) { throw contractError( @@ -152,18 +155,28 @@ function describeModelStorage( }; } -function describeFieldColumn(modelName: string, field: ModelField): ColumnDescription { +function describeFieldColumn( + modelName: string, + field: ModelField, + defaultNamespaceId: string, +): ColumnDescription { const site = { kind: 'field', modelName, fieldName: field.fieldName } as const; if (isValueObjectMember(field)) { - return describeColumn( - { - columnName: field.columnName, - descriptor: field.descriptor, - nullable: field.nullable, - ...ifDefined('default', field.default), + return { + ...describeColumn( + { + columnName: field.columnName, + descriptor: field.descriptor, + nullable: field.nullable, + ...ifDefined('default', field.default), + }, + site, + ), + valueObject: { + reference: valueObjectReference(field.valueObjectName, defaultNamespaceId), + many: field.many === true ? { elementNullable: field.elementNullable === true } : false, }, - site, - ); + }; } return describeColumn(field, site); } diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/domain-fields.ts b/packages/2-sql-family/2-authoring/contract-ts/src/domain-fields.ts index 85929cf0d560..1943c82a2f00 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/domain-fields.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/domain-fields.ts @@ -1,4 +1,8 @@ -import type { ContractField, ContractValueObjectField } from '@internal/contract/types'; +import type { + ContractField, + ContractValueObjectField, + EntityCoordinate, +} from '@internal/contract/types'; import { nonEmptyTypeParams } from '@internal/sql-contract/types'; import { ifDefined } from '@internal/utils/defined'; import { @@ -10,6 +14,19 @@ import { type EnumValueSetRefs, enumValueSetRefs } from './enum-members'; type Member = ScalarMemberNode | ValueObjectMemberNode; +/** The storage coordinate of a value object. Value objects are all in the default namespace. */ +export function valueObjectReference( + valueObjectName: string, + defaultNamespaceId: string, +): EntityCoordinate<'storage', 'valueObject'> { + return { + plane: 'storage', + namespaceId: defaultNamespaceId, + entityKind: 'valueObject', + entityName: valueObjectName, + }; +} + function fieldWithoutValueSet( field: Member, defaultNamespaceId: string, @@ -19,10 +36,7 @@ function fieldWithoutValueSet( return { type: { kind: 'valueObject', - plane: 'storage', - namespaceId: defaultNamespaceId, - entityKind: 'valueObject', - entityName: field.valueObjectName, + ...valueObjectReference(field.valueObjectName, defaultNamespaceId), }, nullable: field.nullable, many, diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/lower-column.ts b/packages/2-sql-family/2-authoring/contract-ts/src/lower-column.ts index 9799134aa642..f968a5a995b1 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/lower-column.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/lower-column.ts @@ -1,5 +1,11 @@ +import type { StorageBase } from '@internal/contract/types'; import type { CodecLookupWithDescriptors, TypeLookups } from '@internal/framework-components/codec'; import { dataTypeParams } from '@internal/framework-components/codec'; +import { + type CheckExpressionCandidate, + type CheckExpressionRenderer, + resolveValueObject, +} from '@internal/sql-contract/check-expressions'; import { type SqlDataType, sqlDataTypeOfCodec, @@ -19,12 +25,8 @@ import { contractError } from './contract-errors'; import { type ColumnSite, columnSiteMeta, columnSiteSubject } from './declaration-sites'; import { checkMemberValues, enumValueSetRefs } from './enum-members'; import { type CollectedColumnEntities, collectEntityFromColumn } from './pack-entities'; -import type { ColumnDescription } from './storage-description'; -import { - type CheckExpressionRenderer, - type ColumnTypeQualifier, - resolveColumnDescriptor, -} from './target-authoring-hooks'; +import type { ColumnDescription, ColumnValueObject } from './storage-description'; +import { type ColumnTypeQualifier, resolveColumnDescriptor } from './target-authoring-hooks'; /** * Names the target's rendered checks and lowers them into contract entities. @@ -37,13 +39,9 @@ import { * rather than having to assume something about SQL text it declares itself * unable to read. */ -function lowerRenderedChecks( +export function lowerRenderedChecks( tableName: string, - candidates: ReadonlyArray<{ - readonly kind: 'membership' | 'elementNotNull'; - readonly columnName: string; - readonly expression: string; - }>, + candidates: ReadonlyArray, ): CheckConstraint[] { return candidates.map( (candidate) => @@ -80,6 +78,8 @@ export interface ColumnLoweringContext { readonly defaultNamespaceId: string; readonly qualifyColumnType: ColumnTypeQualifier | undefined; readonly renderCheckExpressions: CheckExpressionRenderer | undefined; + /** The value objects and value sets storage will hold, which a value object column's check is derived from. */ + readonly valueObjectStorage: Pick; /** Receives the pack entity a column's type constructor refers to. */ readonly collectedColumnEntities: CollectedColumnEntities; } @@ -162,10 +162,28 @@ export function lowerColumn( const candidates = render({ tableName: placement.tableName, columnName: description.columnName, - many: many !== false, - elementNullable, + many, memberValues: enumTypeHandle !== undefined ? checkMemberValues(enumTypeHandle, codecLookup) : undefined, + valueObjectField: + description.valueObject !== undefined + ? resolvedValueObjectField(description.valueObject, description.nullable, context) + : undefined, }); return { column, derivedChecks: lowerRenderedChecks(placement.tableName, candidates) }; } + +function resolvedValueObjectField( + valueObject: ColumnValueObject, + nullable: boolean, + context: ColumnLoweringContext, +) { + return { + nullable, + many: valueObject.many, + type: { + kind: 'valueObject', + valueObject: resolveValueObject(context.valueObjectStorage, valueObject.reference), + }, + } as const; +} diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/lower-table.ts b/packages/2-sql-family/2-authoring/contract-ts/src/lower-table.ts index 68bcc328d874..eeb4b01b6881 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/lower-table.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/lower-table.ts @@ -12,7 +12,7 @@ import { type StorageTableInput, } from '@internal/sql-contract/types'; import { - type CheckKind, + CHECK_KINDS, composeCheckWirePrefix, derivedCheckPrefixes, } from '@internal/sql-schema-ir/naming'; @@ -23,8 +23,6 @@ import { contractError } from './contract-errors'; import { type ColumnLoweringContext, lowerColumn } from './lower-column'; import type { TableDescription } from './storage-description'; -const DERIVABLE_CHECK_KINDS: readonly CheckKind[] = ['membership', 'elementNotNull']; - /** * Which of `columnNames` could have produced `prefix` for some * {@link CheckKind} — the reverse of {@link derivedCheckPrefixes}, used only @@ -38,9 +36,7 @@ function columnsProducingCheckPrefix( prefix: string, ): readonly string[] { return columnNames.filter((columnName) => - DERIVABLE_CHECK_KINDS.some( - (kind) => composeCheckWirePrefix(tableName, columnName, kind) === prefix, - ), + CHECK_KINDS.some((kind) => composeCheckWirePrefix(tableName, columnName, kind) === prefix), ); } @@ -117,7 +113,7 @@ export function lowerTable( /** * The checks sorted by name in UTF-16 code-unit order, the order contract canonicalization gives them. The table's storage is then the same whichever declaration each column came from and in whatever order the columns were listed. */ -function inCanonicalOrder(checks: readonly CheckConstraint[]): readonly CheckConstraint[] { +export function inCanonicalOrder(checks: readonly CheckConstraint[]): readonly CheckConstraint[] { return [...checks].sort((a, b) => compareCodeUnits(a.name, b.name)); } diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/storage-description.ts b/packages/2-sql-family/2-authoring/contract-ts/src/storage-description.ts index 5b189dfac6db..3e5694db0183 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/storage-description.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/storage-description.ts @@ -1,4 +1,4 @@ -import type { ControlPolicy } from '@internal/contract/types'; +import type { ControlPolicy, EntityCoordinate } from '@internal/contract/types'; import type { EnumTypeHandle } from '@internal/contract-authoring'; import type { ColumnTypeDescriptor } from '@internal/framework-components/codec'; import type { ForeignKeyAuthoringInput } from '@internal/sql-contract/foreign-key-materialization'; @@ -20,9 +20,16 @@ export interface ColumnDescription { readonly default: AuthoredColumnDefault | undefined; /** The `enumType()` handle that types the column, which gives it a storage value set and a membership check. */ readonly enumTypeHandle: EnumTypeHandle | undefined; + /** The value object the column holds, and whether the field is a list of them, stored as one JSON array. */ + readonly valueObject: ColumnValueObject | undefined; readonly site: ColumnSite; } +export interface ColumnValueObject { + readonly reference: EntityCoordinate<'storage', 'valueObject'>; + readonly many: false | { readonly elementNullable: boolean }; +} + /** One table, before lowering. It holds every table-level property a model or table node can state, and its foreign keys already name their target tables. */ export interface TableDescription { readonly namespaceId: string; diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/target-authoring-hooks.ts b/packages/2-sql-family/2-authoring/contract-ts/src/target-authoring-hooks.ts index aa9bc6089f0d..b10b4d84fe6c 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/target-authoring-hooks.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/target-authoring-hooks.ts @@ -1,5 +1,6 @@ import type { AuthoringContributions } from '@internal/framework-components/authoring'; import type { ColumnTypeDescriptor } from '@internal/framework-components/codec'; +import type { CheckExpressionRenderer } from '@internal/sql-contract/check-expressions'; import { ifDefined } from '@internal/utils/defined'; import type { ContractDefinition } from './contract-definition'; @@ -61,31 +62,6 @@ function hasColumnTypeQualifier( return 'qualifyColumnType' in authoring && typeof authoring.qualifyColumnType === 'function'; } -/** - * A target's contract-construction-time check renderer, contributed through - * `target.authoring.renderCheckExpressions`. Given one column's shape it - * returns the checks that column needs, each as a kind, the column it - * constrains, and an opaque predicate body (no surrounding `CHECK (…)`). The - * target owns the SQL — quoting, escaping, predicate choice — and nothing - * else: naming is composed here, so the family never depends on a property of - * text it cannot read. `memberValues` is - * supplied only for a domain enum authored through an `enumType()` handle, so - * a target cannot accidentally write a membership check for a column whose - * native type already enforces its member set. Targets without the hook write - * no checks. - */ -export type CheckExpressionRenderer = (input: { - readonly tableName: string; - readonly columnName: string; - readonly many: boolean; - readonly elementNullable: boolean; - readonly memberValues: readonly (string | number)[] | undefined; -}) => ReadonlyArray<{ - readonly kind: 'membership' | 'elementNotNull'; - readonly columnName: string; - readonly expression: string; -}>; - /** * Structural check for a target that contributes a `renderCheckExpressions` * hook, duck-typed for the same reason {@link hasColumnTypeQualifier} is: the @@ -101,6 +77,9 @@ function hasCheckExpressionRenderer( ); } +/** + * The target's check renderer, contributed through `target.authoring.renderCheckExpressions`. The target owns the SQL of each check and the family names it, so the family never depends on a property of text it cannot read. `memberValues` is supplied only for a domain enum authored through an `enumType()` handle and for a discriminator column, so a target cannot write a membership check for a column whose native type already enforces its member set. Targets without the hook write no checks. + */ export function resolveCheckExpressionRenderer( target: ContractDefinition['target'], ): CheckExpressionRenderer | undefined { diff --git a/packages/2-sql-family/2-authoring/contract-ts/test/check-constraint.authoring.test.ts b/packages/2-sql-family/2-authoring/contract-ts/test/check-constraint.authoring.test.ts index 0b413afff738..8bf20a11a0e5 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/test/check-constraint.authoring.test.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/test/check-constraint.authoring.test.ts @@ -2,6 +2,7 @@ import { applySpecifierDefaultControlPolicy } from '@internal/contract/apply-spe import type { Contract, ControlPolicy } from '@internal/contract/types'; import { emptyCodecLookup } from '@internal/framework-components/codec'; import type { FamilyPackRef, TargetPackRef } from '@internal/framework-components/components'; +import type { CheckExpressionInput } from '@internal/sql-contract/check-expressions'; import { sql } from '@internal/sql-contract/sql-expression'; import { CheckConstraint, @@ -44,13 +45,7 @@ const sqlFamilyPack = { }, } as const satisfies FamilyPackRef<'sql'>; -interface RenderInput { - readonly tableName: string; - readonly columnName: string; - readonly many: boolean; - readonly elementNullable: boolean; - readonly memberValues: readonly (string | number)[] | undefined; -} +type RenderInput = CheckExpressionInput; /** * Stands in for the Postgres pack's `renderCheckExpressions`, reproducing the @@ -87,7 +82,7 @@ function renderCheckExpressions(input: RenderInput): ReadonlyArray<{ : `${column} IN (${members})`, }); } - if (input.many && !input.elementNullable) { + if (input.many !== false && !input.many.elementNullable) { candidates.push({ kind: 'elementNotNull', columnName: input.columnName, @@ -311,8 +306,8 @@ describe('check emission — entity-ref-resolved value set (native enum shape)', tableName: 'User', columnName: 'role', many: false, - elementNullable: false, memberValues: undefined, + valueObjectField: undefined, }); }); }); @@ -512,9 +507,9 @@ describe('check emission — guards', () => { expect(hookCalls.at(-1)).toEqual({ tableName: 'User', columnName: 'special', - many, - elementNullable: false, + many: many ? { elementNullable: false } : false, memberValues: [1.5, 'NaN'], + valueObjectField: undefined, }); }, ); diff --git a/packages/2-sql-family/2-authoring/contract-ts/test/contract-builder.derived-checks.test.ts b/packages/2-sql-family/2-authoring/contract-ts/test/contract-builder.derived-checks.test.ts new file mode 100644 index 000000000000..220bc74fe3b6 --- /dev/null +++ b/packages/2-sql-family/2-authoring/contract-ts/test/contract-builder.derived-checks.test.ts @@ -0,0 +1,260 @@ +import { crossRef } from '@internal/contract/types'; +import type { CheckExpressionInput } from '@internal/sql-contract/check-expressions'; +import { composeCheckWirePrefix } from '@internal/sql-schema-ir/naming'; +import { describe, expect, it } from 'vitest'; +import { createTestSqlNamespace } from '../../../1-core/contract/test/test-support'; +import { testTypeLookups } from '../../../1-core/contract/test/test-type-lookups'; +import { buildSqlContractFromDefinition } from '../src/contract-builder'; +import type { ContractDefinition, ModelNode } from '../src/contract-definition'; +import { enumType, member } from '../src/enum-type'; + +const renderedInputs: CheckExpressionInput[] = []; + +function renderCheckExpressions(input: CheckExpressionInput) { + renderedInputs.push(input); + return [ + ...(input.memberValues === undefined + ? [] + : [ + { + kind: 'membership' as const, + columnName: input.columnName, + expression: `"${input.columnName}" IN (${input.memberValues.join(', ')})`, + }, + ]), + ...(input.valueObjectField === undefined + ? [] + : [ + { + kind: 'valueObject' as const, + columnName: input.columnName, + expression: `shape of "${input.columnName}"`, + }, + ]), + ]; +} + +const targetPack = { + kind: 'target', + id: 'postgres', + familyId: 'sql', + targetId: 'postgres', + version: '0.0.1', + defaultNamespaceId: 'public', + authoring: { field: {}, renderCheckExpressions }, +} as const; + +const int4 = { codecId: 'pg/int4@1' } as const; +const text = { codecId: 'pg/text@1' } as const; +const jsonb = { codecId: 'pg/jsonb@1' } as const; +const idField = { fieldName: 'id', columnName: 'id', descriptor: int4, nullable: false } as const; + +const Kind = enumType('Kind', text, member('Home', 'home'), member('Work', 'work')); + +function definition(models: readonly ModelNode[]): ContractDefinition { + return { + warnings: undefined, + target: targetPack, + createNamespace: createTestSqlNamespace, + enums: { Kind }, + models, + valueObjects: [ + { + name: 'Address', + fields: [ + { fieldName: 'street', descriptor: text, nullable: false }, + { fieldName: 'kind', descriptor: text, nullable: true, enumTypeHandle: Kind }, + { fieldName: 'geo', valueObjectName: 'Geo', nullable: false }, + ], + }, + { name: 'Geo', fields: [{ fieldName: 'lat', descriptor: text, nullable: false }] }, + ], + }; +} + +function userModel(control?: 'external'): ModelNode { + return { + modelName: 'User', + tableName: 'user', + fields: [ + idField, + { + fieldName: 'home', + columnName: 'home', + valueObjectName: 'Address', + descriptor: jsonb, + nullable: true, + }, + { + fieldName: 'stops', + columnName: 'stops', + valueObjectName: 'Address', + descriptor: jsonb, + nullable: false, + many: true, + elementNullable: true, + }, + ], + id: { columns: ['id'] }, + ...(control !== undefined ? { control } : {}), + }; +} + +function build(models: readonly ModelNode[]) { + renderedInputs.length = 0; + return buildSqlContractFromDefinition( + definition(models), + testTypeLookups.codecLookup, + testTypeLookups.dataTypeLookup, + ); +} + +function checkNames(contract: ReturnType, tableName: string) { + return (contract.storage.namespaces['public']?.entries.table?.[tableName]?.checks ?? []).map( + (check) => check.prefix, + ); +} + +const resolvedAddress = { + kind: 'valueObject', + valueObject: { + fields: { + street: { + nullable: false, + many: false, + type: { kind: 'scalar', codecId: 'pg/text@1', values: undefined }, + }, + kind: { + nullable: true, + many: false, + type: { kind: 'scalar', codecId: 'pg/text@1', values: ['home', 'work'] }, + }, + geo: { + nullable: false, + many: false, + type: { + kind: 'valueObject', + valueObject: { + fields: { + lat: { + nullable: false, + many: false, + type: { kind: 'scalar', codecId: 'pg/text@1', values: undefined }, + }, + }, + }, + }, + }, + }, + }, +} as const; + +describe('the derived check of a column that holds a value object', () => { + it('hands the renderer the value object resolved from storage, with the field nullability and list shape', () => { + build([userModel()]); + expect( + renderedInputs + .filter((input) => input.valueObjectField !== undefined) + .map(({ columnName, many, valueObjectField }) => ({ columnName, many, valueObjectField })), + ).toEqual([ + { + columnName: 'home', + many: false, + valueObjectField: { nullable: true, many: false, type: resolvedAddress }, + }, + { + columnName: 'stops', + many: false, + valueObjectField: { + nullable: false, + many: { elementNullable: true }, + type: resolvedAddress, + }, + }, + ]); + }); + + it('is named after the table, the column and the value object kind', () => { + expect(checkNames(build([userModel()]), 'user')).toEqual([ + composeCheckWirePrefix('user', 'home', 'valueObject'), + composeCheckWirePrefix('user', 'stops', 'valueObject'), + ]); + }); + + it('is not derived for a table that is not managed', () => { + expect(checkNames(build([userModel('external')]), 'user')).toEqual([]); + }); + + it('refuses an authored check whose name a value object check would use', () => { + const model = { + ...userModel(), + checks: [ + { + expression: 'true', + map: undefined, + name: composeCheckWirePrefix('user', 'home', 'valueObject'), + }, + ], + }; + expect(() => build([model])).toThrow(/derived enforcement check would use for column "home"/); + }); +}); + +function taskModels(options: { readonly typedByEnum?: boolean } = {}): readonly ModelNode[] { + return [ + { + modelName: 'Task', + tableName: 'task', + fields: [ + idField, + { + fieldName: 'type', + columnName: 'type', + descriptor: text, + nullable: false, + ...(options.typedByEnum === true ? { enumTypeHandle: Kind } : {}), + }, + ], + id: { columns: ['id'] }, + discriminator: { field: 'type' }, + variants: { work: crossRef('Bug', 'public'), home: crossRef('Feature', 'public') }, + }, + ...['Bug', 'Feature'].map((modelName) => ({ + modelName, + tableName: 'task', + fields: [], + sharesBaseTable: true, + base: crossRef('Task', 'public'), + })), + ]; +} + +describe('the derived check of a discriminator column', () => { + it('is a membership check holding the variant values, sorted', () => { + const contract = build(taskModels()); + expect(contract.storage.namespaces['public']?.entries.table?.['task']?.checks).toEqual([ + expect.objectContaining({ + prefix: composeCheckWirePrefix('task', 'type', 'membership'), + expression: '"type" IN (home, work)', + }), + ]); + }); + + it('is only the enum membership check when an enum types the discriminator', () => { + const contract = build(taskModels({ typedByEnum: true })); + expect({ + checks: checkNames(contract, 'task'), + memberValues: renderedInputs.map((input) => input.memberValues), + }).toEqual({ + checks: [composeCheckWirePrefix('task', 'type', 'membership')], + memberValues: [undefined, ['home', 'work']], + }); + }); + + it('is not derived for a table that is not managed', () => { + const [task, ...variants] = taskModels(); + expect( + checkNames(build([{ ...task, control: 'external' } as ModelNode, ...variants]), 'task'), + ).toEqual([]); + }); +}); diff --git a/packages/2-sql-family/2-authoring/contract-ts/test/fixtures/managed-user-contract.ts b/packages/2-sql-family/2-authoring/contract-ts/test/fixtures/managed-user-contract.ts index 7d4d6508029c..f258ce4eac00 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/test/fixtures/managed-user-contract.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/test/fixtures/managed-user-contract.ts @@ -1,4 +1,5 @@ import type { FamilyPackRef } from '@internal/framework-components/components'; +import type { CheckExpressionInput } from '@internal/sql-contract/check-expressions'; import { createTestSqlNamespace } from '../../../../1-core/contract/test/test-support'; import { testTypeLookups } from '../../../../1-core/contract/test/test-type-lookups'; import { defineContract } from '../../src/contract-builder'; @@ -19,13 +20,7 @@ const sqlFamilyPack = { }, } as const satisfies FamilyPackRef<'sql'>; -export function renderCheckExpressions(input: { - readonly tableName: string; - readonly columnName: string; - readonly many: boolean; - readonly elementNullable: boolean; - readonly memberValues: readonly string[] | undefined; -}): ReadonlyArray<{ +export function renderCheckExpressions(input: CheckExpressionInput): ReadonlyArray<{ readonly kind: 'membership' | 'elementNotNull'; readonly columnName: string; readonly expression: string; @@ -47,7 +42,7 @@ export function renderCheckExpressions(input: { : `${column} IN (${members})`, }); } - if (input.many && !input.elementNullable) { + if (input.many !== false && !input.many.elementNullable) { candidates.push({ kind: 'elementNotNull', columnName: input.columnName, diff --git a/packages/2-sql-family/2-authoring/contract-ts/test/merge-tables.test.ts b/packages/2-sql-family/2-authoring/contract-ts/test/merge-tables.test.ts index 30b54f25e6e7..9dfc89adea22 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/test/merge-tables.test.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/test/merge-tables.test.ts @@ -10,6 +10,7 @@ function column(modelName: string, columnName: string, nullable = true): ColumnD many: false, default: undefined, enumTypeHandle: undefined, + valueObject: undefined, site: { kind: 'field', modelName, fieldName: columnName }, }; } diff --git a/packages/2-sql-family/2-authoring/contract-ts/test/table-node-helpers.ts b/packages/2-sql-family/2-authoring/contract-ts/test/table-node-helpers.ts index bd6f129f2d88..72e714f630a7 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/test/table-node-helpers.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/test/table-node-helpers.ts @@ -1,4 +1,5 @@ import type { Contract } from '@internal/contract/types'; +import type { CheckExpressionInput } from '@internal/sql-contract/check-expressions'; import type { SqlStorage } from '@internal/sql-contract/types'; import { invariant } from '@internal/utils/assertions'; import { createTestSqlNamespace } from '../../../1-core/contract/test/test-support'; @@ -14,13 +15,7 @@ import { type ValueObjectFieldNode, } from '../src/contract-definition'; -interface RenderInput { - readonly tableName: string; - readonly columnName: string; - readonly many: boolean; - readonly elementNullable: boolean; - readonly memberValues: readonly (string | number)[] | undefined; -} +type RenderInput = CheckExpressionInput; function renderCheckExpressions(input: RenderInput): ReadonlyArray<{ readonly kind: 'membership' | 'elementNotNull'; @@ -38,7 +33,7 @@ function renderCheckExpressions(input: RenderInput): ReadonlyArray<{ }, ] : []), - ...(input.many && !input.elementNullable + ...(input.many !== false && !input.many.elementNullable ? [ { kind: 'elementNotNull' as const, diff --git a/packages/3-targets/postgres/1-target/src/core/check-expressions.ts b/packages/3-targets/postgres/1-target/src/core/check-expressions.ts index 63c69f3e0fc8..bc069709104b 100644 --- a/packages/3-targets/postgres/1-target/src/core/check-expressions.ts +++ b/packages/3-targets/postgres/1-target/src/core/check-expressions.ts @@ -1,41 +1,13 @@ -import type { CheckKind } from '@internal/sql-schema-ir/naming'; +import type { + CheckExpressionCandidate, + CheckExpressionInput, + ResolvedValueObject, + ResolvedValueObjectField, + ResolvedValueObjectFieldType, +} from '@internal/sql-contract/check-expressions'; import { invariant } from '@internal/utils/assertions'; import { escapeLiteral, quoteIdentifier } from './sql-utils'; - -/** - * What a rendered check enforces. The contract builder turns this into the - * wire-name prefix's trailing segment, so the kind stays readable in the - * physical constraint name. The vocabulary is the family's, so this names - * `CheckKind` rather than restating its members. - */ -export type PostgresCheckKind = CheckKind; - -/** - * One column's shape, as the contract builder knows it. `memberValues` is - * present only for a domain enum authored through an `enumType()` handle — - * a column bound to a native enum type carries its enforcement in the type - * itself and arrives here with `memberValues` undefined. - */ -export interface PostgresCheckExpressionInput { - readonly tableName: string; - readonly columnName: string; - readonly many: boolean | { readonly elementNullable: boolean }; - readonly memberValues: readonly (string | number)[] | undefined; -} - -/** - * A check the target wants written. The target contributes only what it - * uniquely knows — which predicate, spelled how — while the family owns the - * naming: it composes the prefix from the table, the column, and the kind, - * caps it, and appends the content hash. Nothing here is a name. - */ -export interface PostgresCheckExpressionCandidate { - readonly kind: PostgresCheckKind; - /** The column this predicate constrains; the family names the check after it. */ - readonly columnName: string; - /** Opaque SQL: the predicate body, without the surrounding `CHECK (…)`. */ - readonly expression: string; -} +import { valueObjectJsonTypeOfCodec } from './value-object-json-types'; /** * Renders the checks a Postgres column needs, as opaque predicate text. @@ -52,14 +24,12 @@ export interface PostgresCheckExpressionCandidate { * output instead, which for `inet` always carries the prefix length. */ export function postgresRenderCheckExpressions( - input: PostgresCheckExpressionInput, -): readonly PostgresCheckExpressionCandidate[] { - const candidates: PostgresCheckExpressionCandidate[] = []; + input: CheckExpressionInput, +): readonly CheckExpressionCandidate[] { + const candidates: CheckExpressionCandidate[] = []; const column = quoteIdentifier(input.columnName); const isMany = input.many !== false; - const elementNullable = - (typeof input.many === 'object' && input.many.elementNullable) || - ('elementNullable' in input && input.elementNullable === true); + const elementNullable = input.many !== false && input.many.elementNullable; if (input.memberValues !== undefined) { invariant( @@ -89,9 +59,105 @@ export function postgresRenderCheckExpressions( }); } + if (input.valueObjectField !== undefined) { + candidates.push({ + kind: 'valueObject', + columnName: input.columnName, + expression: valueObjectCheck(column, input.valueObjectField), + }); + } + return candidates; } +/** + * One strict `jsonpath` predicate over the whole column value. Strict mode keeps a comparison from unwrapping an array, and `IS TRUE` makes a predicate that evaluates to unknown fail the check, as a CHECK would otherwise pass on NULL. Member values are read through `keyvalue()`, so an absent member is never an error. + */ +function valueObjectCheck(column: string, field: ResolvedValueObjectField): string { + const predicate = fieldPredicate(field, '$'); + invariant(predicate !== undefined, 'a column that holds a value object always has a predicate'); + const match = (path: string) => + `jsonb_path_match(${column}, '${escapeLiteral(`strict ${path}`)}') IS TRUE`; + return field.nullable + ? `${column} IS NULL OR ${match(`$.type() == "null" || (${predicate})`)}` + : match(predicate); +} + +/** What the value at `path` must satisfy, or `undefined` when any value is accepted. */ +function fieldPredicate(field: ResolvedValueObjectField, path: string): string | undefined { + if (field.many === false) return valuePredicate(field.type, path); + const element = valuePredicate(field.type, '@'); + const { elementNullable } = field.many; + const invalidElement = + element === undefined + ? elementNullable + ? undefined + : '@.type() == "null"' + : elementNullable + ? `!(@.type() == "null" || (${element}))` + : `!((${element}))`; + const isArray = `${path}.type() == "array"`; + return invalidElement === undefined + ? isArray + : `${isArray} && !exists(${path}[*] ? (${invalidElement}))`; +} + +function valuePredicate(type: ResolvedValueObjectFieldType, path: string): string | undefined { + switch (type.kind) { + case 'valueObject': + return objectPredicate(type.valueObject, path); + case 'openObject': + return `${path}.type() == "object"`; + case 'scalar': { + if (type.values !== undefined) return valueSetPredicate(type.values, path); + const jsonType = valueObjectJsonTypeOfCodec(type.codecId); + return jsonType === undefined ? undefined : `${path}.type() == "${jsonType}"`; + } + } +} + +function valueSetPredicate(values: readonly (string | number)[], path: string): string { + const alternatives = (jsonType: 'string' | 'number', members: readonly (string | number)[]) => + `${path}.type() == "${jsonType}" && (${members.map((value) => `${path} == ${jsonPathLiteral(value)}`).join(' || ')})`; + const strings = values.filter((value) => typeof value === 'string'); + const numbers = values.filter((value) => typeof value === 'number'); + invariant(values.length > 0, 'a value set restricting a value object member has no values'); + if (numbers.length === 0) return alternatives('string', strings); + if (strings.length === 0) return alternatives('number', numbers); + return `(${alternatives('string', strings)}) || (${alternatives('number', numbers)})`; +} + +function objectPredicate(valueObject: ResolvedValueObject, path: string): string { + const names = Object.keys(valueObject.fields).sort(); + const members = names.flatMap((name) => { + const field = valueObject.fields[name]; + invariant(field !== undefined, `value object member "${name}" is missing`); + const isMember = `@.key == ${jsonPathLiteral(name)}`; + const predicate = fieldPredicate(field, '@.value'); + if (!field.nullable) { + return [ + predicate === undefined + ? `exists(${path}.keyvalue() ? (${isMember}))` + : `exists(${path}.keyvalue() ? (${isMember} && (${predicate})))`, + ]; + } + return predicate === undefined + ? [] + : [ + `!exists(${path}.keyvalue() ? (${isMember} && !(@.value.type() == "null" || (${predicate}))))`, + ]; + }); + const otherKey = + names.length === 0 + ? `!exists(${path}.keyvalue())` + : `!exists(${path}.keyvalue() ? (${names.map((name) => `@.key != ${jsonPathLiteral(name)}`).join(' && ')}))`; + return [`${path}.type() == "object"`, ...members, otherKey].join(' && '); +} + +function jsonPathLiteral(value: string | number): string { + return typeof value === 'string' ? JSON.stringify(value) : String(value); +} + function sqlLiteral(value: string | number): string { return typeof value === 'string' ? `'${escapeLiteral(value)}'` : String(value); } diff --git a/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-model-blocks.ts b/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-model-blocks.ts index 6fe197b11f1f..4b0e5bb54f17 100644 --- a/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-model-blocks.ts +++ b/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-model-blocks.ts @@ -209,8 +209,9 @@ function computeDerivedCheckNames(table: SqlTableIR): ReadonlySet { for (const candidate of postgresRenderCheckExpressions({ tableName: table.name, columnName: column.name, - many: column.many === true, + many: column.many === true ? { elementNullable: false } : false, memberValues: undefined, + valueObjectField: undefined, })) { const derivedName = formatWireName( composeCheckWirePrefix(table.name, column.name, candidate.kind), @@ -329,8 +330,9 @@ function enforcesElementNotNull( return postgresRenderCheckExpressions({ tableName: table.name, columnName: column.name, - many: true, + many: { elementNullable: false }, memberValues: undefined, + valueObjectField: undefined, }).some( (candidate) => candidate.kind === 'elementNotNull' && diff --git a/packages/3-targets/postgres/1-target/src/core/value-object-json-types.ts b/packages/3-targets/postgres/1-target/src/core/value-object-json-types.ts new file mode 100644 index 000000000000..983cea67d3cd --- /dev/null +++ b/packages/3-targets/postgres/1-target/src/core/value-object-json-types.ts @@ -0,0 +1,66 @@ +import { + PG_BOOL_CODEC_ID, + PG_CHAR_CODEC_ID, + PG_DATE_STRING_CODEC_ID, + PG_DATE_TEMPORAL_CODEC_ID, + PG_FLOAT_CODEC_ID, + PG_FLOAT4_CODEC_ID, + PG_FLOAT8_CODEC_ID, + PG_INT_CODEC_ID, + PG_INT2_CODEC_ID, + PG_INT4_CODEC_ID, + PG_INT8_NUMBER_CODEC_ID, + PG_TEXT_CODEC_ID, + PG_TIME_STRING_CODEC_ID, + PG_TIME_TEMPORAL_CODEC_ID, + PG_TIMESTAMP_STRING_CODEC_ID, + PG_TIMESTAMP_TEMPORAL_CODEC_ID, + PG_TIMESTAMPTZ_DATE_CODEC_ID, + PG_TIMESTAMPTZ_STRING_CODEC_ID, + PG_TIMESTAMPTZ_TEMPORAL_CODEC_ID, + PG_UUID_CODEC_ID, + PG_VARCHAR_CODEC_ID, + SQL_CHAR_CODEC_ID, + SQL_FLOAT_CODEC_ID, + SQL_INT_CODEC_ID, + SQL_TEXT_CODEC_ID, + SQL_VARCHAR_CODEC_ID, +} from './codec-ids'; + +type JsonType = 'string' | 'number' | 'boolean'; + +/** + * The JSON type a value object member of each codec has in the JSON the SQL ORM writes: the type its JavaScript value serializes to. JSON members hold any value, and a codec missing here is checked for presence only. + */ +const JSON_TYPE_OF_CODEC: Readonly> = { + [PG_TEXT_CODEC_ID]: 'string', + [PG_CHAR_CODEC_ID]: 'string', + [PG_VARCHAR_CODEC_ID]: 'string', + [SQL_TEXT_CODEC_ID]: 'string', + [SQL_CHAR_CODEC_ID]: 'string', + [SQL_VARCHAR_CODEC_ID]: 'string', + [PG_UUID_CODEC_ID]: 'string', + [PG_DATE_TEMPORAL_CODEC_ID]: 'string', + [PG_TIMESTAMP_TEMPORAL_CODEC_ID]: 'string', + [PG_TIMESTAMPTZ_TEMPORAL_CODEC_ID]: 'string', + [PG_TIME_TEMPORAL_CODEC_ID]: 'string', + [PG_DATE_STRING_CODEC_ID]: 'string', + [PG_TIMESTAMP_STRING_CODEC_ID]: 'string', + [PG_TIMESTAMPTZ_STRING_CODEC_ID]: 'string', + [PG_TIMESTAMPTZ_DATE_CODEC_ID]: 'string', + [PG_TIME_STRING_CODEC_ID]: 'string', + [PG_INT_CODEC_ID]: 'number', + [PG_INT2_CODEC_ID]: 'number', + [PG_INT4_CODEC_ID]: 'number', + [PG_INT8_NUMBER_CODEC_ID]: 'number', + [PG_FLOAT_CODEC_ID]: 'number', + [PG_FLOAT4_CODEC_ID]: 'number', + [PG_FLOAT8_CODEC_ID]: 'number', + [SQL_INT_CODEC_ID]: 'number', + [SQL_FLOAT_CODEC_ID]: 'number', + [PG_BOOL_CODEC_ID]: 'boolean', +}; + +export function valueObjectJsonTypeOfCodec(codecId: string): JsonType | undefined { + return Object.hasOwn(JSON_TYPE_OF_CODEC, codecId) ? JSON_TYPE_OF_CODEC[codecId] : undefined; +} diff --git a/packages/3-targets/postgres/1-target/src/exports/types.ts b/packages/3-targets/postgres/1-target/src/exports/types.ts index 89938d1a256b..8eb7180307ca 100644 --- a/packages/3-targets/postgres/1-target/src/exports/types.ts +++ b/packages/3-targets/postgres/1-target/src/exports/types.ts @@ -1,9 +1,4 @@ -export { - type PostgresCheckExpressionCandidate, - type PostgresCheckExpressionInput, - type PostgresCheckKind, - postgresRenderCheckExpressions, -} from '../core/check-expressions'; +export { postgresRenderCheckExpressions } from '../core/check-expressions'; export { PostgresNativeEnum, type PostgresNativeEnumInput } from '../core/postgres-native-enum'; export { PostgresRlsEnablement, diff --git a/packages/3-targets/postgres/1-target/test/check-expressions.integration.test.ts b/packages/3-targets/postgres/1-target/test/check-expressions.integration.test.ts index 631b7ce3aa65..8c881c74d720 100644 --- a/packages/3-targets/postgres/1-target/test/check-expressions.integration.test.ts +++ b/packages/3-targets/postgres/1-target/test/check-expressions.integration.test.ts @@ -14,8 +14,9 @@ describe('numeric membership checks in Postgres', () => { const checks = postgresRenderCheckExpressions({ tableName: 'members', columnName: 'value', - many, + many: many ? { elementNullable: false } : false, memberValues, + valueObjectField: undefined, }); await client.query( `CREATE TABLE members (value ${storageType}${many ? '[]' : ''}, ${checks.map(({ expression }) => `CHECK (${expression})`).join(', ')})`, @@ -59,6 +60,7 @@ describe('list membership checks in Postgres', () => { columnName, many: { elementNullable: false }, memberValues: members, + valueObjectField: undefined, }), ); await client.query( @@ -127,6 +129,7 @@ describe('list membership checks in Postgres', () => { columnName: many ? 'list' : 'scalar', many: many ? { elementNullable: false } : false, memberValues: members, + valueObjectField: undefined, }), ); await client.query( diff --git a/packages/3-targets/postgres/1-target/test/check-expressions.test.ts b/packages/3-targets/postgres/1-target/test/check-expressions.test.ts index d1dd10bc3dc3..c509d17e783e 100644 --- a/packages/3-targets/postgres/1-target/test/check-expressions.test.ts +++ b/packages/3-targets/postgres/1-target/test/check-expressions.test.ts @@ -1,11 +1,13 @@ +import type { CheckExpressionInput } from '@internal/sql-contract/check-expressions'; import { describe, expect, it } from 'vitest'; import { postgresRenderCheckExpressions } from '../src/core/check-expressions'; -const base = { +const base: CheckExpressionInput = { tableName: 'User', columnName: 'role', many: false, memberValues: undefined, + valueObjectField: undefined, }; describe('postgresRenderCheckExpressions', () => { @@ -153,6 +155,7 @@ describe('postgresRenderCheckExpressions', () => { columnName: 'sta"tus', many: false, memberValues: ["o'brien"], + valueObjectField: undefined, }), ).toEqual([ { kind: 'membership', columnName: 'sta"tus', expression: `"sta""tus" IN ('o''brien')` }, diff --git a/packages/3-targets/postgres/1-target/test/check-expressions.value-object.test.ts b/packages/3-targets/postgres/1-target/test/check-expressions.value-object.test.ts new file mode 100644 index 000000000000..6e74c505d015 --- /dev/null +++ b/packages/3-targets/postgres/1-target/test/check-expressions.value-object.test.ts @@ -0,0 +1,160 @@ +import type { + ResolvedValueObject, + ResolvedValueObjectField, + ResolvedValueObjectFieldType, +} from '@internal/sql-contract/check-expressions'; +import { describe, expect, it } from 'vitest'; +import { postgresRenderCheckExpressions } from '../src/core/check-expressions'; + +function scalar( + codecId: string, + values: readonly (string | number)[] | undefined = undefined, +): ResolvedValueObjectFieldType { + return { kind: 'scalar', codecId, values }; +} + +function member( + type: ResolvedValueObjectFieldType, + options: Partial> = {}, +): ResolvedValueObjectField { + return { nullable: false, many: false, ...options, type }; +} + +function holding(valueObject: ResolvedValueObject): ResolvedValueObjectFieldType { + return { kind: 'valueObject', valueObject }; +} + +function render( + valueObjectField: ResolvedValueObjectField, +): readonly { readonly kind: string; readonly expression: string }[] { + return postgresRenderCheckExpressions({ + tableName: 'user', + columnName: 'address', + many: false, + memberValues: undefined, + valueObjectField, + }).map(({ kind, expression }) => ({ kind, expression })); +} + +const street = member(scalar('pg/text@1')); +const flat: ResolvedValueObject = { fields: { street } }; + +const isObject = '$.type() == "object"'; +const hasStreet = 'exists($.keyvalue() ? (@.key == "street" && (@.value.type() == "string")))'; +const onlyStreet = '!exists($.keyvalue() ? (@.key != "street"))'; + +describe('postgresRenderCheckExpressions for a column that holds a value object', () => { + it('requires an object with each required member, of its JSON type, and no other key', () => { + expect(render(member(holding(flat)))).toEqual([ + { + kind: 'valueObject', + expression: `jsonb_path_match("address", 'strict ${isObject} && ${hasStreet} && ${onlyStreet}') IS TRUE`, + }, + ]); + }); + + it('accepts SQL NULL and JSON null in a nullable column', () => { + expect(render(member(holding(flat), { nullable: true }))).toEqual([ + { + kind: 'valueObject', + expression: `"address" IS NULL OR jsonb_path_match("address", 'strict $.type() == "null" || (${isObject} && ${hasStreet} && ${onlyStreet})') IS TRUE`, + }, + ]); + }); + + it('lets a nullable member be absent or JSON null', () => { + const zip = member(scalar('pg/int4@1'), { nullable: true }); + expect(render(member(holding({ fields: { zip } })))).toEqual([ + { + kind: 'valueObject', + expression: `jsonb_path_match("address", 'strict ${isObject} && !exists($.keyvalue() ? (@.key == "zip" && !(@.value.type() == "null" || (@.value.type() == "number")))) && !exists($.keyvalue() ? (@.key != "zip"))') IS TRUE`, + }, + ]); + }); + + it('checks list members and their element nullability', () => { + const tags = member(scalar('pg/text@1'), { many: { elementNullable: false } }); + const flags = member(scalar('pg/bool@1'), { many: { elementNullable: true } }); + expect(render(member(holding({ fields: { tags, flags } })))).toEqual([ + { + kind: 'valueObject', + expression: `jsonb_path_match("address", 'strict ${isObject} && exists($.keyvalue() ? (@.key == "flags" && (@.value.type() == "array" && !exists(@.value[*] ? (!(@.type() == "null" || (@.type() == "boolean"))))))) && exists($.keyvalue() ? (@.key == "tags" && (@.value.type() == "array" && !exists(@.value[*] ? (!((@.type() == "string"))))))) && !exists($.keyvalue() ? (@.key != "flags" && @.key != "tags"))') IS TRUE`, + }, + ]); + }); + + it('restricts an enum member to its value set', () => { + const kind = member(scalar('pg/text@1', ['home', "o'brien"])); + expect(render(member(holding({ fields: { kind } })))).toEqual([ + { + kind: 'valueObject', + expression: `jsonb_path_match("address", 'strict ${isObject} && exists($.keyvalue() ? (@.key == "kind" && (@.value.type() == "string" && (@.value == "home" || @.value == "o''brien")))) && !exists($.keyvalue() ? (@.key != "kind"))') IS TRUE`, + }, + ]); + }); + + it('restricts a numeric enum member to its value set', () => { + const rank = member(scalar('pg/int4@1', [1, -2])); + expect(render(member(holding({ fields: { rank } })))).toEqual([ + { + kind: 'valueObject', + expression: `jsonb_path_match("address", 'strict ${isObject} && exists($.keyvalue() ? (@.key == "rank" && (@.value.type() == "number" && (@.value == 1 || @.value == -2)))) && !exists($.keyvalue() ? (@.key != "rank"))') IS TRUE`, + }, + ]); + }); + + it('inlines a nested value object and a list of them', () => { + const geo = member(holding(flat)); + const stops = member(holding(flat), { many: { elementNullable: false } }); + const nestedAt = (path: string) => + `${path}.type() == "object" && exists(${path}.keyvalue() ? (@.key == "street" && (@.value.type() == "string"))) && !exists(${path}.keyvalue() ? (@.key != "street"))`; + expect(render(member(holding({ fields: { geo, stops } })))).toEqual([ + { + kind: 'valueObject', + expression: `jsonb_path_match("address", 'strict ${isObject} && exists($.keyvalue() ? (@.key == "geo" && (${nestedAt('@.value')}))) && exists($.keyvalue() ? (@.key == "stops" && (@.value.type() == "array" && !exists(@.value[*] ? (!((${nestedAt('@')}))))))) && !exists($.keyvalue() ? (@.key != "geo" && @.key != "stops"))') IS TRUE`, + }, + ]); + }); + + it('accepts any object where a value object refers back into itself', () => { + const children = member({ kind: 'openObject' }, { many: { elementNullable: false } }); + expect(render(member(holding({ fields: { children } })))).toEqual([ + { + kind: 'valueObject', + expression: `jsonb_path_match("address", 'strict ${isObject} && exists($.keyvalue() ? (@.key == "children" && (@.value.type() == "array" && !exists(@.value[*] ? (!((@.type() == "object"))))))) && !exists($.keyvalue() ? (@.key != "children"))') IS TRUE`, + }, + ]); + }); + + it('checks a list column as an array of the value object', () => { + expect(render(member(holding(flat), { many: { elementNullable: false } }))).toEqual([ + { + kind: 'valueObject', + expression: `jsonb_path_match("address", 'strict $.type() == "array" && !exists($[*] ? (!((@.type() == "object" && exists(@.keyvalue() ? (@.key == "street" && (@.value.type() == "string"))) && !exists(@.keyvalue() ? (@.key != "street"))))))') IS TRUE`, + }, + ]); + }); + + it('checks only the presence of a member whose codec has no known JSON type, and of a JSON member', () => { + const bytes = member(scalar('pg/bytea@1')); + const data = member(scalar('pg/jsonb@1')); + const notes = member(scalar('pg/jsonb@1'), { nullable: true }); + const blobs = member(scalar('pg/bytea@1'), { many: { elementNullable: false } }); + expect(render(member(holding({ fields: { blobs, bytes, data, notes } })))).toEqual([ + { + kind: 'valueObject', + expression: `jsonb_path_match("address", 'strict ${isObject} && exists($.keyvalue() ? (@.key == "blobs" && (@.value.type() == "array" && !exists(@.value[*] ? (@.type() == "null"))))) && exists($.keyvalue() ? (@.key == "bytes")) && exists($.keyvalue() ? (@.key == "data")) && !exists($.keyvalue() ? (@.key != "blobs" && @.key != "bytes" && @.key != "data" && @.key != "notes"))') IS TRUE`, + }, + ]); + }); + + it('writes member names as jsonpath strings', () => { + const quoted = member(scalar('pg/text@1')); + expect(render(member(holding({ fields: { 'say "hi"': quoted } })))).toEqual([ + { + kind: 'valueObject', + expression: `jsonb_path_match("address", 'strict ${isObject} && exists($.keyvalue() ? (@.key == "say \\"hi\\"" && (@.value.type() == "string"))) && !exists($.keyvalue() ? (@.key != "say \\"hi\\""))') IS TRUE`, + }, + ]); + }); +}); diff --git a/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.check.test.ts b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.check.test.ts index 09fc7cc66c91..e19f8dc73002 100644 --- a/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.check.test.ts +++ b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.check.test.ts @@ -19,8 +19,9 @@ import { printPslFromFlat } from '../fixtures'; const elementCandidate = postgresRenderCheckExpressions({ tableName: 'users', columnName: 'tags', - many: true, + many: { elementNullable: false }, memberValues: undefined, + valueObjectField: undefined, })[0]; const derivedPrefix = composeCheckWirePrefix('users', 'tags', 'elementNotNull'); diff --git a/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.list-element-nullability.test.ts b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.list-element-nullability.test.ts index 0f8f01fd0ed6..cfd185587acb 100644 --- a/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.list-element-nullability.test.ts +++ b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.list-element-nullability.test.ts @@ -17,8 +17,9 @@ import { printPslFromFlat } from '../fixtures'; const elementCandidate = postgresRenderCheckExpressions({ tableName: 'users', columnName: 'tags', - many: true, + many: { elementNullable: false }, memberValues: undefined, + valueObjectField: undefined, })[0]; const derivedPrefix = composeCheckWirePrefix('users', 'tags', 'elementNotNull'); diff --git a/packages/3-targets/postgres/2-adapter/test/migrations/alter-column-type.integration.test.ts b/packages/3-targets/postgres/2-adapter/test/migrations/alter-column-type.integration.test.ts index 7f9aa04d8f34..7f570cfc9600 100644 --- a/packages/3-targets/postgres/2-adapter/test/migrations/alter-column-type.integration.test.ts +++ b/packages/3-targets/postgres/2-adapter/test/migrations/alter-column-type.integration.test.ts @@ -51,6 +51,7 @@ function elementChecks(): CheckConstraint[] { columnName: 'tags', many: { elementNullable: false }, memberValues: undefined, + valueObjectField: undefined, }).map( (candidate) => new CheckConstraint({ diff --git a/packages/3-targets/postgres/2-adapter/test/migrations/check-lifecycle-e2e.integration.test.ts b/packages/3-targets/postgres/2-adapter/test/migrations/check-lifecycle-e2e.integration.test.ts index 07e57b46b1ab..e4582d19fedf 100644 --- a/packages/3-targets/postgres/2-adapter/test/migrations/check-lifecycle-e2e.integration.test.ts +++ b/packages/3-targets/postgres/2-adapter/test/migrations/check-lifecycle-e2e.integration.test.ts @@ -108,6 +108,7 @@ function checksForColumn( columnName, many: options.many, memberValues: options.memberValues, + valueObjectField: undefined, }).map( (candidate) => new CheckConstraint({ diff --git a/packages/3-targets/postgres/2-adapter/test/migrations/native-array-columns.integration.test.ts b/packages/3-targets/postgres/2-adapter/test/migrations/native-array-columns.integration.test.ts index 1e539d5d285f..be083f5cc76f 100644 --- a/packages/3-targets/postgres/2-adapter/test/migrations/native-array-columns.integration.test.ts +++ b/packages/3-targets/postgres/2-adapter/test/migrations/native-array-columns.integration.test.ts @@ -43,6 +43,7 @@ function declaredArrayElementChecks(): CheckConstraint[] { columnName, many: { elementNullable: false }, memberValues: undefined, + valueObjectField: undefined, }).map( (candidate) => new CheckConstraint({ diff --git a/packages/9-public/@prisma/orm-family-sql/package.json b/packages/9-public/@prisma/orm-family-sql/package.json index a0fcb11b226f..0eb46a611477 100644 --- a/packages/9-public/@prisma/orm-family-sql/package.json +++ b/packages/9-public/@prisma/orm-family-sql/package.json @@ -57,6 +57,7 @@ "./contract": "./dist/contract.mjs", "./contract/authored-check-naming": "./dist/contract__authored-check-naming.mjs", "./contract/canonicalization-hooks": "./dist/contract__canonicalization-hooks.mjs", + "./contract/check-expressions": "./dist/contract__check-expressions.mjs", "./contract/contract-view": "./dist/contract__contract-view.mjs", "./contract/data-type": "./dist/contract__data-type.mjs", "./contract/data-type-support": "./dist/contract__data-type-support.mjs", diff --git a/packages/9-public/@prisma/orm-postgres/package.json b/packages/9-public/@prisma/orm-postgres/package.json index ca2c2c64a8ed..219cba528084 100644 --- a/packages/9-public/@prisma/orm-postgres/package.json +++ b/packages/9-public/@prisma/orm-postgres/package.json @@ -97,6 +97,7 @@ "./family-contract": "./dist/family-contract.mjs", "./family-contract/authored-check-naming": "./dist/family-contract__authored-check-naming.mjs", "./family-contract/canonicalization-hooks": "./dist/family-contract__canonicalization-hooks.mjs", + "./family-contract/check-expressions": "./dist/family-contract__check-expressions.mjs", "./family-contract/contract-view": "./dist/family-contract__contract-view.mjs", "./family-contract/data-type": "./dist/family-contract__data-type.mjs", "./family-contract/data-type-support": "./dist/family-contract__data-type-support.mjs", diff --git a/test/integration/test/sql-orm-client/fixtures/value-object-checks/contract.prisma b/test/integration/test/sql-orm-client/fixtures/value-object-checks/contract.prisma new file mode 100644 index 000000000000..6f0bea1c83f7 --- /dev/null +++ b/test/integration/test/sql-orm-client/fixtures/value-object-checks/contract.prisma @@ -0,0 +1,62 @@ +// use prisma-8 + +// Value objects with nested, list, enum and self-referring members, and a +// polymorphic base whose discriminator has no enum. On Postgres each value +// object column and the discriminator column derive a check. + +enum PlaceKind { + @@type("pg/text@1") + home + work +} + +type Geo { + lat Float + lng Float +} + +type Place { + label String + kind PlaceKind + note String? + tags String[] + ratings Int?[] + geo Geo + stops Geo[] +} + +type NavItem { + title String + children NavItem[] +} + +model Trip { + id Int @id + origin Place + detour Place? + legs Place[] + menu NavItem? + + @@map("trip") +} + +model Shape { + id Int @id + kind String + name String + + @@discriminator(kind) + @@map("shape") +} + +model Circle { + radius Int? + + @@base(Shape, "circle") +} + +model Square { + side Int? + + @@base(Shape, "square") +} diff --git a/test/integration/test/sql-orm-client/value-object-checks.test.ts b/test/integration/test/sql-orm-client/value-object-checks.test.ts new file mode 100644 index 000000000000..f9e7592b78be --- /dev/null +++ b/test/integration/test/sql-orm-client/value-object-checks.test.ts @@ -0,0 +1,288 @@ +import { readFileSync } from 'node:fs'; +import { fileURLToPath } from 'node:url'; +import postgresAdapterControl from '@internal/adapter-postgres/control'; +import postgresAdapter from '@internal/adapter-postgres/runtime'; +import type { Contract } from '@internal/contract/types'; +import postgresDriverControl from '@internal/driver-postgres/control'; +import postgresDriver from '@internal/driver-postgres/runtime'; +import sqlFamilyControl, { INIT_ADDITIVE_POLICY } from '@internal/family-sql/control'; +import { APP_SPACE_ID, createControlStack } from '@internal/framework-components/control'; +import { instantiateExecutionStack } from '@internal/framework-components/execution'; +import { buildFabricatedMigrationEdge } from '@internal/migration-tools/aggregate'; +import { PostgresRuntimeImpl } from '@internal/postgres/runtime'; +import type { SqlStorage } from '@internal/sql-contract/types'; +import { Collection } from '@internal/sql-orm-client'; +import { createExecutionContext, createSqlExecutionStack } from '@internal/sql-runtime'; +import postgresTargetControl from '@internal/target-postgres/control'; +import postgresTarget from '@internal/target-postgres/runtime'; +import { timeouts, withDevDatabase } from '@repo/test-utils'; +import { dirname, join } from 'pathe'; +import { Client } from 'pg'; +import { describe, expect, it } from 'vitest'; +import { authorSqlContractFromPsl } from '../scalar-lists/psl-list-authoring'; + +const schema = readFileSync( + join(dirname(fileURLToPath(import.meta.url)), 'fixtures/value-object-checks/contract.prisma'), + 'utf-8', +); + +const controlStack = createControlStack({ + family: sqlFamilyControl, + target: postgresTargetControl, + adapter: postgresAdapterControl, + driver: postgresDriverControl, + extensions: [], +}); + +async function authored(): Promise> { + const result = await authorSqlContractFromPsl(schema); + expect(result.diagnostics).toEqual([]); + const { contract } = result; + if (contract === undefined) throw new Error('the fixture did not author a contract'); + return contract; +} + +const frameworkComponents = [ + postgresTargetControl, + postgresAdapterControl, + postgresDriverControl, +] as const; + +/** Plans the contract against the empty database and applies it, as `db update` does, then verifies the database against it. */ +async function pushAndVerify(contract: Contract, connectionString: string) { + const family = sqlFamilyControl.create(controlStack); + const driver = await postgresDriverControl.create(connectionString); + try { + const planResult = postgresTargetControl + .createPlanner(postgresAdapterControl.create(controlStack)) + .plan({ + contract, + schema: await family.introspect({ driver, contract }), + policy: INIT_ADDITIVE_POLICY, + fromContract: null, + origin: null, + statements: [], + frameworkComponents, + spaceId: APP_SPACE_ID, + snapshotsImportPath: '../../snapshots', + }); + if (planResult.kind !== 'success') throw new Error(JSON.stringify(planResult)); + const { plan } = planResult; + const applied = await postgresTargetControl.createRunner(family).execute({ + driver, + perSpaceOptions: [ + { + space: plan.spaceId ?? APP_SPACE_ID, + plan, + migrationEdges: [ + buildFabricatedMigrationEdge({ + currentMarkerStorageHash: plan.origin?.storageHash, + destinationStorageHash: plan.destination.storageHash, + operationCount: plan.operations.length, + }), + ], + driver, + destinationContract: contract, + policy: INIT_ADDITIVE_POLICY, + frameworkComponents, + }, + ], + }); + if (!applied.ok) throw new Error(JSON.stringify(applied.failure)); + return family.verifySchema({ + contract, + schema: await family.introspect({ driver, contract }), + strict: true, + frameworkComponents, + }); + } finally { + await driver.close(); + } +} + +/** One connection for the ORM and for raw SQL: the PGlite-backed dev server takes one at a time. */ +async function withRuntime( + contract: Contract, + connectionString: string, + fn: (runtime: PostgresRuntimeImpl, client: Client) => Promise, +): Promise { + const client = new Client({ connectionString }); + await client.connect(); + const stack = createSqlExecutionStack({ + target: postgresTarget, + adapter: postgresAdapter, + driver: { + ...postgresDriver, + create() { + return postgresDriver.create({ cursor: { disabled: true } }); + }, + }, + }); + const instance = instantiateExecutionStack(stack); + if (instance.adapter === undefined || instance.driver === undefined) { + throw new Error('the Postgres execution stack has no adapter or driver'); + } + await instance.driver.connect({ kind: 'pgClient', client, ownedByDriver: false }); + const runtime = new PostgresRuntimeImpl({ + closeRefusal: undefined, + context: createExecutionContext({ contract, stack }), + adapter: instance.adapter, + driver: instance.driver, + middleware: [], + }); + try { + await fn(runtime, client); + } finally { + await runtime.close(); + await client.end(); + } +} + +const geo = { lat: 59.9, lng: 10.7 }; +const place = { + label: 'Home', + kind: 'home', + tags: ['a'], + ratings: [1, null], + geo, + stops: [geo], +}; + +function without(key: string, value: Record) { + return Object.fromEntries(Object.entries(value).filter(([name]) => name !== key)); +} + +async function insertTrip( + client: Client, + columns: Partial>, +) { + const values = { origin: place, legs: [], ...columns }; + const names = Object.keys(values); + await client.query( + `insert into trip (id, ${names.map((name) => `"${name}"`).join(', ')}) values ((select coalesce(max(id), 0) + 1 from trip), ${names.map((_, index) => `$${index + 1}::jsonb`).join(', ')})`, + Object.values(values).map((value) => (value === undefined ? null : JSON.stringify(value))), + ); +} + +const refused = { code: '23514' }; + +describe('derived checks of value object and discriminator columns on Postgres', () => { + it( + 'are created by db update, verify cleanly, accept the rows the ORM writes and refuse rows that do not fit', + async () => { + const contract = await authored(); + await withDevDatabase(async ({ connectionString }) => { + expect(await pushAndVerify(contract, connectionString)).toMatchObject({ ok: true }); + + await withRuntime(contract, connectionString, async (runtime, client) => { + const tables = contract.storage.namespaces['public']?.entries.table ?? {}; + const declared = ['trip', 'shape'].flatMap((table) => tables[table]?.checks ?? []); + const live = await client.query<{ conname: string }>( + "select conname from pg_constraint where contype = 'c' and conrelid in ('trip'::regclass, 'shape'::regclass) order by conname", + ); + expect({ + prefixes: declared.map((check) => check.prefix), + live: live.rows.map((row) => row.conname), + }).toEqual({ + prefixes: [ + 'trip_detour_value_object', + 'trip_legs_value_object', + 'trip_menu_value_object', + 'trip_origin_value_object', + 'shape_kind_check', + ], + live: declared.map((check) => check.name).sort(), + }); + + const context = createExecutionContext({ + contract, + stack: createSqlExecutionStack({ target: postgresTarget, adapter: postgresAdapter }), + }); + const trips = new Collection({ runtime, context }, 'Trip', { namespaceId: 'public' }); + await trips.create({ + id: 1, + origin: { ...place, note: 'gate code 4' }, + detour: { ...place, note: null, ratings: [] }, + legs: [place, place], + menu: { title: 'root', children: [{ title: 'child', children: [] }] }, + } as never); + await trips.create({ id: 2, origin: place, legs: [] } as never); + const written = await client.query( + 'select id, detour is null as detour_sql_null, menu is null as menu_sql_null from trip order by id', + ); + expect(written.rows).toEqual([ + { id: 1, detour_sql_null: false, menu_sql_null: false }, + { id: 2, detour_sql_null: true, menu_sql_null: true }, + ]); + + const acceptedRows: Record>> = { + 'SQL NULL in a nullable column': { detour: undefined }, + 'JSON null in a nullable column': { detour: null }, + 'a nullable member that is absent': { origin: place }, + 'a nullable member that is JSON null': { origin: { ...place, note: null } }, + 'null elements in a list whose elements are nullable': { + origin: { ...place, ratings: [null, 2] }, + }, + 'any object below where a value object refers back into itself': { + menu: { title: 'a', children: [{ anything: 1 }] }, + }, + }; + const refusedRows: Record>> = { + 'a missing key': { origin: without('label', place) }, + 'an extra key': { origin: { ...place, extra: 1 } }, + 'a wrong JSON type': { origin: { ...place, label: 1 } }, + 'a value outside the enum': { origin: { ...place, kind: 'school' } }, + 'a null element in a list whose elements are not nullable': { + origin: { ...place, tags: [null] }, + }, + 'a list member that is not an array': { origin: { ...place, ratings: 'x' } }, + 'a nested object with a wrong JSON type': { + origin: { ...place, geo: { lat: 'x', lng: 1 } }, + }, + 'a nested object with a missing key': { origin: { ...place, geo: { lat: 1 } } }, + 'a list of nested objects with an extra key': { + origin: { ...place, stops: [{ ...geo, alt: 1 }] }, + }, + 'JSON null in a required column': { origin: null }, + 'a null element in a list column': { legs: [null] }, + 'an object in a list column': { legs: place }, + 'a member below where a value object refers back into itself that is not an object': { + menu: { title: 'a', children: [1] }, + }, + 'a missing key above where a value object refers back into itself': { + menu: { children: [] }, + }, + }; + const outcomes = async (rows: Record>>) => { + const results: Record = {}; + for (const [name, columns] of Object.entries(rows)) { + results[name] = await insertTrip(client, columns).then( + () => 'accepted', + (error: { code?: string }) => ({ code: error.code }), + ); + } + return results; + }; + expect({ + accepted: await outcomes(acceptedRows), + refused: await outcomes(refusedRows), + }).toEqual({ + accepted: Object.fromEntries( + Object.keys(acceptedRows).map((name) => [name, 'accepted']), + ), + refused: Object.fromEntries(Object.keys(refusedRows).map((name) => [name, refused])), + }); + + const shapes = new Collection({ runtime, context }, 'Shape', { namespaceId: 'public' }); + await shapes + .variant('circle' as never) + .create({ id: 1, name: 'wheel', radius: 2 } as never); + await expect( + client.query(`insert into shape (id, kind, name) values (2, 'triangle', 'x')`), + ).rejects.toMatchObject(refused); + }); + }); + }, + timeouts.spinUpPpgDev, + ); +}); From 613a5ca57f556af1d10de977d0383e082f2c76bf Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 14:16:06 +0200 Subject: [PATCH 035/104] TML-3537: contract print leaves out the derived value object and discriminator checks and the discriminator value set Signed-off-by: willbot Signed-off-by: Will Madden --- .../core/psl-print/contract-model-index.ts | 16 +++ .../src/core/psl-print/model-attributes.ts | 20 ++-- .../src/core/psl-print/psl-contract.ts | 60 ++++++++-- .../1-target/src/core/psl-print/refusals.ts | 2 +- .../src/core/psl-print/scalar-fields.ts | 33 ++++-- .../test/psl-print/discriminator.test.ts | 109 ++++++++++++++++++ .../1-target/test/psl-print/member-support.ts | 5 + .../1-target/test/psl-print/members.test.ts | 1 + ...red-contract-roundtrip.integration.test.ts | 10 ++ 9 files changed, 234 insertions(+), 22 deletions(-) create mode 100644 packages/3-targets/postgres/1-target/test/psl-print/discriminator.test.ts diff --git a/packages/3-targets/postgres/1-target/src/core/psl-print/contract-model-index.ts b/packages/3-targets/postgres/1-target/src/core/psl-print/contract-model-index.ts index 04c483dec81e..c4169344fb17 100644 --- a/packages/3-targets/postgres/1-target/src/core/psl-print/contract-model-index.ts +++ b/packages/3-targets/postgres/1-target/src/core/psl-print/contract-model-index.ts @@ -142,3 +142,19 @@ export function isVariantLinkForeignKey(fk: ForeignKey, variant: VariantInfo | u fk.onUpdate === undefined ); } + +/** + * The value set the PSL source writes for a discriminator, keyed `.` and holding the variants' values, sorted. A discriminator an enum types keeps the enum's value set, so it writes none. + */ +export function discriminatorValueSetOf( + entry: ModelWithTable, +): { readonly name: string; readonly values: readonly string[] } | undefined { + const { discriminator, variants } = entry.model; + if (discriminator === undefined || variants === undefined) return undefined; + const name = `${entry.name}.${discriminator.field}`; + const columnName = entry.storage.fields[discriminator.field]?.column; + const valueSet = columnName === undefined ? undefined : entry.table.columns[columnName]?.valueSet; + return valueSet?.entityName === name && valueSet.namespaceId === entry.namespaceId + ? { name, values: Object.keys(variants).sort() } + : undefined; +} diff --git a/packages/3-targets/postgres/1-target/src/core/psl-print/model-attributes.ts b/packages/3-targets/postgres/1-target/src/core/psl-print/model-attributes.ts index b608fda7dfbd..90216b6341c5 100644 --- a/packages/3-targets/postgres/1-target/src/core/psl-print/model-attributes.ts +++ b/packages/3-targets/postgres/1-target/src/core/psl-print/model-attributes.ts @@ -1,4 +1,5 @@ import type { PslModelAttribute } from '@internal/framework-components/psl-ast'; +import type { ResolvedValueObjectField } from '@internal/sql-contract/check-expressions'; import { escapePslString } from '@internal/sql-contract/data-type-support'; import type { StorageTable } from '@internal/sql-contract/types'; import { pslModelMapName } from '@internal/sql-contract-psl/map-names'; @@ -36,30 +37,35 @@ export interface DerivedCheck { * The checks the PSL source derives itself for a table, keyed by name, so * they are not also written as `@@check`. A table that is not managed derives * none. A managed one derives a membership check for each column typed by a - * domain enum and an element-not-null check for each list column whose - * elements are not nullable. + * domain enum and for a discriminator column, an element-not-null check for + * each list column whose elements are not nullable, and a value object check + * for each column that holds a value object. */ export function derivedChecks(input: { readonly table: StorageTable; readonly tableName: string; readonly managed: boolean; - readonly domainEnumValues: ReadonlyMap; + /** The values of each value set a domain enum or a discriminator writes, by name. */ + readonly memberValueSets: ReadonlyMap; + /** The value object field each column that holds one stores, by column name. */ + readonly valueObjectFields: ReadonlyMap; }): ReadonlyMap { const checks = new Map(); if (!input.managed) return checks; for (const [columnName, column] of Object.entries(input.table.columns)) { - const enumValues = + const setValues = column.codecId === PG_ENUM_CODEC_ID || column.valueSet === undefined ? undefined - : input.domainEnumValues.get(column.valueSet.entityName); - const memberValues = enumValues?.filter( + : input.memberValueSets.get(column.valueSet.entityName); + const memberValues = setValues?.filter( (value): value is string | number => typeof value === 'string' || typeof value === 'number', ); for (const candidate of postgresRenderCheckExpressions({ tableName: input.tableName, columnName, many: column.many, - memberValues: memberValues?.length === enumValues?.length ? memberValues : undefined, + memberValues: memberValues?.length === setValues?.length ? memberValues : undefined, + valueObjectField: input.valueObjectFields.get(columnName), })) { const prefix = composeCheckWirePrefix(input.tableName, columnName, candidate.kind); checks.set(formatWireName(prefix, computeCheckContentHash(candidate.expression)), { diff --git a/packages/3-targets/postgres/1-target/src/core/psl-print/psl-contract.ts b/packages/3-targets/postgres/1-target/src/core/psl-print/psl-contract.ts index 705e5dbf788b..193d51ed8f18 100644 --- a/packages/3-targets/postgres/1-target/src/core/psl-print/psl-contract.ts +++ b/packages/3-targets/postgres/1-target/src/core/psl-print/psl-contract.ts @@ -18,6 +18,10 @@ import { makePslNamespaceEntries, UNSPECIFIED_PSL_NAMESPACE_ID, } from '@internal/framework-components/psl-ast'; +import { + type ResolvedValueObjectField, + resolveValueObject, +} from '@internal/sql-contract/check-expressions'; import type { ForeignKey, SqlModelStorage, SqlStorage } from '@internal/sql-contract/types'; import { invariant } from '@internal/utils/assertions'; import { blindCast } from '@internal/utils/casts'; @@ -25,6 +29,7 @@ import { DEFAULT_NAMESPACE_ID } from '../namespace-ids'; import { PostgresNativeEnum } from '../postgres-native-enum'; import { SYNTHETIC_SPAN } from '../psl-build/psl-literals'; import { + discriminatorValueSetOf, indexContractModels, type ModelWithTable, modelCoordinate, @@ -102,13 +107,41 @@ function nativeEnumsOf( return nativeEnums; } -/** The value sets the PSL source derives for a namespace from the enum blocks written for it. */ +/** The value object field each column of a table holds, over the fields of every model the table stores. */ +function valueObjectFieldsOf( + contract: Contract, + entries: readonly ModelWithTable[], +): ReadonlyMap { + const fields = new Map(); + for (const entry of entries) { + for (const [fieldName, field] of Object.entries(entry.model.fields)) { + const columnName = entry.storage.fields[fieldName]?.column; + if (field.type.kind !== 'valueObject' || columnName === undefined) continue; + fields.set(columnName, { + nullable: field.nullable, + many: field.many ?? false, + type: { + kind: 'valueObject', + valueObject: resolveValueObject(contract.storage, field.type), + }, + }); + } + } + return fields; +} + +/** The value sets the PSL source derives for a namespace from the enum blocks written for it and from its discriminators. */ function derivedValueSetsOf( namespaceId: string, enums: NativeEnumEmission, defaultDomainEnums: Readonly>, + namespaceModels: readonly ModelWithTable[], ): ReadonlyMap { const derived = new Map(enums.derivedValueSets); + for (const entry of namespaceModels) { + const valueSet = discriminatorValueSetOf(entry); + if (valueSet !== undefined) derived.set(valueSet.name, valueSet.values); + } if (namespaceId !== DEFAULT_NAMESPACE_ID) return derived; for (const [name, domainEnum] of Object.entries(defaultDomainEnums)) { if (derived.has(name)) refuseEnumAndNativeEnumSharingName(namespaceId, name); @@ -171,12 +204,20 @@ function buildModel( } } refuseUntravelledForeignKeys(entry, variant, travelledForeignKeys); + const discriminatorValueSet = discriminatorValueSetOf(entry); const checks = derivedChecks({ table: entry.table, tableName: entry.tableName, managed: effectiveControlPolicy(entry.table.control, all.contract.defaultControlPolicy) === 'managed', - domainEnumValues: all.domainEnumValues, + memberValueSets: + discriminatorValueSet === undefined + ? all.domainEnumValues + : new Map([ + ...all.domainEnumValues, + [discriminatorValueSet.name, discriminatorValueSet.values], + ]), + valueObjectFields: valueObjectFieldsOf(all.contract, [entry, ...singleTableVariants]), }); if (variant?.singleTable !== true) refuseUnderivedChecks(entry, checks); return { @@ -222,13 +263,14 @@ function buildNamespace( refuseUnderivedValueSets({ namespaceId, actual: valueSets, - derived: derivedValueSetsOf(namespaceId, enums, all.defaultDomainEnums), + derived: derivedValueSetsOf( + namespaceId, + enums, + all.defaultDomainEnums, + models.filter((entry) => entry.namespaceId === namespaceId), + ), }); - const rlsTables = rlsEnabledTables(namespaceId, namespace.entries['rls']); - const namespaceModels = models - .filter((entry) => entry.namespaceId === namespaceId) - .map((entry) => buildModel(all, entry, enums, rlsTables)); const compositeTypes = buildCompositeTypes({ contract, namespaceId, @@ -237,6 +279,10 @@ function buildNamespace( enumBlockNames: enums.blockNamesByTypeName, domainEnums: all.defaultDomainEnums, }); + const rlsTables = rlsEnabledTables(namespaceId, namespace.entries['rls']); + const namespaceModels = models + .filter((entry) => entry.namespaceId === namespaceId) + .map((entry) => buildModel(all, entry, enums, rlsTables)); const modelNameForTable = (tableName: string): string | undefined => models.find( (entry) => diff --git a/packages/3-targets/postgres/1-target/src/core/psl-print/refusals.ts b/packages/3-targets/postgres/1-target/src/core/psl-print/refusals.ts index d461a7d3e5eb..8ffd819061bb 100644 --- a/packages/3-targets/postgres/1-target/src/core/psl-print/refusals.ts +++ b/packages/3-targets/postgres/1-target/src/core/psl-print/refusals.ts @@ -572,7 +572,7 @@ export function refuseUnderivedChecks( if (check?.prefix === expected.prefix && check.expression === expected.expression) continue; throw unsupported( `table "${entry.namespaceId}"."${entry.tableName}" ${check === undefined ? 'lacks' : 'changes'} the check "${name}" the PSL source derives, so it cannot be written in Prisma 8 PSL.`, - 'The PSL source derives a membership check for each column of a managed table typed by an enum, and an element-not-null check for each list column whose elements are not nullable.', + 'The PSL source derives, on a managed table, a membership check for each column typed by an enum and for a discriminator column, an element-not-null check for each list column whose elements are not nullable, and a value object check for each column that holds a value object.', KEEP_SOURCE, { namespaceId: entry.namespaceId, table: entry.tableName, name }, ); diff --git a/packages/3-targets/postgres/1-target/src/core/psl-print/scalar-fields.ts b/packages/3-targets/postgres/1-target/src/core/psl-print/scalar-fields.ts index 9770ffa8d844..9cddfd727d4e 100644 --- a/packages/3-targets/postgres/1-target/src/core/psl-print/scalar-fields.ts +++ b/packages/3-targets/postgres/1-target/src/core/psl-print/scalar-fields.ts @@ -3,11 +3,7 @@ import type { SqlPslBuildContext } from '@internal/family-sql/control'; import type { InferredTypeConstructors } from '@internal/family-sql/psl-build'; import type { PslField, PslFieldAttribute } from '@internal/framework-components/psl-ast'; import { escapePslString } from '@internal/sql-contract/data-type-support'; -import { - nonEmptyTypeParams, - type SqlStorage, - type StorageColumn, -} from '@internal/sql-contract/types'; +import { nonEmptyTypeParams, type SqlStorage, StorageColumn } from '@internal/sql-contract/types'; import { pslFieldMapName } from '@internal/sql-contract-psl/map-names'; import { assertDefined } from '@internal/utils/assertions'; import { ifDefined } from '@internal/utils/defined'; @@ -20,7 +16,11 @@ import { } from '../psl-build/psl-literals'; import { buildColumnDefault } from './column-defaults'; import { buildColumnType, type PslColumnType } from './column-types'; -import type { ModelWithTable, VariantInfo } from './contract-model-index'; +import { + discriminatorValueSetOf, + type ModelWithTable, + type VariantInfo, +} from './contract-model-index'; import type { NativeEnumEmission } from './enum-blocks'; import { buildGeneratorAttributes } from './generated-values'; import { @@ -107,11 +107,17 @@ export function buildScalarFields(input: { input; const primaryKeyColumns = variant === undefined ? (entry.table.primaryKey?.columns ?? []) : []; const fields: PslField[] = []; + const discriminatorField = + discriminatorValueSetOf(entry) === undefined ? undefined : entry.model.discriminator?.field; for (const [fieldName, fieldStorage] of Object.entries(entry.storage.fields)) { const columnName = fieldStorage.column; const coordinate = `"${entry.namespaceId}"."${entry.tableName}"."${columnName}"`; - const column = entry.table.columns[columnName]; + const storedColumn = entry.table.columns[columnName]; + const column = + fieldName === discriminatorField && storedColumn !== undefined + ? withoutValueSet(storedColumn) + : storedColumn; const field = entry.model.fields[fieldName]; assertDefined(column, `column ${coordinate} is not declared by its table`); if (field === undefined) refuseStorageOfUndeclaredField({ entry, fieldName, coordinate }); @@ -187,3 +193,16 @@ export function buildScalarFields(input: { return fields; } + +/** A discriminator column without the value set the PSL source writes for it from the variants, so it is written as the plain column it is declared as. */ +function withoutValueSet(column: StorageColumn): StorageColumn { + return new StorageColumn({ + dataType: column.dataType, + codecId: column.codecId, + nullable: column.nullable, + many: column.many, + ...ifDefined('typeParams', column.typeParams), + ...ifDefined('default', column.default), + ...ifDefined('control', column.control), + }); +} diff --git a/packages/3-targets/postgres/1-target/test/psl-print/discriminator.test.ts b/packages/3-targets/postgres/1-target/test/psl-print/discriminator.test.ts new file mode 100644 index 000000000000..29f38d97f2e9 --- /dev/null +++ b/packages/3-targets/postgres/1-target/test/psl-print/discriminator.test.ts @@ -0,0 +1,109 @@ +import { + composeCheckWirePrefix, + computeCheckContentHash, + formatWireName, +} from '@internal/sql-schema-ir/naming'; +import { describe, expect, it } from 'vitest'; +import { attributeText, fieldText } from './print-support'; +import { + deserialize, + INT_COLUMN, + INT_FIELD, + PUBLIC, + printing, + TEXT_COLUMN, + TEXT_FIELD, +} from './refusal-support'; + +const membership = `"type" IN ('bug', 'feature')`; +const prefix = composeCheckWirePrefix('Task', 'type', 'membership'); +const derivedCheck = { + name: formatWireName(prefix, computeCheckContentHash(membership)), + prefix, + expression: membership, +}; + +function taskContract(checks: readonly (typeof derivedCheck)[]) { + const variant = { + storage: { table: 'Task', namespaceId: 'public', fields: {} }, + fields: {}, + relations: {}, + base: { namespace: PUBLIC, model: 'Task' }, + }; + return deserialize({ + roots: { Task: { namespace: PUBLIC, model: 'Task' } }, + namespaces: { + public: { + models: { + Task: { + storage: { + table: 'Task', + namespaceId: 'public', + fields: { id: { column: 'id' }, type: { column: 'type' } }, + }, + fields: { id: INT_FIELD, type: TEXT_FIELD }, + relations: {}, + discriminator: { field: 'type' }, + variants: { + bug: { namespace: PUBLIC, model: 'Bug' }, + feature: { namespace: PUBLIC, model: 'Feature' }, + }, + }, + Bug: variant, + Feature: variant, + }, + }, + }, + storage: { + namespaces: { + public: { + id: 'public', + entries: { + table: { + Task: { + columns: { + id: INT_COLUMN, + type: { + ...TEXT_COLUMN, + valueSet: { + plane: 'storage', + entityKind: 'valueSet', + namespaceId: 'public', + entityName: 'Task.type', + }, + }, + }, + uniques: [], + indexes: [], + foreignKeys: [], + primaryKey: { columns: ['id'] }, + checks, + }, + }, + valueSet: { 'Task.type': { kind: 'valueSet', values: ['bug', 'feature'] } }, + }, + }, + }, + }, + }); +} + +describe('a discriminator column with its value set and membership check', () => { + it('is written as the plain field, without the check or an enum the PSL source derives', () => { + const document = printing(taskContract([derivedCheck]))(); + const task = document.namespaces + .flatMap((namespace) => namespace.models) + .find((model) => model.name === 'Task'); + expect({ + fields: task?.fields.map(fieldText), + attributes: task?.attributes.map(attributeText), + }).toEqual({ + fields: ['id Int @id', 'type String'], + attributes: ['@@discriminator(type)'], + }); + }); + + it('is refused when its table lacks the membership check', () => { + expect(printing(taskContract([]))).toThrow(`lacks the check "${derivedCheck.name}"`); + }); +}); diff --git a/packages/3-targets/postgres/1-target/test/psl-print/member-support.ts b/packages/3-targets/postgres/1-target/test/psl-print/member-support.ts index b0cbb26579a9..4ea88f23ac27 100644 --- a/packages/3-targets/postgres/1-target/test/psl-print/member-support.ts +++ b/packages/3-targets/postgres/1-target/test/psl-print/member-support.ts @@ -1,11 +1,15 @@ import type { ContractField } from '@internal/contract/types'; import { deserialize, widgetContract } from './refusal-support'; +/** A table that derives no checks, so these contracts need not carry the value object check of `address`. */ +const UNCHECKED_TABLE = { control: 'external' } as const; + /** A contract whose one value object, `Address`, has the one member `street`. */ export function addressContract(field: ContractField) { return deserialize( widgetContract({ columns: { address: { dataType: 'pg/jsonb', codecId: 'pg/jsonb@1', nullable: false } }, + table: UNCHECKED_TABLE, fields: { address: { nullable: false, @@ -41,6 +45,7 @@ export function withCountryMembers( return deserialize( widgetContract({ columns: { address: { dataType: 'pg/jsonb', codecId: 'pg/jsonb@1', nullable: false } }, + table: UNCHECKED_TABLE, fields: { address: { nullable: false, diff --git a/packages/3-targets/postgres/1-target/test/psl-print/members.test.ts b/packages/3-targets/postgres/1-target/test/psl-print/members.test.ts index c2c8778b2daf..2af4a30be7bc 100644 --- a/packages/3-targets/postgres/1-target/test/psl-print/members.test.ts +++ b/packages/3-targets/postgres/1-target/test/psl-print/members.test.ts @@ -45,6 +45,7 @@ describe('value-object members are written as the type their domain type reads b deserialize( widgetContract({ columns: { price: { dataType: 'pg/jsonb', codecId: 'pg/jsonb@1', nullable: false } }, + table: { control: 'external' }, fields: { price: { nullable: false, diff --git a/test/integration/test/psl-print/authored-contract-roundtrip.integration.test.ts b/test/integration/test/psl-print/authored-contract-roundtrip.integration.test.ts index 378c187a27a8..530d62f88996 100644 --- a/test/integration/test/psl-print/authored-contract-roundtrip.integration.test.ts +++ b/test/integration/test/psl-print/authored-contract-roundtrip.integration.test.ts @@ -50,6 +50,16 @@ function loadContract(relativePath: string): Contract { * carry. Each is read, printed, read back, and compared. */ const pslCases: ReadonlyArray<{ readonly name: string; readonly schema: string }> = [ + { + name: 'value objects with nested, list, enum and self-referring members, and a discriminator, each with its derived check', + schema: readFileSync( + join( + repoRoot, + 'test/integration/test/sql-orm-client/fixtures/value-object-checks/contract.prisma', + ), + 'utf-8', + ).replace('// use prisma-8\n', ''), + }, { name: 'a model control policy', schema: `model Ledger { From a934fa1b2cc6f61470eb4291aee639dac55d25d5 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 14:16:16 +0200 Subject: [PATCH 036/104] TML-3537: docs describe the value object check and the discriminator check Signed-off-by: willbot Signed-off-by: Will Madden --- ...es, and a discriminator column names a value set.md | 10 +++++++++- docs/architecture docs/subsystems/1. Data Contract.md | 4 ++++ docs/reference/error-reference.md | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/docs/architecture docs/adrs/ADR 278 - Value objects are storage entities, and a discriminator column names a value set.md b/docs/architecture docs/adrs/ADR 278 - Value objects are storage entities, and a discriminator column names a value set.md index 3c55aba2023a..45d3028100ab 100644 --- a/docs/architecture docs/adrs/ADR 278 - Value objects are storage entities, and a discriminator column names a value set.md +++ b/docs/architecture docs/adrs/ADR 278 - Value objects are storage entities, and a discriminator column names a value set.md @@ -98,7 +98,15 @@ Storage already holds stored facts the database does not enforce. SQLite derives "type": { "codecId": "pg/text@1", "dataType": "pg/text", "nullable": false, "valueSet": { "entityKind": "valueSet", "entityName": "Task.type", "namespaceId": "public", "plane": "storage" } } ``` -**Enforcement on Postgres.** A column that holds a value object gets a derived check, as an enum column does, written with `jsonb` operators. It checks that the required keys are present (`"address" ?& array['street', 'city']`) and that no other key is (`"address" - array['street', 'city'] = '{}'`), with a nullable column allowed to be null. For each scalar member, a value that is not JSON `null` must have the declared JSON type (`jsonb_typeof("address" -> 'street') = 'string'`), and JSON `null` is accepted only for a nullable member. A nested value object and a list are checked through `jsonpath`. The exact predicate for every shape SQL authoring produces is settled when the storage change is built; a shape the check cannot cover is raised as an open question, not worked around. The check is opaque and content-named ([ADR 244](ADR%20244%20-%20Check%20constraints%20are%20opaque%20wire-named%20expressions.md)), so any change to the value object changes the check's name, and the diff drops the old check and adds the new one. +**Enforcement on Postgres.** A column that holds a value object gets a derived check, as an enum column does, named `
__value_object`. It is one strict `jsonpath` predicate, written so that an unknown result fails: + +```sql +jsonb_path_match("address", 'strict $.type() == "object" + && exists($.keyvalue() ? (@.key == "street" && (@.value.type() == "string"))) + && !exists($.keyvalue() ? (@.key != "street"))') IS TRUE +``` + +The value must be an object, or for a list an array of objects. Each required member must be present; a nullable member may be absent or JSON `null`; no other key may be. A scalar member must have the JSON type its codec writes, an enum member must hold a value of its value set, and a nested value object is checked the same way, inline. A nullable column also accepts SQL `NULL` and JSON `null`. Members are read through `keyvalue()`, so an absent member is never an error, and strict mode keeps a comparison from unwrapping an array. A value object that contains itself is checked in full down to where it refers back into itself, and any object is accepted below, as the MongoDB validator does. A member whose codec has no known JSON type is checked for presence only. The check is opaque and content-named ([ADR 244](ADR%20244%20-%20Check%20constraints%20are%20opaque%20wire-named%20expressions.md)), so any change to the value object changes the check's name, and the diff drops the old check and adds the new one. **Why every change is an operation.** The storage hash identifies a database state. Two different hashes are two different database states, so the migration between them does something to the database. Because the value object and the discriminator values are enforced, a change to either is a change to a check or a validator, which is an operation. A plan that changes the storage hash and produces no operation is therefore unexpected: it means the database cannot tell the two states apart. Such cases will still arise, so the system handles them rather than refusing: `migration plan` writes the package with no operations and says so, naming what changed, and `db migrate` applies it by moving the marker to the new hash. diff --git a/docs/architecture docs/subsystems/1. Data Contract.md b/docs/architecture docs/subsystems/1. Data Contract.md index 4202f7ed2713..140c4dc5ecb8 100644 --- a/docs/architecture docs/subsystems/1. Data Contract.md +++ b/docs/architecture docs/subsystems/1. Data Contract.md @@ -321,6 +321,10 @@ A `pg/int4@1` enum `Int4Level` with members `Low = 1` and `High = 10`, on column A list column gets `<@` containment instead of `IN`. In the same fixture, `levels FloatMixed[]`, whose members are written `1.5` and `NaN` and stored as `1.5` and `"NaN"`, gets `array_remove("levels", NULL) <@ '{1.5,"NaN"}'`, plus the derived check `array_position("levels", NULL) IS NULL` because its elements are not nullable. +On a `managed` Postgres table two more columns derive checks ([ADR 278](../adrs/ADR%20278%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md)). A discriminator column with no enum gets a membership check, `
__check`, holding the values of its `.` value set. A column that holds a value object gets a check named `
__value_object`: one strict `jsonpath` predicate that the column value is an object (or, for a list, an array of objects) with each required member present, no other key, each scalar member of its JSON type, each enum member one of its value set's values, and each nested value object checked the same way. A nullable column accepts SQL `NULL` and JSON `null`; the ORM writes SQL `NULL` for an absent value. A member whose codec has no known JSON type, and a `Json` member, is checked for presence only. Below the point where a value object refers back into itself, any object is accepted, as the MongoDB validator does. + +`contract infer` produces no value objects and no polymorphism, so on a live table with these checks it prints each one as an authored `@@check` with its exact name in `map:`, as it does for an enum membership check. + ### Storage identity and verification semantics (v1) The control plane verifies that a live database **satisfies** the contract’s storage requirements. For MVP, verification favors **semantic satisfaction** over strict, byte-for-byte equivalence. diff --git a/docs/reference/error-reference.md b/docs/reference/error-reference.md index 5befb37a5513..289d24356a4a 100644 --- a/docs/reference/error-reference.md +++ b/docs/reference/error-reference.md @@ -271,7 +271,7 @@ A value handed to a data type's cast, or to an authoring entry that reads writte ### CONTRACT.CHECK_NAME_RESERVED -An authored `@@check` / `check()`'s `name:` prefix matches the shape a derived enforcement check would use for a column of the same table (`
__check` or `
__elem_not_null`), so it cannot be told apart from a derived check once a non-`managed` table strips those. The message and `collidingColumns` meta name the column(s) whose derived-check shape the prefix matches. Raised while building a SQL contract, once the table's real columns are in hand. The fix is to choose a different `name:`. Payload: `tableName`, `prefix`, `collidingColumns`. +An authored `@@check` / `check()`'s `name:` prefix matches the shape a derived enforcement check would use for a column of the same table (`
__check`, `
__elem_not_null` or `
__value_object`), so it cannot be told apart from a derived check once a non-`managed` table strips those. The message and `collidingColumns` meta name the column(s) whose derived-check shape the prefix matches. Raised while building a SQL contract, once the table's real columns are in hand. The fix is to choose a different `name:`. Payload: `tableName`, `prefix`, `collidingColumns`. ### CONTRACT.CHECK_ON_STI_VARIANT From 6c28383ed6b9007aa216358af40643ceadfe61b0 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 14:20:04 +0200 Subject: [PATCH 037/104] TML-3537: validating a Mongo contract leaves its storage value objects unchanged The storage value object schema filled in nullable and many with a morph, which wrote into the object it validated, so a frozen TypeScript-authored contract threw a TypeError in mongo(). The value object IR node now fills them in, into new objects. Signed-off-by: willbot Signed-off-by: Will Madden --- .../mongo-contract/src/contract-schema.ts | 8 +--- .../src/ir/mongo-value-object.ts | 15 ++++-- .../mongo-contract/test/field-schema.test.ts | 8 ++-- .../test/value-object-schema.test.ts | 21 ++++++--- .../9-facade/test/contract-from-json.test.ts | 47 ++++++++++++++++++- .../value-objects.integration.test.ts | 10 ++-- 6 files changed, 80 insertions(+), 29 deletions(-) diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/src/contract-schema.ts b/packages/2-mongo-family/1-foundation/mongo-contract/src/contract-schema.ts index c7f0efaa7141..9c796001a16b 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/src/contract-schema.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/src/contract-schema.ts @@ -73,7 +73,7 @@ const StorageValueSetRefSchema = type({ 'spaceId?': 'string', }); -const RawStorageValueObjectFieldSchema = type({ +const StorageValueObjectFieldSchema = type({ '+': 'reject', type: FieldTypeSchema, 'nullable?': 'boolean', @@ -88,11 +88,7 @@ const RawStorageValueObjectFieldSchema = type({ export const StorageValueObjectSchema = type({ '+': 'reject', fields: type({ - '[string]': RawStorageValueObjectFieldSchema.pipe((field) => ({ - ...field, - nullable: field.nullable ?? false, - many: field.many ?? false, - })), + '[string]': StorageValueObjectFieldSchema, }), }); diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/src/ir/mongo-value-object.ts b/packages/2-mongo-family/1-foundation/mongo-contract/src/ir/mongo-value-object.ts index 4023165c3fda..0aadcc5c0bd8 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/src/ir/mongo-value-object.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/src/ir/mongo-value-object.ts @@ -1,9 +1,11 @@ import type { ContractValueObjectField } from '@internal/contract/types'; import { freezeNode, IRNodeBase } from '@internal/framework-components/ir'; -/** Hydration / construction input shape for {@link MongoValueObject}; mirrors the stored JSON. */ +/** Hydration / construction input shape for {@link MongoValueObject}; mirrors the stored JSON, where a field may leave out `nullable` and `many`. */ export interface MongoValueObjectInput { - readonly fields: Readonly>; + readonly fields: Readonly< + Record & { readonly nullable?: boolean }> + >; } /** @@ -21,7 +23,14 @@ export class MongoValueObject extends IRNodeBase { enumerable: false, configurable: false, }); - this.fields = Object.freeze({ ...input.fields }); + this.fields = Object.freeze( + Object.fromEntries( + Object.entries(input.fields).map(([name, field]) => [ + name, + Object.freeze({ ...field, nullable: field.nullable ?? false, many: field.many ?? false }), + ]), + ), + ); freezeNode(this); } } diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/test/field-schema.test.ts b/packages/2-mongo-family/1-foundation/mongo-contract/test/field-schema.test.ts index 76632d7bbbda..5008abac622f 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/test/field-schema.test.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/test/field-schema.test.ts @@ -33,11 +33,9 @@ function contractWithField(kind: 'model' | 'storage value object', field: Record describe.each(['model', 'storage value object'] as const)('%s field schema', (kind) => { const fieldType = { kind: 'scalar', codecId: 'mongo/string@1' }; - it('normalizes an absent many property to false', () => { - const field = { type: fieldType }; - expect(Object.hasOwn(field, 'many')).toBe(false); - expect(MongoContractSchema(contractWithField(kind, field))).toEqual( - contractWithField(kind, { type: fieldType, nullable: false, many: false }), + it('accepts a field with no many property', () => { + expect(MongoContractSchema(contractWithField(kind, { type: fieldType }))).not.toBeInstanceOf( + type.errors, ); }); diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/test/value-object-schema.test.ts b/packages/2-mongo-family/1-foundation/mongo-contract/test/value-object-schema.test.ts index 777eb8b209af..8b23f2c45c0f 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/test/value-object-schema.test.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/test/value-object-schema.test.ts @@ -29,6 +29,14 @@ function valueObjectType(name: string) { const address = { fields: { street: text, city: text } }; +function deepFreeze(value: T): T { + if (typeof value === 'object' && value !== null) { + for (const child of Object.values(value)) deepFreeze(child); + Object.freeze(value); + } + return value; +} + function contractWith( addressField: Record, valueObject: Record = { Address: address }, @@ -96,17 +104,16 @@ describe('the Mongo contract schema with value objects in storage', () => { expect(MongoContractSchema(contract)).toEqual(contract); }); - it('normalizes an absent many on a storage value object member to false', () => { + it('leaves frozen storage unchanged, and the value object fills in an absent nullable and many', () => { const contract = contractWith( { type: valueObjectType('Address'), nullable: false, many: false }, { Address: { fields: { street: { type: text.type } } } }, ); - expect(MongoContractSchema(contract)).toEqual( - contractWith( - { type: valueObjectType('Address'), nullable: false, many: false }, - { Address: { fields: { street: text } } }, - ), - ); + deepFreeze(contract.storage); + expect({ + parsed: MongoContractSchema(contract), + valueObject: new MongoValueObject({ fields: { street: { type: text.type } } }).fields, + }).toEqual({ parsed: contract, valueObject: { street: text } }); }); it('refuses a field type that names its value object the old way', () => { diff --git a/packages/3-targets/mongo/9-facade/test/contract-from-json.test.ts b/packages/3-targets/mongo/9-facade/test/contract-from-json.test.ts index 8fffdf15912e..bae3a9616f3d 100644 --- a/packages/3-targets/mongo/9-facade/test/contract-from-json.test.ts +++ b/packages/3-targets/mongo/9-facade/test/contract-from-json.test.ts @@ -5,7 +5,14 @@ import type { Contract } from '../../../../2-mongo-family/1-foundation/mongo-con import fixtureContractJson from '../../../../2-mongo-family/1-foundation/mongo-contract/test/fixtures/orm-contract.json' with { type: 'json', }; -import { defineContract, enumType, field, member, model } from '../src/exports/contract-builder'; +import { + defineContract, + enumType, + field, + member, + model, + valueObject, +} from '../src/exports/contract-builder'; import mongo from '../src/exports/runtime'; import mongoStatic, { contractFromJson } from '../src/exports/static'; @@ -82,4 +89,42 @@ describe('validating a contract once', () => { expect(second.contract).toBe(first.contract); expect(fromFirst.contract).toBe(first.contract); }); + + it('takes a TypeScript-authored contract with a value object', () => { + const Address = valueObject('Address', { + fields: { street: field.string(), zip: field.string().optional() }, + }); + const authored = defineContract({ + valueObjects: { Address }, + models: { + Shop: model('Shop', { + collection: 'shops', + fields: { _id: field.objectId(), address: field.valueObject(Address) }, + }), + }, + }); + + const { contract } = mongo({ contract: authored }); + + expect( + JSON.parse( + JSON.stringify( + Object.values(contract.storage.namespaces).map( + (namespace) => namespace.entries.valueObject, + ), + ), + ), + ).toContainEqual({ + Address: { + fields: { + street: { + nullable: false, + many: false, + type: { kind: 'scalar', codecId: 'mongo/string@1' }, + }, + zip: { nullable: true, many: false, type: { kind: 'scalar', codecId: 'mongo/string@1' } }, + }, + }, + }); + }); }); diff --git a/test/integration/test/value-objects/value-objects.integration.test.ts b/test/integration/test/value-objects/value-objects.integration.test.ts index 37345f472c9e..633139b2014b 100644 --- a/test/integration/test/value-objects/value-objects.integration.test.ts +++ b/test/integration/test/value-objects/value-objects.integration.test.ts @@ -199,10 +199,6 @@ function valueObjectsAtDefaultNamespace(contract: Contract) { return storageValueObjectsAtDefaultNamespace(contract.storage); } -function asJson(contract: Contract): unknown { - return JSON.parse(JSON.stringify(contract)); -} - function valueObjectRef(namespaceId: string, entityName: string) { return { kind: 'valueObject', @@ -227,7 +223,7 @@ describeWithMongoDB('value objects: end-to-end Mongo', (ctx) => { expect(userFields['homeAddress']!.type.kind).toBe('valueObject'); const validated = { - contract: new MongoContractSerializer().deserializeContract(asJson(contract)), + contract: new MongoContractSerializer().deserializeContract(contract), }; const orm = mongoOrm({ @@ -265,7 +261,7 @@ describeWithMongoDB('value objects: end-to-end Mongo', (ctx) => { if (!result.ok) throw new Error(`Interpretation failed: ${result.failure.summary}`); const validated = { - contract: new MongoContractSerializer().deserializeContract(asJson(result.value)), + contract: new MongoContractSerializer().deserializeContract(result.value), }; const orm = mongoOrm({ contract: validated.contract, @@ -310,7 +306,7 @@ type Address { if (!result.ok) throw new Error(`Interpretation failed: ${result.failure.summary}`); const validated = { - contract: new MongoContractSerializer().deserializeContract(asJson(result.value)), + contract: new MongoContractSerializer().deserializeContract(result.value), }; const orm = mongoOrm({ contract: validated.contract, From c2b4f23b050df201f1e05d85f4306a924e9dd9a7 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 14:20:43 +0200 Subject: [PATCH 038/104] TML-3537: a print test reaches the refusal of a value object member whose value set reference is not the derived one Signed-off-by: willbot Signed-off-by: Will Madden --- .../1-target/test/psl-print/refusals-storage.test.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/3-targets/postgres/1-target/test/psl-print/refusals-storage.test.ts b/packages/3-targets/postgres/1-target/test/psl-print/refusals-storage.test.ts index 74db2f7849bf..644a1e1dd16c 100644 --- a/packages/3-targets/postgres/1-target/test/psl-print/refusals-storage.test.ts +++ b/packages/3-targets/postgres/1-target/test/psl-print/refusals-storage.test.ts @@ -330,7 +330,7 @@ describe('value objects', () => { ); }); - it('refuses a value-object member whose value set no enum derives', () => { + it('refuses a value set no enum derives, in the namespace that holds it', () => { expect( printing( withCountryMembers( @@ -341,6 +341,16 @@ describe('value objects', () => { ).toThrow(refusal({ namespaceId: 'public', name: 'Label' })); }); + it('refuses a value-object member whose value set is not the one the enum of its name derives', () => { + expect( + printing( + withCountryMembers({ + country: { ...TEXT_FIELD, valueSet: { ...countryValueSet, spaceId: 'other' } }, + }), + ), + ).toThrow(refusal({ coordinate: '"public".Address.country' })); + }); + it('refuses a value-object member typed by a domain enum that also has type parameters', () => { expect( printing( From 0579aea50c2772abf5542e296a22976c9f054a69 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 14:23:55 +0200 Subject: [PATCH 039/104] TML-3537: dot paths into a Mongo value object end at a value object already on the path Signed-off-by: willbot Signed-off-by: Will Madden --- .../orm/src/field-accessor.ts | 17 +++- .../orm/test/dot-path.recursive.test-d.ts | 62 ++++++++++++++ .../query-builder/src/resolve-path.ts | 33 ++++++-- .../test/resolve-path.recursive.test-d.ts | 80 +++++++++++++++++++ 4 files changed, 182 insertions(+), 10 deletions(-) create mode 100644 packages/2-mongo-family/5-query-builders/orm/test/dot-path.recursive.test-d.ts create mode 100644 packages/2-mongo-family/5-query-builders/query-builder/test/resolve-path.recursive.test-d.ts diff --git a/packages/2-mongo-family/5-query-builders/orm/src/field-accessor.ts b/packages/2-mongo-family/5-query-builders/orm/src/field-accessor.ts index 4efc780b1572..3ec309a1473a 100644 --- a/packages/2-mongo-family/5-query-builders/orm/src/field-accessor.ts +++ b/packages/2-mongo-family/5-query-builders/orm/src/field-accessor.ts @@ -114,17 +114,30 @@ type VOValueObjectFieldKeys> = { : never; }[keyof Fields & string]; +/** A value object's storage coordinate as one string, so the value objects a path has entered form a union. */ +type ValueObjectKey = + `${Reference['namespaceId']}.${Reference['entityName']}`; + +/** The dot paths into a value object's fields. A path ends at a value object it has already entered, so a value object that contains itself gives finitely many paths. */ type VODotPaths< TContract extends MongoContract, Fields extends Record, Prefix extends string, + Entered extends string, > = | { [K in VOScalarFieldKeys]: `${Prefix}${K}` }[VOScalarFieldKeys] | { [K in VOValueObjectFieldKeys]: Fields[K] extends { readonly type: infer Reference extends ValueObjectFieldType; } - ? VODotPaths, `${Prefix}${K}.`> + ? ValueObjectKey extends Entered + ? never + : VODotPaths< + TContract, + VOFields, + `${Prefix}${K}.`, + Entered | ValueObjectKey + > : never; }[VOValueObjectFieldKeys]; @@ -138,7 +151,7 @@ export type DotPath< >]: MongoModelsMap[ModelName]['fields'][K] extends { readonly type: infer Reference extends ValueObjectFieldType; } - ? VODotPaths, `${K}.`> + ? VODotPaths, `${K}.`, ValueObjectKey> : never; }[ValueObjectFieldKeys]; diff --git a/packages/2-mongo-family/5-query-builders/orm/test/dot-path.recursive.test-d.ts b/packages/2-mongo-family/5-query-builders/orm/test/dot-path.recursive.test-d.ts new file mode 100644 index 000000000000..7324ebd25de1 --- /dev/null +++ b/packages/2-mongo-family/5-query-builders/orm/test/dot-path.recursive.test-d.ts @@ -0,0 +1,62 @@ +import { expectTypeOf, test } from 'vitest'; +import type { Contract } from '../../../1-foundation/mongo-contract/test/fixtures/orm-contract'; +import type { DotPath } from '../src/field-accessor'; + +type NodeRef = { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Node'; +}; + +type WithTree = Contract & { + readonly storage: { + readonly namespaces: { + readonly __unbound__: { + readonly entries: { + readonly valueObject: { + readonly Node: { + readonly fields: { + readonly label: { + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + readonly nullable: false; + readonly many: false; + }; + readonly child: { + readonly type: NodeRef; + readonly nullable: true; + readonly many: false; + }; + }; + }; + }; + }; + }; + }; + }; + readonly domain: { + readonly namespaces: { + readonly __unbound__: { + readonly models: { + readonly User: { + readonly fields: { + readonly tree: { + readonly type: NodeRef; + readonly nullable: false; + readonly many: false; + }; + }; + }; + }; + }; + }; + }; +}; + +test('dot paths end at a value object already on the path', () => { + type Paths = DotPath; + expectTypeOf<'tree.label'>().toExtend(); + expectTypeOf<'tree.child.label'>().not.toExtend(); + expectTypeOf<'homeAddress.city'>().toExtend(); +}); diff --git a/packages/2-mongo-family/5-query-builders/query-builder/src/resolve-path.ts b/packages/2-mongo-family/5-query-builders/query-builder/src/resolve-path.ts index 018164b74630..a3124a51503f 100644 --- a/packages/2-mongo-family/5-query-builders/query-builder/src/resolve-path.ts +++ b/packages/2-mongo-family/5-query-builders/query-builder/src/resolve-path.ts @@ -122,7 +122,7 @@ type FieldToLeaf = F extends { * `ModelNestedShape` hover output and `keyof`/indexed-access resolution * concrete instead of collapsing to `{ [x: string]: … }`. */ -type TranslateField = F extends { +type TranslateField = F extends { readonly many: { readonly elementNullable: boolean }; } ? FieldToLeaf @@ -130,25 +130,42 @@ type TranslateField = F extends { readonly type: infer Reference extends ValueObjectFieldType; readonly nullable: infer Null extends boolean; } - ? ObjectField> & { readonly nullable: Null } + ? ValueObjectKey extends Entered + ? DocField & { readonly nullable: Null } + : ObjectField>> & { + readonly nullable: Null; + } : F extends { readonly type: { readonly kind: 'scalar'; readonly codecId: string }; } ? FieldToLeaf : DocField; +/** A value object's storage coordinate as one string, so the value objects a path has entered form a union. */ +type ValueObjectKey = + `${Reference['namespaceId']}.${Reference['entityName']}`; + /** * Resolve the storage value object a field's reference names into its own - * `NestedDocShape`. The mapped iteration is inlined here (not delegated + * `NestedDocShape`. A field that refers to a value object the path has already + * entered is an opaque leaf, so a value object that contains itself ends there. The mapped iteration is inlined here (not delegated * to a generic helper) so that the homomorphism over the value object's * `fields` is preserved and the hover / indexed-access surface stays * concrete at instantiation time. */ -type VONestedShape = [ - ContractValueObjectAt, -] extends [infer VO extends { readonly fields: Record }] +type VONestedShape< + TContract extends MongoContract, + Reference extends ValueObjectFieldType, + Entered extends string, +> = [ContractValueObjectAt] extends [ + infer VO extends { readonly fields: Record }, +] ? { - readonly [K in keyof VO['fields'] & string]: TranslateField; + readonly [K in keyof VO['fields'] & string]: TranslateField< + TContract, + VO['fields'][K], + Entered + >; } : never; @@ -174,7 +191,7 @@ export type ModelNestedShape< TContract, ModelName, K - >]: TranslateField[ModelName]['fields'][K]>; + >]: TranslateField[ModelName]['fields'][K], never>; }; // ── Path walking ───────────────────────────────────────────────────────── diff --git a/packages/2-mongo-family/5-query-builders/query-builder/test/resolve-path.recursive.test-d.ts b/packages/2-mongo-family/5-query-builders/query-builder/test/resolve-path.recursive.test-d.ts new file mode 100644 index 000000000000..e72f21327eed --- /dev/null +++ b/packages/2-mongo-family/5-query-builders/query-builder/test/resolve-path.recursive.test-d.ts @@ -0,0 +1,80 @@ +import { expectTypeOf } from 'vitest'; +import type { ModelNestedShape, ValidPaths } from '../src/resolve-path'; +import type { TestContract } from './fixtures/test-contract'; + +type NodeRef = { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Node'; +}; + +type RecursiveContract = Omit & { + readonly storage: { + readonly storageHash: TestContract['storage']['storageHash']; + readonly namespaces: { + readonly __unbound__: { + readonly id: '__unbound__'; + readonly kind: 'mongo-namespace'; + readonly entries: { + readonly collection: { readonly trees: { readonly kind: 'mongo-collection' } }; + readonly valueObject: { + readonly Node: { + readonly fields: { + readonly label: { + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + readonly nullable: false; + readonly many: false; + }; + readonly child: { + readonly type: NodeRef; + readonly nullable: true; + readonly many: false; + }; + }; + }; + }; + }; + }; + }; + }; + readonly domain: { + readonly namespaces: { + readonly __unbound__: { + readonly models: { + readonly Tree: { + readonly fields: { + readonly _id: { + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; + readonly nullable: false; + readonly many: false; + }; + readonly tree: { + readonly type: NodeRef; + readonly nullable: false; + readonly many: false; + }; + }; + readonly relations: Record; + readonly storage: { + readonly collection: 'trees'; + readonly fields: { + readonly _id: { readonly field: '_id' }; + readonly tree: { readonly field: 'tree' }; + }; + }; + }; + }; + }; + }; + }; +}; + +describe('a value object that contains itself', () => { + it('ends dot paths at the value object already on the path', () => { + expectTypeOf>>().toEqualTypeOf< + '_id' | 'tree' | 'tree.label' | 'tree.child' + >(); + }); +}); From 25330924ec4459b6ef6efeebe9a4d93b9dfd56f8 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 14:24:02 +0200 Subject: [PATCH 040/104] TML-3537: a Mongo ORM filter encodes its value through a list index in a dot path Signed-off-by: willbot Signed-off-by: Will Madden --- .../5-query-builders/orm/src/collection.ts | 6 ++++++ .../orm/test/integration/value-objects.test.ts | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/packages/2-mongo-family/5-query-builders/orm/src/collection.ts b/packages/2-mongo-family/5-query-builders/orm/src/collection.ts index 58438ea0e9af..2e00a9fa9e58 100644 --- a/packages/2-mongo-family/5-query-builders/orm/src/collection.ts +++ b/packages/2-mongo-family/5-query-builders/orm/src/collection.ts @@ -260,6 +260,8 @@ function isUnknownRecord(value: unknown): value is Record { return typeof value === 'object' && value !== null && !Array.isArray(value); } +const LIST_INDEX = /^\d+$/; + class MongoCollectionImpl< TContract extends MongoContractWithTypeMaps, ModelName extends string & keyof MongoModelsMap, @@ -1059,6 +1061,10 @@ class MongoCollectionImpl< let field: ContractField | undefined = head === undefined ? undefined : this.#modelFields()[head]; for (const segment of rest) { + if (field?.many && LIST_INDEX.test(segment)) { + field = { ...field, many: false }; + continue; + } if (field?.type.kind !== 'valueObject') return undefined; field = this.#valueObjectAt(field.type)?.fields[segment]; } diff --git a/packages/2-mongo-family/5-query-builders/orm/test/integration/value-objects.test.ts b/packages/2-mongo-family/5-query-builders/orm/test/integration/value-objects.test.ts index 2559a099ed60..7df0759a4c2e 100644 --- a/packages/2-mongo-family/5-query-builders/orm/test/integration/value-objects.test.ts +++ b/packages/2-mongo-family/5-query-builders/orm/test/integration/value-objects.test.ts @@ -195,4 +195,22 @@ describe('value objects in the Mongo ORM', { timeout: timeouts.spinUpMongoMemory landmark: { label: 'Basilique', placeId: new ObjectId(landmarkId) }, }); }); + + it('filters by a dot path through a list index into a list of value objects', async () => { + const places = placesOf(mongoOrm({ contract, executor: runtime, enums })); + const firstId = new ObjectId().toHexString(); + const secondId = new ObjectId().toHexString(); + await places.create({ + address: { city: 'Lyon', landmark: { label: 'Fourvière', placeId: firstId } }, + stops: [ + { label: 'Bellecour', placeId: firstId }, + { label: 'Terreaux', placeId: secondId }, + ], + }); + + const found = await places.where(MongoFieldFilter.eq('stops.1.placeId', secondId)).all(); + expect(found.map((place) => place.stops.map((stop) => stop.label))).toEqual([ + ['Bellecour', 'Terreaux'], + ]); + }); }); From cf19f774897e86b7d9b51dacda2806c06207122d Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 14:29:38 +0200 Subject: [PATCH 041/104] TML-3537: the MySQL and SQLite packages export the shared check expression module Signed-off-by: willbot Signed-off-by: Will Madden --- packages/9-public/@prisma/orm-mysql/package.json | 1 + packages/9-public/@prisma/orm-sqlite/package.json | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/9-public/@prisma/orm-mysql/package.json b/packages/9-public/@prisma/orm-mysql/package.json index 227c012f73a2..4eab48876f87 100644 --- a/packages/9-public/@prisma/orm-mysql/package.json +++ b/packages/9-public/@prisma/orm-mysql/package.json @@ -83,6 +83,7 @@ "./family-contract": "./dist/family-contract.mjs", "./family-contract/authored-check-naming": "./dist/family-contract__authored-check-naming.mjs", "./family-contract/canonicalization-hooks": "./dist/family-contract__canonicalization-hooks.mjs", + "./family-contract/check-expressions": "./dist/family-contract__check-expressions.mjs", "./family-contract/contract-view": "./dist/family-contract__contract-view.mjs", "./family-contract/data-type": "./dist/family-contract__data-type.mjs", "./family-contract/data-type-support": "./dist/family-contract__data-type-support.mjs", diff --git a/packages/9-public/@prisma/orm-sqlite/package.json b/packages/9-public/@prisma/orm-sqlite/package.json index 31a32a33f726..6937b3115832 100644 --- a/packages/9-public/@prisma/orm-sqlite/package.json +++ b/packages/9-public/@prisma/orm-sqlite/package.json @@ -92,6 +92,7 @@ "./family-contract": "./dist/family-contract.mjs", "./family-contract/authored-check-naming": "./dist/family-contract__authored-check-naming.mjs", "./family-contract/canonicalization-hooks": "./dist/family-contract__canonicalization-hooks.mjs", + "./family-contract/check-expressions": "./dist/family-contract__check-expressions.mjs", "./family-contract/contract-view": "./dist/family-contract__contract-view.mjs", "./family-contract/data-type": "./dist/family-contract__data-type.mjs", "./family-contract/data-type-support": "./dist/family-contract__data-type-support.mjs", From 9692d3f8ef2cef23f824e06e5e131d8a289a331b Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 14:30:28 +0200 Subject: [PATCH 042/104] TML-3537: dispatch brief for re-emit, example migrations, the upgrade script and docs Signed-off-by: willbot Signed-off-by: Will Madden --- .../16-re-emit-migrations-upgrade-docs.md | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 projects/migration-statements/slices/remaining-nouns/dispatches/16-re-emit-migrations-upgrade-docs.md diff --git a/projects/migration-statements/slices/remaining-nouns/dispatches/16-re-emit-migrations-upgrade-docs.md b/projects/migration-statements/slices/remaining-nouns/dispatches/16-re-emit-migrations-upgrade-docs.md new file mode 100644 index 000000000000..d1f500c6da5f --- /dev/null +++ b/projects/migration-statements/slices/remaining-nouns/dispatches/16-re-emit-migrations-upgrade-docs.md @@ -0,0 +1,87 @@ +# Dispatch 16 — re-emit, example migrations, the upgrade script, and docs + +**Plan:** [`../plan-pr3.md`](../plan-pr3.md) · **ADR:** ADR 278 · **Grounding:** `wip/pr3-grounding.md` § C and risks 6–8 · **Builds on:** dispatches 11, 14 and 15, and the merge of main (`4aaa36d9bc`) · **Branch:** `tml-3537-value-objects-storage` + +## Outcome + +The whole repository is on the new contract shape, users can move their projects to it, and the docs describe it. + +### 1. Re-emit + +- Every committed `contract.json`, `contract.d.ts` and `expected.contract.json` is re-emitted with the repository's own scripts (`pnpm fixtures:emit`), and `pnpm fixtures:check` is clean afterwards. +- Generated test fixtures are regenerated by their generator, never by hand. In particular, `packages/2-mongo-family/3-tooling/emitter/test/generate-orm-fixture.ts` writes both `mongo-contract/test/fixtures/orm-contract.d.ts` and `9-family/test/fixtures/orm-contract.d.ts`. A dispatch 13 hand edit put the first copy out of step with the second. Also regenerate `mongo-orm/test/fixtures/stored-names-contract.*`. +- Hand-written test contracts with `valueObjects` in the domain (grounding C: about 77 test files) move to storage. +- These tests are red today and must be green afterwards: + - every sql-orm-client test that loads the generated fixtures; + - mongo-orm `collection.stored-names.test.ts` and `stored-names.test-d.ts`; + - target-postgres `full-text-index-contract-load.test.ts`; + - family-mongo `mongo-contract-view` and `mongo-migration`; + - the `authored-contract-roundtrip` cases for value objects, polymorphism and "refuses a to-one relation"; + - the `retail-store` build and the typecheck of every example. + +### 2. Example migrations + +- Each `examples/prisma-8-demo` migration that introduces a value object or a variant gains the operations that create the new checks (plan decision 7; Will, 2026-10-23). That means the value object check, the discriminator membership check, and the discriminator value set. +- `scripts/regen-example-migrations.mjs` re-serializes `ops.json` from `migration.ts`, so the operations go into `migration.ts`. Write them as a user would by hand, with the same factory calls the planner renders. +- Check on PGlite: `db migrate` runs the whole chain from empty, and then `db verify` with strict schema verification is clean. +- Do the same for every other example with a Postgres migration chain that holds value objects or variants. The Mongo examples (`mongo-demo`, `retail-store`) need no new operations, since Mongo gets no new checks; confirm with `db verify` on mongodb-memory-server. + +### 3. Upgrade instructions and script + +- Add fragments for the app and for extensions, following the `record-upgrade-instructions` skill (`skills-contrib/record-upgrade-instructions/SKILL.md`). The precedent is the rc.14 to rc.15 `data-type-in-contract` fragment and script under `skills/prisma-8/upgrading/{app,extension}/upgrades/8.0.0-rc.14-to-8.0.0-rc.15/`. Use the upgrade folder for the current release line; the skill says how to name it. +- **The app script** rewrites a user's committed contract snapshots into the new shape. That changes their storage hashes. So the script must also rename each snapshot directory and update every manifest and `migration.json` that names it, keeping the migration graph whole. If that cannot be done safely, it must leave the old snapshots readable instead. Decide from the precedent, and say which you chose and why. +- Run the script on a copy of a project with a real migration history, inside `wip/`. A good candidate is the `prisma-8-demo` chain as it was at `origin/main`. Afterwards, `migration status` and `db migrate` on a fresh PGlite database must work. Commit a test for the script if the precedent has one. +- **The app fragment says:** + - Re-emit the contract. An old `contract.json` is refused with the re-emit message. + - Run the script for the committed snapshots. + - `ValueObjectTypesFromDomain`, which was public through `@prisma/orm-mongo/contract/types`, is now `ValueObjectTypesFromStorage`. A committed `contract.d.ts` that imports it compiles again once re-emitted. + - On Postgres, the next migration adds a check to every value object column and every discriminator column of a managed table. Rows that do not fit fail the apply. That includes rows with extra keys, which the SQL ORM never refused. Say how to find such rows first, with a query using the same predicate. +- **The extension fragment says:** + - `contractModelToMongoResultShape` and `contractFieldToMongoFieldShape` take `storage` instead of `valueObjects`. + - `compileMongoQuery`'s fifth argument is the storage. + - `MongoValueObjects` is removed. + - The removed Postgres exports `PostgresCheckKind`, `PostgresCheckExpressionInput` and `PostgresCheckExpressionCandidate` are replaced by `CheckExpressionInput` and `CheckExpressionCandidate` from the family's check expression module. + - This replaces the advice in the rc.14 release notes (`docs/releases/v8.0.0-rc.14.md:65-76`, `CHANGELOG.md:887-898`). +- `check:upgrade-coverage --mode pr --prev $(git merge-base HEAD origin/main) --head HEAD` passes. Dispatch 15 saw it fail on diffs in `examples/prisma-8-demo-mysql` that the dispatch did not touch. Find out why, and fix it or explain it. + +### 4. Docs + +- **ADR 278:** check that it matches what was built. Notes on what changed from its first text, for example the jsonpath predicate and the discriminator value set on SQL only, go in the ADR's own words, with no ticket or dispatch ids. +- **ADR 172 and ADR 178:** add a short note where they say value objects live in the domain plane. +- **Data Contract subsystem doc and MongoDB Family doc:** update them for the new shape. +- **Contract Emitter & Types subsystem doc**, around lines 262 and 272: + - Remove the domain `valueObjects` line and `ValueObjectTypesFromDomain`. + - Describe `ValueObjectTypesFromStorage`, `entries.valueObject`, and the optional sixth parameter of `FieldTypesFromDomain`. +- **The prisma-8 skill** (`skills/prisma-8/`): update its references to value objects. +- **READMEs** of the packages whose public surface changed. + +## Rules + +- Never run `pnpm test:integration`, `pnpm test:e2e` or `pnpm test:all`. Run only named files inside `test/integration`, with `pnpm test `. Run journeys with `pnpm test:journeys `. +- Use `mise exec --` for node and pnpm. Never `npx`. Do not write to `/tmp` or any system temp directory. Save slow output under `wip/` and read the file. Never use bare `git stash`. +- Prose in Markdown is never hard-wrapped. ADRs carry no ticket ids, dispatch ids or slice names. +- No comments where the code can say it. No bare `as` casts in production code, including the upgrade script. No `any`. +- Commit with `git commit -s --trailer "Signed-off-by: Will Madden "`. No AI attribution lines. Stage files explicitly. Commit re-emitted files apart from hand-written changes. +- Do not push. Do not open a PR. + +## Done when + +- `pnpm fixtures:check` is clean, and so is `pnpm build`. +- `pnpm test:packages` passes, apart from failures you show have other causes, such as the tarball tests' `@vercel/detect-agent` trust warning. +- Typecheck passes in every example. +- Typecheck, lint and `test --coverage` pass in every package this dispatch touches. +- `lint:deps`, `lint:ratchets`, `check:error-reference` and `check:upgrade-coverage` pass. +- The example migration chains pass on PGlite, as section 2 describes. +- The upgrade script is validated on a real history, as section 3 describes. + +## Report + +Plain English, short sentences. Cover: +- what was re-emitted; +- each example migration's new operations; +- the script's approach to hashes and manifests, and the validation run; +- the fragments; +- the docs changed; +- the checks; +- anything still red, and why; +- the commits. From 0b3eba9cea1082e127da2795f7a096f86a23453e Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 14:48:45 +0200 Subject: [PATCH 043/104] TML-3537: the Mongo ORM list-index filter test reads with first(), which its type has, and a second place shows the filter applies Signed-off-by: willbot Signed-off-by: Will Madden --- .../orm/test/integration/value-objects.test.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/2-mongo-family/5-query-builders/orm/test/integration/value-objects.test.ts b/packages/2-mongo-family/5-query-builders/orm/test/integration/value-objects.test.ts index 7df0759a4c2e..ec3bde065542 100644 --- a/packages/2-mongo-family/5-query-builders/orm/test/integration/value-objects.test.ts +++ b/packages/2-mongo-family/5-query-builders/orm/test/integration/value-objects.test.ts @@ -200,6 +200,13 @@ describe('value objects in the Mongo ORM', { timeout: timeouts.spinUpMongoMemory const places = placesOf(mongoOrm({ contract, executor: runtime, enums })); const firstId = new ObjectId().toHexString(); const secondId = new ObjectId().toHexString(); + await places.create({ + address: { city: 'Paris', landmark: { label: 'Louvre', placeId: secondId } }, + stops: [ + { label: 'Châtelet', placeId: secondId }, + { label: 'Bastille', placeId: firstId }, + ], + }); await places.create({ address: { city: 'Lyon', landmark: { label: 'Fourvière', placeId: firstId } }, stops: [ @@ -208,9 +215,9 @@ describe('value objects in the Mongo ORM', { timeout: timeouts.spinUpMongoMemory ], }); - const found = await places.where(MongoFieldFilter.eq('stops.1.placeId', secondId)).all(); - expect(found.map((place) => place.stops.map((stop) => stop.label))).toEqual([ - ['Bellecour', 'Terreaux'], - ]); + const found = await places.where(MongoFieldFilter.eq('stops.1.placeId', secondId)).first(); + expect(found).toMatchObject({ + stops: [{ label: 'Bellecour' }, { label: 'Terreaux' }], + }); }); }); From 1f3fbf96d51a82d2d974ab4453c5040a3443986a Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 14:51:45 +0200 Subject: [PATCH 044/104] TML-3537: contract print takes a value object column's nullability from the column, so a variant on its base table prints Signed-off-by: willbot Signed-off-by: Will Madden --- .../src/core/psl-print/psl-contract.ts | 9 ++++--- ...red-contract-roundtrip.integration.test.ts | 27 +++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/packages/3-targets/postgres/1-target/src/core/psl-print/psl-contract.ts b/packages/3-targets/postgres/1-target/src/core/psl-print/psl-contract.ts index 193d51ed8f18..a2ef867ef318 100644 --- a/packages/3-targets/postgres/1-target/src/core/psl-print/psl-contract.ts +++ b/packages/3-targets/postgres/1-target/src/core/psl-print/psl-contract.ts @@ -107,7 +107,7 @@ function nativeEnumsOf( return nativeEnums; } -/** The value object field each column of a table holds, over the fields of every model the table stores. */ +/** The value object field each column of a table holds, over the fields of every model the table stores. Nullability comes from the column, as in the build: a variant that shares its base table has a nullable column. */ function valueObjectFieldsOf( contract: Contract, entries: readonly ModelWithTable[], @@ -116,9 +116,12 @@ function valueObjectFieldsOf( for (const entry of entries) { for (const [fieldName, field] of Object.entries(entry.model.fields)) { const columnName = entry.storage.fields[fieldName]?.column; - if (field.type.kind !== 'valueObject' || columnName === undefined) continue; + const column = columnName === undefined ? undefined : entry.table.columns[columnName]; + if (field.type.kind !== 'valueObject' || columnName === undefined || column === undefined) { + continue; + } fields.set(columnName, { - nullable: field.nullable, + nullable: column.nullable, many: field.many ?? false, type: { kind: 'valueObject', diff --git a/test/integration/test/psl-print/authored-contract-roundtrip.integration.test.ts b/test/integration/test/psl-print/authored-contract-roundtrip.integration.test.ts index 530d62f88996..f5a1e20d4ea5 100644 --- a/test/integration/test/psl-print/authored-contract-roundtrip.integration.test.ts +++ b/test/integration/test/psl-print/authored-contract-roundtrip.integration.test.ts @@ -60,6 +60,33 @@ const pslCases: ReadonlyArray<{ readonly name: string; readonly schema: string } 'utf-8', ).replace('// use prisma-8\n', ''), }, + { + name: 'a value object field of a variant that shares the base table, whose column is nullable', + schema: `type Geo { + lat Float + lng Float +} + +model Shape { + id Int @id + kind String + + @@discriminator(kind) +} + +model Circle { + center Geo + + @@base(Shape, "circle") +} + +model Square { + side Int + + @@base(Shape, "square") +} +`, + }, { name: 'a model control policy', schema: `model Ledger { From 04c29255a800d20a236167cbaf2f5bd88b74adfa Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 14:54:29 +0200 Subject: [PATCH 045/104] TML-3537: the Postgres value object check refuses JSON null for a required member whose codec has no JSON type, except a JSON member Signed-off-by: willbot Signed-off-by: Will Madden --- .../1-target/src/core/check-expressions.ts | 15 +++++++++++++-- .../1-target/src/core/value-object-json-types.ts | 10 +++++++++- .../test/check-expressions.value-object.test.ts | 4 ++-- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/packages/3-targets/postgres/1-target/src/core/check-expressions.ts b/packages/3-targets/postgres/1-target/src/core/check-expressions.ts index bc069709104b..43eb308d5809 100644 --- a/packages/3-targets/postgres/1-target/src/core/check-expressions.ts +++ b/packages/3-targets/postgres/1-target/src/core/check-expressions.ts @@ -7,7 +7,10 @@ import type { } from '@internal/sql-contract/check-expressions'; import { invariant } from '@internal/utils/assertions'; import { escapeLiteral, quoteIdentifier } from './sql-utils'; -import { valueObjectJsonTypeOfCodec } from './value-object-json-types'; +import { + valueObjectJsonTypeOfCodec, + valueObjectMemberHoldsAnyJson, +} from './value-object-json-types'; /** * Renders the checks a Postgres column needs, as opaque predicate text. @@ -133,7 +136,7 @@ function objectPredicate(valueObject: ResolvedValueObject, path: string): string const field = valueObject.fields[name]; invariant(field !== undefined, `value object member "${name}" is missing`); const isMember = `@.key == ${jsonPathLiteral(name)}`; - const predicate = fieldPredicate(field, '@.value'); + const predicate = requiredMemberPredicate(field, fieldPredicate(field, '@.value')); if (!field.nullable) { return [ predicate === undefined @@ -154,6 +157,14 @@ function objectPredicate(valueObject: ResolvedValueObject, path: string): string return [`${path}.type() == "object"`, ...members, otherKey].join(' && '); } +function requiredMemberPredicate( + field: ResolvedValueObjectField, + predicate: string | undefined, +): string | undefined { + if (predicate !== undefined || field.nullable || field.type.kind !== 'scalar') return predicate; + return valueObjectMemberHoldsAnyJson(field.type.codecId) ? undefined : '@.value.type() != "null"'; +} + function jsonPathLiteral(value: string | number): string { return typeof value === 'string' ? JSON.stringify(value) : String(value); } diff --git a/packages/3-targets/postgres/1-target/src/core/value-object-json-types.ts b/packages/3-targets/postgres/1-target/src/core/value-object-json-types.ts index 983cea67d3cd..ffff44af802a 100644 --- a/packages/3-targets/postgres/1-target/src/core/value-object-json-types.ts +++ b/packages/3-targets/postgres/1-target/src/core/value-object-json-types.ts @@ -10,6 +10,8 @@ import { PG_INT2_CODEC_ID, PG_INT4_CODEC_ID, PG_INT8_NUMBER_CODEC_ID, + PG_JSON_CODEC_ID, + PG_JSONB_CODEC_ID, PG_TEXT_CODEC_ID, PG_TIME_STRING_CODEC_ID, PG_TIME_TEMPORAL_CODEC_ID, @@ -30,7 +32,7 @@ import { type JsonType = 'string' | 'number' | 'boolean'; /** - * The JSON type a value object member of each codec has in the JSON the SQL ORM writes: the type its JavaScript value serializes to. JSON members hold any value, and a codec missing here is checked for presence only. + * The JSON type a value object member of each codec has in the JSON the SQL ORM writes: the type its JavaScript value serializes to. JSON members hold any value, and a member of a codec missing here is checked only for being present and, when required, not JSON null. */ const JSON_TYPE_OF_CODEC: Readonly> = { [PG_TEXT_CODEC_ID]: 'string', @@ -64,3 +66,9 @@ const JSON_TYPE_OF_CODEC: Readonly> = { export function valueObjectJsonTypeOfCodec(codecId: string): JsonType | undefined { return Object.hasOwn(JSON_TYPE_OF_CODEC, codecId) ? JSON_TYPE_OF_CODEC[codecId] : undefined; } + +const JSON_CODECS: ReadonlySet = new Set([PG_JSON_CODEC_ID, PG_JSONB_CODEC_ID]); + +export function valueObjectMemberHoldsAnyJson(codecId: string): boolean { + return JSON_CODECS.has(codecId); +} diff --git a/packages/3-targets/postgres/1-target/test/check-expressions.value-object.test.ts b/packages/3-targets/postgres/1-target/test/check-expressions.value-object.test.ts index 6e74c505d015..5bbec69029c2 100644 --- a/packages/3-targets/postgres/1-target/test/check-expressions.value-object.test.ts +++ b/packages/3-targets/postgres/1-target/test/check-expressions.value-object.test.ts @@ -135,7 +135,7 @@ describe('postgresRenderCheckExpressions for a column that holds a value object' ]); }); - it('checks only the presence of a member whose codec has no known JSON type, and of a JSON member', () => { + it('refuses JSON null for a required member whose codec has no known JSON type, and checks only the presence of a JSON member', () => { const bytes = member(scalar('pg/bytea@1')); const data = member(scalar('pg/jsonb@1')); const notes = member(scalar('pg/jsonb@1'), { nullable: true }); @@ -143,7 +143,7 @@ describe('postgresRenderCheckExpressions for a column that holds a value object' expect(render(member(holding({ fields: { blobs, bytes, data, notes } })))).toEqual([ { kind: 'valueObject', - expression: `jsonb_path_match("address", 'strict ${isObject} && exists($.keyvalue() ? (@.key == "blobs" && (@.value.type() == "array" && !exists(@.value[*] ? (@.type() == "null"))))) && exists($.keyvalue() ? (@.key == "bytes")) && exists($.keyvalue() ? (@.key == "data")) && !exists($.keyvalue() ? (@.key != "blobs" && @.key != "bytes" && @.key != "data" && @.key != "notes"))') IS TRUE`, + expression: `jsonb_path_match("address", 'strict ${isObject} && exists($.keyvalue() ? (@.key == "blobs" && (@.value.type() == "array" && !exists(@.value[*] ? (@.type() == "null"))))) && exists($.keyvalue() ? (@.key == "bytes" && (@.value.type() != "null"))) && exists($.keyvalue() ? (@.key == "data")) && !exists($.keyvalue() ? (@.key != "blobs" && @.key != "bytes" && @.key != "data" && @.key != "notes"))') IS TRUE`, }, ]); }); From 06e5a738ba0ccbb3c576215de9a326e114a2770d Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 14:55:05 +0200 Subject: [PATCH 046/104] TML-3537: the Postgres value object check requires a Decimal member to be a JSON string, as the SQL ORM writes it Signed-off-by: willbot Signed-off-by: Will Madden --- .../1-target/src/core/value-object-json-types.ts | 4 +++- .../test/check-expressions.value-object.test.ts | 10 ++++++++++ projects/migration-statements/deferred.md | 7 +++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/3-targets/postgres/1-target/src/core/value-object-json-types.ts b/packages/3-targets/postgres/1-target/src/core/value-object-json-types.ts index ffff44af802a..bd9945ec2999 100644 --- a/packages/3-targets/postgres/1-target/src/core/value-object-json-types.ts +++ b/packages/3-targets/postgres/1-target/src/core/value-object-json-types.ts @@ -12,6 +12,7 @@ import { PG_INT8_NUMBER_CODEC_ID, PG_JSON_CODEC_ID, PG_JSONB_CODEC_ID, + PG_NUMERIC_CODEC_ID, PG_TEXT_CODEC_ID, PG_TIME_STRING_CODEC_ID, PG_TIME_TEMPORAL_CODEC_ID, @@ -32,7 +33,7 @@ import { type JsonType = 'string' | 'number' | 'boolean'; /** - * The JSON type a value object member of each codec has in the JSON the SQL ORM writes: the type its JavaScript value serializes to. JSON members hold any value, and a member of a codec missing here is checked only for being present and, when required, not JSON null. + * The JSON type a value object member of each codec has in the JSON the SQL ORM writes: the type its JavaScript value serializes to, or for `Decimal` the string the ORM writes. JSON members hold any value, and a member of a codec missing here is checked only for being present and, when required, not JSON null. */ const JSON_TYPE_OF_CODEC: Readonly> = { [PG_TEXT_CODEC_ID]: 'string', @@ -42,6 +43,7 @@ const JSON_TYPE_OF_CODEC: Readonly> = { [SQL_CHAR_CODEC_ID]: 'string', [SQL_VARCHAR_CODEC_ID]: 'string', [PG_UUID_CODEC_ID]: 'string', + [PG_NUMERIC_CODEC_ID]: 'string', [PG_DATE_TEMPORAL_CODEC_ID]: 'string', [PG_TIMESTAMP_TEMPORAL_CODEC_ID]: 'string', [PG_TIMESTAMPTZ_TEMPORAL_CODEC_ID]: 'string', diff --git a/packages/3-targets/postgres/1-target/test/check-expressions.value-object.test.ts b/packages/3-targets/postgres/1-target/test/check-expressions.value-object.test.ts index 5bbec69029c2..d3fd54a7c709 100644 --- a/packages/3-targets/postgres/1-target/test/check-expressions.value-object.test.ts +++ b/packages/3-targets/postgres/1-target/test/check-expressions.value-object.test.ts @@ -148,6 +148,16 @@ describe('postgresRenderCheckExpressions for a column that holds a value object' ]); }); + it('requires a decimal member to be a JSON string, as the ORM writes it', () => { + const amount = member(scalar('pg/numeric@1')); + expect(render(member(holding({ fields: { amount } })))).toEqual([ + { + kind: 'valueObject', + expression: `jsonb_path_match("address", 'strict ${isObject} && exists($.keyvalue() ? (@.key == "amount" && (@.value.type() == "string"))) && !exists($.keyvalue() ? (@.key != "amount"))') IS TRUE`, + }, + ]); + }); + it('writes member names as jsonpath strings', () => { const quoted = member(scalar('pg/text@1')); expect(render(member(holding({ fields: { 'say "hi"': quoted } })))).toEqual([ diff --git a/projects/migration-statements/deferred.md b/projects/migration-statements/deferred.md index f624a7fcc516..5e0ee4e28280 100644 --- a/projects/migration-statements/deferred.md +++ b/projects/migration-statements/deferred.md @@ -238,3 +238,10 @@ A column's `valueSet: { plane: 'storage', entityKind: 'valueSet', entityName }` ## Composite types are not namespaced in SQL PSL SQL PSL resolves a composite type by its bare name across all namespaces and writes every value object to the default storage namespace. Two composite types with the same name in different PSL namespaces are refused with `PSL_DUPLICATE_DECLARATION` (before PR 3 of the remaining-nouns slice they silently overwrote each other). Letting each namespace declare its own needs namespace-qualified type references in PSL and writing each value object to its namespace's storage. When that changes, `generate-contract-dts.ts` must also emit one `Output` alias per storage namespace; today it emits one per name, from the first namespace. Found in the review of that PR, 2026-10-23. + +## The SQL ORM cannot write some value object members + +Found in the review of the value object storage PR, 2026-10-23. Both limits are older than that PR. + +- A `BigInt` member of a value object cannot be written: the `pg/jsonb@1` codec throws "Do not know how to serialize a BigInt". +- A `Bytes` member is written as an object of byte indexes, such as `{"0":1,"1":2}`, not as bytes. From 7572360e89d3bab633b1562fb0518d220ccbaf0e Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 14:56:16 +0200 Subject: [PATCH 047/104] TML-3537: the table lowering context names its storage storage, as resolveValueObject does Signed-off-by: willbot Signed-off-by: Will Madden --- .../2-authoring/contract-ts/src/build-contract.ts | 8 ++------ .../2-authoring/contract-ts/src/lower-column.ts | 4 ++-- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts b/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts index 12f4d0b77d41..d0fb521affa3 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts @@ -181,7 +181,7 @@ function storageValueObjects( } /** The value objects and value sets storage will hold, in the shape storage readers take. */ -function valueObjectStorageOf( +function storageOf( defaultNamespaceId: string, valueObjects: Record | undefined, storageValueSetsByNs: Readonly>>, @@ -367,11 +367,7 @@ export function buildSqlContractFromDefinition( defaultNamespaceId, qualifyColumnType, renderCheckExpressions, - valueObjectStorage: valueObjectStorageOf( - defaultNamespaceId, - valueObjects, - storageValueSetsByNs, - ), + storage: storageOf(defaultNamespaceId, valueObjects, storageValueSetsByNs), collectedColumnEntities, defaultControlPolicy: definition.defaultControlPolicy, indexTypeRegistry, diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/lower-column.ts b/packages/2-sql-family/2-authoring/contract-ts/src/lower-column.ts index f968a5a995b1..397ccabb1c65 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/lower-column.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/lower-column.ts @@ -79,7 +79,7 @@ export interface ColumnLoweringContext { readonly qualifyColumnType: ColumnTypeQualifier | undefined; readonly renderCheckExpressions: CheckExpressionRenderer | undefined; /** The value objects and value sets storage will hold, which a value object column's check is derived from. */ - readonly valueObjectStorage: Pick; + readonly storage: Pick; /** Receives the pack entity a column's type constructor refers to. */ readonly collectedColumnEntities: CollectedColumnEntities; } @@ -183,7 +183,7 @@ function resolvedValueObjectField( many: valueObject.many, type: { kind: 'valueObject', - valueObject: resolveValueObject(context.valueObjectStorage, valueObject.reference), + valueObject: resolveValueObject(context.storage, valueObject.reference), }, } as const; } From 3b17e63c1fe1ec2afb183305e9fff463caab8bf5 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 15:45:49 +0200 Subject: [PATCH 048/104] TML-3537: hand-written test contracts and fixture generators write value objects into storage The SQL ORM test contract adds its Address value object to storage and refers to it by coordinate. The Mongo fixture generator also writes the 9-family migration contract types, and imports codec types by a path each fixture folder can resolve. The value-objects and recursive value object fixtures are emitted by fixtures:emit. Two Mongo ORM tests that relied on unresolved codec types now state the real types. Signed-off-by: willbot Signed-off-by: Will Madden --- .../emitter/test/generate-orm-fixture.ts | 84 +- .../test/collection.mutation-defaults.test.ts | 4 +- .../test/fixtures/stored-names-contract.json | 33 +- .../orm/test/orm-types.test-d.ts | 6 +- .../test/fixtures/migration-contract.json | 731 +++++++++++------- .../9-family/test/fixtures/orm-contract.json | 138 ++-- .../scripts/emit-fixture-configs.mjs | 2 + .../test/sql-orm-client/fixtures/contract.ts | 57 +- .../fixtures/generated/mongo-contract.d.ts | 52 +- .../fixtures/generated/mongo-contract.json | 70 +- .../test/value-objects/fixtures/schema.prisma | 2 + 11 files changed, 712 insertions(+), 467 deletions(-) diff --git a/packages/2-mongo-family/3-tooling/emitter/test/generate-orm-fixture.ts b/packages/2-mongo-family/3-tooling/emitter/test/generate-orm-fixture.ts index a4a3647e4d04..8e572b0f506a 100644 --- a/packages/2-mongo-family/3-tooling/emitter/test/generate-orm-fixture.ts +++ b/packages/2-mongo-family/3-tooling/emitter/test/generate-orm-fixture.ts @@ -1,22 +1,32 @@ import { writeFileSync } from 'node:fs'; -import { resolve } from 'node:path'; +import { relative, resolve } from 'node:path'; import { type Contract, coreHash, crossRef, profileHash } from '@internal/contract/types'; import { generateContractDts } from '@internal/emitter'; import { blindCast } from '@internal/utils/casts'; import ormContractJson from '../../../1-foundation/mongo-contract/test/fixtures/orm-contract.json'; import storedNamesContractJson from '../../../5-query-builders/orm/test/fixtures/stored-names-contract.json'; +import migrationContractJson from '../../../9-family/test/fixtures/migration-contract.json'; import { mongoEmission } from '../src/index'; -const codecImports = [ - { - package: '@internal/target-mongo/codec-types', - named: 'CodecTypes', - alias: 'MongoCodecTypes', - }, -]; +const targetCodecTypes = resolve( + import.meta.dirname, + '../../../../3-targets/mongo/1-target/src/exports/codec-types', +); + +function codecImportsFrom(fixtureDir: string, viaPackage: boolean) { + return [ + { + package: viaPackage + ? '@internal/target-mongo/codec-types' + : relative(fixtureDir, targetCodecTypes), + named: 'CodecTypes', + alias: 'MongoCodecTypes', + }, + ]; +} function fixtureContract( - json: typeof ormContractJson | typeof storedNamesContractJson, + json: typeof ormContractJson | typeof storedNamesContractJson | typeof migrationContractJson, hash: string, ) { return { @@ -40,46 +50,46 @@ function fixtureContract( } as Contract; } -const fixturesDir = resolve( +const mongoContractFixtures = resolve( import.meta.dirname, '../../../1-foundation/mongo-contract/test/fixtures', ); +const familyFixtures = resolve(import.meta.dirname, '../../../9-family/test/fixtures'); +const ormFixtures = resolve(import.meta.dirname, '../../../5-query-builders/orm/test/fixtures'); -const outputs = [ +const fixtures = [ { - output: generateContractDts( - fixtureContract(ormContractJson, 'orm'), - mongoEmission, - codecImports, - { - storageHash: 'orm-storage', - profileHash: 'orm-profile', - }, - ), + json: ormContractJson, + hash: 'orm', targets: [ - resolve(fixturesDir, 'orm-contract.d.ts'), - resolve(import.meta.dirname, '../../../9-family/test/fixtures/orm-contract.d.ts'), + { dir: mongoContractFixtures, file: 'orm-contract.d.ts', viaPackage: false }, + { dir: familyFixtures, file: 'orm-contract.d.ts', viaPackage: false }, ], }, { - output: generateContractDts( - fixtureContract(storedNamesContractJson, 'stored-names'), - mongoEmission, - codecImports, - { storageHash: 'stored-names-storage', profileHash: 'stored-names-profile' }, - ), - targets: [ - resolve( - import.meta.dirname, - '../../../5-query-builders/orm/test/fixtures/stored-names-contract.d.ts', - ), - ], + json: storedNamesContractJson, + hash: 'stored-names', + targets: [{ dir: ormFixtures, file: 'stored-names-contract.d.ts', viaPackage: true }], + }, + { + json: migrationContractJson, + hash: 'migration', + targets: [{ dir: familyFixtures, file: 'migration-contract.d.ts', viaPackage: false }], }, ]; -for (const { output, targets } of outputs) { - for (const target of targets) { - writeFileSync(target, output); +for (const { json, hash, targets } of fixtures) { + for (const { dir, file, viaPackage } of targets) { + const target = resolve(dir, file); + writeFileSync( + target, + generateContractDts( + fixtureContract(json, hash), + mongoEmission, + codecImportsFrom(dir, viaPackage), + { storageHash: `${hash}-storage`, profileHash: `${hash}-profile` }, + ), + ); console.log(`Generated ${target}`); } } diff --git a/packages/2-mongo-family/5-query-builders/orm/test/collection.mutation-defaults.test.ts b/packages/2-mongo-family/5-query-builders/orm/test/collection.mutation-defaults.test.ts index 8f5b5ea59f48..52be601ff329 100644 --- a/packages/2-mongo-family/5-query-builders/orm/test/collection.mutation-defaults.test.ts +++ b/packages/2-mongo-family/5-query-builders/orm/test/collection.mutation-defaults.test.ts @@ -203,7 +203,9 @@ describe('ORM update paths apply onUpdate defaults', () => { it('update whose only field is undefined adds nothing', async () => { const defaults = fakeMutationDefaults(); const { executor, plans } = recordingExecutor([]); - await users(executor, defaults).where(byEmail).update({ name: undefined }); + await users(executor, defaults) + .where(byEmail) + .update({ name: undefined } as unknown as { name?: string }); expect(unwrap(commandOf(plans, 'findOneAndUpdate')['update'])).toEqual({ $set: {} }); expect(defaults.calls[0]?.values).toEqual({}); }); diff --git a/packages/2-mongo-family/5-query-builders/orm/test/fixtures/stored-names-contract.json b/packages/2-mongo-family/5-query-builders/orm/test/fixtures/stored-names-contract.json index 197ee3401579..16cdbb7cc382 100644 --- a/packages/2-mongo-family/5-query-builders/orm/test/fixtures/stored-names-contract.json +++ b/packages/2-mongo-family/5-query-builders/orm/test/fixtures/stored-names-contract.json @@ -25,6 +25,20 @@ "users": {}, "posts": {}, "tasks": {} + }, + "valueObject": { + "HomeAddress": { + "fields": { + "city": { + "type": { + "kind": "scalar", + "codecId": "mongo/string@1" + }, + "nullable": false, + "many": false + } + } + } } } } @@ -88,7 +102,10 @@ "home": { "type": { "kind": "valueObject", - "name": "HomeAddress" + "plane": "storage", + "namespaceId": "__unbound__", + "entityKind": "valueObject", + "entityName": "HomeAddress" }, "nullable": true, "many": false @@ -305,20 +322,6 @@ "namespace": "__unbound__" } } - }, - "valueObjects": { - "HomeAddress": { - "fields": { - "city": { - "type": { - "kind": "scalar", - "codecId": "mongo/string@1" - }, - "nullable": false, - "many": false - } - } - } } } } diff --git a/packages/2-mongo-family/5-query-builders/orm/test/orm-types.test-d.ts b/packages/2-mongo-family/5-query-builders/orm/test/orm-types.test-d.ts index 106ccbb44c88..e09f2564604c 100644 --- a/packages/2-mongo-family/5-query-builders/orm/test/orm-types.test-d.ts +++ b/packages/2-mongo-family/5-query-builders/orm/test/orm-types.test-d.ts @@ -10,7 +10,6 @@ import type { DotPath, FieldAccessor, FieldExpression, - FieldOperation, ResolveDotPathType, } from '../src/field-accessor'; import type { MongoOrmClient } from '../src/mongo-orm'; @@ -507,10 +506,7 @@ test('include() on 1:N reference relation all() returns array type', () => { test('FieldAccessor has FieldExpression for scalar fields', () => { type Accessor = FieldAccessor; expectTypeOf().toExtend>(); - expectTypeOf().toMatchTypeOf<{ - set: (v: unknown) => FieldOperation; - unset: () => FieldOperation; - }>(); + expectTypeOf().toExtend>(); }); test('FieldAccessor has FieldExpression for array fields', () => { diff --git a/packages/2-mongo-family/9-family/test/fixtures/migration-contract.json b/packages/2-mongo-family/9-family/test/fixtures/migration-contract.json index 1656b5373c15..380cd17dce02 100644 --- a/packages/2-mongo-family/9-family/test/fixtures/migration-contract.json +++ b/packages/2-mongo-family/9-family/test/fixtures/migration-contract.json @@ -2,7 +2,7 @@ "_generated": { "message": "This file is automatically generated by \"prisma contract emit\".", "regenerate": "To regenerate, run: prisma contract emit", - "warning": "\u26a0\ufe0f GENERATED FILE - DO NOT EDIT" + "warning": "⚠️ GENERATED FILE - DO NOT EDIT" }, "capabilities": {}, "domain": { @@ -66,7 +66,14 @@ }, "relations": {}, "storage": { - "fields": { "brand": { "field": "brand" }, "productId": { "field": "productId" } }, + "fields": { + "brand": { + "field": "brand" + }, + "productId": { + "field": "productId" + } + }, "collection": "events" } }, @@ -87,7 +94,10 @@ "nullable": false, "type": { "kind": "valueObject", - "name": "CartItem" + "plane": "storage", + "namespaceId": "__unbound__", + "entityKind": "valueObject", + "entityName": "CartItem" } }, "userId": { @@ -103,15 +113,27 @@ "user": { "cardinality": "N:1", "nullable": false, - "on": { "localFields": ["userId"], "targetFields": ["_id"] }, - "to": { "model": "User", "namespace": "__unbound__" } + "on": { + "localFields": ["userId"], + "targetFields": ["_id"] + }, + "to": { + "model": "User", + "namespace": "__unbound__" + } } }, "storage": { "fields": { - "_id": { "field": "_id" }, - "items": { "field": "items" }, - "userId": { "field": "userId" } + "_id": { + "field": "_id" + }, + "items": { + "field": "items" + }, + "userId": { + "field": "userId" + } }, "collection": "carts" } @@ -165,11 +187,21 @@ "relations": {}, "storage": { "fields": { - "_id": { "field": "_id" }, - "sessionId": { "field": "sessionId" }, - "timestamp": { "field": "timestamp" }, - "type": { "field": "type" }, - "userId": { "field": "userId" } + "_id": { + "field": "_id" + }, + "sessionId": { + "field": "sessionId" + }, + "timestamp": { + "field": "timestamp" + }, + "type": { + "field": "type" + }, + "userId": { + "field": "userId" + } }, "collection": "events" }, @@ -213,7 +245,10 @@ "nullable": false, "type": { "kind": "valueObject", - "name": "InvoiceLineItem" + "plane": "storage", + "namespaceId": "__unbound__", + "entityKind": "valueObject", + "entityName": "InvoiceLineItem" } }, "orderId": { @@ -265,13 +300,27 @@ }, "storage": { "fields": { - "_id": { "field": "_id" }, - "issuedAt": { "field": "issuedAt" }, - "items": { "field": "items" }, - "orderId": { "field": "orderId" }, - "subtotal": { "field": "subtotal" }, - "tax": { "field": "tax" }, - "total": { "field": "total" } + "_id": { + "field": "_id" + }, + "issuedAt": { + "field": "issuedAt" + }, + "items": { + "field": "items" + }, + "orderId": { + "field": "orderId" + }, + "subtotal": { + "field": "subtotal" + }, + "tax": { + "field": "tax" + }, + "total": { + "field": "total" + } }, "collection": "invoices" } @@ -330,12 +379,24 @@ "relations": {}, "storage": { "fields": { - "_id": { "field": "_id" }, - "city": { "field": "city" }, - "country": { "field": "country" }, - "name": { "field": "name" }, - "postalCode": { "field": "postalCode" }, - "streetAndNumber": { "field": "streetAndNumber" } + "_id": { + "field": "_id" + }, + "city": { + "field": "city" + }, + "country": { + "field": "country" + }, + "name": { + "field": "name" + }, + "postalCode": { + "field": "postalCode" + }, + "streetAndNumber": { + "field": "streetAndNumber" + } }, "collection": "locations" } @@ -357,7 +418,10 @@ "nullable": false, "type": { "kind": "valueObject", - "name": "OrderLineItem" + "plane": "storage", + "namespaceId": "__unbound__", + "entityKind": "valueObject", + "entityName": "OrderLineItem" } }, "shippingAddress": { @@ -375,7 +439,10 @@ "nullable": false, "type": { "kind": "valueObject", - "name": "StatusEntry" + "plane": "storage", + "namespaceId": "__unbound__", + "entityKind": "valueObject", + "entityName": "StatusEntry" } }, "type": { @@ -416,18 +483,36 @@ "user": { "cardinality": "N:1", "nullable": false, - "on": { "localFields": ["userId"], "targetFields": ["_id"] }, - "to": { "model": "User", "namespace": "__unbound__" } + "on": { + "localFields": ["userId"], + "targetFields": ["_id"] + }, + "to": { + "model": "User", + "namespace": "__unbound__" + } } }, "storage": { "fields": { - "_id": { "field": "_id" }, - "items": { "field": "items" }, - "shippingAddress": { "field": "shippingAddress" }, - "statusHistory": { "field": "statusHistory" }, - "type": { "field": "type" }, - "userId": { "field": "userId" } + "_id": { + "field": "_id" + }, + "items": { + "field": "items" + }, + "shippingAddress": { + "field": "shippingAddress" + }, + "statusHistory": { + "field": "statusHistory" + }, + "type": { + "field": "type" + }, + "userId": { + "field": "userId" + } }, "collection": "orders" } @@ -488,7 +573,10 @@ "nullable": false, "type": { "kind": "valueObject", - "name": "Image" + "plane": "storage", + "namespaceId": "__unbound__", + "entityKind": "valueObject", + "entityName": "Image" }, "many": false }, @@ -504,7 +592,10 @@ "nullable": false, "type": { "kind": "valueObject", - "name": "Price" + "plane": "storage", + "namespaceId": "__unbound__", + "entityKind": "valueObject", + "entityName": "Price" }, "many": false }, @@ -542,18 +633,42 @@ "relations": {}, "storage": { "fields": { - "_id": { "field": "_id" }, - "articleType": { "field": "articleType" }, - "brand": { "field": "brand" }, - "code": { "field": "code" }, - "description": { "field": "description" }, - "embedding": { "field": "embedding" }, - "image": { "field": "image" }, - "name": { "field": "name" }, - "price": { "field": "price" }, - "primaryCategory": { "field": "primaryCategory" }, - "status": { "field": "status" }, - "subCategory": { "field": "subCategory" } + "_id": { + "field": "_id" + }, + "articleType": { + "field": "articleType" + }, + "brand": { + "field": "brand" + }, + "code": { + "field": "code" + }, + "description": { + "field": "description" + }, + "embedding": { + "field": "embedding" + }, + "image": { + "field": "image" + }, + "name": { + "field": "name" + }, + "price": { + "field": "price" + }, + "primaryCategory": { + "field": "primaryCategory" + }, + "status": { + "field": "status" + }, + "subCategory": { + "field": "subCategory" + } }, "collection": "products" } @@ -574,7 +689,14 @@ } }, "relations": {}, - "storage": { "fields": { "query": { "field": "query" } }, "collection": "events" } + "storage": { + "fields": { + "query": { + "field": "query" + } + }, + "collection": "events" + } }, "User": { "fields": { @@ -590,7 +712,10 @@ "nullable": true, "type": { "kind": "valueObject", - "name": "Address" + "plane": "storage", + "namespaceId": "__unbound__", + "entityKind": "valueObject", + "entityName": "Address" }, "many": false }, @@ -637,10 +762,18 @@ }, "storage": { "fields": { - "_id": { "field": "_id" }, - "address": { "field": "address" }, - "email": { "field": "email" }, - "name": { "field": "name" } + "_id": { + "field": "_id" + }, + "address": { + "field": "address" + }, + "email": { + "field": "email" + }, + "name": { + "field": "name" + } }, "collection": "users" } @@ -687,242 +820,20 @@ "relations": {}, "storage": { "fields": { - "brand": { "field": "brand" }, - "exitMethod": { "field": "exitMethod" }, - "productId": { "field": "productId" }, - "subCategory": { "field": "subCategory" } - }, - "collection": "events" - } - } - }, - "valueObjects": { - "Address": { - "fields": { - "city": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" + "brand": { + "field": "brand" }, - "many": false - }, - "country": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - }, - "many": false - }, - "postalCode": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - }, - "many": false - }, - "streetAndNumber": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - }, - "many": false - } - } - }, - "CartItem": { - "fields": { - "amount": { - "nullable": false, - "type": { - "codecId": "mongo/int32@1", - "kind": "scalar" - }, - "many": false - }, - "brand": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" + "exitMethod": { + "field": "exitMethod" }, - "many": false - }, - "image": { - "nullable": false, - "type": { - "kind": "valueObject", - "name": "Image" + "productId": { + "field": "productId" }, - "many": false - }, - "name": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - }, - "many": false - }, - "price": { - "nullable": false, - "type": { - "kind": "valueObject", - "name": "Price" - }, - "many": false - }, - "productId": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - }, - "many": false - } - } - }, - "Image": { - "fields": { - "url": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - }, - "many": false - } - } - }, - "InvoiceLineItem": { - "fields": { - "amount": { - "nullable": false, - "type": { - "codecId": "mongo/int32@1", - "kind": "scalar" - }, - "many": false - }, - "lineTotal": { - "nullable": false, - "type": { - "codecId": "mongo/double@1", - "kind": "scalar" - }, - "many": false - }, - "name": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - }, - "many": false - }, - "unitPrice": { - "nullable": false, - "type": { - "codecId": "mongo/double@1", - "kind": "scalar" - }, - "many": false - } - } - }, - "OrderLineItem": { - "fields": { - "amount": { - "nullable": false, - "type": { - "codecId": "mongo/int32@1", - "kind": "scalar" - }, - "many": false - }, - "brand": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - }, - "many": false - }, - "image": { - "nullable": false, - "type": { - "kind": "valueObject", - "name": "Image" - }, - "many": false - }, - "name": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - }, - "many": false - }, - "price": { - "nullable": false, - "type": { - "kind": "valueObject", - "name": "Price" - }, - "many": false - }, - "productId": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - }, - "many": false - } - } - }, - "Price": { - "fields": { - "amount": { - "nullable": false, - "type": { - "codecId": "mongo/double@1", - "kind": "scalar" - }, - "many": false - }, - "currency": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - }, - "many": false - } - } - }, - "StatusEntry": { - "fields": { - "status": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - }, - "many": false + "subCategory": { + "field": "subCategory" + } }, - "timestamp": { - "nullable": false, - "type": { - "codecId": "mongo/date@1", - "kind": "scalar" - }, - "many": false - } + "collection": "events" } } } @@ -1624,6 +1535,248 @@ "kind": "valueSet", "values": ["active", "discontinued", "out-of-stock"] } + }, + "valueObject": { + "Address": { + "fields": { + "city": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + }, + "many": false + }, + "country": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + }, + "many": false + }, + "postalCode": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + }, + "many": false + }, + "streetAndNumber": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + }, + "many": false + } + } + }, + "CartItem": { + "fields": { + "amount": { + "nullable": false, + "type": { + "codecId": "mongo/int32@1", + "kind": "scalar" + }, + "many": false + }, + "brand": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + }, + "many": false + }, + "image": { + "nullable": false, + "type": { + "kind": "valueObject", + "plane": "storage", + "namespaceId": "__unbound__", + "entityKind": "valueObject", + "entityName": "Image" + }, + "many": false + }, + "name": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + }, + "many": false + }, + "price": { + "nullable": false, + "type": { + "kind": "valueObject", + "plane": "storage", + "namespaceId": "__unbound__", + "entityKind": "valueObject", + "entityName": "Price" + }, + "many": false + }, + "productId": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + }, + "many": false + } + } + }, + "Image": { + "fields": { + "url": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + }, + "many": false + } + } + }, + "InvoiceLineItem": { + "fields": { + "amount": { + "nullable": false, + "type": { + "codecId": "mongo/int32@1", + "kind": "scalar" + }, + "many": false + }, + "lineTotal": { + "nullable": false, + "type": { + "codecId": "mongo/double@1", + "kind": "scalar" + }, + "many": false + }, + "name": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + }, + "many": false + }, + "unitPrice": { + "nullable": false, + "type": { + "codecId": "mongo/double@1", + "kind": "scalar" + }, + "many": false + } + } + }, + "OrderLineItem": { + "fields": { + "amount": { + "nullable": false, + "type": { + "codecId": "mongo/int32@1", + "kind": "scalar" + }, + "many": false + }, + "brand": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + }, + "many": false + }, + "image": { + "nullable": false, + "type": { + "kind": "valueObject", + "plane": "storage", + "namespaceId": "__unbound__", + "entityKind": "valueObject", + "entityName": "Image" + }, + "many": false + }, + "name": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + }, + "many": false + }, + "price": { + "nullable": false, + "type": { + "kind": "valueObject", + "plane": "storage", + "namespaceId": "__unbound__", + "entityKind": "valueObject", + "entityName": "Price" + }, + "many": false + }, + "productId": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + }, + "many": false + } + } + }, + "Price": { + "fields": { + "amount": { + "nullable": false, + "type": { + "codecId": "mongo/double@1", + "kind": "scalar" + }, + "many": false + }, + "currency": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + }, + "many": false + } + } + }, + "StatusEntry": { + "fields": { + "status": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + }, + "many": false + }, + "timestamp": { + "nullable": false, + "type": { + "codecId": "mongo/date@1", + "kind": "scalar" + }, + "many": false + } + } + } } }, "id": "__unbound__", diff --git a/packages/2-mongo-family/9-family/test/fixtures/orm-contract.json b/packages/2-mongo-family/9-family/test/fixtures/orm-contract.json index 9aee4ab42e9e..a95f3db83085 100644 --- a/packages/2-mongo-family/9-family/test/fixtures/orm-contract.json +++ b/packages/2-mongo-family/9-family/test/fixtures/orm-contract.json @@ -19,6 +19,28 @@ "collection": { "tasks": {}, "users": {} + }, + "valueObject": { + "HomeAddress": { + "fields": { + "city": { + "type": { + "kind": "scalar", + "codecId": "mongo/string@1" + }, + "nullable": false, + "many": false + }, + "country": { + "type": { + "kind": "scalar", + "codecId": "mongo/string@1" + }, + "nullable": false, + "many": false + } + } + } } } } @@ -31,13 +53,21 @@ "models": { "Task": { "storage": { + "collection": "tasks", "fields": { - "_id": { "field": "_id" }, - "title": { "field": "title" }, - "type": { "field": "type" }, - "assigneeId": { "field": "assigneeId" } + "_id": { + "field": "_id" + }, + "title": { + "field": "title" + }, + "type": { + "field": "type" + }, + "assigneeId": { + "field": "assigneeId" + } }, - "collection": "tasks", "relations": { "comments": { "field": "comments" @@ -114,7 +144,14 @@ } }, "Bug": { - "storage": { "fields": { "severity": { "field": "severity" } }, "collection": "tasks" }, + "storage": { + "collection": "tasks", + "fields": { + "severity": { + "field": "severity" + } + } + }, "fields": { "severity": { "type": { @@ -133,11 +170,15 @@ }, "Feature": { "storage": { + "collection": "tasks", "fields": { - "priority": { "field": "priority" }, - "targetRelease": { "field": "targetRelease" } - }, - "collection": "tasks" + "priority": { + "field": "priority" + }, + "targetRelease": { + "field": "targetRelease" + } + } }, "fields": { "priority": { @@ -165,15 +206,27 @@ }, "User": { "storage": { + "collection": "users", "fields": { - "_id": { "field": "_id" }, - "name": { "field": "name" }, - "email": { "field": "email" }, - "loginCount": { "field": "loginCount" }, - "tags": { "field": "tags" }, - "homeAddress": { "field": "homeAddress" } + "_id": { + "field": "_id" + }, + "name": { + "field": "name" + }, + "email": { + "field": "email" + }, + "loginCount": { + "field": "loginCount" + }, + "tags": { + "field": "tags" + }, + "homeAddress": { + "field": "homeAddress" + } }, - "collection": "users", "relations": { "addresses": { "field": "addresses" @@ -226,7 +279,10 @@ "homeAddress": { "type": { "kind": "valueObject", - "name": "HomeAddress" + "plane": "storage", + "namespaceId": "__unbound__", + "entityKind": "valueObject", + "entityName": "HomeAddress" }, "nullable": true, "many": false @@ -256,9 +312,15 @@ "Address": { "storage": { "fields": { - "street": { "field": "street" }, - "city": { "field": "city" }, - "zip": { "field": "zip" } + "street": { + "field": "street" + }, + "city": { + "field": "city" + }, + "zip": { + "field": "zip" + } } }, "fields": { @@ -293,9 +355,15 @@ "Comment": { "storage": { "fields": { - "_id": { "field": "_id" }, - "text": { "field": "text" }, - "createdAt": { "field": "createdAt" } + "_id": { + "field": "_id" + }, + "text": { + "field": "text" + }, + "createdAt": { + "field": "createdAt" + } } }, "fields": { @@ -327,28 +395,6 @@ "relations": {}, "owner": "Task" } - }, - "valueObjects": { - "HomeAddress": { - "fields": { - "city": { - "type": { - "kind": "scalar", - "codecId": "mongo/string@1" - }, - "nullable": false, - "many": false - }, - "country": { - "type": { - "kind": "scalar", - "codecId": "mongo/string@1" - }, - "nullable": false, - "many": false - } - } - } } } } diff --git a/test/integration/scripts/emit-fixture-configs.mjs b/test/integration/scripts/emit-fixture-configs.mjs index 47d03f0cd125..6a3b69eb970f 100644 --- a/test/integration/scripts/emit-fixture-configs.mjs +++ b/test/integration/scripts/emit-fixture-configs.mjs @@ -28,6 +28,8 @@ const fixtureRoots = [ 'test/mongo/list-filters', 'test/mongo/temporal-presets', 'test/mongo/prisma6-source', + 'test/mongo/recursive-value-objects', + 'test/value-objects/fixtures', ]; function findConfigs(dir) { diff --git a/test/integration/test/sql-orm-client/fixtures/contract.ts b/test/integration/test/sql-orm-client/fixtures/contract.ts index 62fddc13afc6..dc8eada6def8 100644 --- a/test/integration/test/sql-orm-client/fixtures/contract.ts +++ b/test/integration/test/sql-orm-client/fixtures/contract.ts @@ -186,9 +186,37 @@ const baseContract = defineContract({ const defaultNamespace = baseContract.domain.namespaces['public']!; const userModel = defaultNamespace.models['User']!; +const publicStorage = baseContract.storage.namespaces['public']!; + +const textMember = (nullable: Nullable) => ({ + many: false as const, + nullable, + type: { kind: 'scalar' as const, codecId: 'pg/text@1' as const }, +}); export const contract = { ...baseContract, + storage: { + ...baseContract.storage, + namespaces: { + ...baseContract.storage.namespaces, + public: { + ...publicStorage, + entries: { + ...publicStorage.entries, + valueObject: { + Address: { + fields: { + street: textMember(false), + city: textMember(false), + zip: textMember(true), + }, + }, + }, + }, + }, + }, + }, domain: { namespaces: { ...baseContract.domain.namespaces, @@ -203,28 +231,13 @@ export const contract = { address: { many: false as const, nullable: true as const, - type: { kind: 'valueObject' as const, name: 'Address' }, - }, - }, - }, - }, - valueObjects: { - Address: { - fields: { - street: { - many: false as const, - nullable: false as const, - type: { kind: 'scalar' as const, codecId: 'pg/text@1' as const }, - }, - city: { - many: false as const, - nullable: false as const, - type: { kind: 'scalar' as const, codecId: 'pg/text@1' as const }, - }, - zip: { - many: false as const, - nullable: true as const, - type: { kind: 'scalar' as const, codecId: 'pg/text@1' as const }, + type: { + kind: 'valueObject' as const, + plane: 'storage' as const, + namespaceId: 'public' as const, + entityKind: 'valueObject' as const, + entityName: 'Address' as const, + }, }, }, }, diff --git a/test/integration/test/value-objects/fixtures/generated/mongo-contract.d.ts b/test/integration/test/value-objects/fixtures/generated/mongo-contract.d.ts index 312ae5eb07bb..516135459811 100644 --- a/test/integration/test/value-objects/fixtures/generated/mongo-contract.d.ts +++ b/test/integration/test/value-objects/fixtures/generated/mongo-contract.d.ts @@ -60,35 +60,29 @@ type ContractBase = { }; readonly location: { readonly many: false; readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Location' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Location'; + }; }; readonly notes: { readonly many: false; readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Location' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Location'; + }; }; }; readonly relations: Record; readonly storage: { readonly collection: 'shops'; readonly fields: { readonly _id: { readonly field: '_id' }; readonly name: { readonly field: 'name' }; readonly location: { readonly field: 'location' }; readonly notes: { readonly field: 'notes' } } }; }; }; - readonly valueObjects: { - readonly Location: { - readonly fields: { - readonly street: { readonly many: false; - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly city: { readonly many: false; - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly zip: { readonly many: false; - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - }; }; }; }; @@ -101,6 +95,24 @@ type ContractBase = { readonly collection: { readonly shops: { readonly kind: 'mongo-collection' }; }; + readonly valueObject: { + readonly Location: { + readonly fields: { + readonly street: { readonly many: false; + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly city: { readonly many: false; + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly zip: { readonly many: false; + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + }; }; }; }; diff --git a/test/integration/test/value-objects/fixtures/generated/mongo-contract.json b/test/integration/test/value-objects/fixtures/generated/mongo-contract.json index 64b418080e34..1d00154fceee 100644 --- a/test/integration/test/value-objects/fixtures/generated/mongo-contract.json +++ b/test/integration/test/value-objects/fixtures/generated/mongo-contract.json @@ -19,6 +19,36 @@ "shops": { "kind": "mongo-collection" } + }, + "valueObject": { + "Location": { + "fields": { + "street": { + "many": false, + "type": { + "kind": "scalar", + "codecId": "mongo/string@1" + }, + "nullable": false + }, + "city": { + "many": false, + "type": { + "kind": "scalar", + "codecId": "mongo/string@1" + }, + "nullable": false + }, + "zip": { + "many": false, + "type": { + "kind": "scalar", + "codecId": "mongo/string@1" + }, + "nullable": false + } + } + } } } } @@ -54,7 +84,10 @@ "many": false, "type": { "kind": "valueObject", - "name": "Location" + "plane": "storage", + "namespaceId": "__unbound__", + "entityKind": "valueObject", + "entityName": "Location" }, "nullable": false }, @@ -62,7 +95,10 @@ "many": false, "type": { "kind": "valueObject", - "name": "Location" + "plane": "storage", + "namespaceId": "__unbound__", + "entityKind": "valueObject", + "entityName": "Location" }, "nullable": true } @@ -86,36 +122,6 @@ } } } - }, - "valueObjects": { - "Location": { - "fields": { - "street": { - "many": false, - "type": { - "kind": "scalar", - "codecId": "mongo/string@1" - }, - "nullable": false - }, - "city": { - "many": false, - "type": { - "kind": "scalar", - "codecId": "mongo/string@1" - }, - "nullable": false - }, - "zip": { - "many": false, - "type": { - "kind": "scalar", - "codecId": "mongo/string@1" - }, - "nullable": false - } - } - } } } } diff --git a/test/integration/test/value-objects/fixtures/schema.prisma b/test/integration/test/value-objects/fixtures/schema.prisma index 543086cd05f0..4ef870f3ab00 100644 --- a/test/integration/test/value-objects/fixtures/schema.prisma +++ b/test/integration/test/value-objects/fixtures/schema.prisma @@ -1,3 +1,5 @@ +// use prisma-8 + model Shop { id Int @id name String From a37accd17f74f9a64ecc022abf85f92c1250bebe Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 15:46:01 +0200 Subject: [PATCH 049/104] TML-3537: re-emit every contract, fixture and example migration snapshot with value objects in storage Generated by pnpm fixtures:emit, the Mongo ORM fixture generator and the mongo-parity update. Each example migration snapshot moves to its new storage hash, and the snapshots that no migration names any more are removed. Signed-off-by: willbot Signed-off-by: Will Madden --- .../src/prisma/contract.d.ts | 1 - .../src/mongo/generated/contract.d.ts | 1 - .../src/postgres/generated/contract.d.ts | 1 - .../mongo-blog-leaderboard/src/contract.d.ts | 92 +- .../mongo-blog-leaderboard/src/contract.json | 75 +- .../20260409T1030_migration/migration.json | 4 +- .../app/20260409T1030_migration/migration.ts | 4 +- .../migration.json | 6 +- .../migration.ts | 8 +- .../migration.json | 6 +- .../migration.ts | 8 +- .../contract.d.ts | 92 +- .../contract.json | 1 + .../contract.d.ts | 92 +- .../contract.json | 1 + .../contract.json | 1 - .../contract.d.ts | 92 +- .../contract.json | 1 + .../contract.json | 1 - .../contract.json | 1 - examples/mongo-demo/src/contract.d.ts | 92 +- examples/mongo-demo/src/contract.json | 75 +- .../app/src/contract.d.ts | 1 - .../contract.d.ts | 1 - .../packages/audit/src/contract.d.ts | 1 - .../contract.d.ts | 1 - .../packages/feature-flags/src/contract.d.ts | 1 - .../paradedb-demo/src/prisma/contract.d.ts | 1 - .../src/prisma/contract.d.ts | 102 +- .../src/prisma/contract.json | 102 +- .../src/prisma/contract.d.ts | 1 - .../src/prisma/contract.d.ts | 1 - .../migration.json | 6 +- .../migration.ts | 8 +- .../migration.json | 6 +- .../migration.ts | 8 +- .../migration.json | 6 +- .../migration.ts | 8 +- .../migration.json | 6 +- .../migration.ts | 8 +- .../contract.json | 1 - .../contract.json | 1 - .../contract.d.ts | 102 +- .../contract.json | 1 + .../contract.d.ts | 102 +- .../contract.json | 1 + .../contract.d.ts | 102 +- .../contract.json | 1 + .../contract.json | 1 - .../contract.json | 1 - .../contract.d.ts | 102 +- .../contract.json | 1 + .../contract.d.ts | 102 +- .../contract.json | 1 + .../contract.d.ts | 1447 ---------------- .../contract.json | 1 - .../contract.d.ts | 1495 ---------------- .../contract.json | 1 - .../contract.json | 1 - .../prisma-8-demo/src/prisma/contract.d.ts | 102 +- .../prisma-8-demo/src/prisma/contract.json | 102 +- .../contract.d.ts | 1 - .../src/prisma/contract.d.ts | 1 - .../generated/prisma8/contract.d.ts | 1 - .../src/prisma/contract.d.ts | 1 - .../app/20260513T0505_initial/migration.json | 4 +- .../app/20260513T0505_initial/migration.ts | 4 +- .../migration.json | 6 +- .../migration.ts | 8 +- .../migration.json | 6 +- .../migration.ts | 8 +- .../ops.json | 6 +- .../migration.json | 6 +- .../migration.ts | 8 +- .../contract.d.ts | 488 +++--- .../contract.json | 1 + .../contract.d.ts | 495 +++--- .../contract.json | 1 + .../contract.json | 1 - .../contract.json | 1 - .../contract.d.ts | 514 +++--- .../contract.json | 1 + .../contract.d.ts | 1475 ---------------- .../contract.json | 1 - .../contract.json | 1 - .../contract.json | 1 - .../contract.d.ts | 1498 ---------------- .../contract.json | 1 - .../contract.d.ts | 507 +++--- .../contract.json | 1 + examples/retail-store/src/contract.d.ts | 488 +++--- examples/retail-store/src/contract.json | 459 ++--- examples/supabase/src/contract.d.ts | 1 - .../test/fixtures/orm-contract.d.ts | 376 +--- .../test/fixtures/stored-names-contract.d.ts | 50 +- .../test/fixtures/migration-contract.d.ts | 1523 ++--------------- .../9-family/test/fixtures/orm-contract.d.ts | 384 +---- .../test/fixtures/generated/contract.d.ts | 1 - .../contract.d.ts | 1 - .../3-extensions/paradedb/src/contract.d.ts | 1 - .../contract.d.ts | 1 - .../3-extensions/pgvector/src/contract.d.ts | 1 - .../contract.d.ts | 1 - .../3-extensions/postgis/src/contract.d.ts | 1 - .../generated/contract.d.ts | 20 +- .../generated/contract.json | 25 +- .../generated/contract.d.ts | 1 - .../test/fixtures/generated/contract.d.ts | 78 +- .../test/fixtures/generated/contract.json | 59 +- .../generated/contract.d.ts | 1 - .../on-update-only/generated/contract.d.ts | 1 - .../polymorphism/generated/contract.d.ts | 46 +- .../polymorphism/generated/contract.json | 49 +- .../scope-namespace/generated/contract.d.ts | 1 - .../soft-delete/generated/contract.d.ts | 56 +- .../soft-delete/generated/contract.json | 38 +- .../supabase/src/contract/contract.d.ts | 1 - .../test/fixtures/example-app/contract.d.ts | 1 - .../test/fixtures/no-policy/contract.d.ts | 1 - .../fixtures/renamed-policy/contract.d.ts | 1 - .../test/fixtures/generated/contract.d.ts | 1 - .../test/fixtures/generated/contract.d.ts | 1 - .../test/fixtures/generated/contract.d.ts | 1 - .../sqlite/fixtures/generated/contract.d.ts | 1 - .../enums-and-lists/expected.contract.json | 64 +- .../side-by-side/mongo/contract.json | 47 +- .../fixtures/mongo/generated/contract.d.ts | 157 +- .../fixtures/mongo/generated/contract.json | 151 +- .../fixtures/postgres/generated/contract.d.ts | 1 - .../fixtures/sqlite/generated/contract.d.ts | 1 - .../_fixture-mongo/generated/contract.d.ts | 1 - .../generated/contract.d.ts | 1 - .../_fixture-postgres/generated/contract.d.ts | 1 - .../_fixture-sqlite/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - test/integration/test/fixtures/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 147 +- .../_fixture/generated/contract.json | 121 +- .../mongo/fixtures/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 64 +- .../_fixture/generated/contract.json | 38 +- .../_fixture/generated/contract.d.ts | 67 +- .../_fixture/generated/contract.json | 38 +- .../_fixture/generated/contract.d.ts | 134 +- .../_fixture/generated/contract.json | 56 +- .../_fixture-aliases/generated/contract.d.ts | 1 - .../generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../fixtures/generated/contract.d.ts | 1 - .../fixtures/postgres/generated/contract.d.ts | 1 - .../fixtures/sqlite/generated/contract.d.ts | 1 - .../_fixture/decimal/generated/contract.d.ts | 1 - .../_fixture/numeric/generated/contract.d.ts | 1 - .../count/_fixture/generated/contract.d.ts | 1 - .../_fixture/main/generated/contract.d.ts | 1 - .../regression-21789/generated/contract.d.ts | 1 - .../_fixture/common/generated/contract.d.ts | 1 - .../_fixture/decimal/generated/contract.d.ts | 1 - .../_fixture/compound/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/nested/generated/contract.d.ts | 1 - .../_fixture/self/generated/contract.d.ts | 1 - .../_fixture/common/generated/contract.d.ts | 1 - .../_fixture/decimal/generated/contract.d.ts | 1 - .../_fixture/common/generated/contract.d.ts | 1 - .../_fixture/decimal/generated/contract.d.ts | 1 - .../_fixture/decimal/generated/contract.d.ts | 1 - .../_fixture/numeric/generated/contract.d.ts | 1 - .../_fixture/base/generated/contract.d.ts | 1 - .../_fixture/nested/generated/contract.d.ts | 1 - .../bigint/_fixture/generated/contract.d.ts | 1 - .../bool/_fixture/generated/contract.d.ts | 1 - .../relations/generated/contract.d.ts | 1 - .../_fixture/scalars/generated/contract.d.ts | 1 - .../datetime/_fixture/generated/contract.d.ts | 1 - .../decimal/_fixture/generated/contract.d.ts | 1 - .../_fixture/postgres/generated/contract.d.ts | 1 - .../float/_fixture/generated/contract.d.ts | 1 - .../int/_fixture/generated/contract.d.ts | 1 - .../_fixture/scalar/generated/contract.d.ts | 1 - .../_fixture/other/generated/contract.d.ts | 1 - .../_fixture/string/generated/contract.d.ts | 1 - .../string/_fixture/generated/contract.d.ts | 1 - .../_fixture/common/generated/contract.d.ts | 1 - .../_fixture/decimal/generated/contract.d.ts | 1 - .../_fixture/enum/generated/contract.d.ts | 1 - .../_fixture/json/generated/contract.d.ts | 1 - .../_fixture/lists/generated/contract.d.ts | 1 - .../distinct/_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../decimal-list/generated/contract.d.ts | 1 - .../_fixture/enum/generated/contract.d.ts | 1 - .../_fixture/list/generated/contract.d.ts | 1 - .../_fixture/mixed/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/common/generated/contract.d.ts | 1 - .../_fixture/default/generated/contract.d.ts | 1 - .../_fixture/list/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/list/generated/contract.d.ts | 1 - .../_fixture/complex/generated/contract.d.ts | 1 - .../one-to-many/generated/contract.d.ts | 1 - .../one-to-one-list/generated/contract.d.ts | 1 - .../one-to-one/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../generated/contract.d.ts | 1 - .../many-to-many/generated/contract.d.ts | 1 - .../one-to-many/generated/contract.d.ts | 1 - .../filters/_fixture/generated/contract.d.ts | 1 - .../_fixture/optional/generated/contract.d.ts | 1 - .../_fixture/base/generated/contract.d.ts | 1 - .../_fixture/decimal/generated/contract.d.ts | 1 - .../_fixture/json/generated/contract.d.ts | 1 - .../_fixture/25103/generated/contract.d.ts | 1 - .../l2-to-one/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/21356/generated/contract.d.ts | 1 - .../_fixture/21366/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 116 +- .../_fixture/generated/contract.json | 92 +- .../_fixture/generated/contract.d.ts | 116 +- .../_fixture/generated/contract.json | 92 +- .../_fixture/generated/contract.d.ts | 116 +- .../_fixture/generated/contract.json | 92 +- .../_fixture/generated/contract.d.ts | 116 +- .../_fixture/generated/contract.json | 92 +- .../_fixture/generated/contract.d.ts | 116 +- .../_fixture/generated/contract.json | 92 +- .../_fixture/generated/contract.d.ts | 116 +- .../_fixture/generated/contract.json | 92 +- .../_fixture/generated/contract.d.ts | 116 +- .../_fixture/generated/contract.json | 92 +- .../_fixture/generated/contract.d.ts | 116 +- .../_fixture/generated/contract.json | 92 +- .../_fixture/generated/contract.d.ts | 116 +- .../_fixture/generated/contract.json | 92 +- .../_fixture/generated/contract.d.ts | 116 +- .../_fixture/generated/contract.json | 92 +- .../_fixture/generated/contract.d.ts | 124 +- .../_fixture/generated/contract.json | 97 +- .../_fixture/generated/contract.d.ts | 124 +- .../_fixture/generated/contract.json | 97 +- .../_fixture/generated/contract.d.ts | 124 +- .../_fixture/generated/contract.json | 97 +- .../_fixture/generated/contract.d.ts | 124 +- .../_fixture/generated/contract.json | 97 +- .../_fixture/generated/contract.d.ts | 124 +- .../_fixture/generated/contract.json | 97 +- .../_fixture/generated/contract.d.ts | 124 +- .../_fixture/generated/contract.json | 97 +- .../_fixture/generated/contract.d.ts | 124 +- .../_fixture/generated/contract.json | 97 +- .../_fixture/generated/contract.d.ts | 124 +- .../_fixture/generated/contract.json | 97 +- .../_fixture/generated/contract.d.ts | 124 +- .../_fixture/generated/contract.json | 97 +- .../_fixture/generated/contract.d.ts | 124 +- .../_fixture/generated/contract.json | 97 +- .../_fixture/generated/contract.d.ts | 173 +- .../_fixture/generated/contract.json | 148 +- .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/mongo/generated/contract.d.ts | 56 +- .../_fixture/mongo/generated/contract.json | 33 +- .../distinct/_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/sqlite/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../enums/_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../different-names/generated/contract.d.ts | 1 - .../identical-names/generated/contract.d.ts | 1 - .../_fixture/no-map/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../cascade-map/generated/contract.d.ts | 1 - .../cascade-plain/generated/contract.d.ts | 1 - .../noaction-map/generated/contract.d.ts | 1 - .../noaction-plain/generated/contract.d.ts | 1 - .../restrict-map/generated/contract.d.ts | 1 - .../restrict-plain/generated/contract.d.ts | 1 - .../setnull-map/generated/contract.d.ts | 1 - .../setnull-plain/generated/contract.d.ts | 1 - .../cascade-map/generated/contract.d.ts | 1 - .../cascade-plain/generated/contract.d.ts | 1 - .../noaction-map/generated/contract.d.ts | 1 - .../noaction-plain/generated/contract.d.ts | 1 - .../restrict-map/generated/contract.d.ts | 1 - .../restrict-plain/generated/contract.d.ts | 1 - .../setnull-map/generated/contract.d.ts | 1 - .../setnull-plain/generated/contract.d.ts | 1 - .../cascade-nomap/generated/contract.d.ts | 1 - .../default-nomap/generated/contract.d.ts | 1 - .../noaction-nomap/generated/contract.d.ts | 1 - .../restrict-nomap/generated/contract.d.ts | 1 - .../generated-no-pgvector/contract.d.ts | 1 - .../fixtures/generated/contract.d.ts | 1 - .../generated/contract.d.ts | 20 +- .../generated/contract.json | 25 +- .../generated/contract.d.ts | 1 - .../generated/contract.d.ts | 1 - .../fixtures/generated/contract.d.ts | 78 +- .../fixtures/generated/contract.json | 59 +- .../generated/contract.d.ts | 1 - .../generated/contract.d.ts | 1 - .../generated/contract.d.ts | 1 - .../fixtures/mn-psl/generated/contract.d.ts | 1 - .../generated/contract.d.ts | 1 - .../on-update-only/generated/contract.d.ts | 1 - .../generated/contract.d.ts | 55 +- .../generated/contract.json | 75 +- .../generated/contract.d.ts | 27 +- .../generated/contract.json | 26 +- .../polymorphism/generated/contract.d.ts | 46 +- .../polymorphism/generated/contract.json | 49 +- .../scalar-lists/generated/contract.d.ts | 1 - .../scope-namespace/generated/contract.d.ts | 1 - .../self-relations/generated/contract.d.ts | 1 - .../soft-delete/generated/contract.d.ts | 56 +- .../soft-delete/generated/contract.json | 38 +- .../_fixture-aliases/generated/contract.d.ts | 1 - .../generated/contract.d.ts | 1 - .../_fixture-now/generated/contract.d.ts | 1 - .../generated/contract.d.ts | 1 - .../_fixture/generated/contract.d.ts | 1 - .../fixtures/generated/sql-contract.d.ts | 437 +++-- .../fixtures/generated/sql-contract.json | 95 +- 408 files changed, 6295 insertions(+), 14098 deletions(-) rename examples/mongo-demo/migrations/snapshots/{da1339e341177f79b37f765f08200844d7cb4d59d26fe27fe4d95b0112b0c2cd => 396942f6c9ee5f946d7f67b397929bab327fe84dd8f7f80e87b8097f416fb901}/contract.d.ts (88%) create mode 100644 examples/mongo-demo/migrations/snapshots/396942f6c9ee5f946d7f67b397929bab327fe84dd8f7f80e87b8097f416fb901/contract.json rename examples/mongo-demo/migrations/snapshots/{d264cabd6a02546054a776ed3c04438b9a22989f406c49f5590f7f2101156eb0 => 406b672b68a6b01f04c0d6f910f5fefa4ac16d48b26e6c91b43856cd42c8cc5d}/contract.d.ts (89%) create mode 100644 examples/mongo-demo/migrations/snapshots/406b672b68a6b01f04c0d6f910f5fefa4ac16d48b26e6c91b43856cd42c8cc5d/contract.json delete mode 100644 examples/mongo-demo/migrations/snapshots/62671fbf016c515d808ac613743fddbf3db80384c1997251eac9d6f5fc063590/contract.json rename examples/mongo-demo/migrations/snapshots/{62671fbf016c515d808ac613743fddbf3db80384c1997251eac9d6f5fc063590 => 95332b0bd6fbbfab4462d882c037a574c92e51897249c333ba681b12c4d603c9}/contract.d.ts (89%) create mode 100644 examples/mongo-demo/migrations/snapshots/95332b0bd6fbbfab4462d882c037a574c92e51897249c333ba681b12c4d603c9/contract.json delete mode 100644 examples/mongo-demo/migrations/snapshots/d264cabd6a02546054a776ed3c04438b9a22989f406c49f5590f7f2101156eb0/contract.json delete mode 100644 examples/mongo-demo/migrations/snapshots/da1339e341177f79b37f765f08200844d7cb4d59d26fe27fe4d95b0112b0c2cd/contract.json delete mode 100644 examples/prisma-8-demo/migrations/snapshots/0ce16e4140d056a3abddbe18bd7ee5198d392370f0aeb23236216c51505944de/contract.json delete mode 100644 examples/prisma-8-demo/migrations/snapshots/0ce1b6cf3e03ac1498f39eb8ef08d36d73ee2f0bc7fe368b97f1497ba050501a/contract.json rename examples/prisma-8-demo/migrations/snapshots/{58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b => 11d8695d1b8ea9c748834093ac4111ce68145827709363b7c748c2892be84146}/contract.d.ts (96%) create mode 100644 examples/prisma-8-demo/migrations/snapshots/11d8695d1b8ea9c748834093ac4111ce68145827709363b7c748c2892be84146/contract.json rename examples/prisma-8-demo/migrations/snapshots/{0ce16e4140d056a3abddbe18bd7ee5198d392370f0aeb23236216c51505944de => 1c819419d4357de21d9aa9404a0b1d3639e3bd33c3fe6176207c53b51365fa1f}/contract.d.ts (96%) create mode 100644 examples/prisma-8-demo/migrations/snapshots/1c819419d4357de21d9aa9404a0b1d3639e3bd33c3fe6176207c53b51365fa1f/contract.json rename examples/prisma-8-demo/migrations/snapshots/{d44ab6f7caddcc384332c669737008aec7ec8c9f5b013cb502a03c2eb04be9c0 => 3c2324f6061012e9d50f869b3e6ae6aa41159acd559b1cc5459bc64e2ccb0f4e}/contract.d.ts (96%) create mode 100644 examples/prisma-8-demo/migrations/snapshots/3c2324f6061012e9d50f869b3e6ae6aa41159acd559b1cc5459bc64e2ccb0f4e/contract.json delete mode 100644 examples/prisma-8-demo/migrations/snapshots/58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b/contract.json delete mode 100644 examples/prisma-8-demo/migrations/snapshots/6e6fbbc22d1d4904fbc8668644c5cabe694d946e550a2f0cdd07aaab308b953f/contract.json rename examples/prisma-8-demo/migrations/snapshots/{6e6fbbc22d1d4904fbc8668644c5cabe694d946e550a2f0cdd07aaab308b953f => 9301c8a3b2ec83c89b4eb12568ccb0c9e916ba199d8743ab38ed468483d6921f}/contract.d.ts (96%) create mode 100644 examples/prisma-8-demo/migrations/snapshots/9301c8a3b2ec83c89b4eb12568ccb0c9e916ba199d8743ab38ed468483d6921f/contract.json rename examples/prisma-8-demo/migrations/snapshots/{0ce1b6cf3e03ac1498f39eb8ef08d36d73ee2f0bc7fe368b97f1497ba050501a => 99a4393ee9f877db228baee1164325543fa018be780fd1253250d7d81539f38e}/contract.d.ts (96%) create mode 100644 examples/prisma-8-demo/migrations/snapshots/99a4393ee9f877db228baee1164325543fa018be780fd1253250d7d81539f38e/contract.json delete mode 100644 examples/prisma-8-demo/migrations/snapshots/9fc3736e0efd980892a19f4d704ca30bcd0de243080e78ed1279b1f3a50f9a7e/contract.d.ts delete mode 100644 examples/prisma-8-demo/migrations/snapshots/9fc3736e0efd980892a19f4d704ca30bcd0de243080e78ed1279b1f3a50f9a7e/contract.json delete mode 100644 examples/prisma-8-demo/migrations/snapshots/a6e86cb536b51ac3b7542cb02dbf9f5004139e8dc5ee1b33a554c436d8cd2b2a/contract.d.ts delete mode 100644 examples/prisma-8-demo/migrations/snapshots/a6e86cb536b51ac3b7542cb02dbf9f5004139e8dc5ee1b33a554c436d8cd2b2a/contract.json delete mode 100644 examples/prisma-8-demo/migrations/snapshots/d44ab6f7caddcc384332c669737008aec7ec8c9f5b013cb502a03c2eb04be9c0/contract.json rename examples/retail-store/migrations/snapshots/{41bd5540ea833c009b5eacf6db7d489e88bdd5062554398ba8aa2657fa19d27e => 2f0e67cbc2a193e280f07c26972a484af4b62d3472c018c73bfd095749d4d0f5}/contract.d.ts (82%) create mode 100644 examples/retail-store/migrations/snapshots/2f0e67cbc2a193e280f07c26972a484af4b62d3472c018c73bfd095749d4d0f5/contract.json rename examples/retail-store/migrations/snapshots/{6969147309c36a2e1cef69c2077abfa7ddb568d171ad99f03506d9f428b0a595 => 32b49a010a6bec06827848a71ade1588d7863e429644edc402becee50580be78}/contract.d.ts (81%) create mode 100644 examples/retail-store/migrations/snapshots/32b49a010a6bec06827848a71ade1588d7863e429644edc402becee50580be78/contract.json delete mode 100644 examples/retail-store/migrations/snapshots/41bd5540ea833c009b5eacf6db7d489e88bdd5062554398ba8aa2657fa19d27e/contract.json delete mode 100644 examples/retail-store/migrations/snapshots/6969147309c36a2e1cef69c2077abfa7ddb568d171ad99f03506d9f428b0a595/contract.json rename examples/retail-store/migrations/snapshots/{9af6d90aaa4cd03bf7eb2040a1c1b5c1225a2b33883ea3d0552f312f4684f8dc => 917a8cff4030fa9140b93367a663f33d66cd14e92547b51e23a9ff82ffabf27f}/contract.d.ts (80%) create mode 100644 examples/retail-store/migrations/snapshots/917a8cff4030fa9140b93367a663f33d66cd14e92547b51e23a9ff82ffabf27f/contract.json delete mode 100644 examples/retail-store/migrations/snapshots/9414a8f88a64f9decc0e019967459e49da083f011cf91898094b7bccab6c1810/contract.d.ts delete mode 100644 examples/retail-store/migrations/snapshots/9414a8f88a64f9decc0e019967459e49da083f011cf91898094b7bccab6c1810/contract.json delete mode 100644 examples/retail-store/migrations/snapshots/977a060afe52c4a56f93f2f33d65b8b6b4cc4ded04d16fe6b4f8e7e9e61192d3/contract.json delete mode 100644 examples/retail-store/migrations/snapshots/9af6d90aaa4cd03bf7eb2040a1c1b5c1225a2b33883ea3d0552f312f4684f8dc/contract.json delete mode 100644 examples/retail-store/migrations/snapshots/bd938b4f8a10c688bd32dc61ec1dd808dcf34e725f08505b39ce365a39c97e1b/contract.d.ts delete mode 100644 examples/retail-store/migrations/snapshots/bd938b4f8a10c688bd32dc61ec1dd808dcf34e725f08505b39ce365a39c97e1b/contract.json rename examples/retail-store/migrations/snapshots/{977a060afe52c4a56f93f2f33d65b8b6b4cc4ded04d16fe6b4f8e7e9e61192d3 => c529f34639ba48ec978d133093cd95ac2a5a76196ea39b3fa71179a9ef2c3467}/contract.d.ts (82%) create mode 100644 examples/retail-store/migrations/snapshots/c529f34639ba48ec978d133093cd95ac2a5a76196ea39b3fa71179a9ef2c3467/contract.json diff --git a/apps/telemetry-backend/src/prisma/contract.d.ts b/apps/telemetry-backend/src/prisma/contract.d.ts index 6b9db76303bb..a72a21f62f1b 100644 --- a/apps/telemetry-backend/src/prisma/contract.d.ts +++ b/apps/telemetry-backend/src/prisma/contract.d.ts @@ -577,7 +577,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/examples/bundle-size/src/mongo/generated/contract.d.ts b/examples/bundle-size/src/mongo/generated/contract.d.ts index 6bee6468e8f1..a2e3425203a4 100644 --- a/examples/bundle-size/src/mongo/generated/contract.d.ts +++ b/examples/bundle-size/src/mongo/generated/contract.d.ts @@ -121,7 +121,6 @@ type ContractBase = Omit< readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/examples/bundle-size/src/postgres/generated/contract.d.ts b/examples/bundle-size/src/postgres/generated/contract.d.ts index 8fa3efb80080..908ed0cd919f 100644 --- a/examples/bundle-size/src/postgres/generated/contract.d.ts +++ b/examples/bundle-size/src/postgres/generated/contract.d.ts @@ -398,7 +398,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/examples/mongo-blog-leaderboard/src/contract.d.ts b/examples/mongo-blog-leaderboard/src/contract.d.ts index dde93a89227e..cfcf297baede 100644 --- a/examples/mongo-blog-leaderboard/src/contract.d.ts +++ b/examples/mongo-blog-leaderboard/src/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@prisma/orm-mongo/contract/types'; export type StorageHash = - StorageHashBase<'bd6f4b342df331f80b6e10550b5ee6d9a8b9dc3b279f3eec78b779d6871993d8'>; + StorageHashBase<'4211584674f0ef61d518d3b24b3380e1760d7e46d6dd7098b1b0b1dc19ff3e30'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -53,14 +53,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -231,6 +233,28 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + }; }; }; }; @@ -357,7 +381,13 @@ type ContractBase = Omit< readonly fields: { readonly address: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; }; readonly bio: { readonly nullable: true; @@ -401,56 +431,12 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - }; }; }; }; readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/examples/mongo-blog-leaderboard/src/contract.json b/examples/mongo-blog-leaderboard/src/contract.json index 3043b8028674..491858a73218 100644 --- a/examples/mongo-blog-leaderboard/src/contract.json +++ b/examples/mongo-blog-leaderboard/src/contract.json @@ -180,8 +180,11 @@ "address": { "nullable": true, "type": { + "entityKind": "valueObject", + "entityName": "Address", "kind": "valueObject", - "name": "Address" + "namespaceId": "__unbound__", + "plane": "storage" } }, "bio": { @@ -251,40 +254,6 @@ } } } - }, - "valueObjects": { - "Address": { - "fields": { - "city": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "country": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "street": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "zip": { - "nullable": true, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - } - } - } } } } @@ -490,13 +459,47 @@ "validationLevel": "strict" } } + }, + "valueObject": { + "Address": { + "fields": { + "city": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "country": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "street": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "zip": { + "nullable": true, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + } + } + } } }, "id": "__unbound__", "kind": "mongo-database" } }, - "storageHash": "bd6f4b342df331f80b6e10550b5ee6d9a8b9dc3b279f3eec78b779d6871993d8" + "storageHash": "4211584674f0ef61d518d3b24b3380e1760d7e46d6dd7098b1b0b1dc19ff3e30" }, "capabilities": {}, "extensions": {}, diff --git a/examples/mongo-demo/migrations/app/20260409T1030_migration/migration.json b/examples/mongo-demo/migrations/app/20260409T1030_migration/migration.json index 3cb8739196ad..eb148543a554 100644 --- a/examples/mongo-demo/migrations/app/20260409T1030_migration/migration.json +++ b/examples/mongo-demo/migrations/app/20260409T1030_migration/migration.json @@ -1,7 +1,7 @@ { "from": null, - "to": "da1339e341177f79b37f765f08200844d7cb4d59d26fe27fe4d95b0112b0c2cd", + "to": "396942f6c9ee5f946d7f67b397929bab327fe84dd8f7f80e87b8097f416fb901", "providedInvariants": [], "createdAt": "2026-04-09T10:30:13.948Z", - "migrationHash": "3a28bd528b39d9b9c9a52082485a3274a8de15257cf9296776c290cc7b4e4fb5" + "migrationHash": "a4dbf4e008b176a2fed392de87662f68cd4c2f537b60cf760e234048e6f55abe" } diff --git a/examples/mongo-demo/migrations/app/20260409T1030_migration/migration.ts b/examples/mongo-demo/migrations/app/20260409T1030_migration/migration.ts index ee3167d3c896..24f8a81660b0 100644 --- a/examples/mongo-demo/migrations/app/20260409T1030_migration/migration.ts +++ b/examples/mongo-demo/migrations/app/20260409T1030_migration/migration.ts @@ -1,6 +1,6 @@ import { createIndex, Migration, MigrationCLI } from '@prisma/orm-mongo/target/migration'; -import type { Contract as End } from '../../snapshots/da1339e341177f79b37f765f08200844d7cb4d59d26fe27fe4d95b0112b0c2cd/contract'; -import endContract from '../../snapshots/da1339e341177f79b37f765f08200844d7cb4d59d26fe27fe4d95b0112b0c2cd/contract.json' with { +import type { Contract as End } from '../../snapshots/396942f6c9ee5f946d7f67b397929bab327fe84dd8f7f80e87b8097f416fb901/contract'; +import endContract from '../../snapshots/396942f6c9ee5f946d7f67b397929bab327fe84dd8f7f80e87b8097f416fb901/contract.json' with { type: 'json', }; diff --git a/examples/mongo-demo/migrations/app/20260626T1605_add_user_role_enum/migration.json b/examples/mongo-demo/migrations/app/20260626T1605_add_user_role_enum/migration.json index dfb26991f67f..8ee13dc3e337 100644 --- a/examples/mongo-demo/migrations/app/20260626T1605_add_user_role_enum/migration.json +++ b/examples/mongo-demo/migrations/app/20260626T1605_add_user_role_enum/migration.json @@ -1,7 +1,7 @@ { - "from": "da1339e341177f79b37f765f08200844d7cb4d59d26fe27fe4d95b0112b0c2cd", - "to": "62671fbf016c515d808ac613743fddbf3db80384c1997251eac9d6f5fc063590", + "from": "396942f6c9ee5f946d7f67b397929bab327fe84dd8f7f80e87b8097f416fb901", + "to": "95332b0bd6fbbfab4462d882c037a574c92e51897249c333ba681b12c4d603c9", "providedInvariants": [], "createdAt": "2026-06-26T16:05:45.955Z", - "migrationHash": "671d9b676330b6af88db75c5566f1fe1a7d7d4d0af4ca282828f093b8dd295c0" + "migrationHash": "08ac0867c606d262ac4dc80b3ad7f1f0afc8418bd72fe957c13c9c549bfc4651" } diff --git a/examples/mongo-demo/migrations/app/20260626T1605_add_user_role_enum/migration.ts b/examples/mongo-demo/migrations/app/20260626T1605_add_user_role_enum/migration.ts index 9471c658b0b7..9284d486566d 100755 --- a/examples/mongo-demo/migrations/app/20260626T1605_add_user_role_enum/migration.ts +++ b/examples/mongo-demo/migrations/app/20260626T1605_add_user_role_enum/migration.ts @@ -1,11 +1,11 @@ #!/usr/bin/env -S node import { collMod, Migration, MigrationCLI } from '@prisma/orm-mongo/target/migration'; -import type { Contract as End } from '../../snapshots/62671fbf016c515d808ac613743fddbf3db80384c1997251eac9d6f5fc063590/contract'; -import endContract from '../../snapshots/62671fbf016c515d808ac613743fddbf3db80384c1997251eac9d6f5fc063590/contract.json' with { +import type { Contract as End } from '../../snapshots/95332b0bd6fbbfab4462d882c037a574c92e51897249c333ba681b12c4d603c9/contract'; +import endContract from '../../snapshots/95332b0bd6fbbfab4462d882c037a574c92e51897249c333ba681b12c4d603c9/contract.json' with { type: 'json', }; -import type { Contract as Start } from '../../snapshots/da1339e341177f79b37f765f08200844d7cb4d59d26fe27fe4d95b0112b0c2cd/contract'; -import startContract from '../../snapshots/da1339e341177f79b37f765f08200844d7cb4d59d26fe27fe4d95b0112b0c2cd/contract.json' with { +import type { Contract as Start } from '../../snapshots/396942f6c9ee5f946d7f67b397929bab327fe84dd8f7f80e87b8097f416fb901/contract'; +import startContract from '../../snapshots/396942f6c9ee5f946d7f67b397929bab327fe84dd8f7f80e87b8097f416fb901/contract.json' with { type: 'json', }; diff --git a/examples/mongo-demo/migrations/app/20260626T1916_add_posts_indexes/migration.json b/examples/mongo-demo/migrations/app/20260626T1916_add_posts_indexes/migration.json index 6d6ea9a9ebad..76235940857c 100644 --- a/examples/mongo-demo/migrations/app/20260626T1916_add_posts_indexes/migration.json +++ b/examples/mongo-demo/migrations/app/20260626T1916_add_posts_indexes/migration.json @@ -1,7 +1,7 @@ { - "from": "62671fbf016c515d808ac613743fddbf3db80384c1997251eac9d6f5fc063590", - "to": "d264cabd6a02546054a776ed3c04438b9a22989f406c49f5590f7f2101156eb0", + "from": "95332b0bd6fbbfab4462d882c037a574c92e51897249c333ba681b12c4d603c9", + "to": "406b672b68a6b01f04c0d6f910f5fefa4ac16d48b26e6c91b43856cd42c8cc5d", "providedInvariants": [], "createdAt": "2026-06-26T19:16:47.657Z", - "migrationHash": "a435f5206064150010733fe0d93690358b92cb892cfdfbd80c1b4aade84c16ef" + "migrationHash": "a77e60df92e1d3ee6a5a000904e3aa1869a382d602279a64bce9038c17c0475d" } diff --git a/examples/mongo-demo/migrations/app/20260626T1916_add_posts_indexes/migration.ts b/examples/mongo-demo/migrations/app/20260626T1916_add_posts_indexes/migration.ts index 146f9deaca22..83da597d716d 100755 --- a/examples/mongo-demo/migrations/app/20260626T1916_add_posts_indexes/migration.ts +++ b/examples/mongo-demo/migrations/app/20260626T1916_add_posts_indexes/migration.ts @@ -1,11 +1,11 @@ #!/usr/bin/env -S node import { createIndex, Migration, MigrationCLI } from '@prisma/orm-mongo/target/migration'; -import type { Contract as Start } from '../../snapshots/62671fbf016c515d808ac613743fddbf3db80384c1997251eac9d6f5fc063590/contract'; -import startContract from '../../snapshots/62671fbf016c515d808ac613743fddbf3db80384c1997251eac9d6f5fc063590/contract.json' with { +import type { Contract as End } from '../../snapshots/406b672b68a6b01f04c0d6f910f5fefa4ac16d48b26e6c91b43856cd42c8cc5d/contract'; +import endContract from '../../snapshots/406b672b68a6b01f04c0d6f910f5fefa4ac16d48b26e6c91b43856cd42c8cc5d/contract.json' with { type: 'json', }; -import type { Contract as End } from '../../snapshots/d264cabd6a02546054a776ed3c04438b9a22989f406c49f5590f7f2101156eb0/contract'; -import endContract from '../../snapshots/d264cabd6a02546054a776ed3c04438b9a22989f406c49f5590f7f2101156eb0/contract.json' with { +import type { Contract as Start } from '../../snapshots/95332b0bd6fbbfab4462d882c037a574c92e51897249c333ba681b12c4d603c9/contract'; +import startContract from '../../snapshots/95332b0bd6fbbfab4462d882c037a574c92e51897249c333ba681b12c4d603c9/contract.json' with { type: 'json', }; diff --git a/examples/mongo-demo/migrations/snapshots/da1339e341177f79b37f765f08200844d7cb4d59d26fe27fe4d95b0112b0c2cd/contract.d.ts b/examples/mongo-demo/migrations/snapshots/396942f6c9ee5f946d7f67b397929bab327fe84dd8f7f80e87b8097f416fb901/contract.d.ts similarity index 88% rename from examples/mongo-demo/migrations/snapshots/da1339e341177f79b37f765f08200844d7cb4d59d26fe27fe4d95b0112b0c2cd/contract.d.ts rename to examples/mongo-demo/migrations/snapshots/396942f6c9ee5f946d7f67b397929bab327fe84dd8f7f80e87b8097f416fb901/contract.d.ts index 4d5e1f4928c0..450aa81593d9 100644 --- a/examples/mongo-demo/migrations/snapshots/da1339e341177f79b37f765f08200844d7cb4d59d26fe27fe4d95b0112b0c2cd/contract.d.ts +++ b/examples/mongo-demo/migrations/snapshots/396942f6c9ee5f946d7f67b397929bab327fe84dd8f7f80e87b8097f416fb901/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@prisma/orm-mongo/contract/types'; export type StorageHash = - StorageHashBase<'da1339e341177f79b37f765f08200844d7cb4d59d26fe27fe4d95b0112b0c2cd'>; + StorageHashBase<'396942f6c9ee5f946d7f67b397929bab327fe84dd8f7f80e87b8097f416fb901'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -53,14 +53,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -229,6 +231,28 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + }; }; }; }; @@ -355,7 +379,13 @@ type ContractBase = Omit< readonly fields: { readonly address: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; }; readonly bio: { readonly nullable: true; @@ -399,56 +429,12 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - }; }; }; }; readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/examples/mongo-demo/migrations/snapshots/396942f6c9ee5f946d7f67b397929bab327fe84dd8f7f80e87b8097f416fb901/contract.json b/examples/mongo-demo/migrations/snapshots/396942f6c9ee5f946d7f67b397929bab327fe84dd8f7f80e87b8097f416fb901/contract.json new file mode 100644 index 000000000000..5699666ec9cd --- /dev/null +++ b/examples/mongo-demo/migrations/snapshots/396942f6c9ee5f946d7f67b397929bab327fe84dd8f7f80e87b8097f416fb901/contract.json @@ -0,0 +1 @@ +{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{},"domain":{"namespaces":{"__unbound__":{"models":{"Article":{"base":{"model":"Post","namespace":"__unbound__"},"fields":{"summary":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"posts","fields":{"summary":{"field":"summary"}}}},"Post":{"discriminator":{"field":"kind"},"fields":{"authorId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"content":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"createdAt":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{"author":{"cardinality":"N:1","nullable":false,"on":{"localFields":["authorId"],"targetFields":["id"]},"to":{"model":"User","namespace":"__unbound__"}}},"storage":{"collection":"posts","fields":{"authorId":{"field":"authorId"},"content":{"field":"content"},"createdAt":{"field":"createdAt"},"id":{"field":"_id"},"kind":{"field":"kind"},"title":{"field":"title"}}},"variants":{"article":{"model":"Article","namespace":"__unbound__"},"tutorial":{"model":"Tutorial","namespace":"__unbound__"}}},"Tutorial":{"base":{"model":"Post","namespace":"__unbound__"},"fields":{"difficulty":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"duration":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"posts","fields":{"difficulty":{"field":"difficulty"},"duration":{"field":"duration"}}}},"User":{"fields":{"address":{"nullable":true,"type":{"entityKind":"valueObject","entityName":"Address","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"bio":{"nullable":true,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["authorId"]},"to":{"model":"Post","namespace":"__unbound__"}}},"storage":{"collection":"users","fields":{"address":{"field":"address"},"bio":{"field":"bio"},"email":{"field":"email"},"id":{"field":"_id"},"name":{"field":"name"}}}}}}}},"extensions":{},"meta":{},"profileHash":"251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3","roots":{"posts":{"model":"Post","namespace":"__unbound__"},"users":{"model":"User","namespace":"__unbound__"}},"schemaVersion":"1","storage":{"namespaces":{"__unbound__":{"entries":{"collection":{"posts":{"indexes":[{"keys":[{"direction":1,"field":"summary"}],"kind":"mongo-index","partialFilterExpression":{"kind":"article"},"unique":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"bsonType":"object","oneOf":[{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"kind":{"enum":["article"]},"summary":{"bsonType":"string"},"title":{"bsonType":"string"}},"required":["kind","summary"]},{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"difficulty":{"bsonType":"string"},"duration":{"bsonType":"int"},"kind":{"enum":["tutorial"]},"title":{"bsonType":"string"}},"required":["difficulty","duration","kind"]}],"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"kind":{"bsonType":"string"},"title":{"bsonType":"string"}},"required":["_id","authorId","content","createdAt","kind","title"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"users":{"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"address":{"oneOf":[{"bsonType":"null"},{"additionalProperties":false,"bsonType":"object","properties":{"city":{"bsonType":"string"},"country":{"bsonType":"string"},"street":{"bsonType":"string"},"zip":{"bsonType":["null","string"]}},"required":["city","country","street"]}]},"bio":{"bsonType":["null","string"]},"email":{"bsonType":"string"},"name":{"bsonType":"string"}},"required":["_id","email","name"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}}},"valueObject":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}}}},"id":"__unbound__","kind":"mongo-database"}},"storageHash":"396942f6c9ee5f946d7f67b397929bab327fe84dd8f7f80e87b8097f416fb901"},"target":"mongo","targetFamily":"mongo"} diff --git a/examples/mongo-demo/migrations/snapshots/d264cabd6a02546054a776ed3c04438b9a22989f406c49f5590f7f2101156eb0/contract.d.ts b/examples/mongo-demo/migrations/snapshots/406b672b68a6b01f04c0d6f910f5fefa4ac16d48b26e6c91b43856cd42c8cc5d/contract.d.ts similarity index 89% rename from examples/mongo-demo/migrations/snapshots/d264cabd6a02546054a776ed3c04438b9a22989f406c49f5590f7f2101156eb0/contract.d.ts rename to examples/mongo-demo/migrations/snapshots/406b672b68a6b01f04c0d6f910f5fefa4ac16d48b26e6c91b43856cd42c8cc5d/contract.d.ts index af70721dd428..80c6ac7a1cf7 100644 --- a/examples/mongo-demo/migrations/snapshots/d264cabd6a02546054a776ed3c04438b9a22989f406c49f5590f7f2101156eb0/contract.d.ts +++ b/examples/mongo-demo/migrations/snapshots/406b672b68a6b01f04c0d6f910f5fefa4ac16d48b26e6c91b43856cd42c8cc5d/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@prisma/orm-mongo/contract/types'; export type StorageHash = - StorageHashBase<'d264cabd6a02546054a776ed3c04438b9a22989f406c49f5590f7f2101156eb0'>; + StorageHashBase<'406b672b68a6b01f04c0d6f910f5fefa4ac16d48b26e6c91b43856cd42c8cc5d'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -64,14 +64,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -256,6 +258,28 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + }; }; }; }; @@ -382,7 +406,13 @@ type ContractBase = Omit< readonly fields: { readonly address: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; }; readonly bio: { readonly nullable: true; @@ -431,28 +461,6 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - }; readonly enum: { readonly UserRole: { readonly codecId: 'mongo/string@1'; @@ -476,28 +484,6 @@ type ContractBase = Omit< readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/examples/mongo-demo/migrations/snapshots/406b672b68a6b01f04c0d6f910f5fefa4ac16d48b26e6c91b43856cd42c8cc5d/contract.json b/examples/mongo-demo/migrations/snapshots/406b672b68a6b01f04c0d6f910f5fefa4ac16d48b26e6c91b43856cd42c8cc5d/contract.json new file mode 100644 index 000000000000..83ba3e77dc37 --- /dev/null +++ b/examples/mongo-demo/migrations/snapshots/406b672b68a6b01f04c0d6f910f5fefa4ac16d48b26e6c91b43856cd42c8cc5d/contract.json @@ -0,0 +1 @@ +{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{},"domain":{"namespaces":{"__unbound__":{"enum":{"UserRole":{"codecId":"mongo/string@1","members":[{"name":"Admin","value":"admin"},{"name":"Author","value":"author"},{"name":"Reader","value":"reader"}]}},"models":{"Article":{"base":{"model":"Post","namespace":"__unbound__"},"fields":{"summary":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"posts","fields":{"summary":{"field":"summary"}}}},"Post":{"discriminator":{"field":"kind"},"fields":{"authorId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"content":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"createdAt":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{"author":{"cardinality":"N:1","nullable":false,"on":{"localFields":["authorId"],"targetFields":["id"]},"to":{"model":"User","namespace":"__unbound__"}}},"storage":{"collection":"posts","fields":{"authorId":{"field":"authorId"},"content":{"field":"content"},"createdAt":{"field":"createdAt"},"id":{"field":"_id"},"kind":{"field":"kind"},"title":{"field":"title"}}},"variants":{"article":{"model":"Article","namespace":"__unbound__"},"tutorial":{"model":"Tutorial","namespace":"__unbound__"}}},"Tutorial":{"base":{"model":"Post","namespace":"__unbound__"},"fields":{"difficulty":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"duration":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"posts","fields":{"difficulty":{"field":"difficulty"},"duration":{"field":"duration"}}}},"User":{"fields":{"address":{"nullable":true,"type":{"entityKind":"valueObject","entityName":"Address","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"bio":{"nullable":true,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"role":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"UserRole","namespaceId":"__unbound__","plane":"domain"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["authorId"]},"to":{"model":"Post","namespace":"__unbound__"}}},"storage":{"collection":"users","fields":{"address":{"field":"address"},"bio":{"field":"bio"},"email":{"field":"email"},"id":{"field":"_id"},"name":{"field":"name"},"role":{"field":"role"}}}}}}}},"extensions":{},"meta":{},"profileHash":"251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3","roots":{"posts":{"model":"Post","namespace":"__unbound__"},"users":{"model":"User","namespace":"__unbound__"}},"schemaVersion":"1","storage":{"namespaces":{"__unbound__":{"entries":{"collection":{"posts":{"indexes":[{"keys":[{"direction":1,"field":"authorId"}],"kind":"mongo-index"},{"keys":[{"direction":-1,"field":"createdAt"},{"direction":1,"field":"authorId"}],"kind":"mongo-index"},{"keys":[{"direction":1,"field":"summary"}],"kind":"mongo-index","partialFilterExpression":{"kind":"article"},"unique":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"bsonType":"object","oneOf":[{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"kind":{"enum":["article"]},"summary":{"bsonType":"string"},"title":{"bsonType":"string"}},"required":["kind","summary"]},{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"difficulty":{"bsonType":"string"},"duration":{"bsonType":"int"},"kind":{"enum":["tutorial"]},"title":{"bsonType":"string"}},"required":["difficulty","duration","kind"]}],"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"kind":{"bsonType":"string"},"title":{"bsonType":"string"}},"required":["_id","authorId","content","createdAt","kind","title"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"users":{"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"address":{"oneOf":[{"bsonType":"null"},{"additionalProperties":false,"bsonType":"object","properties":{"city":{"bsonType":"string"},"country":{"bsonType":"string"},"street":{"bsonType":"string"},"zip":{"bsonType":["null","string"]}},"required":["city","country","street"]}]},"bio":{"bsonType":["null","string"]},"email":{"bsonType":"string"},"name":{"bsonType":"string"},"role":{"bsonType":"string","enum":["admin","author","reader"]}},"required":["_id","email","name","role"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}}},"valueObject":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}}},"valueSet":{"UserRole":{"kind":"valueSet","values":["admin","author","reader"]}}},"id":"__unbound__","kind":"mongo-database"}},"storageHash":"406b672b68a6b01f04c0d6f910f5fefa4ac16d48b26e6c91b43856cd42c8cc5d"},"target":"mongo","targetFamily":"mongo"} diff --git a/examples/mongo-demo/migrations/snapshots/62671fbf016c515d808ac613743fddbf3db80384c1997251eac9d6f5fc063590/contract.json b/examples/mongo-demo/migrations/snapshots/62671fbf016c515d808ac613743fddbf3db80384c1997251eac9d6f5fc063590/contract.json deleted file mode 100644 index e2172d20550f..000000000000 --- a/examples/mongo-demo/migrations/snapshots/62671fbf016c515d808ac613743fddbf3db80384c1997251eac9d6f5fc063590/contract.json +++ /dev/null @@ -1 +0,0 @@ -{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{},"domain":{"namespaces":{"__unbound__":{"enum":{"UserRole":{"codecId":"mongo/string@1","members":[{"name":"Admin","value":"admin"},{"name":"Author","value":"author"},{"name":"Reader","value":"reader"}]}},"models":{"Article":{"base":{"model":"Post","namespace":"__unbound__"},"fields":{"summary":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"posts","fields":{"summary":{"field":"summary"}}}},"Post":{"discriminator":{"field":"kind"},"fields":{"authorId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"content":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"createdAt":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{"author":{"cardinality":"N:1","nullable":false,"on":{"localFields":["authorId"],"targetFields":["id"]},"to":{"model":"User","namespace":"__unbound__"}}},"storage":{"collection":"posts","fields":{"authorId":{"field":"authorId"},"content":{"field":"content"},"createdAt":{"field":"createdAt"},"id":{"field":"_id"},"kind":{"field":"kind"},"title":{"field":"title"}}},"variants":{"article":{"model":"Article","namespace":"__unbound__"},"tutorial":{"model":"Tutorial","namespace":"__unbound__"}}},"Tutorial":{"base":{"model":"Post","namespace":"__unbound__"},"fields":{"difficulty":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"duration":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"posts","fields":{"difficulty":{"field":"difficulty"},"duration":{"field":"duration"}}}},"User":{"fields":{"address":{"nullable":true,"type":{"kind":"valueObject","name":"Address"}},"bio":{"nullable":true,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"role":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"UserRole","namespaceId":"__unbound__","plane":"domain"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["authorId"]},"to":{"model":"Post","namespace":"__unbound__"}}},"storage":{"collection":"users","fields":{"address":{"field":"address"},"bio":{"field":"bio"},"email":{"field":"email"},"id":{"field":"_id"},"name":{"field":"name"},"role":{"field":"role"}}}}},"valueObjects":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}}}}}},"extensions":{},"meta":{},"profileHash":"251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3","roots":{"posts":{"model":"Post","namespace":"__unbound__"},"users":{"model":"User","namespace":"__unbound__"}},"schemaVersion":"1","storage":{"namespaces":{"__unbound__":{"entries":{"collection":{"posts":{"indexes":[{"keys":[{"direction":1,"field":"summary"}],"kind":"mongo-index","partialFilterExpression":{"kind":"article"},"unique":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"bsonType":"object","oneOf":[{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"kind":{"enum":["article"]},"summary":{"bsonType":"string"},"title":{"bsonType":"string"}},"required":["kind","summary"]},{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"difficulty":{"bsonType":"string"},"duration":{"bsonType":"int"},"kind":{"enum":["tutorial"]},"title":{"bsonType":"string"}},"required":["difficulty","duration","kind"]}],"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"kind":{"bsonType":"string"},"title":{"bsonType":"string"}},"required":["_id","authorId","content","createdAt","kind","title"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"users":{"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"address":{"oneOf":[{"bsonType":"null"},{"additionalProperties":false,"bsonType":"object","properties":{"city":{"bsonType":"string"},"country":{"bsonType":"string"},"street":{"bsonType":"string"},"zip":{"bsonType":["null","string"]}},"required":["city","country","street"]}]},"bio":{"bsonType":["null","string"]},"email":{"bsonType":"string"},"name":{"bsonType":"string"},"role":{"bsonType":"string","enum":["admin","author","reader"]}},"required":["_id","email","name","role"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}}},"valueSet":{"UserRole":{"kind":"valueSet","values":["admin","author","reader"]}}},"id":"__unbound__","kind":"mongo-database"}},"storageHash":"62671fbf016c515d808ac613743fddbf3db80384c1997251eac9d6f5fc063590"},"target":"mongo","targetFamily":"mongo"} diff --git a/examples/mongo-demo/migrations/snapshots/62671fbf016c515d808ac613743fddbf3db80384c1997251eac9d6f5fc063590/contract.d.ts b/examples/mongo-demo/migrations/snapshots/95332b0bd6fbbfab4462d882c037a574c92e51897249c333ba681b12c4d603c9/contract.d.ts similarity index 89% rename from examples/mongo-demo/migrations/snapshots/62671fbf016c515d808ac613743fddbf3db80384c1997251eac9d6f5fc063590/contract.d.ts rename to examples/mongo-demo/migrations/snapshots/95332b0bd6fbbfab4462d882c037a574c92e51897249c333ba681b12c4d603c9/contract.d.ts index 6512ab80b5a1..c61fa21a4db6 100644 --- a/examples/mongo-demo/migrations/snapshots/62671fbf016c515d808ac613743fddbf3db80384c1997251eac9d6f5fc063590/contract.d.ts +++ b/examples/mongo-demo/migrations/snapshots/95332b0bd6fbbfab4462d882c037a574c92e51897249c333ba681b12c4d603c9/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@prisma/orm-mongo/contract/types'; export type StorageHash = - StorageHashBase<'62671fbf016c515d808ac613743fddbf3db80384c1997251eac9d6f5fc063590'>; + StorageHashBase<'95332b0bd6fbbfab4462d882c037a574c92e51897249c333ba681b12c4d603c9'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -64,14 +64,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -245,6 +247,28 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + }; }; }; }; @@ -371,7 +395,13 @@ type ContractBase = Omit< readonly fields: { readonly address: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; }; readonly bio: { readonly nullable: true; @@ -420,28 +450,6 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - }; readonly enum: { readonly UserRole: { readonly codecId: 'mongo/string@1'; @@ -465,28 +473,6 @@ type ContractBase = Omit< readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/examples/mongo-demo/migrations/snapshots/95332b0bd6fbbfab4462d882c037a574c92e51897249c333ba681b12c4d603c9/contract.json b/examples/mongo-demo/migrations/snapshots/95332b0bd6fbbfab4462d882c037a574c92e51897249c333ba681b12c4d603c9/contract.json new file mode 100644 index 000000000000..feed3a57d193 --- /dev/null +++ b/examples/mongo-demo/migrations/snapshots/95332b0bd6fbbfab4462d882c037a574c92e51897249c333ba681b12c4d603c9/contract.json @@ -0,0 +1 @@ +{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{},"domain":{"namespaces":{"__unbound__":{"enum":{"UserRole":{"codecId":"mongo/string@1","members":[{"name":"Admin","value":"admin"},{"name":"Author","value":"author"},{"name":"Reader","value":"reader"}]}},"models":{"Article":{"base":{"model":"Post","namespace":"__unbound__"},"fields":{"summary":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"posts","fields":{"summary":{"field":"summary"}}}},"Post":{"discriminator":{"field":"kind"},"fields":{"authorId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"content":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"createdAt":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{"author":{"cardinality":"N:1","nullable":false,"on":{"localFields":["authorId"],"targetFields":["id"]},"to":{"model":"User","namespace":"__unbound__"}}},"storage":{"collection":"posts","fields":{"authorId":{"field":"authorId"},"content":{"field":"content"},"createdAt":{"field":"createdAt"},"id":{"field":"_id"},"kind":{"field":"kind"},"title":{"field":"title"}}},"variants":{"article":{"model":"Article","namespace":"__unbound__"},"tutorial":{"model":"Tutorial","namespace":"__unbound__"}}},"Tutorial":{"base":{"model":"Post","namespace":"__unbound__"},"fields":{"difficulty":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"duration":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"posts","fields":{"difficulty":{"field":"difficulty"},"duration":{"field":"duration"}}}},"User":{"fields":{"address":{"nullable":true,"type":{"entityKind":"valueObject","entityName":"Address","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"bio":{"nullable":true,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"role":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"UserRole","namespaceId":"__unbound__","plane":"domain"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["authorId"]},"to":{"model":"Post","namespace":"__unbound__"}}},"storage":{"collection":"users","fields":{"address":{"field":"address"},"bio":{"field":"bio"},"email":{"field":"email"},"id":{"field":"_id"},"name":{"field":"name"},"role":{"field":"role"}}}}}}}},"extensions":{},"meta":{},"profileHash":"251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3","roots":{"posts":{"model":"Post","namespace":"__unbound__"},"users":{"model":"User","namespace":"__unbound__"}},"schemaVersion":"1","storage":{"namespaces":{"__unbound__":{"entries":{"collection":{"posts":{"indexes":[{"keys":[{"direction":1,"field":"summary"}],"kind":"mongo-index","partialFilterExpression":{"kind":"article"},"unique":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"bsonType":"object","oneOf":[{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"kind":{"enum":["article"]},"summary":{"bsonType":"string"},"title":{"bsonType":"string"}},"required":["kind","summary"]},{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"difficulty":{"bsonType":"string"},"duration":{"bsonType":"int"},"kind":{"enum":["tutorial"]},"title":{"bsonType":"string"}},"required":["difficulty","duration","kind"]}],"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"kind":{"bsonType":"string"},"title":{"bsonType":"string"}},"required":["_id","authorId","content","createdAt","kind","title"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"users":{"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"address":{"oneOf":[{"bsonType":"null"},{"additionalProperties":false,"bsonType":"object","properties":{"city":{"bsonType":"string"},"country":{"bsonType":"string"},"street":{"bsonType":"string"},"zip":{"bsonType":["null","string"]}},"required":["city","country","street"]}]},"bio":{"bsonType":["null","string"]},"email":{"bsonType":"string"},"name":{"bsonType":"string"},"role":{"bsonType":"string","enum":["admin","author","reader"]}},"required":["_id","email","name","role"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}}},"valueObject":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}}},"valueSet":{"UserRole":{"kind":"valueSet","values":["admin","author","reader"]}}},"id":"__unbound__","kind":"mongo-database"}},"storageHash":"95332b0bd6fbbfab4462d882c037a574c92e51897249c333ba681b12c4d603c9"},"target":"mongo","targetFamily":"mongo"} diff --git a/examples/mongo-demo/migrations/snapshots/d264cabd6a02546054a776ed3c04438b9a22989f406c49f5590f7f2101156eb0/contract.json b/examples/mongo-demo/migrations/snapshots/d264cabd6a02546054a776ed3c04438b9a22989f406c49f5590f7f2101156eb0/contract.json deleted file mode 100644 index a19d2d831a90..000000000000 --- a/examples/mongo-demo/migrations/snapshots/d264cabd6a02546054a776ed3c04438b9a22989f406c49f5590f7f2101156eb0/contract.json +++ /dev/null @@ -1 +0,0 @@ -{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{},"domain":{"namespaces":{"__unbound__":{"enum":{"UserRole":{"codecId":"mongo/string@1","members":[{"name":"Admin","value":"admin"},{"name":"Author","value":"author"},{"name":"Reader","value":"reader"}]}},"models":{"Article":{"base":{"model":"Post","namespace":"__unbound__"},"fields":{"summary":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"posts","fields":{"summary":{"field":"summary"}}}},"Post":{"discriminator":{"field":"kind"},"fields":{"authorId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"content":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"createdAt":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{"author":{"cardinality":"N:1","nullable":false,"on":{"localFields":["authorId"],"targetFields":["id"]},"to":{"model":"User","namespace":"__unbound__"}}},"storage":{"collection":"posts","fields":{"authorId":{"field":"authorId"},"content":{"field":"content"},"createdAt":{"field":"createdAt"},"id":{"field":"_id"},"kind":{"field":"kind"},"title":{"field":"title"}}},"variants":{"article":{"model":"Article","namespace":"__unbound__"},"tutorial":{"model":"Tutorial","namespace":"__unbound__"}}},"Tutorial":{"base":{"model":"Post","namespace":"__unbound__"},"fields":{"difficulty":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"duration":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"posts","fields":{"difficulty":{"field":"difficulty"},"duration":{"field":"duration"}}}},"User":{"fields":{"address":{"nullable":true,"type":{"kind":"valueObject","name":"Address"}},"bio":{"nullable":true,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"role":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"UserRole","namespaceId":"__unbound__","plane":"domain"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["authorId"]},"to":{"model":"Post","namespace":"__unbound__"}}},"storage":{"collection":"users","fields":{"address":{"field":"address"},"bio":{"field":"bio"},"email":{"field":"email"},"id":{"field":"_id"},"name":{"field":"name"},"role":{"field":"role"}}}}},"valueObjects":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}}}}}},"extensions":{},"meta":{},"profileHash":"251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3","roots":{"posts":{"model":"Post","namespace":"__unbound__"},"users":{"model":"User","namespace":"__unbound__"}},"schemaVersion":"1","storage":{"namespaces":{"__unbound__":{"entries":{"collection":{"posts":{"indexes":[{"keys":[{"direction":1,"field":"authorId"}],"kind":"mongo-index"},{"keys":[{"direction":-1,"field":"createdAt"},{"direction":1,"field":"authorId"}],"kind":"mongo-index"},{"keys":[{"direction":1,"field":"summary"}],"kind":"mongo-index","partialFilterExpression":{"kind":"article"},"unique":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"bsonType":"object","oneOf":[{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"kind":{"enum":["article"]},"summary":{"bsonType":"string"},"title":{"bsonType":"string"}},"required":["kind","summary"]},{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"difficulty":{"bsonType":"string"},"duration":{"bsonType":"int"},"kind":{"enum":["tutorial"]},"title":{"bsonType":"string"}},"required":["difficulty","duration","kind"]}],"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"kind":{"bsonType":"string"},"title":{"bsonType":"string"}},"required":["_id","authorId","content","createdAt","kind","title"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"users":{"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"address":{"oneOf":[{"bsonType":"null"},{"additionalProperties":false,"bsonType":"object","properties":{"city":{"bsonType":"string"},"country":{"bsonType":"string"},"street":{"bsonType":"string"},"zip":{"bsonType":["null","string"]}},"required":["city","country","street"]}]},"bio":{"bsonType":["null","string"]},"email":{"bsonType":"string"},"name":{"bsonType":"string"},"role":{"bsonType":"string","enum":["admin","author","reader"]}},"required":["_id","email","name","role"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}}},"valueSet":{"UserRole":{"kind":"valueSet","values":["admin","author","reader"]}}},"id":"__unbound__","kind":"mongo-database"}},"storageHash":"d264cabd6a02546054a776ed3c04438b9a22989f406c49f5590f7f2101156eb0"},"target":"mongo","targetFamily":"mongo"} diff --git a/examples/mongo-demo/migrations/snapshots/da1339e341177f79b37f765f08200844d7cb4d59d26fe27fe4d95b0112b0c2cd/contract.json b/examples/mongo-demo/migrations/snapshots/da1339e341177f79b37f765f08200844d7cb4d59d26fe27fe4d95b0112b0c2cd/contract.json deleted file mode 100644 index 948bf482e3ca..000000000000 --- a/examples/mongo-demo/migrations/snapshots/da1339e341177f79b37f765f08200844d7cb4d59d26fe27fe4d95b0112b0c2cd/contract.json +++ /dev/null @@ -1 +0,0 @@ -{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{},"domain":{"namespaces":{"__unbound__":{"models":{"Article":{"base":{"model":"Post","namespace":"__unbound__"},"fields":{"summary":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"posts","fields":{"summary":{"field":"summary"}}}},"Post":{"discriminator":{"field":"kind"},"fields":{"authorId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"content":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"createdAt":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{"author":{"cardinality":"N:1","nullable":false,"on":{"localFields":["authorId"],"targetFields":["id"]},"to":{"model":"User","namespace":"__unbound__"}}},"storage":{"collection":"posts","fields":{"authorId":{"field":"authorId"},"content":{"field":"content"},"createdAt":{"field":"createdAt"},"id":{"field":"_id"},"kind":{"field":"kind"},"title":{"field":"title"}}},"variants":{"article":{"model":"Article","namespace":"__unbound__"},"tutorial":{"model":"Tutorial","namespace":"__unbound__"}}},"Tutorial":{"base":{"model":"Post","namespace":"__unbound__"},"fields":{"difficulty":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"duration":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"posts","fields":{"difficulty":{"field":"difficulty"},"duration":{"field":"duration"}}}},"User":{"fields":{"address":{"nullable":true,"type":{"kind":"valueObject","name":"Address"}},"bio":{"nullable":true,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["authorId"]},"to":{"model":"Post","namespace":"__unbound__"}}},"storage":{"collection":"users","fields":{"address":{"field":"address"},"bio":{"field":"bio"},"email":{"field":"email"},"id":{"field":"_id"},"name":{"field":"name"}}}}},"valueObjects":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}}}}}},"extensions":{},"meta":{},"profileHash":"251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3","roots":{"posts":{"model":"Post","namespace":"__unbound__"},"users":{"model":"User","namespace":"__unbound__"}},"schemaVersion":"1","storage":{"namespaces":{"__unbound__":{"entries":{"collection":{"posts":{"indexes":[{"keys":[{"direction":1,"field":"summary"}],"kind":"mongo-index","partialFilterExpression":{"kind":"article"},"unique":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"bsonType":"object","oneOf":[{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"kind":{"enum":["article"]},"summary":{"bsonType":"string"},"title":{"bsonType":"string"}},"required":["kind","summary"]},{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"difficulty":{"bsonType":"string"},"duration":{"bsonType":"int"},"kind":{"enum":["tutorial"]},"title":{"bsonType":"string"}},"required":["difficulty","duration","kind"]}],"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"kind":{"bsonType":"string"},"title":{"bsonType":"string"}},"required":["_id","authorId","content","createdAt","kind","title"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"users":{"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"address":{"oneOf":[{"bsonType":"null"},{"additionalProperties":false,"bsonType":"object","properties":{"city":{"bsonType":"string"},"country":{"bsonType":"string"},"street":{"bsonType":"string"},"zip":{"bsonType":["null","string"]}},"required":["city","country","street"]}]},"bio":{"bsonType":["null","string"]},"email":{"bsonType":"string"},"name":{"bsonType":"string"}},"required":["_id","email","name"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}}}},"id":"__unbound__","kind":"mongo-database"}},"storageHash":"da1339e341177f79b37f765f08200844d7cb4d59d26fe27fe4d95b0112b0c2cd"},"target":"mongo","targetFamily":"mongo"} diff --git a/examples/mongo-demo/src/contract.d.ts b/examples/mongo-demo/src/contract.d.ts index af70721dd428..80c6ac7a1cf7 100644 --- a/examples/mongo-demo/src/contract.d.ts +++ b/examples/mongo-demo/src/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@prisma/orm-mongo/contract/types'; export type StorageHash = - StorageHashBase<'d264cabd6a02546054a776ed3c04438b9a22989f406c49f5590f7f2101156eb0'>; + StorageHashBase<'406b672b68a6b01f04c0d6f910f5fefa4ac16d48b26e6c91b43856cd42c8cc5d'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -64,14 +64,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -256,6 +258,28 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + }; }; }; }; @@ -382,7 +406,13 @@ type ContractBase = Omit< readonly fields: { readonly address: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; }; readonly bio: { readonly nullable: true; @@ -431,28 +461,6 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - }; readonly enum: { readonly UserRole: { readonly codecId: 'mongo/string@1'; @@ -476,28 +484,6 @@ type ContractBase = Omit< readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/examples/mongo-demo/src/contract.json b/examples/mongo-demo/src/contract.json index 8e6eaca148f4..536570edf7cb 100644 --- a/examples/mongo-demo/src/contract.json +++ b/examples/mongo-demo/src/contract.json @@ -199,8 +199,11 @@ "address": { "nullable": true, "type": { + "entityKind": "valueObject", + "entityName": "Address", "kind": "valueObject", - "name": "Address" + "namespaceId": "__unbound__", + "plane": "storage" } }, "bio": { @@ -286,40 +289,6 @@ } } } - }, - "valueObjects": { - "Address": { - "fields": { - "city": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "country": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "street": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "zip": { - "nullable": true, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - } - } - } } } } @@ -552,6 +521,40 @@ } } }, + "valueObject": { + "Address": { + "fields": { + "city": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "country": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "street": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "zip": { + "nullable": true, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + } + } + } + }, "valueSet": { "UserRole": { "kind": "valueSet", @@ -567,7 +570,7 @@ "kind": "mongo-database" } }, - "storageHash": "d264cabd6a02546054a776ed3c04438b9a22989f406c49f5590f7f2101156eb0" + "storageHash": "406b672b68a6b01f04c0d6f910f5fefa4ac16d48b26e6c91b43856cd42c8cc5d" }, "capabilities": {}, "extensions": {}, diff --git a/examples/multi-extension-monorepo/app/src/contract.d.ts b/examples/multi-extension-monorepo/app/src/contract.d.ts index f824b988935d..5037b4544f64 100644 --- a/examples/multi-extension-monorepo/app/src/contract.d.ts +++ b/examples/multi-extension-monorepo/app/src/contract.d.ts @@ -406,7 +406,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/examples/multi-extension-monorepo/packages/audit/migrations/snapshots/d60ebdd877de6283fafbbe0dc9a4fa13c1197009a53ba95ed37ba04fa3640cb1/contract.d.ts b/examples/multi-extension-monorepo/packages/audit/migrations/snapshots/d60ebdd877de6283fafbbe0dc9a4fa13c1197009a53ba95ed37ba04fa3640cb1/contract.d.ts index 3488479cd7fd..359cb7726dd1 100644 --- a/examples/multi-extension-monorepo/packages/audit/migrations/snapshots/d60ebdd877de6283fafbbe0dc9a4fa13c1197009a53ba95ed37ba04fa3640cb1/contract.d.ts +++ b/examples/multi-extension-monorepo/packages/audit/migrations/snapshots/d60ebdd877de6283fafbbe0dc9a4fa13c1197009a53ba95ed37ba04fa3640cb1/contract.d.ts @@ -406,7 +406,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/examples/multi-extension-monorepo/packages/audit/src/contract.d.ts b/examples/multi-extension-monorepo/packages/audit/src/contract.d.ts index 3488479cd7fd..359cb7726dd1 100644 --- a/examples/multi-extension-monorepo/packages/audit/src/contract.d.ts +++ b/examples/multi-extension-monorepo/packages/audit/src/contract.d.ts @@ -406,7 +406,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/examples/multi-extension-monorepo/packages/feature-flags/migrations/snapshots/9ea058903cdc9636107c83c1a6bbb6c3f28d92d4515cff00c31c2b4e37bb5bd4/contract.d.ts b/examples/multi-extension-monorepo/packages/feature-flags/migrations/snapshots/9ea058903cdc9636107c83c1a6bbb6c3f28d92d4515cff00c31c2b4e37bb5bd4/contract.d.ts index 24f1c76a2e72..9deb8a32353c 100644 --- a/examples/multi-extension-monorepo/packages/feature-flags/migrations/snapshots/9ea058903cdc9636107c83c1a6bbb6c3f28d92d4515cff00c31c2b4e37bb5bd4/contract.d.ts +++ b/examples/multi-extension-monorepo/packages/feature-flags/migrations/snapshots/9ea058903cdc9636107c83c1a6bbb6c3f28d92d4515cff00c31c2b4e37bb5bd4/contract.d.ts @@ -394,7 +394,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/examples/multi-extension-monorepo/packages/feature-flags/src/contract.d.ts b/examples/multi-extension-monorepo/packages/feature-flags/src/contract.d.ts index 24f1c76a2e72..9deb8a32353c 100644 --- a/examples/multi-extension-monorepo/packages/feature-flags/src/contract.d.ts +++ b/examples/multi-extension-monorepo/packages/feature-flags/src/contract.d.ts @@ -394,7 +394,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/examples/paradedb-demo/src/prisma/contract.d.ts b/examples/paradedb-demo/src/prisma/contract.d.ts index 88ebb99c1466..f734d217b386 100644 --- a/examples/paradedb-demo/src/prisma/contract.d.ts +++ b/examples/paradedb-demo/src/prisma/contract.d.ts @@ -445,7 +445,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/examples/prisma-8-cloudflare-worker/src/prisma/contract.d.ts b/examples/prisma-8-cloudflare-worker/src/prisma/contract.d.ts index 821a1af79612..809e96f7adf1 100644 --- a/examples/prisma-8-cloudflare-worker/src/prisma/contract.d.ts +++ b/examples/prisma-8-cloudflare-worker/src/prisma/contract.d.ts @@ -20,11 +20,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@prisma/orm-postgres/contract/types'; export type StorageHash = - StorageHashBase<'b596b070c94c65a7c369422bd7b328fa8a3c7b5c33440ff96d6951eab14f40a0'>; + StorageHashBase<'01135ceb8a6ba131344b2edb3a823effbca7c99558cc540281005bd3c9f093a6'>; export type ExecutionHash = ExecutionHashBase<'0a7f0d7c621b64f307b872110f0effe4cd04d5410d04e913f6550ef107d46788'>; export type ProfileHash = @@ -238,15 +238,15 @@ type DefaultLiteralValue = CodecId extends keyo : CodecTypes[CodecId]['json'] : Encoded; type ParameterizedOutputTypeFunctions = PgParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -257,6 +257,9 @@ export type AddressInput = ValueObjectInputTypes['public']['Address']; type FieldTypeOverrides = { readonly public: { readonly models: { + readonly Task: { + readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; + }; readonly User: { readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; }; @@ -268,17 +271,22 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = { readonly public: { + readonly task: { + readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; + }; readonly user: { readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; }; @@ -641,11 +649,37 @@ type ContractBase = Omit< }; }; readonly valueSet: { + readonly 'Task.type': { + readonly kind: 'valueSet'; + readonly values: readonly ['bug', 'feature']; + }; readonly user_type: { readonly kind: 'valueSet'; readonly values: readonly ['admin', 'user']; }; }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + }; + }; }; }; }; @@ -823,7 +857,13 @@ type ContractBase = Omit< readonly fields: { readonly address: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: 'public'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; }; readonly createdAt: { readonly nullable: false; @@ -881,28 +921,6 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - }; - }; readonly enum: { readonly user_type: { readonly codecId: 'pg/text@1'; @@ -981,28 +999,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/examples/prisma-8-cloudflare-worker/src/prisma/contract.json b/examples/prisma-8-cloudflare-worker/src/prisma/contract.json index a0aa3aa1f77c..f33cf85c91e6 100644 --- a/examples/prisma-8-cloudflare-worker/src/prisma/contract.json +++ b/examples/prisma-8-cloudflare-worker/src/prisma/contract.json @@ -290,8 +290,11 @@ "address": { "nullable": true, "type": { + "entityKind": "valueObject", + "entityName": "Address", "kind": "valueObject", - "name": "Address" + "namespaceId": "public", + "plane": "storage" } }, "createdAt": { @@ -393,40 +396,6 @@ "table": "user" } } - }, - "valueObjects": { - "Address": { - "fields": { - "city": { - "nullable": false, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" - } - }, - "country": { - "nullable": false, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" - } - }, - "street": { - "nullable": false, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" - } - }, - "zip": { - "nullable": true, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" - } - } - } - } } } } @@ -598,6 +567,13 @@ "uniques": [] }, "task": { + "checks": [ + { + "expression": "\"type\" IN ('bug', 'feature')", + "name": "task_type_check_cda3f5e7", + "prefix": "task_type_check" + } + ], "columns": { "createdAt": { "codecId": "pg/timestamptz-temporal@1", @@ -635,7 +611,13 @@ "type": { "codecId": "pg/text@1", "dataType": "pg/text", - "nullable": false + "nullable": false, + "valueSet": { + "entityKind": "valueSet", + "entityName": "Task.type", + "namespaceId": "public", + "plane": "storage" + } }, "userId": { "codecId": "pg/text@1", @@ -683,6 +665,11 @@ }, "user": { "checks": [ + { + "expression": "\"address\" IS NULL OR jsonb_path_match(\"address\", 'strict $.type() == \"null\" || ($.type() == \"object\" && exists($.keyvalue() ? (@.key == \"city\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"country\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"street\" && (@.value.type() == \"string\"))) && !exists($.keyvalue() ? (@.key == \"zip\" && !(@.value.type() == \"null\" || (@.value.type() == \"string\")))) && !exists($.keyvalue() ? (@.key != \"city\" && @.key != \"country\" && @.key != \"street\" && @.key != \"zip\")))') IS TRUE", + "name": "user_address_value_object_69432dc1", + "prefix": "user_address_value_object" + }, { "expression": "\"kind\" IN ('admin', 'user')", "name": "user_kind_check_836d43ef", @@ -741,7 +728,48 @@ "uniques": [] } }, + "valueObject": { + "Address": { + "fields": { + "city": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "country": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "street": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "zip": { + "nullable": true, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + } + } + } + }, "valueSet": { + "Task.type": { + "kind": "valueSet", + "values": [ + "bug", + "feature" + ] + }, "user_type": { "kind": "valueSet", "values": [ @@ -755,7 +783,7 @@ "kind": "postgres-schema" } }, - "storageHash": "b596b070c94c65a7c369422bd7b328fa8a3c7b5c33440ff96d6951eab14f40a0" + "storageHash": "01135ceb8a6ba131344b2edb3a823effbca7c99558cc540281005bd3c9f093a6" }, "execution": { "executionHash": "0a7f0d7c621b64f307b872110f0effe4cd04d5410d04e913f6550ef107d46788", diff --git a/examples/prisma-8-demo-mysql/src/prisma/contract.d.ts b/examples/prisma-8-demo-mysql/src/prisma/contract.d.ts index 6dd6955c1b05..e7217b831946 100644 --- a/examples/prisma-8-demo-mysql/src/prisma/contract.d.ts +++ b/examples/prisma-8-demo-mysql/src/prisma/contract.d.ts @@ -341,7 +341,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/examples/prisma-8-demo-sqlite/src/prisma/contract.d.ts b/examples/prisma-8-demo-sqlite/src/prisma/contract.d.ts index 8ec655c64258..438ea69fd955 100644 --- a/examples/prisma-8-demo-sqlite/src/prisma/contract.d.ts +++ b/examples/prisma-8-demo-sqlite/src/prisma/contract.d.ts @@ -685,7 +685,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/examples/prisma-8-demo/migrations/app/20260810T1108_add_post_engagement_counters/migration.json b/examples/prisma-8-demo/migrations/app/20260810T1108_add_post_engagement_counters/migration.json index 9f0536ef42d7..7e7cf66be30b 100644 --- a/examples/prisma-8-demo/migrations/app/20260810T1108_add_post_engagement_counters/migration.json +++ b/examples/prisma-8-demo/migrations/app/20260810T1108_add_post_engagement_counters/migration.json @@ -1,7 +1,7 @@ { - "from": "58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b", - "to": "d44ab6f7caddcc384332c669737008aec7ec8c9f5b013cb502a03c2eb04be9c0", + "from": "11d8695d1b8ea9c748834093ac4111ce68145827709363b7c748c2892be84146", + "to": "3c2324f6061012e9d50f869b3e6ae6aa41159acd559b1cc5459bc64e2ccb0f4e", "providedInvariants": [], "createdAt": "2026-08-10T11:08:15.318Z", - "migrationHash": "30ef568500a1d674c45bf47a53c6308e4bc66c2e5f7ea0244b6e75862fe33f19" + "migrationHash": "4fac9889c6dbe41bf57c3cab8c5757be2ae20cf3e5255e7680006b164252b235" } diff --git a/examples/prisma-8-demo/migrations/app/20260810T1108_add_post_engagement_counters/migration.ts b/examples/prisma-8-demo/migrations/app/20260810T1108_add_post_engagement_counters/migration.ts index 446676eed417..1598443c392f 100755 --- a/examples/prisma-8-demo/migrations/app/20260810T1108_add_post_engagement_counters/migration.ts +++ b/examples/prisma-8-demo/migrations/app/20260810T1108_add_post_engagement_counters/migration.ts @@ -1,11 +1,11 @@ #!/usr/bin/env -S node import { col, Migration, MigrationCLI } from '@prisma/orm-postgres/migration'; -import type { Contract as Start } from '../../snapshots/58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b/contract'; -import startContract from '../../snapshots/58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b/contract.json' with { +import type { Contract as End } from '../../snapshots/3c2324f6061012e9d50f869b3e6ae6aa41159acd559b1cc5459bc64e2ccb0f4e/contract'; +import endContract from '../../snapshots/3c2324f6061012e9d50f869b3e6ae6aa41159acd559b1cc5459bc64e2ccb0f4e/contract.json' with { type: 'json', }; -import type { Contract as End } from '../../snapshots/d44ab6f7caddcc384332c669737008aec7ec8c9f5b013cb502a03c2eb04be9c0/contract'; -import endContract from '../../snapshots/d44ab6f7caddcc384332c669737008aec7ec8c9f5b013cb502a03c2eb04be9c0/contract.json' with { +import type { Contract as Start } from '../../snapshots/11d8695d1b8ea9c748834093ac4111ce68145827709363b7c748c2892be84146/contract'; +import startContract from '../../snapshots/11d8695d1b8ea9c748834093ac4111ce68145827709363b7c748c2892be84146/contract.json' with { type: 'json', }; diff --git a/examples/prisma-8-demo/migrations/app/20260917T0818_add_post_expires_at/migration.json b/examples/prisma-8-demo/migrations/app/20260917T0818_add_post_expires_at/migration.json index b4f9c3bd010b..dc3290d54471 100644 --- a/examples/prisma-8-demo/migrations/app/20260917T0818_add_post_expires_at/migration.json +++ b/examples/prisma-8-demo/migrations/app/20260917T0818_add_post_expires_at/migration.json @@ -1,7 +1,7 @@ { - "from": "d44ab6f7caddcc384332c669737008aec7ec8c9f5b013cb502a03c2eb04be9c0", - "to": "0ce16e4140d056a3abddbe18bd7ee5198d392370f0aeb23236216c51505944de", + "from": "3c2324f6061012e9d50f869b3e6ae6aa41159acd559b1cc5459bc64e2ccb0f4e", + "to": "1c819419d4357de21d9aa9404a0b1d3639e3bd33c3fe6176207c53b51365fa1f", "providedInvariants": [], "createdAt": "2026-09-17T08:18:38.345Z", - "migrationHash": "62eb41b027ffc482395373ed4cec0cea2060e9332bd814fb8e63f5ac7cc7b29b" + "migrationHash": "eca5ce6d981ff0b55c6213e7f9ed947e73ba9dad012f372b9b02b024b2526309" } diff --git a/examples/prisma-8-demo/migrations/app/20260917T0818_add_post_expires_at/migration.ts b/examples/prisma-8-demo/migrations/app/20260917T0818_add_post_expires_at/migration.ts index 5851159d1e37..f7e4583551fd 100755 --- a/examples/prisma-8-demo/migrations/app/20260917T0818_add_post_expires_at/migration.ts +++ b/examples/prisma-8-demo/migrations/app/20260917T0818_add_post_expires_at/migration.ts @@ -1,11 +1,11 @@ #!/usr/bin/env -S node import { col, fn, Migration, MigrationCLI } from '@prisma/orm-postgres/migration'; -import type { Contract as End } from '../../snapshots/0ce16e4140d056a3abddbe18bd7ee5198d392370f0aeb23236216c51505944de/contract'; -import endContract from '../../snapshots/0ce16e4140d056a3abddbe18bd7ee5198d392370f0aeb23236216c51505944de/contract.json' with { +import type { Contract as End } from '../../snapshots/1c819419d4357de21d9aa9404a0b1d3639e3bd33c3fe6176207c53b51365fa1f/contract'; +import endContract from '../../snapshots/1c819419d4357de21d9aa9404a0b1d3639e3bd33c3fe6176207c53b51365fa1f/contract.json' with { type: 'json', }; -import type { Contract as Start } from '../../snapshots/d44ab6f7caddcc384332c669737008aec7ec8c9f5b013cb502a03c2eb04be9c0/contract'; -import startContract from '../../snapshots/d44ab6f7caddcc384332c669737008aec7ec8c9f5b013cb502a03c2eb04be9c0/contract.json' with { +import type { Contract as Start } from '../../snapshots/3c2324f6061012e9d50f869b3e6ae6aa41159acd559b1cc5459bc64e2ccb0f4e/contract'; +import startContract from '../../snapshots/3c2324f6061012e9d50f869b3e6ae6aa41159acd559b1cc5459bc64e2ccb0f4e/contract.json' with { type: 'json', }; diff --git a/examples/prisma-8-demo/migrations/app/20260922T1218_add_post_title_search/migration.json b/examples/prisma-8-demo/migrations/app/20260922T1218_add_post_title_search/migration.json index b0a6391a8cb6..b3d9e977bd4a 100644 --- a/examples/prisma-8-demo/migrations/app/20260922T1218_add_post_title_search/migration.json +++ b/examples/prisma-8-demo/migrations/app/20260922T1218_add_post_title_search/migration.json @@ -1,7 +1,7 @@ { - "from": "0ce16e4140d056a3abddbe18bd7ee5198d392370f0aeb23236216c51505944de", - "to": "6e6fbbc22d1d4904fbc8668644c5cabe694d946e550a2f0cdd07aaab308b953f", + "from": "1c819419d4357de21d9aa9404a0b1d3639e3bd33c3fe6176207c53b51365fa1f", + "to": "9301c8a3b2ec83c89b4eb12568ccb0c9e916ba199d8743ab38ed468483d6921f", "providedInvariants": [], "createdAt": "2026-09-22T12:18:55.150Z", - "migrationHash": "27be4eb0ec18aedaf49c8606d19688bb3a7c3ac0e775700b09f52e31a2ad472e" + "migrationHash": "ac4b947f7363460ecaea06bf886099b04082020c0c02d791fa6a872bb14534e8" } diff --git a/examples/prisma-8-demo/migrations/app/20260922T1218_add_post_title_search/migration.ts b/examples/prisma-8-demo/migrations/app/20260922T1218_add_post_title_search/migration.ts index 01851cad9f91..c1c68f9cbd85 100755 --- a/examples/prisma-8-demo/migrations/app/20260922T1218_add_post_title_search/migration.ts +++ b/examples/prisma-8-demo/migrations/app/20260922T1218_add_post_title_search/migration.ts @@ -1,11 +1,11 @@ #!/usr/bin/env -S node import { Migration, MigrationCLI } from '@prisma/orm-postgres/migration'; -import type { Contract as Start } from '../../snapshots/0ce16e4140d056a3abddbe18bd7ee5198d392370f0aeb23236216c51505944de/contract'; -import startContract from '../../snapshots/0ce16e4140d056a3abddbe18bd7ee5198d392370f0aeb23236216c51505944de/contract.json' with { +import type { Contract as Start } from '../../snapshots/1c819419d4357de21d9aa9404a0b1d3639e3bd33c3fe6176207c53b51365fa1f/contract'; +import startContract from '../../snapshots/1c819419d4357de21d9aa9404a0b1d3639e3bd33c3fe6176207c53b51365fa1f/contract.json' with { type: 'json', }; -import type { Contract as End } from '../../snapshots/6e6fbbc22d1d4904fbc8668644c5cabe694d946e550a2f0cdd07aaab308b953f/contract'; -import endContract from '../../snapshots/6e6fbbc22d1d4904fbc8668644c5cabe694d946e550a2f0cdd07aaab308b953f/contract.json' with { +import type { Contract as End } from '../../snapshots/9301c8a3b2ec83c89b4eb12568ccb0c9e916ba199d8743ab38ed468483d6921f/contract'; +import endContract from '../../snapshots/9301c8a3b2ec83c89b4eb12568ccb0c9e916ba199d8743ab38ed468483d6921f/contract.json' with { type: 'json', }; diff --git a/examples/prisma-8-demo/migrations/app/20261009T1354_add_post_body_search/migration.json b/examples/prisma-8-demo/migrations/app/20261009T1354_add_post_body_search/migration.json index 808b4a75c3a2..88bce7dc68e6 100644 --- a/examples/prisma-8-demo/migrations/app/20261009T1354_add_post_body_search/migration.json +++ b/examples/prisma-8-demo/migrations/app/20261009T1354_add_post_body_search/migration.json @@ -1,7 +1,7 @@ { - "from": "6e6fbbc22d1d4904fbc8668644c5cabe694d946e550a2f0cdd07aaab308b953f", - "to": "0ce1b6cf3e03ac1498f39eb8ef08d36d73ee2f0bc7fe368b97f1497ba050501a", + "from": "9301c8a3b2ec83c89b4eb12568ccb0c9e916ba199d8743ab38ed468483d6921f", + "to": "99a4393ee9f877db228baee1164325543fa018be780fd1253250d7d81539f38e", "providedInvariants": [], "createdAt": "2026-10-09T13:54:52.344Z", - "migrationHash": "6fd650eef48eddc461e8cbf14297934b97b479e46de2d392a27b6eb935a40c5e" + "migrationHash": "523dbd078828411759a63f37d5155bba4c75ebc38d158d985a43802bd5121d90" } diff --git a/examples/prisma-8-demo/migrations/app/20261009T1354_add_post_body_search/migration.ts b/examples/prisma-8-demo/migrations/app/20261009T1354_add_post_body_search/migration.ts index 127ea966b558..ab1c27daa02e 100755 --- a/examples/prisma-8-demo/migrations/app/20261009T1354_add_post_body_search/migration.ts +++ b/examples/prisma-8-demo/migrations/app/20261009T1354_add_post_body_search/migration.ts @@ -1,11 +1,11 @@ #!/usr/bin/env -S node import { col, Migration, MigrationCLI } from '@prisma/orm-postgres/migration'; -import type { Contract as End } from '../../snapshots/0ce1b6cf3e03ac1498f39eb8ef08d36d73ee2f0bc7fe368b97f1497ba050501a/contract'; -import endContract from '../../snapshots/0ce1b6cf3e03ac1498f39eb8ef08d36d73ee2f0bc7fe368b97f1497ba050501a/contract.json' with { +import type { Contract as End } from '../../snapshots/99a4393ee9f877db228baee1164325543fa018be780fd1253250d7d81539f38e/contract'; +import endContract from '../../snapshots/99a4393ee9f877db228baee1164325543fa018be780fd1253250d7d81539f38e/contract.json' with { type: 'json', }; -import type { Contract as Start } from '../../snapshots/6e6fbbc22d1d4904fbc8668644c5cabe694d946e550a2f0cdd07aaab308b953f/contract'; -import startContract from '../../snapshots/6e6fbbc22d1d4904fbc8668644c5cabe694d946e550a2f0cdd07aaab308b953f/contract.json' with { +import type { Contract as Start } from '../../snapshots/9301c8a3b2ec83c89b4eb12568ccb0c9e916ba199d8743ab38ed468483d6921f/contract'; +import startContract from '../../snapshots/9301c8a3b2ec83c89b4eb12568ccb0c9e916ba199d8743ab38ed468483d6921f/contract.json' with { type: 'json', }; diff --git a/examples/prisma-8-demo/migrations/snapshots/0ce16e4140d056a3abddbe18bd7ee5198d392370f0aeb23236216c51505944de/contract.json b/examples/prisma-8-demo/migrations/snapshots/0ce16e4140d056a3abddbe18bd7ee5198d392370f0aeb23236216c51505944de/contract.json deleted file mode 100644 index 1b0713a207e8..000000000000 --- a/examples/prisma-8-demo/migrations/snapshots/0ce16e4140d056a3abddbe18bd7ee5198d392370f0aeb23236216c51505944de/contract.json +++ /dev/null @@ -1 +0,0 @@ -{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{"postgres":{"distinctOn":true,"forKeyShare":true,"forNoKeyUpdate":true,"jsonAgg":true,"lateral":true,"limit":true,"orderBy":true,"pgvector.cosine":true,"returning":true},"sql":{"checkConstraint":true,"defaultInInsert":true,"enums":true,"forShare":true,"forUpdate":true,"insertOnConflictSkip":true,"insertOnConflictWithoutTarget":true,"lateral":true,"lockNowait":true,"lockOf":true,"lockSkipLocked":true,"returning":true,"scalarList":true}},"domain":{"namespaces":{"public":{"enum":{"Priority":{"codecId":"pg/int4@1","members":[{"name":"Low","value":0},{"name":"High","value":1},{"name":"Urgent","value":2}]},"user_type":{"codecId":"pg/text@1","members":[{"name":"admin","value":"admin"},{"name":"user","value":"user"}]}},"models":{"Bug":{"base":{"model":"Task","namespace":"public"},"fields":{"severity":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"stepsToRepro":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"severity":{"column":"severity"},"stepsToRepro":{"column":"stepsToRepro"}},"namespaceId":"public","table":"bug"}},"Feature":{"base":{"model":"Task","namespace":"public"},"fields":{"priority":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"targetRelease":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"priority":{"column":"priority"},"targetRelease":{"column":"targetRelease"}},"namespaceId":"public","table":"feature"}},"Post":{"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"embedding":{"nullable":true,"type":{"codecId":"pg/vector@1","kind":"scalar","typeParams":{"length":1536}}},"expiresAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"impressionCount":{"nullable":true,"type":{"codecId":"pg/int8@1","kind":"scalar"}},"priority":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"Priority","namespaceId":"public","plane":"domain"}},"reachScore":{"nullable":true,"type":{"codecId":"pg/unboundedint@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"viewCount":{"nullable":true,"type":{"codecId":"pg/int8number@1","kind":"scalar"}}},"relations":{"tags":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["postId"]},"through":{"childColumns":["tagId"],"namespaceId":"public","parentColumns":["postId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Tag","namespace":"public"}},"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"embedding":{"column":"embedding"},"expiresAt":{"column":"expiresAt"},"id":{"column":"id"},"impressionCount":{"column":"impressionCount"},"priority":{"column":"priority"},"reachScore":{"column":"reachScore"},"title":{"column":"title"},"userId":{"column":"userId"},"viewCount":{"column":"viewCount"}},"namespaceId":"public","table":"post"}},"PostTag":{"fields":{"postId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"tagId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"post":{"cardinality":"N:1","nullable":false,"on":{"localFields":["postId"],"targetFields":["id"]},"to":{"model":"Post","namespace":"public"}},"tag":{"cardinality":"N:1","nullable":false,"on":{"localFields":["tagId"],"targetFields":["id"]},"to":{"model":"Tag","namespace":"public"}}},"storage":{"fields":{"postId":{"column":"postId"},"tagId":{"column":"tagId"}},"namespaceId":"public","table":"post_tag"}},"Tag":{"fields":{"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"label":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{"posts":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["tagId"]},"through":{"childColumns":["postId"],"namespaceId":"public","parentColumns":["tagId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Post","namespace":"public"}}},"storage":{"fields":{"id":{"column":"id"},"label":{"column":"label"}},"namespaceId":"public","table":"tag"}},"Task":{"discriminator":{"field":"type"},"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"description":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"status":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"type":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"description":{"column":"description"},"id":{"column":"id"},"status":{"column":"status"},"title":{"column":"title"},"type":{"column":"type"},"userId":{"column":"userId"}},"namespaceId":"public","table":"task"},"variants":{"bug":{"model":"Bug","namespace":"public"},"feature":{"model":"Feature","namespace":"public"}}},"User":{"fields":{"address":{"nullable":true,"type":{"kind":"valueObject","name":"Address"}},"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"displayName":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"user_type","namespaceId":"public","plane":"domain"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Post","namespace":"public"}},"tasks":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Task","namespace":"public"}}},"storage":{"fields":{"address":{"column":"address"},"createdAt":{"column":"createdAt"},"displayName":{"column":"displayName"},"email":{"column":"email"},"id":{"column":"id"},"kind":{"column":"kind"}},"namespaceId":"public","table":"user"}}},"valueObjects":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}}}}}}},"execution":{"executionHash":"eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655","mutations":{"defaults":[{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"post","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"tag","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"task","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"user","field":"id","namespace":"public"}}]}},"extensions":{"demo/engagement-stats":{"familyId":"sql","id":"demo/engagement-stats","kind":"extension","targetId":"postgres","types":{"aggregateDescriptors":[{"input":{"kind":"trait","trait":"numeric"},"nullable":true,"operation":"stddev","output":{"codecId":"pg/numeric@1","kind":"codec"}}]},"version":"0.0.1"},"pgvector":{"capabilities":{"postgres":{"pgvector.cosine":true}},"familyId":"sql","id":"pgvector","kind":"extension","targetId":"postgres","types":{"codecTypes":{"import":{"alias":"PgVectorTypes","named":"CodecTypes","package":"@internal/extension-pgvector/codec-types"},"parameterizedOutputTypes":{"alias":"PgVectorParameterizedOutputTypes","named":"ParameterizedOutputTypesForTypeParams","package":"@internal/extension-pgvector/codec-types"}},"operationTypes":{"import":{"alias":"PgVectorOperationTypes","named":"OperationTypes","package":"@internal/extension-pgvector/operation-types"}},"queryOperationTypes":{"import":{"alias":"PgVectorQueryOperationTypes","named":"QueryOperationTypes","package":"@internal/extension-pgvector/operation-types"}}},"version":"0.0.1"}},"meta":{},"profileHash":"3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2","roots":{"post":{"model":"Post","namespace":"public"},"post_tag":{"model":"PostTag","namespace":"public"},"tag":{"model":"Tag","namespace":"public"},"task":{"model":"Task","namespace":"public"},"user":{"model":"User","namespace":"public"}},"schemaVersion":"1","storage":{"namespaces":{"public":{"entries":{"table":{"bug":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"severity":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"stepsToRepro":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"bug"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"feature":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"priority":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"targetRelease":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"feature"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"post":{"checks":[{"expression":"\"priority\" IN (0, 1, 2)","name":"post_priority_check_b236ace9","prefix":"post_priority_check"}],"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"embedding":{"codecId":"pg/vector@1","dataType":"pgvector/vector","nullable":true,"typeParams":{"length":1536}},"expiresAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"(now() + '7 days'::interval)","kind":"function"},"nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"impressionCount":{"codecId":"pg/int8@1","dataType":"pg/int8","nullable":true},"priority":{"codecId":"pg/int4@1","dataType":"pg/int4","default":{"kind":"literal","value":0},"nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Priority","namespaceId":"public","plane":"storage"}},"reachScore":{"codecId":"pg/unboundedint@1","dataType":"pg/numeric","nullable":true},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"viewCount":{"codecId":"pg/int8number@1","dataType":"pg/int8","nullable":true}},"foreignKeys":[{"index":{"name":"post_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"post"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"post_userId_idx_a489d58a","prefix":"post_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"post_tag":{"columns":{"postId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"tagId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"post_tag_postId_idx_a7a72715"},"source":{"columns":["postId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"post"}},{"index":{"name":"post_tag_tagId_idx_86854244"},"source":{"columns":["tagId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"tag"}}],"indexes":[{"columns":["postId"],"name":"post_tag_postId_idx_a7a72715","prefix":"post_tag_postId_idx","unique":false},{"columns":["tagId"],"name":"post_tag_tagId_idx_86854244","prefix":"post_tag_tagId_idx","unique":false}],"primaryKey":{"columns":["postId","tagId"]},"uniques":[]},"tag":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"label":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[{"columns":["label"]}]},"task":{"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"description":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"status":{"codecId":"pg/text@1","dataType":"pg/text","default":{"kind":"literal","value":"open"},"nullable":false},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"type":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"task_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"task"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"task_userId_idx_a489d58a","prefix":"task_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"user":{"checks":[{"expression":"\"kind\" IN ('admin', 'user')","name":"user_kind_check_836d43ef","prefix":"user_kind_check"}],"columns":{"address":{"codecId":"pg/jsonb@1","dataType":"pg/jsonb","nullable":true},"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"displayName":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"email":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"kind":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"user_type","namespaceId":"public","plane":"storage"}}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]}},"valueSet":{"Priority":{"kind":"valueSet","values":[0,1,2]},"user_type":{"kind":"valueSet","values":["admin","user"]}}},"id":"public","kind":"postgres-schema"}},"storageHash":"0ce16e4140d056a3abddbe18bd7ee5198d392370f0aeb23236216c51505944de"},"target":"postgres","targetFamily":"sql"} diff --git a/examples/prisma-8-demo/migrations/snapshots/0ce1b6cf3e03ac1498f39eb8ef08d36d73ee2f0bc7fe368b97f1497ba050501a/contract.json b/examples/prisma-8-demo/migrations/snapshots/0ce1b6cf3e03ac1498f39eb8ef08d36d73ee2f0bc7fe368b97f1497ba050501a/contract.json deleted file mode 100644 index 3d7625e612b5..000000000000 --- a/examples/prisma-8-demo/migrations/snapshots/0ce1b6cf3e03ac1498f39eb8ef08d36d73ee2f0bc7fe368b97f1497ba050501a/contract.json +++ /dev/null @@ -1 +0,0 @@ -{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{"postgres":{"distinctOn":true,"forKeyShare":true,"forNoKeyUpdate":true,"jsonAgg":true,"lateral":true,"limit":true,"orderBy":true,"pgvector.cosine":true,"returning":true},"sql":{"checkConstraint":true,"defaultInInsert":true,"enums":true,"forShare":true,"forUpdate":true,"insertOnConflictSkip":true,"insertOnConflictWithoutTarget":true,"lateral":true,"lockNowait":true,"lockOf":true,"lockSkipLocked":true,"returning":true,"scalarList":true}},"domain":{"namespaces":{"public":{"enum":{"Priority":{"codecId":"pg/int4@1","members":[{"name":"Low","value":0},{"name":"High","value":1},{"name":"Urgent","value":2}]},"user_type":{"codecId":"pg/text@1","members":[{"name":"admin","value":"admin"},{"name":"user","value":"user"}]}},"models":{"Bug":{"base":{"model":"Task","namespace":"public"},"fields":{"severity":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"stepsToRepro":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"severity":{"column":"severity"},"stepsToRepro":{"column":"stepsToRepro"}},"namespaceId":"public","table":"bug"}},"Feature":{"base":{"model":"Task","namespace":"public"},"fields":{"priority":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"targetRelease":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"priority":{"column":"priority"},"targetRelease":{"column":"targetRelease"}},"namespaceId":"public","table":"feature"}},"Post":{"fields":{"body":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"embedding":{"nullable":true,"type":{"codecId":"pg/vector@1","kind":"scalar","typeParams":{"length":1536}}},"expiresAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"impressionCount":{"nullable":true,"type":{"codecId":"pg/int8@1","kind":"scalar"}},"priority":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"Priority","namespaceId":"public","plane":"domain"}},"reachScore":{"nullable":true,"type":{"codecId":"pg/unboundedint@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"viewCount":{"nullable":true,"type":{"codecId":"pg/int8number@1","kind":"scalar"}}},"relations":{"tags":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["postId"]},"through":{"childColumns":["tagId"],"namespaceId":"public","parentColumns":["postId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Tag","namespace":"public"}},"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"body":{"column":"body"},"createdAt":{"column":"createdAt"},"embedding":{"column":"embedding"},"expiresAt":{"column":"expiresAt"},"id":{"column":"id"},"impressionCount":{"column":"impressionCount"},"priority":{"column":"priority"},"reachScore":{"column":"reachScore"},"title":{"column":"title"},"userId":{"column":"userId"},"viewCount":{"column":"viewCount"}},"namespaceId":"public","table":"post"}},"PostTag":{"fields":{"postId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"tagId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"post":{"cardinality":"N:1","nullable":false,"on":{"localFields":["postId"],"targetFields":["id"]},"to":{"model":"Post","namespace":"public"}},"tag":{"cardinality":"N:1","nullable":false,"on":{"localFields":["tagId"],"targetFields":["id"]},"to":{"model":"Tag","namespace":"public"}}},"storage":{"fields":{"postId":{"column":"postId"},"tagId":{"column":"tagId"}},"namespaceId":"public","table":"post_tag"}},"Tag":{"fields":{"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"label":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{"posts":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["tagId"]},"through":{"childColumns":["postId"],"namespaceId":"public","parentColumns":["tagId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Post","namespace":"public"}}},"storage":{"fields":{"id":{"column":"id"},"label":{"column":"label"}},"namespaceId":"public","table":"tag"}},"Task":{"discriminator":{"field":"type"},"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"description":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"status":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"type":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"description":{"column":"description"},"id":{"column":"id"},"status":{"column":"status"},"title":{"column":"title"},"type":{"column":"type"},"userId":{"column":"userId"}},"namespaceId":"public","table":"task"},"variants":{"bug":{"model":"Bug","namespace":"public"},"feature":{"model":"Feature","namespace":"public"}}},"User":{"fields":{"address":{"nullable":true,"type":{"kind":"valueObject","name":"Address"}},"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"displayName":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"user_type","namespaceId":"public","plane":"domain"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Post","namespace":"public"}},"tasks":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Task","namespace":"public"}}},"storage":{"fields":{"address":{"column":"address"},"createdAt":{"column":"createdAt"},"displayName":{"column":"displayName"},"email":{"column":"email"},"id":{"column":"id"},"kind":{"column":"kind"}},"namespaceId":"public","table":"user"}}},"valueObjects":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}}}}}}},"execution":{"executionHash":"eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655","mutations":{"defaults":[{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"post","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"tag","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"task","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"user","field":"id","namespace":"public"}}]}},"extensions":{"demo/engagement-stats":{"familyId":"sql","id":"demo/engagement-stats","kind":"extension","targetId":"postgres","types":{"aggregateDescriptors":[{"input":{"kind":"trait","trait":"numeric"},"nullable":true,"operation":"stddev","output":{"codecId":"pg/numeric@1","kind":"codec"}}]},"version":"0.0.1"},"pgvector":{"capabilities":{"postgres":{"pgvector.cosine":true}},"familyId":"sql","id":"pgvector","kind":"extension","targetId":"postgres","types":{"codecTypes":{"import":{"alias":"PgVectorTypes","named":"CodecTypes","package":"@internal/extension-pgvector/codec-types"},"parameterizedOutputTypes":{"alias":"PgVectorParameterizedOutputTypes","named":"ParameterizedOutputTypesForTypeParams","package":"@internal/extension-pgvector/codec-types"}},"operationTypes":{"import":{"alias":"PgVectorOperationTypes","named":"OperationTypes","package":"@internal/extension-pgvector/operation-types"}},"queryOperationTypes":{"import":{"alias":"PgVectorQueryOperationTypes","named":"QueryOperationTypes","package":"@internal/extension-pgvector/operation-types"}}},"version":"0.0.1"}},"meta":{},"profileHash":"3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2","roots":{"post":{"model":"Post","namespace":"public"},"post_tag":{"model":"PostTag","namespace":"public"},"tag":{"model":"Tag","namespace":"public"},"task":{"model":"Task","namespace":"public"},"user":{"model":"User","namespace":"public"}},"schemaVersion":"1","storage":{"namespaces":{"public":{"entries":{"table":{"bug":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"severity":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"stepsToRepro":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"bug"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"feature":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"priority":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"targetRelease":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"feature"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"post":{"checks":[{"expression":"\"priority\" IN (0, 1, 2)","name":"post_priority_check_b236ace9","prefix":"post_priority_check"}],"columns":{"body":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true},"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"embedding":{"codecId":"pg/vector@1","dataType":"pgvector/vector","nullable":true,"typeParams":{"length":1536}},"expiresAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"(now() + '7 days'::interval)","kind":"function"},"nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"impressionCount":{"codecId":"pg/int8@1","dataType":"pg/int8","nullable":true},"priority":{"codecId":"pg/int4@1","dataType":"pg/int4","default":{"kind":"literal","value":0},"nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Priority","namespaceId":"public","plane":"storage"}},"reachScore":{"codecId":"pg/unboundedint@1","dataType":"pg/numeric","nullable":true},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"viewCount":{"codecId":"pg/int8number@1","dataType":"pg/int8","nullable":true}},"foreignKeys":[{"index":{"name":"post_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"post"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["title","body"],"name":"post_search_f03270be","options":{"language":"english","weightGroups":[["title"],["body"]]},"prefix":"post_search","type":"fullText","unique":false},{"columns":["title"],"name":"post_title_search_e0dd1131","options":{"language":"english","weightGroups":[["title"]]},"prefix":"post_title_search","type":"fullText","unique":false},{"columns":["userId"],"name":"post_userId_idx_a489d58a","prefix":"post_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"post_tag":{"columns":{"postId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"tagId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"post_tag_postId_idx_a7a72715"},"source":{"columns":["postId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"post"}},{"index":{"name":"post_tag_tagId_idx_86854244"},"source":{"columns":["tagId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"tag"}}],"indexes":[{"columns":["postId"],"name":"post_tag_postId_idx_a7a72715","prefix":"post_tag_postId_idx","unique":false},{"columns":["tagId"],"name":"post_tag_tagId_idx_86854244","prefix":"post_tag_tagId_idx","unique":false}],"primaryKey":{"columns":["postId","tagId"]},"uniques":[]},"tag":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"label":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[{"columns":["label"]}]},"task":{"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"description":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"status":{"codecId":"pg/text@1","dataType":"pg/text","default":{"kind":"literal","value":"open"},"nullable":false},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"type":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"task_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"task"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"task_userId_idx_a489d58a","prefix":"task_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"user":{"checks":[{"expression":"\"kind\" IN ('admin', 'user')","name":"user_kind_check_836d43ef","prefix":"user_kind_check"}],"columns":{"address":{"codecId":"pg/jsonb@1","dataType":"pg/jsonb","nullable":true},"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"displayName":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"email":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"kind":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"user_type","namespaceId":"public","plane":"storage"}}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]}},"valueSet":{"Priority":{"kind":"valueSet","values":[0,1,2]},"user_type":{"kind":"valueSet","values":["admin","user"]}}},"id":"public","kind":"postgres-schema"}},"storageHash":"0ce1b6cf3e03ac1498f39eb8ef08d36d73ee2f0bc7fe368b97f1497ba050501a"},"target":"postgres","targetFamily":"sql"} diff --git a/examples/prisma-8-demo/migrations/snapshots/58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b/contract.d.ts b/examples/prisma-8-demo/migrations/snapshots/11d8695d1b8ea9c748834093ac4111ce68145827709363b7c748c2892be84146/contract.d.ts similarity index 96% rename from examples/prisma-8-demo/migrations/snapshots/58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b/contract.d.ts rename to examples/prisma-8-demo/migrations/snapshots/11d8695d1b8ea9c748834093ac4111ce68145827709363b7c748c2892be84146/contract.d.ts index 64fd0c6ba064..c8ff8e83bab8 100644 --- a/examples/prisma-8-demo/migrations/snapshots/58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b/contract.d.ts +++ b/examples/prisma-8-demo/migrations/snapshots/11d8695d1b8ea9c748834093ac4111ce68145827709363b7c748c2892be84146/contract.d.ts @@ -25,11 +25,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@prisma/orm-postgres/contract/types'; export type StorageHash = - StorageHashBase<'58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b'>; + StorageHashBase<'11d8695d1b8ea9c748834093ac4111ce68145827709363b7c748c2892be84146'>; export type ExecutionHash = ExecutionHashBase<'eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655'>; export type ProfileHash = @@ -261,15 +261,15 @@ type DefaultLiteralValue = CodecId extends keyo : Encoded; type ParameterizedOutputTypeFunctions = PgParameterizedOutputTypes | PgVectorParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -283,6 +283,9 @@ type FieldTypeOverrides = { readonly Post: { readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 }; }; + readonly Task: { + readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; + }; readonly User: { readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; }; @@ -294,18 +297,23 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = { readonly public: { readonly post: { readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 } }; + readonly task: { + readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; + }; readonly user: { readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; }; @@ -782,11 +790,37 @@ type ContractBase = Omit< }; readonly valueSet: { readonly Priority: { readonly kind: 'valueSet'; readonly values: readonly [0, 1, 2] }; + readonly 'Task.type': { + readonly kind: 'valueSet'; + readonly values: readonly ['bug', 'feature']; + }; readonly user_type: { readonly kind: 'valueSet'; readonly values: readonly ['admin', 'user']; }; }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + }; + }; }; }; }; @@ -1072,7 +1106,13 @@ type ContractBase = Omit< readonly fields: { readonly address: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: 'public'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; }; readonly createdAt: { readonly nullable: false; @@ -1130,28 +1170,6 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - }; - }; readonly enum: { readonly Priority: { readonly codecId: 'pg/int4@1'; @@ -1307,28 +1325,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/examples/prisma-8-demo/migrations/snapshots/11d8695d1b8ea9c748834093ac4111ce68145827709363b7c748c2892be84146/contract.json b/examples/prisma-8-demo/migrations/snapshots/11d8695d1b8ea9c748834093ac4111ce68145827709363b7c748c2892be84146/contract.json new file mode 100644 index 000000000000..d1a142478ccd --- /dev/null +++ b/examples/prisma-8-demo/migrations/snapshots/11d8695d1b8ea9c748834093ac4111ce68145827709363b7c748c2892be84146/contract.json @@ -0,0 +1 @@ +{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{"postgres":{"distinctOn":true,"forKeyShare":true,"forNoKeyUpdate":true,"jsonAgg":true,"lateral":true,"limit":true,"orderBy":true,"pgvector.cosine":true,"returning":true},"sql":{"checkConstraint":true,"defaultInInsert":true,"enums":true,"forShare":true,"forUpdate":true,"insertOnConflictSkip":true,"insertOnConflictWithoutTarget":true,"lateral":true,"lockNowait":true,"lockOf":true,"lockSkipLocked":true,"returning":true,"scalarList":true}},"domain":{"namespaces":{"public":{"enum":{"Priority":{"codecId":"pg/int4@1","members":[{"name":"Low","value":0},{"name":"High","value":1},{"name":"Urgent","value":2}]},"user_type":{"codecId":"pg/text@1","members":[{"name":"admin","value":"admin"},{"name":"user","value":"user"}]}},"models":{"Bug":{"base":{"model":"Task","namespace":"public"},"fields":{"severity":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"stepsToRepro":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"severity":{"column":"severity"},"stepsToRepro":{"column":"stepsToRepro"}},"namespaceId":"public","table":"bug"}},"Feature":{"base":{"model":"Task","namespace":"public"},"fields":{"priority":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"targetRelease":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"priority":{"column":"priority"},"targetRelease":{"column":"targetRelease"}},"namespaceId":"public","table":"feature"}},"Post":{"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"embedding":{"nullable":true,"type":{"codecId":"pg/vector@1","kind":"scalar","typeParams":{"length":1536}}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"priority":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"Priority","namespaceId":"public","plane":"domain"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"tags":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["postId"]},"through":{"childColumns":["tagId"],"namespaceId":"public","parentColumns":["postId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Tag","namespace":"public"}},"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"embedding":{"column":"embedding"},"id":{"column":"id"},"priority":{"column":"priority"},"title":{"column":"title"},"userId":{"column":"userId"}},"namespaceId":"public","table":"post"}},"PostTag":{"fields":{"postId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"tagId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"post":{"cardinality":"N:1","nullable":false,"on":{"localFields":["postId"],"targetFields":["id"]},"to":{"model":"Post","namespace":"public"}},"tag":{"cardinality":"N:1","nullable":false,"on":{"localFields":["tagId"],"targetFields":["id"]},"to":{"model":"Tag","namespace":"public"}}},"storage":{"fields":{"postId":{"column":"postId"},"tagId":{"column":"tagId"}},"namespaceId":"public","table":"post_tag"}},"Tag":{"fields":{"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"label":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{"posts":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["tagId"]},"through":{"childColumns":["postId"],"namespaceId":"public","parentColumns":["tagId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Post","namespace":"public"}}},"storage":{"fields":{"id":{"column":"id"},"label":{"column":"label"}},"namespaceId":"public","table":"tag"}},"Task":{"discriminator":{"field":"type"},"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"description":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"status":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"type":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"description":{"column":"description"},"id":{"column":"id"},"status":{"column":"status"},"title":{"column":"title"},"type":{"column":"type"},"userId":{"column":"userId"}},"namespaceId":"public","table":"task"},"variants":{"bug":{"model":"Bug","namespace":"public"},"feature":{"model":"Feature","namespace":"public"}}},"User":{"fields":{"address":{"nullable":true,"type":{"entityKind":"valueObject","entityName":"Address","kind":"valueObject","namespaceId":"public","plane":"storage"}},"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"displayName":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"user_type","namespaceId":"public","plane":"domain"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Post","namespace":"public"}},"tasks":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Task","namespace":"public"}}},"storage":{"fields":{"address":{"column":"address"},"createdAt":{"column":"createdAt"},"displayName":{"column":"displayName"},"email":{"column":"email"},"id":{"column":"id"},"kind":{"column":"kind"}},"namespaceId":"public","table":"user"}}}}}},"execution":{"executionHash":"eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655","mutations":{"defaults":[{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"post","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"tag","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"task","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"user","field":"id","namespace":"public"}}]}},"extensions":{"demo/engagement-stats":{"familyId":"sql","id":"demo/engagement-stats","kind":"extension","targetId":"postgres","types":{"aggregateDescriptors":[{"input":{"kind":"trait","trait":"numeric"},"nullable":true,"operation":"stddev","output":{"codecId":"pg/numeric@1","kind":"codec"}}]},"version":"0.0.1"},"pgvector":{"capabilities":{"postgres":{"pgvector.cosine":true}},"familyId":"sql","id":"pgvector","kind":"extension","targetId":"postgres","types":{"codecTypes":{"import":{"alias":"PgVectorTypes","named":"CodecTypes","package":"@internal/extension-pgvector/codec-types"},"parameterizedOutputTypes":{"alias":"PgVectorParameterizedOutputTypes","named":"ParameterizedOutputTypesForTypeParams","package":"@internal/extension-pgvector/codec-types"}},"operationTypes":{"import":{"alias":"PgVectorOperationTypes","named":"OperationTypes","package":"@internal/extension-pgvector/operation-types"}},"queryOperationTypes":{"import":{"alias":"PgVectorQueryOperationTypes","named":"QueryOperationTypes","package":"@internal/extension-pgvector/operation-types"}}},"version":"0.0.1"}},"meta":{},"profileHash":"3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2","roots":{"post":{"model":"Post","namespace":"public"},"post_tag":{"model":"PostTag","namespace":"public"},"tag":{"model":"Tag","namespace":"public"},"task":{"model":"Task","namespace":"public"},"user":{"model":"User","namespace":"public"}},"schemaVersion":"1","storage":{"namespaces":{"public":{"entries":{"table":{"bug":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"severity":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"stepsToRepro":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"bug"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"feature":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"priority":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"targetRelease":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"feature"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"post":{"checks":[{"expression":"\"priority\" IN (0, 1, 2)","name":"post_priority_check_b236ace9","prefix":"post_priority_check"}],"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"embedding":{"codecId":"pg/vector@1","dataType":"pgvector/vector","nullable":true,"typeParams":{"length":1536}},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"priority":{"codecId":"pg/int4@1","dataType":"pg/int4","default":{"kind":"literal","value":0},"nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Priority","namespaceId":"public","plane":"storage"}},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"post_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"post"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"post_userId_idx_a489d58a","prefix":"post_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"post_tag":{"columns":{"postId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"tagId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"post_tag_postId_idx_a7a72715"},"source":{"columns":["postId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"post"}},{"index":{"name":"post_tag_tagId_idx_86854244"},"source":{"columns":["tagId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"tag"}}],"indexes":[{"columns":["postId"],"name":"post_tag_postId_idx_a7a72715","prefix":"post_tag_postId_idx","unique":false},{"columns":["tagId"],"name":"post_tag_tagId_idx_86854244","prefix":"post_tag_tagId_idx","unique":false}],"primaryKey":{"columns":["postId","tagId"]},"uniques":[]},"tag":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"label":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[{"columns":["label"]}]},"task":{"checks":[{"expression":"\"type\" IN ('bug', 'feature')","name":"task_type_check_cda3f5e7","prefix":"task_type_check"}],"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"description":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"status":{"codecId":"pg/text@1","dataType":"pg/text","default":{"kind":"literal","value":"open"},"nullable":false},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"type":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Task.type","namespaceId":"public","plane":"storage"}},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"task_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"task"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"task_userId_idx_a489d58a","prefix":"task_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"user":{"checks":[{"expression":"\"address\" IS NULL OR jsonb_path_match(\"address\", 'strict $.type() == \"null\" || ($.type() == \"object\" && exists($.keyvalue() ? (@.key == \"city\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"country\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"street\" && (@.value.type() == \"string\"))) && !exists($.keyvalue() ? (@.key == \"zip\" && !(@.value.type() == \"null\" || (@.value.type() == \"string\")))) && !exists($.keyvalue() ? (@.key != \"city\" && @.key != \"country\" && @.key != \"street\" && @.key != \"zip\")))') IS TRUE","name":"user_address_value_object_69432dc1","prefix":"user_address_value_object"},{"expression":"\"kind\" IN ('admin', 'user')","name":"user_kind_check_836d43ef","prefix":"user_kind_check"}],"columns":{"address":{"codecId":"pg/jsonb@1","dataType":"pg/jsonb","nullable":true},"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"displayName":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"email":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"kind":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"user_type","namespaceId":"public","plane":"storage"}}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]}},"valueObject":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}}}},"valueSet":{"Priority":{"kind":"valueSet","values":[0,1,2]},"Task.type":{"kind":"valueSet","values":["bug","feature"]},"user_type":{"kind":"valueSet","values":["admin","user"]}}},"id":"public","kind":"postgres-schema"}},"storageHash":"11d8695d1b8ea9c748834093ac4111ce68145827709363b7c748c2892be84146"},"target":"postgres","targetFamily":"sql"} diff --git a/examples/prisma-8-demo/migrations/snapshots/0ce16e4140d056a3abddbe18bd7ee5198d392370f0aeb23236216c51505944de/contract.d.ts b/examples/prisma-8-demo/migrations/snapshots/1c819419d4357de21d9aa9404a0b1d3639e3bd33c3fe6176207c53b51365fa1f/contract.d.ts similarity index 96% rename from examples/prisma-8-demo/migrations/snapshots/0ce16e4140d056a3abddbe18bd7ee5198d392370f0aeb23236216c51505944de/contract.d.ts rename to examples/prisma-8-demo/migrations/snapshots/1c819419d4357de21d9aa9404a0b1d3639e3bd33c3fe6176207c53b51365fa1f/contract.d.ts index 909cb96ba6bb..b77938aac132 100644 --- a/examples/prisma-8-demo/migrations/snapshots/0ce16e4140d056a3abddbe18bd7ee5198d392370f0aeb23236216c51505944de/contract.d.ts +++ b/examples/prisma-8-demo/migrations/snapshots/1c819419d4357de21d9aa9404a0b1d3639e3bd33c3fe6176207c53b51365fa1f/contract.d.ts @@ -25,11 +25,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@prisma/orm-postgres/contract/types'; export type StorageHash = - StorageHashBase<'0ce16e4140d056a3abddbe18bd7ee5198d392370f0aeb23236216c51505944de'>; + StorageHashBase<'1c819419d4357de21d9aa9404a0b1d3639e3bd33c3fe6176207c53b51365fa1f'>; export type ExecutionHash = ExecutionHashBase<'eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655'>; export type ProfileHash = @@ -261,15 +261,15 @@ type DefaultLiteralValue = CodecId extends keyo : Encoded; type ParameterizedOutputTypeFunctions = PgParameterizedOutputTypes | PgVectorParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -283,6 +283,9 @@ type FieldTypeOverrides = { readonly Post: { readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 }; }; + readonly Task: { + readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; + }; readonly User: { readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; }; @@ -294,18 +297,23 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = { readonly public: { readonly post: { readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 } }; + readonly task: { + readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; + }; readonly user: { readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; }; @@ -814,11 +822,37 @@ type ContractBase = Omit< }; readonly valueSet: { readonly Priority: { readonly kind: 'valueSet'; readonly values: readonly [0, 1, 2] }; + readonly 'Task.type': { + readonly kind: 'valueSet'; + readonly values: readonly ['bug', 'feature']; + }; readonly user_type: { readonly kind: 'valueSet'; readonly values: readonly ['admin', 'user']; }; }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + }; + }; }; }; }; @@ -1127,7 +1161,13 @@ type ContractBase = Omit< readonly fields: { readonly address: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: 'public'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; }; readonly createdAt: { readonly nullable: false; @@ -1185,28 +1225,6 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - }; - }; readonly enum: { readonly Priority: { readonly codecId: 'pg/int4@1'; @@ -1362,28 +1380,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/examples/prisma-8-demo/migrations/snapshots/1c819419d4357de21d9aa9404a0b1d3639e3bd33c3fe6176207c53b51365fa1f/contract.json b/examples/prisma-8-demo/migrations/snapshots/1c819419d4357de21d9aa9404a0b1d3639e3bd33c3fe6176207c53b51365fa1f/contract.json new file mode 100644 index 000000000000..2ec8794c0430 --- /dev/null +++ b/examples/prisma-8-demo/migrations/snapshots/1c819419d4357de21d9aa9404a0b1d3639e3bd33c3fe6176207c53b51365fa1f/contract.json @@ -0,0 +1 @@ +{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{"postgres":{"distinctOn":true,"forKeyShare":true,"forNoKeyUpdate":true,"jsonAgg":true,"lateral":true,"limit":true,"orderBy":true,"pgvector.cosine":true,"returning":true},"sql":{"checkConstraint":true,"defaultInInsert":true,"enums":true,"forShare":true,"forUpdate":true,"insertOnConflictSkip":true,"insertOnConflictWithoutTarget":true,"lateral":true,"lockNowait":true,"lockOf":true,"lockSkipLocked":true,"returning":true,"scalarList":true}},"domain":{"namespaces":{"public":{"enum":{"Priority":{"codecId":"pg/int4@1","members":[{"name":"Low","value":0},{"name":"High","value":1},{"name":"Urgent","value":2}]},"user_type":{"codecId":"pg/text@1","members":[{"name":"admin","value":"admin"},{"name":"user","value":"user"}]}},"models":{"Bug":{"base":{"model":"Task","namespace":"public"},"fields":{"severity":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"stepsToRepro":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"severity":{"column":"severity"},"stepsToRepro":{"column":"stepsToRepro"}},"namespaceId":"public","table":"bug"}},"Feature":{"base":{"model":"Task","namespace":"public"},"fields":{"priority":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"targetRelease":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"priority":{"column":"priority"},"targetRelease":{"column":"targetRelease"}},"namespaceId":"public","table":"feature"}},"Post":{"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"embedding":{"nullable":true,"type":{"codecId":"pg/vector@1","kind":"scalar","typeParams":{"length":1536}}},"expiresAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"impressionCount":{"nullable":true,"type":{"codecId":"pg/int8@1","kind":"scalar"}},"priority":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"Priority","namespaceId":"public","plane":"domain"}},"reachScore":{"nullable":true,"type":{"codecId":"pg/unboundedint@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"viewCount":{"nullable":true,"type":{"codecId":"pg/int8number@1","kind":"scalar"}}},"relations":{"tags":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["postId"]},"through":{"childColumns":["tagId"],"namespaceId":"public","parentColumns":["postId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Tag","namespace":"public"}},"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"embedding":{"column":"embedding"},"expiresAt":{"column":"expiresAt"},"id":{"column":"id"},"impressionCount":{"column":"impressionCount"},"priority":{"column":"priority"},"reachScore":{"column":"reachScore"},"title":{"column":"title"},"userId":{"column":"userId"},"viewCount":{"column":"viewCount"}},"namespaceId":"public","table":"post"}},"PostTag":{"fields":{"postId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"tagId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"post":{"cardinality":"N:1","nullable":false,"on":{"localFields":["postId"],"targetFields":["id"]},"to":{"model":"Post","namespace":"public"}},"tag":{"cardinality":"N:1","nullable":false,"on":{"localFields":["tagId"],"targetFields":["id"]},"to":{"model":"Tag","namespace":"public"}}},"storage":{"fields":{"postId":{"column":"postId"},"tagId":{"column":"tagId"}},"namespaceId":"public","table":"post_tag"}},"Tag":{"fields":{"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"label":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{"posts":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["tagId"]},"through":{"childColumns":["postId"],"namespaceId":"public","parentColumns":["tagId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Post","namespace":"public"}}},"storage":{"fields":{"id":{"column":"id"},"label":{"column":"label"}},"namespaceId":"public","table":"tag"}},"Task":{"discriminator":{"field":"type"},"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"description":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"status":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"type":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"description":{"column":"description"},"id":{"column":"id"},"status":{"column":"status"},"title":{"column":"title"},"type":{"column":"type"},"userId":{"column":"userId"}},"namespaceId":"public","table":"task"},"variants":{"bug":{"model":"Bug","namespace":"public"},"feature":{"model":"Feature","namespace":"public"}}},"User":{"fields":{"address":{"nullable":true,"type":{"entityKind":"valueObject","entityName":"Address","kind":"valueObject","namespaceId":"public","plane":"storage"}},"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"displayName":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"user_type","namespaceId":"public","plane":"domain"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Post","namespace":"public"}},"tasks":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Task","namespace":"public"}}},"storage":{"fields":{"address":{"column":"address"},"createdAt":{"column":"createdAt"},"displayName":{"column":"displayName"},"email":{"column":"email"},"id":{"column":"id"},"kind":{"column":"kind"}},"namespaceId":"public","table":"user"}}}}}},"execution":{"executionHash":"eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655","mutations":{"defaults":[{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"post","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"tag","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"task","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"user","field":"id","namespace":"public"}}]}},"extensions":{"demo/engagement-stats":{"familyId":"sql","id":"demo/engagement-stats","kind":"extension","targetId":"postgres","types":{"aggregateDescriptors":[{"input":{"kind":"trait","trait":"numeric"},"nullable":true,"operation":"stddev","output":{"codecId":"pg/numeric@1","kind":"codec"}}]},"version":"0.0.1"},"pgvector":{"capabilities":{"postgres":{"pgvector.cosine":true}},"familyId":"sql","id":"pgvector","kind":"extension","targetId":"postgres","types":{"codecTypes":{"import":{"alias":"PgVectorTypes","named":"CodecTypes","package":"@internal/extension-pgvector/codec-types"},"parameterizedOutputTypes":{"alias":"PgVectorParameterizedOutputTypes","named":"ParameterizedOutputTypesForTypeParams","package":"@internal/extension-pgvector/codec-types"}},"operationTypes":{"import":{"alias":"PgVectorOperationTypes","named":"OperationTypes","package":"@internal/extension-pgvector/operation-types"}},"queryOperationTypes":{"import":{"alias":"PgVectorQueryOperationTypes","named":"QueryOperationTypes","package":"@internal/extension-pgvector/operation-types"}}},"version":"0.0.1"}},"meta":{},"profileHash":"3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2","roots":{"post":{"model":"Post","namespace":"public"},"post_tag":{"model":"PostTag","namespace":"public"},"tag":{"model":"Tag","namespace":"public"},"task":{"model":"Task","namespace":"public"},"user":{"model":"User","namespace":"public"}},"schemaVersion":"1","storage":{"namespaces":{"public":{"entries":{"table":{"bug":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"severity":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"stepsToRepro":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"bug"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"feature":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"priority":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"targetRelease":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"feature"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"post":{"checks":[{"expression":"\"priority\" IN (0, 1, 2)","name":"post_priority_check_b236ace9","prefix":"post_priority_check"}],"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"embedding":{"codecId":"pg/vector@1","dataType":"pgvector/vector","nullable":true,"typeParams":{"length":1536}},"expiresAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"(now() + '7 days'::interval)","kind":"function"},"nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"impressionCount":{"codecId":"pg/int8@1","dataType":"pg/int8","nullable":true},"priority":{"codecId":"pg/int4@1","dataType":"pg/int4","default":{"kind":"literal","value":0},"nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Priority","namespaceId":"public","plane":"storage"}},"reachScore":{"codecId":"pg/unboundedint@1","dataType":"pg/numeric","nullable":true},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"viewCount":{"codecId":"pg/int8number@1","dataType":"pg/int8","nullable":true}},"foreignKeys":[{"index":{"name":"post_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"post"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"post_userId_idx_a489d58a","prefix":"post_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"post_tag":{"columns":{"postId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"tagId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"post_tag_postId_idx_a7a72715"},"source":{"columns":["postId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"post"}},{"index":{"name":"post_tag_tagId_idx_86854244"},"source":{"columns":["tagId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"tag"}}],"indexes":[{"columns":["postId"],"name":"post_tag_postId_idx_a7a72715","prefix":"post_tag_postId_idx","unique":false},{"columns":["tagId"],"name":"post_tag_tagId_idx_86854244","prefix":"post_tag_tagId_idx","unique":false}],"primaryKey":{"columns":["postId","tagId"]},"uniques":[]},"tag":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"label":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[{"columns":["label"]}]},"task":{"checks":[{"expression":"\"type\" IN ('bug', 'feature')","name":"task_type_check_cda3f5e7","prefix":"task_type_check"}],"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"description":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"status":{"codecId":"pg/text@1","dataType":"pg/text","default":{"kind":"literal","value":"open"},"nullable":false},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"type":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Task.type","namespaceId":"public","plane":"storage"}},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"task_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"task"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"task_userId_idx_a489d58a","prefix":"task_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"user":{"checks":[{"expression":"\"address\" IS NULL OR jsonb_path_match(\"address\", 'strict $.type() == \"null\" || ($.type() == \"object\" && exists($.keyvalue() ? (@.key == \"city\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"country\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"street\" && (@.value.type() == \"string\"))) && !exists($.keyvalue() ? (@.key == \"zip\" && !(@.value.type() == \"null\" || (@.value.type() == \"string\")))) && !exists($.keyvalue() ? (@.key != \"city\" && @.key != \"country\" && @.key != \"street\" && @.key != \"zip\")))') IS TRUE","name":"user_address_value_object_69432dc1","prefix":"user_address_value_object"},{"expression":"\"kind\" IN ('admin', 'user')","name":"user_kind_check_836d43ef","prefix":"user_kind_check"}],"columns":{"address":{"codecId":"pg/jsonb@1","dataType":"pg/jsonb","nullable":true},"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"displayName":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"email":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"kind":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"user_type","namespaceId":"public","plane":"storage"}}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]}},"valueObject":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}}}},"valueSet":{"Priority":{"kind":"valueSet","values":[0,1,2]},"Task.type":{"kind":"valueSet","values":["bug","feature"]},"user_type":{"kind":"valueSet","values":["admin","user"]}}},"id":"public","kind":"postgres-schema"}},"storageHash":"1c819419d4357de21d9aa9404a0b1d3639e3bd33c3fe6176207c53b51365fa1f"},"target":"postgres","targetFamily":"sql"} diff --git a/examples/prisma-8-demo/migrations/snapshots/d44ab6f7caddcc384332c669737008aec7ec8c9f5b013cb502a03c2eb04be9c0/contract.d.ts b/examples/prisma-8-demo/migrations/snapshots/3c2324f6061012e9d50f869b3e6ae6aa41159acd559b1cc5459bc64e2ccb0f4e/contract.d.ts similarity index 96% rename from examples/prisma-8-demo/migrations/snapshots/d44ab6f7caddcc384332c669737008aec7ec8c9f5b013cb502a03c2eb04be9c0/contract.d.ts rename to examples/prisma-8-demo/migrations/snapshots/3c2324f6061012e9d50f869b3e6ae6aa41159acd559b1cc5459bc64e2ccb0f4e/contract.d.ts index bf0b5936eb55..f1c37f8a2176 100644 --- a/examples/prisma-8-demo/migrations/snapshots/d44ab6f7caddcc384332c669737008aec7ec8c9f5b013cb502a03c2eb04be9c0/contract.d.ts +++ b/examples/prisma-8-demo/migrations/snapshots/3c2324f6061012e9d50f869b3e6ae6aa41159acd559b1cc5459bc64e2ccb0f4e/contract.d.ts @@ -25,11 +25,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@prisma/orm-postgres/contract/types'; export type StorageHash = - StorageHashBase<'d44ab6f7caddcc384332c669737008aec7ec8c9f5b013cb502a03c2eb04be9c0'>; + StorageHashBase<'3c2324f6061012e9d50f869b3e6ae6aa41159acd559b1cc5459bc64e2ccb0f4e'>; export type ExecutionHash = ExecutionHashBase<'eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655'>; export type ProfileHash = @@ -261,15 +261,15 @@ type DefaultLiteralValue = CodecId extends keyo : Encoded; type ParameterizedOutputTypeFunctions = PgParameterizedOutputTypes | PgVectorParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -283,6 +283,9 @@ type FieldTypeOverrides = { readonly Post: { readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 }; }; + readonly Task: { + readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; + }; readonly User: { readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; }; @@ -294,18 +297,23 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = { readonly public: { readonly post: { readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 } }; + readonly task: { + readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; + }; readonly user: { readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; }; @@ -803,11 +811,37 @@ type ContractBase = Omit< }; readonly valueSet: { readonly Priority: { readonly kind: 'valueSet'; readonly values: readonly [0, 1, 2] }; + readonly 'Task.type': { + readonly kind: 'valueSet'; + readonly values: readonly ['bug', 'feature']; + }; readonly user_type: { readonly kind: 'valueSet'; readonly values: readonly ['admin', 'user']; }; }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + }; + }; }; }; }; @@ -1108,7 +1142,13 @@ type ContractBase = Omit< readonly fields: { readonly address: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: 'public'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; }; readonly createdAt: { readonly nullable: false; @@ -1166,28 +1206,6 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - }; - }; readonly enum: { readonly Priority: { readonly codecId: 'pg/int4@1'; @@ -1343,28 +1361,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/examples/prisma-8-demo/migrations/snapshots/3c2324f6061012e9d50f869b3e6ae6aa41159acd559b1cc5459bc64e2ccb0f4e/contract.json b/examples/prisma-8-demo/migrations/snapshots/3c2324f6061012e9d50f869b3e6ae6aa41159acd559b1cc5459bc64e2ccb0f4e/contract.json new file mode 100644 index 000000000000..4854522c8b67 --- /dev/null +++ b/examples/prisma-8-demo/migrations/snapshots/3c2324f6061012e9d50f869b3e6ae6aa41159acd559b1cc5459bc64e2ccb0f4e/contract.json @@ -0,0 +1 @@ +{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{"postgres":{"distinctOn":true,"forKeyShare":true,"forNoKeyUpdate":true,"jsonAgg":true,"lateral":true,"limit":true,"orderBy":true,"pgvector.cosine":true,"returning":true},"sql":{"checkConstraint":true,"defaultInInsert":true,"enums":true,"forShare":true,"forUpdate":true,"insertOnConflictSkip":true,"insertOnConflictWithoutTarget":true,"lateral":true,"lockNowait":true,"lockOf":true,"lockSkipLocked":true,"returning":true,"scalarList":true}},"domain":{"namespaces":{"public":{"enum":{"Priority":{"codecId":"pg/int4@1","members":[{"name":"Low","value":0},{"name":"High","value":1},{"name":"Urgent","value":2}]},"user_type":{"codecId":"pg/text@1","members":[{"name":"admin","value":"admin"},{"name":"user","value":"user"}]}},"models":{"Bug":{"base":{"model":"Task","namespace":"public"},"fields":{"severity":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"stepsToRepro":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"severity":{"column":"severity"},"stepsToRepro":{"column":"stepsToRepro"}},"namespaceId":"public","table":"bug"}},"Feature":{"base":{"model":"Task","namespace":"public"},"fields":{"priority":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"targetRelease":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"priority":{"column":"priority"},"targetRelease":{"column":"targetRelease"}},"namespaceId":"public","table":"feature"}},"Post":{"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"embedding":{"nullable":true,"type":{"codecId":"pg/vector@1","kind":"scalar","typeParams":{"length":1536}}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"impressionCount":{"nullable":true,"type":{"codecId":"pg/int8@1","kind":"scalar"}},"priority":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"Priority","namespaceId":"public","plane":"domain"}},"reachScore":{"nullable":true,"type":{"codecId":"pg/unboundedint@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"viewCount":{"nullable":true,"type":{"codecId":"pg/int8number@1","kind":"scalar"}}},"relations":{"tags":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["postId"]},"through":{"childColumns":["tagId"],"namespaceId":"public","parentColumns":["postId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Tag","namespace":"public"}},"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"embedding":{"column":"embedding"},"id":{"column":"id"},"impressionCount":{"column":"impressionCount"},"priority":{"column":"priority"},"reachScore":{"column":"reachScore"},"title":{"column":"title"},"userId":{"column":"userId"},"viewCount":{"column":"viewCount"}},"namespaceId":"public","table":"post"}},"PostTag":{"fields":{"postId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"tagId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"post":{"cardinality":"N:1","nullable":false,"on":{"localFields":["postId"],"targetFields":["id"]},"to":{"model":"Post","namespace":"public"}},"tag":{"cardinality":"N:1","nullable":false,"on":{"localFields":["tagId"],"targetFields":["id"]},"to":{"model":"Tag","namespace":"public"}}},"storage":{"fields":{"postId":{"column":"postId"},"tagId":{"column":"tagId"}},"namespaceId":"public","table":"post_tag"}},"Tag":{"fields":{"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"label":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{"posts":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["tagId"]},"through":{"childColumns":["postId"],"namespaceId":"public","parentColumns":["tagId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Post","namespace":"public"}}},"storage":{"fields":{"id":{"column":"id"},"label":{"column":"label"}},"namespaceId":"public","table":"tag"}},"Task":{"discriminator":{"field":"type"},"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"description":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"status":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"type":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"description":{"column":"description"},"id":{"column":"id"},"status":{"column":"status"},"title":{"column":"title"},"type":{"column":"type"},"userId":{"column":"userId"}},"namespaceId":"public","table":"task"},"variants":{"bug":{"model":"Bug","namespace":"public"},"feature":{"model":"Feature","namespace":"public"}}},"User":{"fields":{"address":{"nullable":true,"type":{"entityKind":"valueObject","entityName":"Address","kind":"valueObject","namespaceId":"public","plane":"storage"}},"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"displayName":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"user_type","namespaceId":"public","plane":"domain"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Post","namespace":"public"}},"tasks":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Task","namespace":"public"}}},"storage":{"fields":{"address":{"column":"address"},"createdAt":{"column":"createdAt"},"displayName":{"column":"displayName"},"email":{"column":"email"},"id":{"column":"id"},"kind":{"column":"kind"}},"namespaceId":"public","table":"user"}}}}}},"execution":{"executionHash":"eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655","mutations":{"defaults":[{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"post","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"tag","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"task","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"user","field":"id","namespace":"public"}}]}},"extensions":{"demo/engagement-stats":{"familyId":"sql","id":"demo/engagement-stats","kind":"extension","targetId":"postgres","types":{"aggregateDescriptors":[{"input":{"kind":"trait","trait":"numeric"},"nullable":true,"operation":"stddev","output":{"codecId":"pg/numeric@1","kind":"codec"}}]},"version":"0.0.1"},"pgvector":{"capabilities":{"postgres":{"pgvector.cosine":true}},"familyId":"sql","id":"pgvector","kind":"extension","targetId":"postgres","types":{"codecTypes":{"import":{"alias":"PgVectorTypes","named":"CodecTypes","package":"@internal/extension-pgvector/codec-types"},"parameterizedOutputTypes":{"alias":"PgVectorParameterizedOutputTypes","named":"ParameterizedOutputTypesForTypeParams","package":"@internal/extension-pgvector/codec-types"}},"operationTypes":{"import":{"alias":"PgVectorOperationTypes","named":"OperationTypes","package":"@internal/extension-pgvector/operation-types"}},"queryOperationTypes":{"import":{"alias":"PgVectorQueryOperationTypes","named":"QueryOperationTypes","package":"@internal/extension-pgvector/operation-types"}}},"version":"0.0.1"}},"meta":{},"profileHash":"3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2","roots":{"post":{"model":"Post","namespace":"public"},"post_tag":{"model":"PostTag","namespace":"public"},"tag":{"model":"Tag","namespace":"public"},"task":{"model":"Task","namespace":"public"},"user":{"model":"User","namespace":"public"}},"schemaVersion":"1","storage":{"namespaces":{"public":{"entries":{"table":{"bug":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"severity":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"stepsToRepro":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"bug"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"feature":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"priority":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"targetRelease":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"feature"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"post":{"checks":[{"expression":"\"priority\" IN (0, 1, 2)","name":"post_priority_check_b236ace9","prefix":"post_priority_check"}],"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"embedding":{"codecId":"pg/vector@1","dataType":"pgvector/vector","nullable":true,"typeParams":{"length":1536}},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"impressionCount":{"codecId":"pg/int8@1","dataType":"pg/int8","nullable":true},"priority":{"codecId":"pg/int4@1","dataType":"pg/int4","default":{"kind":"literal","value":0},"nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Priority","namespaceId":"public","plane":"storage"}},"reachScore":{"codecId":"pg/unboundedint@1","dataType":"pg/numeric","nullable":true},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"viewCount":{"codecId":"pg/int8number@1","dataType":"pg/int8","nullable":true}},"foreignKeys":[{"index":{"name":"post_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"post"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"post_userId_idx_a489d58a","prefix":"post_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"post_tag":{"columns":{"postId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"tagId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"post_tag_postId_idx_a7a72715"},"source":{"columns":["postId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"post"}},{"index":{"name":"post_tag_tagId_idx_86854244"},"source":{"columns":["tagId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"tag"}}],"indexes":[{"columns":["postId"],"name":"post_tag_postId_idx_a7a72715","prefix":"post_tag_postId_idx","unique":false},{"columns":["tagId"],"name":"post_tag_tagId_idx_86854244","prefix":"post_tag_tagId_idx","unique":false}],"primaryKey":{"columns":["postId","tagId"]},"uniques":[]},"tag":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"label":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[{"columns":["label"]}]},"task":{"checks":[{"expression":"\"type\" IN ('bug', 'feature')","name":"task_type_check_cda3f5e7","prefix":"task_type_check"}],"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"description":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"status":{"codecId":"pg/text@1","dataType":"pg/text","default":{"kind":"literal","value":"open"},"nullable":false},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"type":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Task.type","namespaceId":"public","plane":"storage"}},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"task_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"task"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"task_userId_idx_a489d58a","prefix":"task_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"user":{"checks":[{"expression":"\"address\" IS NULL OR jsonb_path_match(\"address\", 'strict $.type() == \"null\" || ($.type() == \"object\" && exists($.keyvalue() ? (@.key == \"city\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"country\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"street\" && (@.value.type() == \"string\"))) && !exists($.keyvalue() ? (@.key == \"zip\" && !(@.value.type() == \"null\" || (@.value.type() == \"string\")))) && !exists($.keyvalue() ? (@.key != \"city\" && @.key != \"country\" && @.key != \"street\" && @.key != \"zip\")))') IS TRUE","name":"user_address_value_object_69432dc1","prefix":"user_address_value_object"},{"expression":"\"kind\" IN ('admin', 'user')","name":"user_kind_check_836d43ef","prefix":"user_kind_check"}],"columns":{"address":{"codecId":"pg/jsonb@1","dataType":"pg/jsonb","nullable":true},"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"displayName":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"email":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"kind":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"user_type","namespaceId":"public","plane":"storage"}}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]}},"valueObject":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}}}},"valueSet":{"Priority":{"kind":"valueSet","values":[0,1,2]},"Task.type":{"kind":"valueSet","values":["bug","feature"]},"user_type":{"kind":"valueSet","values":["admin","user"]}}},"id":"public","kind":"postgres-schema"}},"storageHash":"3c2324f6061012e9d50f869b3e6ae6aa41159acd559b1cc5459bc64e2ccb0f4e"},"target":"postgres","targetFamily":"sql"} diff --git a/examples/prisma-8-demo/migrations/snapshots/58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b/contract.json b/examples/prisma-8-demo/migrations/snapshots/58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b/contract.json deleted file mode 100644 index fa8fc31297bc..000000000000 --- a/examples/prisma-8-demo/migrations/snapshots/58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b/contract.json +++ /dev/null @@ -1 +0,0 @@ -{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{"postgres":{"distinctOn":true,"forKeyShare":true,"forNoKeyUpdate":true,"jsonAgg":true,"lateral":true,"limit":true,"orderBy":true,"pgvector.cosine":true,"returning":true},"sql":{"checkConstraint":true,"defaultInInsert":true,"enums":true,"forShare":true,"forUpdate":true,"insertOnConflictSkip":true,"insertOnConflictWithoutTarget":true,"lateral":true,"lockNowait":true,"lockOf":true,"lockSkipLocked":true,"returning":true,"scalarList":true}},"domain":{"namespaces":{"public":{"enum":{"Priority":{"codecId":"pg/int4@1","members":[{"name":"Low","value":0},{"name":"High","value":1},{"name":"Urgent","value":2}]},"user_type":{"codecId":"pg/text@1","members":[{"name":"admin","value":"admin"},{"name":"user","value":"user"}]}},"models":{"Bug":{"base":{"model":"Task","namespace":"public"},"fields":{"severity":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"stepsToRepro":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"severity":{"column":"severity"},"stepsToRepro":{"column":"stepsToRepro"}},"namespaceId":"public","table":"bug"}},"Feature":{"base":{"model":"Task","namespace":"public"},"fields":{"priority":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"targetRelease":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"priority":{"column":"priority"},"targetRelease":{"column":"targetRelease"}},"namespaceId":"public","table":"feature"}},"Post":{"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"embedding":{"nullable":true,"type":{"codecId":"pg/vector@1","kind":"scalar","typeParams":{"length":1536}}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"priority":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"Priority","namespaceId":"public","plane":"domain"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"tags":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["postId"]},"through":{"childColumns":["tagId"],"namespaceId":"public","parentColumns":["postId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Tag","namespace":"public"}},"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"embedding":{"column":"embedding"},"id":{"column":"id"},"priority":{"column":"priority"},"title":{"column":"title"},"userId":{"column":"userId"}},"namespaceId":"public","table":"post"}},"PostTag":{"fields":{"postId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"tagId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"post":{"cardinality":"N:1","nullable":false,"on":{"localFields":["postId"],"targetFields":["id"]},"to":{"model":"Post","namespace":"public"}},"tag":{"cardinality":"N:1","nullable":false,"on":{"localFields":["tagId"],"targetFields":["id"]},"to":{"model":"Tag","namespace":"public"}}},"storage":{"fields":{"postId":{"column":"postId"},"tagId":{"column":"tagId"}},"namespaceId":"public","table":"post_tag"}},"Tag":{"fields":{"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"label":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{"posts":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["tagId"]},"through":{"childColumns":["postId"],"namespaceId":"public","parentColumns":["tagId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Post","namespace":"public"}}},"storage":{"fields":{"id":{"column":"id"},"label":{"column":"label"}},"namespaceId":"public","table":"tag"}},"Task":{"discriminator":{"field":"type"},"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"description":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"status":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"type":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"description":{"column":"description"},"id":{"column":"id"},"status":{"column":"status"},"title":{"column":"title"},"type":{"column":"type"},"userId":{"column":"userId"}},"namespaceId":"public","table":"task"},"variants":{"bug":{"model":"Bug","namespace":"public"},"feature":{"model":"Feature","namespace":"public"}}},"User":{"fields":{"address":{"nullable":true,"type":{"kind":"valueObject","name":"Address"}},"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"displayName":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"user_type","namespaceId":"public","plane":"domain"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Post","namespace":"public"}},"tasks":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Task","namespace":"public"}}},"storage":{"fields":{"address":{"column":"address"},"createdAt":{"column":"createdAt"},"displayName":{"column":"displayName"},"email":{"column":"email"},"id":{"column":"id"},"kind":{"column":"kind"}},"namespaceId":"public","table":"user"}}},"valueObjects":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}}}}}}},"execution":{"executionHash":"eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655","mutations":{"defaults":[{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"post","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"tag","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"task","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"user","field":"id","namespace":"public"}}]}},"extensions":{"demo/engagement-stats":{"familyId":"sql","id":"demo/engagement-stats","kind":"extension","targetId":"postgres","types":{"aggregateDescriptors":[{"input":{"kind":"trait","trait":"numeric"},"nullable":true,"operation":"stddev","output":{"codecId":"pg/numeric@1","kind":"codec"}}]},"version":"0.0.1"},"pgvector":{"capabilities":{"postgres":{"pgvector.cosine":true}},"familyId":"sql","id":"pgvector","kind":"extension","targetId":"postgres","types":{"codecTypes":{"import":{"alias":"PgVectorTypes","named":"CodecTypes","package":"@internal/extension-pgvector/codec-types"},"parameterizedOutputTypes":{"alias":"PgVectorParameterizedOutputTypes","named":"ParameterizedOutputTypesForTypeParams","package":"@internal/extension-pgvector/codec-types"}},"operationTypes":{"import":{"alias":"PgVectorOperationTypes","named":"OperationTypes","package":"@internal/extension-pgvector/operation-types"}},"queryOperationTypes":{"import":{"alias":"PgVectorQueryOperationTypes","named":"QueryOperationTypes","package":"@internal/extension-pgvector/operation-types"}}},"version":"0.0.1"}},"meta":{},"profileHash":"3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2","roots":{"post":{"model":"Post","namespace":"public"},"post_tag":{"model":"PostTag","namespace":"public"},"tag":{"model":"Tag","namespace":"public"},"task":{"model":"Task","namespace":"public"},"user":{"model":"User","namespace":"public"}},"schemaVersion":"1","storage":{"namespaces":{"public":{"entries":{"table":{"bug":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"severity":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"stepsToRepro":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"bug"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"feature":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"priority":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"targetRelease":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"feature"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"post":{"checks":[{"expression":"\"priority\" IN (0, 1, 2)","name":"post_priority_check_b236ace9","prefix":"post_priority_check"}],"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"embedding":{"codecId":"pg/vector@1","dataType":"pgvector/vector","nullable":true,"typeParams":{"length":1536}},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"priority":{"codecId":"pg/int4@1","dataType":"pg/int4","default":{"kind":"literal","value":0},"nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Priority","namespaceId":"public","plane":"storage"}},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"post_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"post"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"post_userId_idx_a489d58a","prefix":"post_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"post_tag":{"columns":{"postId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"tagId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"post_tag_postId_idx_a7a72715"},"source":{"columns":["postId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"post"}},{"index":{"name":"post_tag_tagId_idx_86854244"},"source":{"columns":["tagId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"tag"}}],"indexes":[{"columns":["postId"],"name":"post_tag_postId_idx_a7a72715","prefix":"post_tag_postId_idx","unique":false},{"columns":["tagId"],"name":"post_tag_tagId_idx_86854244","prefix":"post_tag_tagId_idx","unique":false}],"primaryKey":{"columns":["postId","tagId"]},"uniques":[]},"tag":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"label":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[{"columns":["label"]}]},"task":{"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"description":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"status":{"codecId":"pg/text@1","dataType":"pg/text","default":{"kind":"literal","value":"open"},"nullable":false},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"type":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"task_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"task"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"task_userId_idx_a489d58a","prefix":"task_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"user":{"checks":[{"expression":"\"kind\" IN ('admin', 'user')","name":"user_kind_check_836d43ef","prefix":"user_kind_check"}],"columns":{"address":{"codecId":"pg/jsonb@1","dataType":"pg/jsonb","nullable":true},"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"displayName":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"email":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"kind":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"user_type","namespaceId":"public","plane":"storage"}}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]}},"valueSet":{"Priority":{"kind":"valueSet","values":[0,1,2]},"user_type":{"kind":"valueSet","values":["admin","user"]}}},"id":"public","kind":"postgres-schema"}},"storageHash":"58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b"},"target":"postgres","targetFamily":"sql"} diff --git a/examples/prisma-8-demo/migrations/snapshots/6e6fbbc22d1d4904fbc8668644c5cabe694d946e550a2f0cdd07aaab308b953f/contract.json b/examples/prisma-8-demo/migrations/snapshots/6e6fbbc22d1d4904fbc8668644c5cabe694d946e550a2f0cdd07aaab308b953f/contract.json deleted file mode 100644 index 7cfdc5d58ac3..000000000000 --- a/examples/prisma-8-demo/migrations/snapshots/6e6fbbc22d1d4904fbc8668644c5cabe694d946e550a2f0cdd07aaab308b953f/contract.json +++ /dev/null @@ -1 +0,0 @@ -{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{"postgres":{"distinctOn":true,"forKeyShare":true,"forNoKeyUpdate":true,"jsonAgg":true,"lateral":true,"limit":true,"orderBy":true,"pgvector.cosine":true,"returning":true},"sql":{"checkConstraint":true,"defaultInInsert":true,"enums":true,"forShare":true,"forUpdate":true,"insertOnConflictSkip":true,"insertOnConflictWithoutTarget":true,"lateral":true,"lockNowait":true,"lockOf":true,"lockSkipLocked":true,"returning":true,"scalarList":true}},"domain":{"namespaces":{"public":{"enum":{"Priority":{"codecId":"pg/int4@1","members":[{"name":"Low","value":0},{"name":"High","value":1},{"name":"Urgent","value":2}]},"user_type":{"codecId":"pg/text@1","members":[{"name":"admin","value":"admin"},{"name":"user","value":"user"}]}},"models":{"Bug":{"base":{"model":"Task","namespace":"public"},"fields":{"severity":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"stepsToRepro":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"severity":{"column":"severity"},"stepsToRepro":{"column":"stepsToRepro"}},"namespaceId":"public","table":"bug"}},"Feature":{"base":{"model":"Task","namespace":"public"},"fields":{"priority":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"targetRelease":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"priority":{"column":"priority"},"targetRelease":{"column":"targetRelease"}},"namespaceId":"public","table":"feature"}},"Post":{"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"embedding":{"nullable":true,"type":{"codecId":"pg/vector@1","kind":"scalar","typeParams":{"length":1536}}},"expiresAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"impressionCount":{"nullable":true,"type":{"codecId":"pg/int8@1","kind":"scalar"}},"priority":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"Priority","namespaceId":"public","plane":"domain"}},"reachScore":{"nullable":true,"type":{"codecId":"pg/unboundedint@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"viewCount":{"nullable":true,"type":{"codecId":"pg/int8number@1","kind":"scalar"}}},"relations":{"tags":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["postId"]},"through":{"childColumns":["tagId"],"namespaceId":"public","parentColumns":["postId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Tag","namespace":"public"}},"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"embedding":{"column":"embedding"},"expiresAt":{"column":"expiresAt"},"id":{"column":"id"},"impressionCount":{"column":"impressionCount"},"priority":{"column":"priority"},"reachScore":{"column":"reachScore"},"title":{"column":"title"},"userId":{"column":"userId"},"viewCount":{"column":"viewCount"}},"namespaceId":"public","table":"post"}},"PostTag":{"fields":{"postId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"tagId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"post":{"cardinality":"N:1","nullable":false,"on":{"localFields":["postId"],"targetFields":["id"]},"to":{"model":"Post","namespace":"public"}},"tag":{"cardinality":"N:1","nullable":false,"on":{"localFields":["tagId"],"targetFields":["id"]},"to":{"model":"Tag","namespace":"public"}}},"storage":{"fields":{"postId":{"column":"postId"},"tagId":{"column":"tagId"}},"namespaceId":"public","table":"post_tag"}},"Tag":{"fields":{"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"label":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{"posts":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["tagId"]},"through":{"childColumns":["postId"],"namespaceId":"public","parentColumns":["tagId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Post","namespace":"public"}}},"storage":{"fields":{"id":{"column":"id"},"label":{"column":"label"}},"namespaceId":"public","table":"tag"}},"Task":{"discriminator":{"field":"type"},"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"description":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"status":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"type":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"description":{"column":"description"},"id":{"column":"id"},"status":{"column":"status"},"title":{"column":"title"},"type":{"column":"type"},"userId":{"column":"userId"}},"namespaceId":"public","table":"task"},"variants":{"bug":{"model":"Bug","namespace":"public"},"feature":{"model":"Feature","namespace":"public"}}},"User":{"fields":{"address":{"nullable":true,"type":{"kind":"valueObject","name":"Address"}},"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"displayName":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"user_type","namespaceId":"public","plane":"domain"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Post","namespace":"public"}},"tasks":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Task","namespace":"public"}}},"storage":{"fields":{"address":{"column":"address"},"createdAt":{"column":"createdAt"},"displayName":{"column":"displayName"},"email":{"column":"email"},"id":{"column":"id"},"kind":{"column":"kind"}},"namespaceId":"public","table":"user"}}},"valueObjects":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}}}}}}},"execution":{"executionHash":"eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655","mutations":{"defaults":[{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"post","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"tag","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"task","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"user","field":"id","namespace":"public"}}]}},"extensions":{"demo/engagement-stats":{"familyId":"sql","id":"demo/engagement-stats","kind":"extension","targetId":"postgres","types":{"aggregateDescriptors":[{"input":{"kind":"trait","trait":"numeric"},"nullable":true,"operation":"stddev","output":{"codecId":"pg/numeric@1","kind":"codec"}}]},"version":"0.0.1"},"pgvector":{"capabilities":{"postgres":{"pgvector.cosine":true}},"familyId":"sql","id":"pgvector","kind":"extension","targetId":"postgres","types":{"codecTypes":{"import":{"alias":"PgVectorTypes","named":"CodecTypes","package":"@internal/extension-pgvector/codec-types"},"parameterizedOutputTypes":{"alias":"PgVectorParameterizedOutputTypes","named":"ParameterizedOutputTypesForTypeParams","package":"@internal/extension-pgvector/codec-types"}},"operationTypes":{"import":{"alias":"PgVectorOperationTypes","named":"OperationTypes","package":"@internal/extension-pgvector/operation-types"}},"queryOperationTypes":{"import":{"alias":"PgVectorQueryOperationTypes","named":"QueryOperationTypes","package":"@internal/extension-pgvector/operation-types"}}},"version":"0.0.1"}},"meta":{},"profileHash":"3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2","roots":{"post":{"model":"Post","namespace":"public"},"post_tag":{"model":"PostTag","namespace":"public"},"tag":{"model":"Tag","namespace":"public"},"task":{"model":"Task","namespace":"public"},"user":{"model":"User","namespace":"public"}},"schemaVersion":"1","storage":{"namespaces":{"public":{"entries":{"table":{"bug":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"severity":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"stepsToRepro":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"bug"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"feature":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"priority":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"targetRelease":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"feature"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"post":{"checks":[{"expression":"\"priority\" IN (0, 1, 2)","name":"post_priority_check_b236ace9","prefix":"post_priority_check"}],"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"embedding":{"codecId":"pg/vector@1","dataType":"pgvector/vector","nullable":true,"typeParams":{"length":1536}},"expiresAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"(now() + '7 days'::interval)","kind":"function"},"nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"impressionCount":{"codecId":"pg/int8@1","dataType":"pg/int8","nullable":true},"priority":{"codecId":"pg/int4@1","dataType":"pg/int4","default":{"kind":"literal","value":0},"nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Priority","namespaceId":"public","plane":"storage"}},"reachScore":{"codecId":"pg/unboundedint@1","dataType":"pg/numeric","nullable":true},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"viewCount":{"codecId":"pg/int8number@1","dataType":"pg/int8","nullable":true}},"foreignKeys":[{"index":{"name":"post_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"post"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["title"],"name":"post_title_search_e0dd1131","options":{"language":"english","weightGroups":[["title"]]},"prefix":"post_title_search","type":"fullText","unique":false},{"columns":["userId"],"name":"post_userId_idx_a489d58a","prefix":"post_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"post_tag":{"columns":{"postId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"tagId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"post_tag_postId_idx_a7a72715"},"source":{"columns":["postId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"post"}},{"index":{"name":"post_tag_tagId_idx_86854244"},"source":{"columns":["tagId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"tag"}}],"indexes":[{"columns":["postId"],"name":"post_tag_postId_idx_a7a72715","prefix":"post_tag_postId_idx","unique":false},{"columns":["tagId"],"name":"post_tag_tagId_idx_86854244","prefix":"post_tag_tagId_idx","unique":false}],"primaryKey":{"columns":["postId","tagId"]},"uniques":[]},"tag":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"label":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[{"columns":["label"]}]},"task":{"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"description":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"status":{"codecId":"pg/text@1","dataType":"pg/text","default":{"kind":"literal","value":"open"},"nullable":false},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"type":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"task_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"task"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"task_userId_idx_a489d58a","prefix":"task_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"user":{"checks":[{"expression":"\"kind\" IN ('admin', 'user')","name":"user_kind_check_836d43ef","prefix":"user_kind_check"}],"columns":{"address":{"codecId":"pg/jsonb@1","dataType":"pg/jsonb","nullable":true},"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"displayName":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"email":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"kind":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"user_type","namespaceId":"public","plane":"storage"}}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]}},"valueSet":{"Priority":{"kind":"valueSet","values":[0,1,2]},"user_type":{"kind":"valueSet","values":["admin","user"]}}},"id":"public","kind":"postgres-schema"}},"storageHash":"6e6fbbc22d1d4904fbc8668644c5cabe694d946e550a2f0cdd07aaab308b953f"},"target":"postgres","targetFamily":"sql"} diff --git a/examples/prisma-8-demo/migrations/snapshots/6e6fbbc22d1d4904fbc8668644c5cabe694d946e550a2f0cdd07aaab308b953f/contract.d.ts b/examples/prisma-8-demo/migrations/snapshots/9301c8a3b2ec83c89b4eb12568ccb0c9e916ba199d8743ab38ed468483d6921f/contract.d.ts similarity index 96% rename from examples/prisma-8-demo/migrations/snapshots/6e6fbbc22d1d4904fbc8668644c5cabe694d946e550a2f0cdd07aaab308b953f/contract.d.ts rename to examples/prisma-8-demo/migrations/snapshots/9301c8a3b2ec83c89b4eb12568ccb0c9e916ba199d8743ab38ed468483d6921f/contract.d.ts index fc656d75473b..478e7bb184fa 100644 --- a/examples/prisma-8-demo/migrations/snapshots/6e6fbbc22d1d4904fbc8668644c5cabe694d946e550a2f0cdd07aaab308b953f/contract.d.ts +++ b/examples/prisma-8-demo/migrations/snapshots/9301c8a3b2ec83c89b4eb12568ccb0c9e916ba199d8743ab38ed468483d6921f/contract.d.ts @@ -25,11 +25,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@prisma/orm-postgres/contract/types'; export type StorageHash = - StorageHashBase<'6e6fbbc22d1d4904fbc8668644c5cabe694d946e550a2f0cdd07aaab308b953f'>; + StorageHashBase<'9301c8a3b2ec83c89b4eb12568ccb0c9e916ba199d8743ab38ed468483d6921f'>; export type ExecutionHash = ExecutionHashBase<'eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655'>; export type ProfileHash = @@ -261,15 +261,15 @@ type DefaultLiteralValue = CodecId extends keyo : Encoded; type ParameterizedOutputTypeFunctions = PgParameterizedOutputTypes | PgVectorParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -283,6 +283,9 @@ type FieldTypeOverrides = { readonly Post: { readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 }; }; + readonly Task: { + readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; + }; readonly User: { readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; }; @@ -294,18 +297,23 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = { readonly public: { readonly post: { readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 } }; + readonly task: { + readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; + }; readonly user: { readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; }; @@ -825,11 +833,37 @@ type ContractBase = Omit< }; readonly valueSet: { readonly Priority: { readonly kind: 'valueSet'; readonly values: readonly [0, 1, 2] }; + readonly 'Task.type': { + readonly kind: 'valueSet'; + readonly values: readonly ['bug', 'feature']; + }; readonly user_type: { readonly kind: 'valueSet'; readonly values: readonly ['admin', 'user']; }; }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + }; + }; }; }; }; @@ -1138,7 +1172,13 @@ type ContractBase = Omit< readonly fields: { readonly address: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: 'public'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; }; readonly createdAt: { readonly nullable: false; @@ -1196,28 +1236,6 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - }; - }; readonly enum: { readonly Priority: { readonly codecId: 'pg/int4@1'; @@ -1373,28 +1391,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/examples/prisma-8-demo/migrations/snapshots/9301c8a3b2ec83c89b4eb12568ccb0c9e916ba199d8743ab38ed468483d6921f/contract.json b/examples/prisma-8-demo/migrations/snapshots/9301c8a3b2ec83c89b4eb12568ccb0c9e916ba199d8743ab38ed468483d6921f/contract.json new file mode 100644 index 000000000000..24cfc59c7e09 --- /dev/null +++ b/examples/prisma-8-demo/migrations/snapshots/9301c8a3b2ec83c89b4eb12568ccb0c9e916ba199d8743ab38ed468483d6921f/contract.json @@ -0,0 +1 @@ +{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{"postgres":{"distinctOn":true,"forKeyShare":true,"forNoKeyUpdate":true,"jsonAgg":true,"lateral":true,"limit":true,"orderBy":true,"pgvector.cosine":true,"returning":true},"sql":{"checkConstraint":true,"defaultInInsert":true,"enums":true,"forShare":true,"forUpdate":true,"insertOnConflictSkip":true,"insertOnConflictWithoutTarget":true,"lateral":true,"lockNowait":true,"lockOf":true,"lockSkipLocked":true,"returning":true,"scalarList":true}},"domain":{"namespaces":{"public":{"enum":{"Priority":{"codecId":"pg/int4@1","members":[{"name":"Low","value":0},{"name":"High","value":1},{"name":"Urgent","value":2}]},"user_type":{"codecId":"pg/text@1","members":[{"name":"admin","value":"admin"},{"name":"user","value":"user"}]}},"models":{"Bug":{"base":{"model":"Task","namespace":"public"},"fields":{"severity":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"stepsToRepro":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"severity":{"column":"severity"},"stepsToRepro":{"column":"stepsToRepro"}},"namespaceId":"public","table":"bug"}},"Feature":{"base":{"model":"Task","namespace":"public"},"fields":{"priority":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"targetRelease":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"priority":{"column":"priority"},"targetRelease":{"column":"targetRelease"}},"namespaceId":"public","table":"feature"}},"Post":{"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"embedding":{"nullable":true,"type":{"codecId":"pg/vector@1","kind":"scalar","typeParams":{"length":1536}}},"expiresAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"impressionCount":{"nullable":true,"type":{"codecId":"pg/int8@1","kind":"scalar"}},"priority":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"Priority","namespaceId":"public","plane":"domain"}},"reachScore":{"nullable":true,"type":{"codecId":"pg/unboundedint@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"viewCount":{"nullable":true,"type":{"codecId":"pg/int8number@1","kind":"scalar"}}},"relations":{"tags":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["postId"]},"through":{"childColumns":["tagId"],"namespaceId":"public","parentColumns":["postId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Tag","namespace":"public"}},"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"embedding":{"column":"embedding"},"expiresAt":{"column":"expiresAt"},"id":{"column":"id"},"impressionCount":{"column":"impressionCount"},"priority":{"column":"priority"},"reachScore":{"column":"reachScore"},"title":{"column":"title"},"userId":{"column":"userId"},"viewCount":{"column":"viewCount"}},"namespaceId":"public","table":"post"}},"PostTag":{"fields":{"postId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"tagId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"post":{"cardinality":"N:1","nullable":false,"on":{"localFields":["postId"],"targetFields":["id"]},"to":{"model":"Post","namespace":"public"}},"tag":{"cardinality":"N:1","nullable":false,"on":{"localFields":["tagId"],"targetFields":["id"]},"to":{"model":"Tag","namespace":"public"}}},"storage":{"fields":{"postId":{"column":"postId"},"tagId":{"column":"tagId"}},"namespaceId":"public","table":"post_tag"}},"Tag":{"fields":{"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"label":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{"posts":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["tagId"]},"through":{"childColumns":["postId"],"namespaceId":"public","parentColumns":["tagId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Post","namespace":"public"}}},"storage":{"fields":{"id":{"column":"id"},"label":{"column":"label"}},"namespaceId":"public","table":"tag"}},"Task":{"discriminator":{"field":"type"},"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"description":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"status":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"type":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"description":{"column":"description"},"id":{"column":"id"},"status":{"column":"status"},"title":{"column":"title"},"type":{"column":"type"},"userId":{"column":"userId"}},"namespaceId":"public","table":"task"},"variants":{"bug":{"model":"Bug","namespace":"public"},"feature":{"model":"Feature","namespace":"public"}}},"User":{"fields":{"address":{"nullable":true,"type":{"entityKind":"valueObject","entityName":"Address","kind":"valueObject","namespaceId":"public","plane":"storage"}},"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"displayName":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"user_type","namespaceId":"public","plane":"domain"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Post","namespace":"public"}},"tasks":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Task","namespace":"public"}}},"storage":{"fields":{"address":{"column":"address"},"createdAt":{"column":"createdAt"},"displayName":{"column":"displayName"},"email":{"column":"email"},"id":{"column":"id"},"kind":{"column":"kind"}},"namespaceId":"public","table":"user"}}}}}},"execution":{"executionHash":"eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655","mutations":{"defaults":[{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"post","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"tag","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"task","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"user","field":"id","namespace":"public"}}]}},"extensions":{"demo/engagement-stats":{"familyId":"sql","id":"demo/engagement-stats","kind":"extension","targetId":"postgres","types":{"aggregateDescriptors":[{"input":{"kind":"trait","trait":"numeric"},"nullable":true,"operation":"stddev","output":{"codecId":"pg/numeric@1","kind":"codec"}}]},"version":"0.0.1"},"pgvector":{"capabilities":{"postgres":{"pgvector.cosine":true}},"familyId":"sql","id":"pgvector","kind":"extension","targetId":"postgres","types":{"codecTypes":{"import":{"alias":"PgVectorTypes","named":"CodecTypes","package":"@internal/extension-pgvector/codec-types"},"parameterizedOutputTypes":{"alias":"PgVectorParameterizedOutputTypes","named":"ParameterizedOutputTypesForTypeParams","package":"@internal/extension-pgvector/codec-types"}},"operationTypes":{"import":{"alias":"PgVectorOperationTypes","named":"OperationTypes","package":"@internal/extension-pgvector/operation-types"}},"queryOperationTypes":{"import":{"alias":"PgVectorQueryOperationTypes","named":"QueryOperationTypes","package":"@internal/extension-pgvector/operation-types"}}},"version":"0.0.1"}},"meta":{},"profileHash":"3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2","roots":{"post":{"model":"Post","namespace":"public"},"post_tag":{"model":"PostTag","namespace":"public"},"tag":{"model":"Tag","namespace":"public"},"task":{"model":"Task","namespace":"public"},"user":{"model":"User","namespace":"public"}},"schemaVersion":"1","storage":{"namespaces":{"public":{"entries":{"table":{"bug":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"severity":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"stepsToRepro":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"bug"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"feature":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"priority":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"targetRelease":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"feature"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"post":{"checks":[{"expression":"\"priority\" IN (0, 1, 2)","name":"post_priority_check_b236ace9","prefix":"post_priority_check"}],"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"embedding":{"codecId":"pg/vector@1","dataType":"pgvector/vector","nullable":true,"typeParams":{"length":1536}},"expiresAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"(now() + '7 days'::interval)","kind":"function"},"nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"impressionCount":{"codecId":"pg/int8@1","dataType":"pg/int8","nullable":true},"priority":{"codecId":"pg/int4@1","dataType":"pg/int4","default":{"kind":"literal","value":0},"nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Priority","namespaceId":"public","plane":"storage"}},"reachScore":{"codecId":"pg/unboundedint@1","dataType":"pg/numeric","nullable":true},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"viewCount":{"codecId":"pg/int8number@1","dataType":"pg/int8","nullable":true}},"foreignKeys":[{"index":{"name":"post_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"post"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["title"],"name":"post_title_search_e0dd1131","options":{"language":"english","weightGroups":[["title"]]},"prefix":"post_title_search","type":"fullText","unique":false},{"columns":["userId"],"name":"post_userId_idx_a489d58a","prefix":"post_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"post_tag":{"columns":{"postId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"tagId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"post_tag_postId_idx_a7a72715"},"source":{"columns":["postId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"post"}},{"index":{"name":"post_tag_tagId_idx_86854244"},"source":{"columns":["tagId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"tag"}}],"indexes":[{"columns":["postId"],"name":"post_tag_postId_idx_a7a72715","prefix":"post_tag_postId_idx","unique":false},{"columns":["tagId"],"name":"post_tag_tagId_idx_86854244","prefix":"post_tag_tagId_idx","unique":false}],"primaryKey":{"columns":["postId","tagId"]},"uniques":[]},"tag":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"label":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[{"columns":["label"]}]},"task":{"checks":[{"expression":"\"type\" IN ('bug', 'feature')","name":"task_type_check_cda3f5e7","prefix":"task_type_check"}],"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"description":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"status":{"codecId":"pg/text@1","dataType":"pg/text","default":{"kind":"literal","value":"open"},"nullable":false},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"type":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Task.type","namespaceId":"public","plane":"storage"}},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"task_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"task"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"task_userId_idx_a489d58a","prefix":"task_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"user":{"checks":[{"expression":"\"address\" IS NULL OR jsonb_path_match(\"address\", 'strict $.type() == \"null\" || ($.type() == \"object\" && exists($.keyvalue() ? (@.key == \"city\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"country\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"street\" && (@.value.type() == \"string\"))) && !exists($.keyvalue() ? (@.key == \"zip\" && !(@.value.type() == \"null\" || (@.value.type() == \"string\")))) && !exists($.keyvalue() ? (@.key != \"city\" && @.key != \"country\" && @.key != \"street\" && @.key != \"zip\")))') IS TRUE","name":"user_address_value_object_69432dc1","prefix":"user_address_value_object"},{"expression":"\"kind\" IN ('admin', 'user')","name":"user_kind_check_836d43ef","prefix":"user_kind_check"}],"columns":{"address":{"codecId":"pg/jsonb@1","dataType":"pg/jsonb","nullable":true},"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"displayName":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"email":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"kind":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"user_type","namespaceId":"public","plane":"storage"}}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]}},"valueObject":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}}}},"valueSet":{"Priority":{"kind":"valueSet","values":[0,1,2]},"Task.type":{"kind":"valueSet","values":["bug","feature"]},"user_type":{"kind":"valueSet","values":["admin","user"]}}},"id":"public","kind":"postgres-schema"}},"storageHash":"9301c8a3b2ec83c89b4eb12568ccb0c9e916ba199d8743ab38ed468483d6921f"},"target":"postgres","targetFamily":"sql"} diff --git a/examples/prisma-8-demo/migrations/snapshots/0ce1b6cf3e03ac1498f39eb8ef08d36d73ee2f0bc7fe368b97f1497ba050501a/contract.d.ts b/examples/prisma-8-demo/migrations/snapshots/99a4393ee9f877db228baee1164325543fa018be780fd1253250d7d81539f38e/contract.d.ts similarity index 96% rename from examples/prisma-8-demo/migrations/snapshots/0ce1b6cf3e03ac1498f39eb8ef08d36d73ee2f0bc7fe368b97f1497ba050501a/contract.d.ts rename to examples/prisma-8-demo/migrations/snapshots/99a4393ee9f877db228baee1164325543fa018be780fd1253250d7d81539f38e/contract.d.ts index 6e6569558199..89ebdece0c99 100644 --- a/examples/prisma-8-demo/migrations/snapshots/0ce1b6cf3e03ac1498f39eb8ef08d36d73ee2f0bc7fe368b97f1497ba050501a/contract.d.ts +++ b/examples/prisma-8-demo/migrations/snapshots/99a4393ee9f877db228baee1164325543fa018be780fd1253250d7d81539f38e/contract.d.ts @@ -25,11 +25,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@prisma/orm-postgres/contract/types'; export type StorageHash = - StorageHashBase<'0ce1b6cf3e03ac1498f39eb8ef08d36d73ee2f0bc7fe368b97f1497ba050501a'>; + StorageHashBase<'99a4393ee9f877db228baee1164325543fa018be780fd1253250d7d81539f38e'>; export type ExecutionHash = ExecutionHashBase<'eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655'>; export type ProfileHash = @@ -261,15 +261,15 @@ type DefaultLiteralValue = CodecId extends keyo : Encoded; type ParameterizedOutputTypeFunctions = PgParameterizedOutputTypes | PgVectorParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -283,6 +283,9 @@ type FieldTypeOverrides = { readonly Post: { readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 }; }; + readonly Task: { + readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; + }; readonly User: { readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; }; @@ -294,18 +297,23 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = { readonly public: { readonly post: { readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 } }; + readonly task: { + readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; + }; readonly user: { readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; }; @@ -843,11 +851,37 @@ type ContractBase = Omit< }; readonly valueSet: { readonly Priority: { readonly kind: 'valueSet'; readonly values: readonly [0, 1, 2] }; + readonly 'Task.type': { + readonly kind: 'valueSet'; + readonly values: readonly ['bug', 'feature']; + }; readonly user_type: { readonly kind: 'valueSet'; readonly values: readonly ['admin', 'user']; }; }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + }; + }; }; }; }; @@ -1161,7 +1195,13 @@ type ContractBase = Omit< readonly fields: { readonly address: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: 'public'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; }; readonly createdAt: { readonly nullable: false; @@ -1219,28 +1259,6 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - }; - }; readonly enum: { readonly Priority: { readonly codecId: 'pg/int4@1'; @@ -1396,28 +1414,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/examples/prisma-8-demo/migrations/snapshots/99a4393ee9f877db228baee1164325543fa018be780fd1253250d7d81539f38e/contract.json b/examples/prisma-8-demo/migrations/snapshots/99a4393ee9f877db228baee1164325543fa018be780fd1253250d7d81539f38e/contract.json new file mode 100644 index 000000000000..e646c1acd07b --- /dev/null +++ b/examples/prisma-8-demo/migrations/snapshots/99a4393ee9f877db228baee1164325543fa018be780fd1253250d7d81539f38e/contract.json @@ -0,0 +1 @@ +{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{"postgres":{"distinctOn":true,"forKeyShare":true,"forNoKeyUpdate":true,"jsonAgg":true,"lateral":true,"limit":true,"orderBy":true,"pgvector.cosine":true,"returning":true},"sql":{"checkConstraint":true,"defaultInInsert":true,"enums":true,"forShare":true,"forUpdate":true,"insertOnConflictSkip":true,"insertOnConflictWithoutTarget":true,"lateral":true,"lockNowait":true,"lockOf":true,"lockSkipLocked":true,"returning":true,"scalarList":true}},"domain":{"namespaces":{"public":{"enum":{"Priority":{"codecId":"pg/int4@1","members":[{"name":"Low","value":0},{"name":"High","value":1},{"name":"Urgent","value":2}]},"user_type":{"codecId":"pg/text@1","members":[{"name":"admin","value":"admin"},{"name":"user","value":"user"}]}},"models":{"Bug":{"base":{"model":"Task","namespace":"public"},"fields":{"severity":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"stepsToRepro":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"severity":{"column":"severity"},"stepsToRepro":{"column":"stepsToRepro"}},"namespaceId":"public","table":"bug"}},"Feature":{"base":{"model":"Task","namespace":"public"},"fields":{"priority":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"targetRelease":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"priority":{"column":"priority"},"targetRelease":{"column":"targetRelease"}},"namespaceId":"public","table":"feature"}},"Post":{"fields":{"body":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"embedding":{"nullable":true,"type":{"codecId":"pg/vector@1","kind":"scalar","typeParams":{"length":1536}}},"expiresAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"impressionCount":{"nullable":true,"type":{"codecId":"pg/int8@1","kind":"scalar"}},"priority":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"Priority","namespaceId":"public","plane":"domain"}},"reachScore":{"nullable":true,"type":{"codecId":"pg/unboundedint@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"viewCount":{"nullable":true,"type":{"codecId":"pg/int8number@1","kind":"scalar"}}},"relations":{"tags":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["postId"]},"through":{"childColumns":["tagId"],"namespaceId":"public","parentColumns":["postId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Tag","namespace":"public"}},"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"body":{"column":"body"},"createdAt":{"column":"createdAt"},"embedding":{"column":"embedding"},"expiresAt":{"column":"expiresAt"},"id":{"column":"id"},"impressionCount":{"column":"impressionCount"},"priority":{"column":"priority"},"reachScore":{"column":"reachScore"},"title":{"column":"title"},"userId":{"column":"userId"},"viewCount":{"column":"viewCount"}},"namespaceId":"public","table":"post"}},"PostTag":{"fields":{"postId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"tagId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"post":{"cardinality":"N:1","nullable":false,"on":{"localFields":["postId"],"targetFields":["id"]},"to":{"model":"Post","namespace":"public"}},"tag":{"cardinality":"N:1","nullable":false,"on":{"localFields":["tagId"],"targetFields":["id"]},"to":{"model":"Tag","namespace":"public"}}},"storage":{"fields":{"postId":{"column":"postId"},"tagId":{"column":"tagId"}},"namespaceId":"public","table":"post_tag"}},"Tag":{"fields":{"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"label":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{"posts":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["tagId"]},"through":{"childColumns":["postId"],"namespaceId":"public","parentColumns":["tagId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Post","namespace":"public"}}},"storage":{"fields":{"id":{"column":"id"},"label":{"column":"label"}},"namespaceId":"public","table":"tag"}},"Task":{"discriminator":{"field":"type"},"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"description":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"status":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"type":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"description":{"column":"description"},"id":{"column":"id"},"status":{"column":"status"},"title":{"column":"title"},"type":{"column":"type"},"userId":{"column":"userId"}},"namespaceId":"public","table":"task"},"variants":{"bug":{"model":"Bug","namespace":"public"},"feature":{"model":"Feature","namespace":"public"}}},"User":{"fields":{"address":{"nullable":true,"type":{"entityKind":"valueObject","entityName":"Address","kind":"valueObject","namespaceId":"public","plane":"storage"}},"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"displayName":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"user_type","namespaceId":"public","plane":"domain"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Post","namespace":"public"}},"tasks":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Task","namespace":"public"}}},"storage":{"fields":{"address":{"column":"address"},"createdAt":{"column":"createdAt"},"displayName":{"column":"displayName"},"email":{"column":"email"},"id":{"column":"id"},"kind":{"column":"kind"}},"namespaceId":"public","table":"user"}}}}}},"execution":{"executionHash":"eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655","mutations":{"defaults":[{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"post","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"tag","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"task","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"user","field":"id","namespace":"public"}}]}},"extensions":{"demo/engagement-stats":{"familyId":"sql","id":"demo/engagement-stats","kind":"extension","targetId":"postgres","types":{"aggregateDescriptors":[{"input":{"kind":"trait","trait":"numeric"},"nullable":true,"operation":"stddev","output":{"codecId":"pg/numeric@1","kind":"codec"}}]},"version":"0.0.1"},"pgvector":{"capabilities":{"postgres":{"pgvector.cosine":true}},"familyId":"sql","id":"pgvector","kind":"extension","targetId":"postgres","types":{"codecTypes":{"import":{"alias":"PgVectorTypes","named":"CodecTypes","package":"@internal/extension-pgvector/codec-types"},"parameterizedOutputTypes":{"alias":"PgVectorParameterizedOutputTypes","named":"ParameterizedOutputTypesForTypeParams","package":"@internal/extension-pgvector/codec-types"}},"operationTypes":{"import":{"alias":"PgVectorOperationTypes","named":"OperationTypes","package":"@internal/extension-pgvector/operation-types"}},"queryOperationTypes":{"import":{"alias":"PgVectorQueryOperationTypes","named":"QueryOperationTypes","package":"@internal/extension-pgvector/operation-types"}}},"version":"0.0.1"}},"meta":{},"profileHash":"3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2","roots":{"post":{"model":"Post","namespace":"public"},"post_tag":{"model":"PostTag","namespace":"public"},"tag":{"model":"Tag","namespace":"public"},"task":{"model":"Task","namespace":"public"},"user":{"model":"User","namespace":"public"}},"schemaVersion":"1","storage":{"namespaces":{"public":{"entries":{"table":{"bug":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"severity":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"stepsToRepro":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"bug"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"feature":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"priority":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"targetRelease":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"feature"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"post":{"checks":[{"expression":"\"priority\" IN (0, 1, 2)","name":"post_priority_check_b236ace9","prefix":"post_priority_check"}],"columns":{"body":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true},"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"embedding":{"codecId":"pg/vector@1","dataType":"pgvector/vector","nullable":true,"typeParams":{"length":1536}},"expiresAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"(now() + '7 days'::interval)","kind":"function"},"nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"impressionCount":{"codecId":"pg/int8@1","dataType":"pg/int8","nullable":true},"priority":{"codecId":"pg/int4@1","dataType":"pg/int4","default":{"kind":"literal","value":0},"nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Priority","namespaceId":"public","plane":"storage"}},"reachScore":{"codecId":"pg/unboundedint@1","dataType":"pg/numeric","nullable":true},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"viewCount":{"codecId":"pg/int8number@1","dataType":"pg/int8","nullable":true}},"foreignKeys":[{"index":{"name":"post_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"post"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["title","body"],"name":"post_search_f03270be","options":{"language":"english","weightGroups":[["title"],["body"]]},"prefix":"post_search","type":"fullText","unique":false},{"columns":["title"],"name":"post_title_search_e0dd1131","options":{"language":"english","weightGroups":[["title"]]},"prefix":"post_title_search","type":"fullText","unique":false},{"columns":["userId"],"name":"post_userId_idx_a489d58a","prefix":"post_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"post_tag":{"columns":{"postId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"tagId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"post_tag_postId_idx_a7a72715"},"source":{"columns":["postId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"post"}},{"index":{"name":"post_tag_tagId_idx_86854244"},"source":{"columns":["tagId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"tag"}}],"indexes":[{"columns":["postId"],"name":"post_tag_postId_idx_a7a72715","prefix":"post_tag_postId_idx","unique":false},{"columns":["tagId"],"name":"post_tag_tagId_idx_86854244","prefix":"post_tag_tagId_idx","unique":false}],"primaryKey":{"columns":["postId","tagId"]},"uniques":[]},"tag":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"label":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[{"columns":["label"]}]},"task":{"checks":[{"expression":"\"type\" IN ('bug', 'feature')","name":"task_type_check_cda3f5e7","prefix":"task_type_check"}],"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"description":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"status":{"codecId":"pg/text@1","dataType":"pg/text","default":{"kind":"literal","value":"open"},"nullable":false},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"type":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Task.type","namespaceId":"public","plane":"storage"}},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"task_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"task"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"task_userId_idx_a489d58a","prefix":"task_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"user":{"checks":[{"expression":"\"address\" IS NULL OR jsonb_path_match(\"address\", 'strict $.type() == \"null\" || ($.type() == \"object\" && exists($.keyvalue() ? (@.key == \"city\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"country\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"street\" && (@.value.type() == \"string\"))) && !exists($.keyvalue() ? (@.key == \"zip\" && !(@.value.type() == \"null\" || (@.value.type() == \"string\")))) && !exists($.keyvalue() ? (@.key != \"city\" && @.key != \"country\" && @.key != \"street\" && @.key != \"zip\")))') IS TRUE","name":"user_address_value_object_69432dc1","prefix":"user_address_value_object"},{"expression":"\"kind\" IN ('admin', 'user')","name":"user_kind_check_836d43ef","prefix":"user_kind_check"}],"columns":{"address":{"codecId":"pg/jsonb@1","dataType":"pg/jsonb","nullable":true},"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"displayName":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"email":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"kind":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"user_type","namespaceId":"public","plane":"storage"}}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]}},"valueObject":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}}}},"valueSet":{"Priority":{"kind":"valueSet","values":[0,1,2]},"Task.type":{"kind":"valueSet","values":["bug","feature"]},"user_type":{"kind":"valueSet","values":["admin","user"]}}},"id":"public","kind":"postgres-schema"}},"storageHash":"99a4393ee9f877db228baee1164325543fa018be780fd1253250d7d81539f38e"},"target":"postgres","targetFamily":"sql"} diff --git a/examples/prisma-8-demo/migrations/snapshots/9fc3736e0efd980892a19f4d704ca30bcd0de243080e78ed1279b1f3a50f9a7e/contract.d.ts b/examples/prisma-8-demo/migrations/snapshots/9fc3736e0efd980892a19f4d704ca30bcd0de243080e78ed1279b1f3a50f9a7e/contract.d.ts deleted file mode 100644 index 987c5a7e3213..000000000000 --- a/examples/prisma-8-demo/migrations/snapshots/9fc3736e0efd980892a19f4d704ca30bcd0de243080e78ed1279b1f3a50f9a7e/contract.d.ts +++ /dev/null @@ -1,1447 +0,0 @@ -// ⚠️ GENERATED FILE - DO NOT EDIT -// This file is automatically generated by 'prisma contract emit'. -// To regenerate, run: prisma contract emit -import type { - CodecTypes as PgVectorTypes, - Vector, -} from '@prisma/orm-extension-pgvector/codec-types'; -import type { QueryOperationTypes as PgVectorQueryOperationTypes } from '@prisma/orm-extension-pgvector/operation-types'; -import type { QueryOperationTypes as PgAdapterQueryOps } from '@prisma/orm-postgres/adapter/operation-types'; -import type { - Bit, - Char, - CodecTypes as PgTypes, - Interval, - JsonValue, - Numeric, - Time, - TimeString, - Timestamp, - TimestampString, - Timestamptz, - TimestamptzString, - Timetz, - VarBit, - Varchar, -} from '@prisma/orm-postgres/target/codec-types'; - -import type { - ContractWithTypeMaps, - RelationKeys, - TypeMaps as TypeMapsType, -} from '@prisma/orm-postgres/family-contract/types'; -import type { - Contract as ContractType, - ExecutionHashBase, - NamespaceId, - ProfileHashBase, - StorageHashBase, -} from '@prisma/orm-postgres/contract/types'; - -export type StorageHash = - StorageHashBase<'9fc3736e0efd980892a19f4d704ca30bcd0de243080e78ed1279b1f3a50f9a7e'>; -export type ExecutionHash = - ExecutionHashBase<'e592be8e097b3e2b4958da0b3ed54265d1bfa0523295fa03e03af2364cccbd08'>; -export type ProfileHash = - ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; - -export type CodecTypes = PgTypes & PgVectorTypes; -export type LaneCodecTypes = CodecTypes; -export type QueryOperationTypes = PgAdapterQueryOps & - PgVectorQueryOperationTypes; -export type AggregateTypes = { - readonly avg: { - readonly byCodec: { - readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/float4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - }; - }; - readonly avgDecimal: { - readonly byCodec: { - readonly 'pg/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - }; - }; - readonly count: { - readonly byCodec: {}; - readonly withoutInput: { readonly output: 'pg/int8number@1'; readonly nullable: false }; - readonly anyInput: { readonly output: 'pg/int8number@1'; readonly nullable: false }; - }; - readonly countBigInt: { - readonly byCodec: {}; - readonly withoutInput: { readonly output: 'pg/int8@1'; readonly nullable: false }; - readonly anyInput: { readonly output: 'pg/int8@1'; readonly nullable: false }; - }; - readonly max: { - readonly byCodec: { - readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true }; - readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true }; - readonly 'pg/date-temporal@1': { - readonly output: 'pg/date-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true }; - readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true }; - readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; - readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true }; - readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; - readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true }; - readonly 'pg/time-temporal@1': { - readonly output: 'pg/time-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-string@1': { - readonly output: 'pg/timestamp-string@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-temporal@1': { - readonly output: 'pg/timestamp-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-string@1': { - readonly output: 'pg/timestamptz-string@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-temporal@1': { - readonly output: 'pg/timestamptz-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { - readonly output: 'pg/unboundedint@1'; - readonly nullable: true; - }; - readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true }; - readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true }; - readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true }; - readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - }; - }; - readonly min: { - readonly byCodec: { - readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true }; - readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true }; - readonly 'pg/date-temporal@1': { - readonly output: 'pg/date-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true }; - readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true }; - readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; - readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true }; - readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; - readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true }; - readonly 'pg/time-temporal@1': { - readonly output: 'pg/time-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-string@1': { - readonly output: 'pg/timestamp-string@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-temporal@1': { - readonly output: 'pg/timestamp-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-string@1': { - readonly output: 'pg/timestamptz-string@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-temporal@1': { - readonly output: 'pg/timestamptz-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { - readonly output: 'pg/unboundedint@1'; - readonly nullable: true; - }; - readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true }; - readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true }; - readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true }; - readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - }; - }; - readonly stddev: { - readonly byCodec: { - readonly 'pg/float@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/float4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/float8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'sql/float@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - }; - }; - readonly sum: { - readonly byCodec: { - readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; - readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { - readonly output: 'pg/unboundedint@1'; - readonly nullable: true; - }; - readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - }; - }; - readonly sumBigInt: { - readonly byCodec: { - readonly 'pg/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { - readonly output: 'pg/unboundedint@1'; - readonly nullable: true; - }; - readonly 'sql/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - }; - }; -}; -type DefaultLiteralValue = CodecId extends keyof CodecTypes - ? Encoded extends CodecTypes[CodecId]['json'] - ? Encoded - : CodecTypes[CodecId]['json'] - : Encoded; -export type AddressOutput = { - readonly street: CodecTypes['pg/text@1']['output']; - readonly city: CodecTypes['pg/text@1']['output']; - readonly zip: CodecTypes['pg/text@1']['output'] | null; - readonly country: CodecTypes['pg/text@1']['output']; -}; -export type AddressInput = { - readonly street: CodecTypes['pg/text@1']['input']; - readonly city: CodecTypes['pg/text@1']['input']; - readonly zip: CodecTypes['pg/text@1']['input'] | null; - readonly country: CodecTypes['pg/text@1']['input']; -}; -export type FieldOutputTypes = { - readonly public: { - readonly Bug: { - readonly severity: CodecTypes['pg/text@1']['output']; - readonly stepsToRepro: CodecTypes['pg/text@1']['output'] | null; - }; - readonly Feature: { - readonly priority: CodecTypes['pg/text@1']['output']; - readonly targetRelease: CodecTypes['pg/text@1']['output'] | null; - }; - readonly Post: { - readonly id: CodecTypes['pg/uuid@1']['output']; - readonly title: CodecTypes['pg/text@1']['output']; - readonly userId: CodecTypes['pg/uuid@1']['output']; - readonly priority: 'low' | 'high' | 'urgent'; - readonly createdAt: CodecTypes['pg/timestamptz-temporal@1']['output']; - readonly embedding: Vector<1536> | null; - }; - readonly PostTag: { - readonly postId: CodecTypes['pg/uuid@1']['output']; - readonly tagId: CodecTypes['pg/uuid@1']['output']; - }; - readonly Tag: { - readonly id: CodecTypes['pg/uuid@1']['output']; - readonly label: CodecTypes['pg/text@1']['output']; - }; - readonly Task: { - readonly id: CodecTypes['pg/uuid@1']['output']; - readonly title: CodecTypes['pg/text@1']['output']; - readonly description: CodecTypes['pg/text@1']['output'] | null; - readonly status: CodecTypes['pg/text@1']['output']; - readonly type: CodecTypes['pg/text@1']['output']; - readonly userId: CodecTypes['pg/uuid@1']['output']; - readonly createdAt: CodecTypes['pg/timestamptz-temporal@1']['output']; - }; - readonly User: { - readonly id: CodecTypes['pg/uuid@1']['output']; - readonly email: CodecTypes['pg/text@1']['output']; - readonly displayName: CodecTypes['pg/text@1']['output']; - readonly createdAt: CodecTypes['pg/timestamptz-temporal@1']['output']; - readonly kind: 'admin' | 'user'; - readonly address: AddressOutput | null; - }; - }; -}; -export type FieldInputTypes = { - readonly public: { - readonly Bug: { - readonly severity: CodecTypes['pg/text@1']['input']; - readonly stepsToRepro: CodecTypes['pg/text@1']['input'] | null; - }; - readonly Feature: { - readonly priority: CodecTypes['pg/text@1']['input']; - readonly targetRelease: CodecTypes['pg/text@1']['input'] | null; - }; - readonly Post: { - readonly id: CodecTypes['pg/uuid@1']['input']; - readonly title: CodecTypes['pg/text@1']['input']; - readonly userId: CodecTypes['pg/uuid@1']['input']; - readonly priority: 'low' | 'high' | 'urgent'; - readonly createdAt: CodecTypes['pg/timestamptz-temporal@1']['input']; - readonly embedding: CodecTypes['pg/vector@1']['input'] | null; - }; - readonly PostTag: { - readonly postId: CodecTypes['pg/uuid@1']['input']; - readonly tagId: CodecTypes['pg/uuid@1']['input']; - }; - readonly Tag: { - readonly id: CodecTypes['pg/uuid@1']['input']; - readonly label: CodecTypes['pg/text@1']['input']; - }; - readonly Task: { - readonly id: CodecTypes['pg/uuid@1']['input']; - readonly title: CodecTypes['pg/text@1']['input']; - readonly description: CodecTypes['pg/text@1']['input'] | null; - readonly status: CodecTypes['pg/text@1']['input']; - readonly type: CodecTypes['pg/text@1']['input']; - readonly userId: CodecTypes['pg/uuid@1']['input']; - readonly createdAt: CodecTypes['pg/timestamptz-temporal@1']['input']; - }; - readonly User: { - readonly id: CodecTypes['pg/uuid@1']['input']; - readonly email: CodecTypes['pg/text@1']['input']; - readonly displayName: CodecTypes['pg/text@1']['input']; - readonly createdAt: CodecTypes['pg/timestamptz-temporal@1']['input']; - readonly kind: 'admin' | 'user'; - readonly address: AddressInput | null; - }; - }; -}; -export type StorageColumnTypes = { - readonly public: { - readonly bug: { - readonly id: CodecTypes['pg/uuid@1']['output']; - readonly severity: CodecTypes['pg/text@1']['output']; - readonly stepsToRepro: CodecTypes['pg/text@1']['output'] | null; - }; - readonly feature: { - readonly id: CodecTypes['pg/uuid@1']['output']; - readonly priority: CodecTypes['pg/text@1']['output']; - readonly targetRelease: CodecTypes['pg/text@1']['output'] | null; - }; - readonly post: { - readonly createdAt: CodecTypes['pg/timestamptz-temporal@1']['output']; - readonly embedding: Vector<1536> | null; - readonly id: CodecTypes['pg/uuid@1']['output']; - readonly priority: 'low' | 'high' | 'urgent'; - readonly title: CodecTypes['pg/text@1']['output']; - readonly userId: CodecTypes['pg/uuid@1']['output']; - }; - readonly post_tag: { - readonly postId: CodecTypes['pg/uuid@1']['output']; - readonly tagId: CodecTypes['pg/uuid@1']['output']; - }; - readonly tag: { - readonly id: CodecTypes['pg/uuid@1']['output']; - readonly label: CodecTypes['pg/text@1']['output']; - }; - readonly task: { - readonly createdAt: CodecTypes['pg/timestamptz-temporal@1']['output']; - readonly description: CodecTypes['pg/text@1']['output'] | null; - readonly id: CodecTypes['pg/uuid@1']['output']; - readonly status: CodecTypes['pg/text@1']['output']; - readonly title: CodecTypes['pg/text@1']['output']; - readonly type: CodecTypes['pg/text@1']['output']; - readonly userId: CodecTypes['pg/uuid@1']['output']; - }; - readonly user: { - readonly address: CodecTypes['pg/jsonb@1']['output'] | null; - readonly createdAt: CodecTypes['pg/timestamptz-temporal@1']['output']; - readonly displayName: CodecTypes['pg/text@1']['output']; - readonly email: CodecTypes['pg/text@1']['output']; - readonly id: CodecTypes['pg/uuid@1']['output']; - readonly kind: 'admin' | 'user'; - }; - }; -}; -export type StorageColumnInputTypes = { - readonly public: { - readonly bug: { - readonly id: CodecTypes['pg/uuid@1']['input']; - readonly severity: CodecTypes['pg/text@1']['input']; - readonly stepsToRepro: CodecTypes['pg/text@1']['input'] | null; - }; - readonly feature: { - readonly id: CodecTypes['pg/uuid@1']['input']; - readonly priority: CodecTypes['pg/text@1']['input']; - readonly targetRelease: CodecTypes['pg/text@1']['input'] | null; - }; - readonly post: { - readonly createdAt: CodecTypes['pg/timestamptz-temporal@1']['input']; - readonly embedding: CodecTypes['pg/vector@1']['input'] | null; - readonly id: CodecTypes['pg/uuid@1']['input']; - readonly priority: 'low' | 'high' | 'urgent'; - readonly title: CodecTypes['pg/text@1']['input']; - readonly userId: CodecTypes['pg/uuid@1']['input']; - }; - readonly post_tag: { - readonly postId: CodecTypes['pg/uuid@1']['input']; - readonly tagId: CodecTypes['pg/uuid@1']['input']; - }; - readonly tag: { - readonly id: CodecTypes['pg/uuid@1']['input']; - readonly label: CodecTypes['pg/text@1']['input']; - }; - readonly task: { - readonly createdAt: CodecTypes['pg/timestamptz-temporal@1']['input']; - readonly description: CodecTypes['pg/text@1']['input'] | null; - readonly id: CodecTypes['pg/uuid@1']['input']; - readonly status: CodecTypes['pg/text@1']['input']; - readonly title: CodecTypes['pg/text@1']['input']; - readonly type: CodecTypes['pg/text@1']['input']; - readonly userId: CodecTypes['pg/uuid@1']['input']; - }; - readonly user: { - readonly address: CodecTypes['pg/jsonb@1']['input'] | null; - readonly createdAt: CodecTypes['pg/timestamptz-temporal@1']['input']; - readonly displayName: CodecTypes['pg/text@1']['input']; - readonly email: CodecTypes['pg/text@1']['input']; - readonly id: CodecTypes['pg/uuid@1']['input']; - readonly kind: 'admin' | 'user'; - }; - }; -}; - -export namespace Models { - export type public_User = { - id: CodecTypes['pg/uuid@1']['output']; - email: CodecTypes['pg/text@1']['output']; - displayName: CodecTypes['pg/text@1']['output']; - createdAt: CodecTypes['pg/timestamptz-temporal@1']['output']; - kind: 'admin' | 'user'; - address: AddressOutput | null; - posts: public_Post[]; - tasks: public_AnyTask[]; - readonly [RelationKeys]?: 'posts' | 'tasks'; - }; - export type public_Post = { - id: CodecTypes['pg/uuid@1']['output']; - title: CodecTypes['pg/text@1']['output']; - userId: CodecTypes['pg/uuid@1']['output']; - priority: 'low' | 'high' | 'urgent'; - createdAt: CodecTypes['pg/timestamptz-temporal@1']['output']; - embedding: Vector<1536> | null; - tags: public_Tag[]; - user: public_User; - readonly [RelationKeys]?: 'tags' | 'user'; - }; - export type public_Tag = { - id: CodecTypes['pg/uuid@1']['output']; - label: CodecTypes['pg/text@1']['output']; - posts: public_Post[]; - readonly [RelationKeys]?: 'posts'; - }; - export type public_PostTag = { - postId: CodecTypes['pg/uuid@1']['output']; - tagId: CodecTypes['pg/uuid@1']['output']; - post: public_Post; - tag: public_Tag; - readonly [RelationKeys]?: 'post' | 'tag'; - }; - export type public_Task = { - id: CodecTypes['pg/uuid@1']['output']; - title: CodecTypes['pg/text@1']['output']; - description: CodecTypes['pg/text@1']['output'] | null; - status: CodecTypes['pg/text@1']['output']; - type: 'bug' | 'feature'; - userId: CodecTypes['pg/uuid@1']['output']; - createdAt: CodecTypes['pg/timestamptz-temporal@1']['output']; - user: public_User; - readonly [RelationKeys]?: 'user'; - }; - export type public_Bug = { - id: CodecTypes['pg/uuid@1']['output']; - title: CodecTypes['pg/text@1']['output']; - description: CodecTypes['pg/text@1']['output'] | null; - status: CodecTypes['pg/text@1']['output']; - type: 'bug'; - userId: CodecTypes['pg/uuid@1']['output']; - createdAt: CodecTypes['pg/timestamptz-temporal@1']['output']; - severity: CodecTypes['pg/text@1']['output']; - stepsToRepro: CodecTypes['pg/text@1']['output'] | null; - user: public_User; - readonly [RelationKeys]?: 'user'; - }; - export type public_Feature = { - id: CodecTypes['pg/uuid@1']['output']; - title: CodecTypes['pg/text@1']['output']; - description: CodecTypes['pg/text@1']['output'] | null; - status: CodecTypes['pg/text@1']['output']; - type: 'feature'; - userId: CodecTypes['pg/uuid@1']['output']; - createdAt: CodecTypes['pg/timestamptz-temporal@1']['output']; - priority: CodecTypes['pg/text@1']['output']; - targetRelease: CodecTypes['pg/text@1']['output'] | null; - user: public_User; - readonly [RelationKeys]?: 'user'; - }; - export type public_AnyTask = public_Bug | public_Feature; -} - -export declare const models: { - public: { - User: Models.public_User; - Post: Models.public_Post; - Tag: Models.public_Tag; - PostTag: Models.public_PostTag; - Task: Models.public_Task; - Bug: Models.public_Bug; - Feature: Models.public_Feature; - AnyTask: Models.public_AnyTask; - }; -}; - -export type TypeMaps = TypeMapsType< - CodecTypes, - QueryOperationTypes, - FieldOutputTypes, - FieldInputTypes, - StorageColumnTypes, - StorageColumnInputTypes, - AggregateTypes ->; - -type ContractBase = Omit< - ContractType<{ - readonly namespaces: { - readonly public: { - readonly id: 'public'; - readonly kind: 'postgres-schema'; - readonly entries: { - readonly table: { - readonly bug: { - columns: { - readonly id: { - readonly many: false; - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - }; - readonly severity: { - readonly many: false; - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - }; - readonly stepsToRepro: { - readonly many: false; - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: true; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly []; - foreignKeys: readonly [ - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'bug'; - readonly columns: readonly ['id']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'task'; - readonly columns: readonly ['id']; - }; - }, - ]; - }; - readonly feature: { - columns: { - readonly id: { - readonly many: false; - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - }; - readonly priority: { - readonly many: false; - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - }; - readonly targetRelease: { - readonly many: false; - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: true; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly []; - foreignKeys: readonly [ - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'feature'; - readonly columns: readonly ['id']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'task'; - readonly columns: readonly ['id']; - }; - }, - ]; - }; - readonly post: { - columns: { - readonly id: { - readonly many: false; - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - }; - readonly title: { - readonly many: false; - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - }; - readonly userId: { - readonly many: false; - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - }; - readonly priority: { - readonly many: false; - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly default: { - readonly kind: 'literal'; - readonly value: DefaultLiteralValue<'pg/text@1', 'low'>; - }; - }; - readonly createdAt: { - readonly many: false; - readonly dataType: 'pg/timestamptz'; - readonly codecId: 'pg/timestamptz-temporal@1'; - readonly nullable: false; - readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; - }; - readonly embedding: { - readonly many: false; - readonly dataType: 'pgvector/vector'; - readonly codecId: 'pg/vector@1'; - readonly nullable: true; - readonly typeParams: { readonly length: 1536 }; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly [ - { - readonly name: 'post_userId_idx_a489d58a'; - readonly prefix: 'post_userId_idx'; - readonly columns: readonly ['userId']; - readonly unique: false; - }, - ]; - foreignKeys: readonly [ - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'post'; - readonly columns: readonly ['userId']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'user'; - readonly columns: readonly ['id']; - }; - }, - ]; - }; - readonly post_tag: { - columns: { - readonly postId: { - readonly many: false; - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - }; - readonly tagId: { - readonly many: false; - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - }; - }; - primaryKey: { readonly columns: readonly ['postId', 'tagId'] }; - uniques: readonly []; - indexes: readonly [ - { - readonly name: 'post_tag_postId_idx_a7a72715'; - readonly prefix: 'post_tag_postId_idx'; - readonly columns: readonly ['postId']; - readonly unique: false; - }, - { - readonly name: 'post_tag_tagId_idx_86854244'; - readonly prefix: 'post_tag_tagId_idx'; - readonly columns: readonly ['tagId']; - readonly unique: false; - }, - ]; - foreignKeys: readonly [ - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'post_tag'; - readonly columns: readonly ['postId']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'post'; - readonly columns: readonly ['id']; - }; - }, - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'post_tag'; - readonly columns: readonly ['tagId']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'tag'; - readonly columns: readonly ['id']; - }; - }, - ]; - }; - readonly tag: { - columns: { - readonly id: { - readonly many: false; - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - }; - readonly label: { - readonly many: false; - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly [{ readonly columns: readonly ['label'] }]; - indexes: readonly []; - foreignKeys: readonly []; - }; - readonly task: { - columns: { - readonly id: { - readonly many: false; - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - }; - readonly title: { - readonly many: false; - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - }; - readonly description: { - readonly many: false; - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: true; - }; - readonly status: { - readonly many: false; - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly default: { - readonly kind: 'literal'; - readonly value: DefaultLiteralValue<'pg/text@1', 'open'>; - }; - }; - readonly type: { - readonly many: false; - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - }; - readonly userId: { - readonly many: false; - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - }; - readonly createdAt: { - readonly many: false; - readonly dataType: 'pg/timestamptz'; - readonly codecId: 'pg/timestamptz-temporal@1'; - readonly nullable: false; - readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly [ - { - readonly name: 'task_userId_idx_a489d58a'; - readonly prefix: 'task_userId_idx'; - readonly columns: readonly ['userId']; - readonly unique: false; - }, - ]; - foreignKeys: readonly [ - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'task'; - readonly columns: readonly ['userId']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'user'; - readonly columns: readonly ['id']; - }; - }, - ]; - }; - readonly user: { - columns: { - readonly id: { - readonly many: false; - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - }; - readonly email: { - readonly many: false; - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - }; - readonly displayName: { - readonly many: false; - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - }; - readonly createdAt: { - readonly many: false; - readonly dataType: 'pg/timestamptz'; - readonly codecId: 'pg/timestamptz-temporal@1'; - readonly nullable: false; - readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; - }; - readonly kind: { - readonly many: false; - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - }; - readonly address: { - readonly many: false; - readonly dataType: 'pg/jsonb'; - readonly codecId: 'pg/jsonb@1'; - readonly nullable: true; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly []; - foreignKeys: readonly []; - }; - }; - readonly valueSet: { - readonly Priority: { - readonly kind: 'valueSet'; - readonly values: readonly ['low', 'high', 'urgent']; - }; - readonly user_type: { - readonly kind: 'valueSet'; - readonly values: readonly ['admin', 'user']; - }; - }; - }; - }; - }; - readonly storageHash: StorageHash; - }>, - 'roots' | 'domain' -> & { - readonly target: 'postgres'; - readonly targetFamily: 'sql'; - readonly roots: { - readonly user: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; - readonly post: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; - readonly tag: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; - readonly post_tag: { readonly namespace: 'public' & NamespaceId; readonly model: 'PostTag' }; - readonly task: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; - }; - readonly domain: { - readonly namespaces: { - readonly public: { - readonly models: { - readonly Bug: { - readonly fields: { - readonly severity: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly stepsToRepro: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - readonly relations: Record; - readonly storage: { - readonly table: 'bug'; - readonly namespaceId: 'public'; - readonly fields: { - readonly severity: { readonly column: 'severity' }; - readonly stepsToRepro: { readonly column: 'stepsToRepro' }; - }; - }; - readonly base: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; - }; - readonly Feature: { - readonly fields: { - readonly priority: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly targetRelease: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - readonly relations: Record; - readonly storage: { - readonly table: 'feature'; - readonly namespaceId: 'public'; - readonly fields: { - readonly priority: { readonly column: 'priority' }; - readonly targetRelease: { readonly column: 'targetRelease' }; - }; - }; - readonly base: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; - }; - readonly Post: { - readonly fields: { - readonly id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly title: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly priority: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly createdAt: { - readonly nullable: false; - readonly type: { - readonly kind: 'scalar'; - readonly codecId: 'pg/timestamptz-temporal@1'; - }; - }; - readonly embedding: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/vector@1' }; - }; - }; - readonly relations: { - readonly tags: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; - readonly cardinality: 'N:M'; - readonly on: { - readonly localFields: readonly ['id']; - readonly targetFields: readonly ['postId']; - }; - readonly through: { - readonly table: 'post_tag'; - readonly namespaceId: 'public'; - readonly parentColumns: readonly ['postId']; - readonly childColumns: readonly ['tagId']; - readonly targetColumns: readonly ['id']; - }; - }; - readonly user: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['userId']; - readonly targetFields: readonly ['id']; - }; - }; - }; - readonly storage: { - readonly table: 'post'; - readonly namespaceId: 'public'; - readonly fields: { - readonly id: { readonly column: 'id' }; - readonly title: { readonly column: 'title' }; - readonly userId: { readonly column: 'userId' }; - readonly priority: { readonly column: 'priority' }; - readonly createdAt: { readonly column: 'createdAt' }; - readonly embedding: { readonly column: 'embedding' }; - }; - }; - }; - readonly PostTag: { - readonly fields: { - readonly postId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly tagId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - }; - readonly relations: { - readonly post: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['postId']; - readonly targetFields: readonly ['id']; - }; - }; - readonly tag: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['tagId']; - readonly targetFields: readonly ['id']; - }; - }; - }; - readonly storage: { - readonly table: 'post_tag'; - readonly namespaceId: 'public'; - readonly fields: { - readonly postId: { readonly column: 'postId' }; - readonly tagId: { readonly column: 'tagId' }; - }; - }; - }; - readonly Tag: { - readonly fields: { - readonly id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly label: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - readonly relations: { - readonly posts: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; - readonly cardinality: 'N:M'; - readonly on: { - readonly localFields: readonly ['id']; - readonly targetFields: readonly ['tagId']; - }; - readonly through: { - readonly table: 'post_tag'; - readonly namespaceId: 'public'; - readonly parentColumns: readonly ['tagId']; - readonly childColumns: readonly ['postId']; - readonly targetColumns: readonly ['id']; - }; - }; - }; - readonly storage: { - readonly table: 'tag'; - readonly namespaceId: 'public'; - readonly fields: { - readonly id: { readonly column: 'id' }; - readonly label: { readonly column: 'label' }; - }; - }; - }; - readonly Task: { - readonly fields: { - readonly id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly title: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly description: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly status: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly type: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly createdAt: { - readonly nullable: false; - readonly type: { - readonly kind: 'scalar'; - readonly codecId: 'pg/timestamptz-temporal@1'; - }; - }; - }; - readonly relations: { - readonly user: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['userId']; - readonly targetFields: readonly ['id']; - }; - }; - }; - readonly storage: { - readonly table: 'task'; - readonly namespaceId: 'public'; - readonly fields: { - readonly id: { readonly column: 'id' }; - readonly title: { readonly column: 'title' }; - readonly description: { readonly column: 'description' }; - readonly status: { readonly column: 'status' }; - readonly type: { readonly column: 'type' }; - readonly userId: { readonly column: 'userId' }; - readonly createdAt: { readonly column: 'createdAt' }; - }; - }; - readonly discriminator: { readonly field: 'type' }; - readonly variants: { - readonly Bug: { readonly value: 'bug' }; - readonly Feature: { readonly value: 'feature' }; - }; - }; - readonly User: { - readonly fields: { - readonly id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly email: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly displayName: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly createdAt: { - readonly nullable: false; - readonly type: { - readonly kind: 'scalar'; - readonly codecId: 'pg/timestamptz-temporal@1'; - }; - }; - readonly kind: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly address: { - readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; - }; - }; - readonly relations: { - readonly posts: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; - readonly cardinality: '1:N'; - readonly on: { - readonly localFields: readonly ['id']; - readonly targetFields: readonly ['userId']; - }; - }; - readonly tasks: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; - readonly cardinality: '1:N'; - readonly on: { - readonly localFields: readonly ['id']; - readonly targetFields: readonly ['userId']; - }; - }; - }; - readonly storage: { - readonly table: 'user'; - readonly namespaceId: 'public'; - readonly fields: { - readonly id: { readonly column: 'id' }; - readonly email: { readonly column: 'email' }; - readonly displayName: { readonly column: 'displayName' }; - readonly createdAt: { readonly column: 'createdAt' }; - readonly kind: { readonly column: 'kind' }; - readonly address: { readonly column: 'address' }; - }; - }; - }; - }; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - }; - }; - readonly enum: { - readonly user_type: { - readonly codecId: 'pg/text@1'; - readonly members: readonly [ - { readonly name: 'admin'; readonly value: 'admin' }, - { readonly name: 'user'; readonly value: 'user' }, - ]; - }; - readonly Priority: { - readonly codecId: 'pg/text@1'; - readonly members: readonly [ - { readonly name: 'Low'; readonly value: 'low' }, - { readonly name: 'High'; readonly value: 'high' }, - { readonly name: 'Urgent'; readonly value: 'urgent' }, - ]; - }; - }; - }; - }; - }; - readonly capabilities: { - readonly postgres: { - readonly distinctOn: true; - readonly jsonAgg: true; - readonly lateral: true; - readonly limit: true; - readonly orderBy: true; - readonly 'pgvector.cosine': true; - readonly returning: true; - }; - readonly sql: { - readonly checkConstraint: true; - readonly defaultInInsert: true; - readonly enums: true; - readonly lateral: true; - readonly returning: true; - readonly scalarList: true; - }; - }; - readonly extensions: { - readonly 'demo/engagement-stats': { - readonly familyId: 'sql'; - readonly id: 'demo/engagement-stats'; - readonly kind: 'extension'; - readonly targetId: 'postgres'; - readonly types: { - readonly aggregateDescriptors: readonly [ - { - readonly input: { readonly kind: 'trait'; readonly trait: 'numeric' }; - readonly lower: unknown; - readonly nullable: true; - readonly operation: 'stddev'; - readonly output: { readonly codecId: 'pg/numeric@1'; readonly kind: 'codec' }; - }, - ]; - }; - readonly version: '0.0.1'; - }; - readonly pgvector: { - readonly capabilities: { readonly postgres: { readonly 'pgvector.cosine': true } }; - readonly familyId: 'sql'; - readonly id: 'pgvector'; - readonly kind: 'extension'; - readonly targetId: 'postgres'; - readonly types: { - readonly codecTypes: { - readonly import: { - readonly alias: 'PgVectorTypes'; - readonly named: 'CodecTypes'; - readonly package: '@internal/extension-pgvector/codec-types'; - }; - readonly typeImports: readonly [ - { - readonly alias: 'Vector'; - readonly named: 'Vector'; - readonly package: '@internal/extension-pgvector/codec-types'; - }, - ]; - }; - readonly operationTypes: { - readonly import: { - readonly alias: 'PgVectorOperationTypes'; - readonly named: 'OperationTypes'; - readonly package: '@internal/extension-pgvector/operation-types'; - }; - }; - readonly queryOperationTypes: { - readonly import: { - readonly alias: 'PgVectorQueryOperationTypes'; - readonly named: 'QueryOperationTypes'; - readonly package: '@internal/extension-pgvector/operation-types'; - }; - }; - }; - readonly version: '0.0.1'; - }; - }; - readonly execution: { - readonly executionHash: ExecutionHash; - readonly mutations: { - readonly defaults: readonly [ - { - readonly ref: { - readonly entry: 'post'; - readonly field: 'id'; - readonly namespace: 'public'; - }; - readonly onCreate: { readonly kind: 'generator'; readonly id: 'uuidv4' }; - }, - { - readonly ref: { - readonly entry: 'tag'; - readonly field: 'id'; - readonly namespace: 'public'; - }; - readonly onCreate: { readonly kind: 'generator'; readonly id: 'uuidv4' }; - }, - { - readonly ref: { - readonly entry: 'task'; - readonly field: 'id'; - readonly namespace: 'public'; - }; - readonly onCreate: { readonly kind: 'generator'; readonly id: 'uuidv4' }; - }, - { - readonly ref: { - readonly entry: 'user'; - readonly field: 'id'; - readonly namespace: 'public'; - }; - readonly onCreate: { readonly kind: 'generator'; readonly id: 'uuidv4' }; - }, - ]; - }; - }; - readonly meta: {}; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - }; - }; - readonly profileHash: ProfileHash; -}; - -export type Contract = ContractWithTypeMaps; - -export type Namespaces = Contract['storage']['namespaces']; diff --git a/examples/prisma-8-demo/migrations/snapshots/9fc3736e0efd980892a19f4d704ca30bcd0de243080e78ed1279b1f3a50f9a7e/contract.json b/examples/prisma-8-demo/migrations/snapshots/9fc3736e0efd980892a19f4d704ca30bcd0de243080e78ed1279b1f3a50f9a7e/contract.json deleted file mode 100644 index a3c017fa8505..000000000000 --- a/examples/prisma-8-demo/migrations/snapshots/9fc3736e0efd980892a19f4d704ca30bcd0de243080e78ed1279b1f3a50f9a7e/contract.json +++ /dev/null @@ -1 +0,0 @@ -{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{"postgres":{"distinctOn":true,"jsonAgg":true,"lateral":true,"limit":true,"orderBy":true,"pgvector.cosine":true,"returning":true},"sql":{"checkConstraint":true,"defaultInInsert":true,"enums":true,"lateral":true,"returning":true,"scalarList":true}},"domain":{"namespaces":{"public":{"enum":{"Priority":{"codecId":"pg/text@1","members":[{"name":"Low","value":"low"},{"name":"High","value":"high"},{"name":"Urgent","value":"urgent"}]},"user_type":{"codecId":"pg/text@1","members":[{"name":"admin","value":"admin"},{"name":"user","value":"user"}]}},"models":{"Bug":{"base":{"model":"Task","namespace":"public"},"fields":{"severity":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"stepsToRepro":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"severity":{"column":"severity"},"stepsToRepro":{"column":"stepsToRepro"}},"namespaceId":"public","table":"bug"}},"Feature":{"base":{"model":"Task","namespace":"public"},"fields":{"priority":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"targetRelease":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"priority":{"column":"priority"},"targetRelease":{"column":"targetRelease"}},"namespaceId":"public","table":"feature"}},"Post":{"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"embedding":{"nullable":true,"type":{"codecId":"pg/vector@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"priority":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"Priority","namespaceId":"public","plane":"domain"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"tags":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["postId"]},"through":{"childColumns":["tagId"],"namespaceId":"public","parentColumns":["postId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Tag","namespace":"public"}},"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"embedding":{"column":"embedding"},"id":{"column":"id"},"priority":{"column":"priority"},"title":{"column":"title"},"userId":{"column":"userId"}},"namespaceId":"public","table":"post"}},"PostTag":{"fields":{"postId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"tagId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"post":{"cardinality":"N:1","nullable":false,"on":{"localFields":["postId"],"targetFields":["id"]},"to":{"model":"Post","namespace":"public"}},"tag":{"cardinality":"N:1","nullable":false,"on":{"localFields":["tagId"],"targetFields":["id"]},"to":{"model":"Tag","namespace":"public"}}},"storage":{"fields":{"postId":{"column":"postId"},"tagId":{"column":"tagId"}},"namespaceId":"public","table":"post_tag"}},"Tag":{"fields":{"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"label":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{"posts":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["tagId"]},"through":{"childColumns":["postId"],"namespaceId":"public","parentColumns":["tagId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Post","namespace":"public"}}},"storage":{"fields":{"id":{"column":"id"},"label":{"column":"label"}},"namespaceId":"public","table":"tag"}},"Task":{"discriminator":{"field":"type"},"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"description":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"status":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"type":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"description":{"column":"description"},"id":{"column":"id"},"status":{"column":"status"},"title":{"column":"title"},"type":{"column":"type"},"userId":{"column":"userId"}},"namespaceId":"public","table":"task"},"variants":{"Bug":{"value":"bug"},"Feature":{"value":"feature"}}},"User":{"fields":{"address":{"nullable":true,"type":{"kind":"valueObject","name":"Address"}},"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"displayName":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"user_type","namespaceId":"public","plane":"domain"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Post","namespace":"public"}},"tasks":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Task","namespace":"public"}}},"storage":{"fields":{"address":{"column":"address"},"createdAt":{"column":"createdAt"},"displayName":{"column":"displayName"},"email":{"column":"email"},"id":{"column":"id"},"kind":{"column":"kind"}},"namespaceId":"public","table":"user"}}},"valueObjects":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}}}}}}},"execution":{"executionHash":"e592be8e097b3e2b4958da0b3ed54265d1bfa0523295fa03e03af2364cccbd08","mutations":{"defaults":[{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"post","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"tag","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"task","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"user","field":"id","namespace":"public"}}]}},"extensions":{"demo/engagement-stats":{"familyId":"sql","id":"demo/engagement-stats","kind":"extension","targetId":"postgres","types":{"aggregateDescriptors":[{"input":{"kind":"trait","trait":"numeric"},"nullable":true,"operation":"stddev","output":{"codecId":"pg/numeric@1","kind":"codec"}}]},"version":"0.0.1"},"pgvector":{"capabilities":{"postgres":{"pgvector.cosine":true}},"familyId":"sql","id":"pgvector","kind":"extension","targetId":"postgres","types":{"codecTypes":{"import":{"alias":"PgVectorTypes","named":"CodecTypes","package":"@internal/extension-pgvector/codec-types"},"typeImports":[{"alias":"Vector","named":"Vector","package":"@internal/extension-pgvector/codec-types"}]},"operationTypes":{"import":{"alias":"PgVectorOperationTypes","named":"OperationTypes","package":"@internal/extension-pgvector/operation-types"}},"queryOperationTypes":{"import":{"alias":"PgVectorQueryOperationTypes","named":"QueryOperationTypes","package":"@internal/extension-pgvector/operation-types"}}},"version":"0.0.1"}},"meta":{},"profileHash":"3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2","roots":{"post":{"model":"Post","namespace":"public"},"post_tag":{"model":"PostTag","namespace":"public"},"tag":{"model":"Tag","namespace":"public"},"task":{"model":"Task","namespace":"public"},"user":{"model":"User","namespace":"public"}},"schemaVersion":"1","storage":{"namespaces":{"public":{"entries":{"table":{"bug":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"severity":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"stepsToRepro":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"bug"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"feature":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"priority":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"targetRelease":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"feature"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"post":{"checks":[{"expression":"\"priority\" IN ('low', 'high', 'urgent')","name":"post_priority_check_09b41aa0","prefix":"post_priority_check"}],"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"embedding":{"codecId":"pg/vector@1","dataType":"pgvector/vector","nullable":true,"typeParams":{"length":1536}},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"priority":{"codecId":"pg/text@1","dataType":"pg/text","default":{"kind":"literal","value":"low"},"nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Priority","namespaceId":"public","plane":"storage"}},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"source":{"columns":["userId"],"namespaceId":"public","tableName":"post"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"post_userId_idx_a489d58a","prefix":"post_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"post_tag":{"columns":{"postId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"tagId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"source":{"columns":["postId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"post"}},{"source":{"columns":["tagId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"tag"}}],"indexes":[{"columns":["postId"],"name":"post_tag_postId_idx_a7a72715","prefix":"post_tag_postId_idx","unique":false},{"columns":["tagId"],"name":"post_tag_tagId_idx_86854244","prefix":"post_tag_tagId_idx","unique":false}],"primaryKey":{"columns":["postId","tagId"]},"uniques":[]},"tag":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"label":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[{"columns":["label"]}]},"task":{"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"description":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"status":{"codecId":"pg/text@1","dataType":"pg/text","default":{"kind":"literal","value":"open"},"nullable":false},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"type":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"source":{"columns":["userId"],"namespaceId":"public","tableName":"task"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"task_userId_idx_a489d58a","prefix":"task_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"user":{"checks":[{"expression":"\"kind\" IN ('admin', 'user')","name":"user_kind_check_836d43ef","prefix":"user_kind_check"}],"columns":{"address":{"codecId":"pg/jsonb@1","dataType":"pg/jsonb","nullable":true},"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"displayName":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"email":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"kind":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"user_type","namespaceId":"public","plane":"storage"}}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]}},"valueSet":{"Priority":{"kind":"valueSet","values":["low","high","urgent"]},"user_type":{"kind":"valueSet","values":["admin","user"]}}},"id":"public","kind":"postgres-schema"}},"storageHash":"9fc3736e0efd980892a19f4d704ca30bcd0de243080e78ed1279b1f3a50f9a7e"},"target":"postgres","targetFamily":"sql"} diff --git a/examples/prisma-8-demo/migrations/snapshots/a6e86cb536b51ac3b7542cb02dbf9f5004139e8dc5ee1b33a554c436d8cd2b2a/contract.d.ts b/examples/prisma-8-demo/migrations/snapshots/a6e86cb536b51ac3b7542cb02dbf9f5004139e8dc5ee1b33a554c436d8cd2b2a/contract.d.ts deleted file mode 100644 index 7d7a41f04eeb..000000000000 --- a/examples/prisma-8-demo/migrations/snapshots/a6e86cb536b51ac3b7542cb02dbf9f5004139e8dc5ee1b33a554c436d8cd2b2a/contract.d.ts +++ /dev/null @@ -1,1495 +0,0 @@ -// ⚠️ GENERATED FILE - DO NOT EDIT -// This file is automatically generated by 'prisma contract emit'. -// To regenerate, run: prisma contract emit -import type { - CodecTypes as PgVectorTypes, - Vector, -} from '@prisma/orm-extension-pgvector/codec-types'; -import type { QueryOperationTypes as PgVectorQueryOperationTypes } from '@prisma/orm-extension-pgvector/operation-types'; -import type { QueryOperationTypes as PgAdapterQueryOps } from '@prisma/orm-postgres/adapter/operation-types'; -import type { - Bit, - Char, - CodecTypes as PgTypes, - Interval, - JsonValue, - Numeric, - Time, - TimeString, - Timestamp, - TimestampString, - Timestamptz, - TimestamptzString, - Timetz, - VarBit, - Varchar, -} from '@prisma/orm-postgres/target/codec-types'; - -import type { - ContractWithTypeMaps, - RelationKeys, - TypeMaps as TypeMapsType, -} from '@prisma/orm-postgres/family-contract/types'; -import type { - Contract as ContractType, - ExecutionHashBase, - NamespaceId, - ProfileHashBase, - StorageHashBase, -} from '@prisma/orm-postgres/contract/types'; - -export type StorageHash = - StorageHashBase<'a6e86cb536b51ac3b7542cb02dbf9f5004139e8dc5ee1b33a554c436d8cd2b2a'>; -export type ExecutionHash = - ExecutionHashBase<'e592be8e097b3e2b4958da0b3ed54265d1bfa0523295fa03e03af2364cccbd08'>; -export type ProfileHash = - ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; - -export type CodecTypes = PgTypes & PgVectorTypes; -export type LaneCodecTypes = CodecTypes; -export type QueryOperationTypes = PgAdapterQueryOps & - PgVectorQueryOperationTypes; -export type AggregateTypes = { - readonly avg: { - readonly byCodec: { - readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/float4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - }; - }; - readonly avgDecimal: { - readonly byCodec: { - readonly 'pg/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - }; - }; - readonly count: { - readonly byCodec: {}; - readonly withoutInput: { readonly output: 'pg/int8number@1'; readonly nullable: false }; - readonly anyInput: { readonly output: 'pg/int8number@1'; readonly nullable: false }; - }; - readonly countBigInt: { - readonly byCodec: {}; - readonly withoutInput: { readonly output: 'pg/int8@1'; readonly nullable: false }; - readonly anyInput: { readonly output: 'pg/int8@1'; readonly nullable: false }; - }; - readonly max: { - readonly byCodec: { - readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true }; - readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true }; - readonly 'pg/date-temporal@1': { - readonly output: 'pg/date-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true }; - readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true }; - readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; - readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true }; - readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; - readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true }; - readonly 'pg/time-temporal@1': { - readonly output: 'pg/time-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-string@1': { - readonly output: 'pg/timestamp-string@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-temporal@1': { - readonly output: 'pg/timestamp-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-string@1': { - readonly output: 'pg/timestamptz-string@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-temporal@1': { - readonly output: 'pg/timestamptz-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { - readonly output: 'pg/unboundedint@1'; - readonly nullable: true; - }; - readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true }; - readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true }; - readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true }; - readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - }; - }; - readonly min: { - readonly byCodec: { - readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true }; - readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true }; - readonly 'pg/date-temporal@1': { - readonly output: 'pg/date-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true }; - readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true }; - readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; - readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true }; - readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; - readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true }; - readonly 'pg/time-temporal@1': { - readonly output: 'pg/time-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-string@1': { - readonly output: 'pg/timestamp-string@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-temporal@1': { - readonly output: 'pg/timestamp-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-string@1': { - readonly output: 'pg/timestamptz-string@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-temporal@1': { - readonly output: 'pg/timestamptz-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { - readonly output: 'pg/unboundedint@1'; - readonly nullable: true; - }; - readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true }; - readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true }; - readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true }; - readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - }; - }; - readonly stddev: { - readonly byCodec: { - readonly 'pg/float@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/float4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/float8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'sql/float@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - }; - }; - readonly sum: { - readonly byCodec: { - readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; - readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { - readonly output: 'pg/unboundedint@1'; - readonly nullable: true; - }; - readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - }; - }; - readonly sumBigInt: { - readonly byCodec: { - readonly 'pg/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { - readonly output: 'pg/unboundedint@1'; - readonly nullable: true; - }; - readonly 'sql/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - }; - }; -}; -type DefaultLiteralValue = CodecId extends keyof CodecTypes - ? Encoded extends CodecTypes[CodecId]['json'] - ? Encoded - : CodecTypes[CodecId]['json'] - : Encoded; -export type AddressOutput = { - readonly street: CodecTypes['pg/text@1']['output']; - readonly city: CodecTypes['pg/text@1']['output']; - readonly zip: CodecTypes['pg/text@1']['output'] | null; - readonly country: CodecTypes['pg/text@1']['output']; -}; -export type AddressInput = { - readonly street: CodecTypes['pg/text@1']['input']; - readonly city: CodecTypes['pg/text@1']['input']; - readonly zip: CodecTypes['pg/text@1']['input'] | null; - readonly country: CodecTypes['pg/text@1']['input']; -}; -export type FieldOutputTypes = { - readonly public: { - readonly Bug: { - readonly severity: CodecTypes['pg/text@1']['output']; - readonly stepsToRepro: CodecTypes['pg/text@1']['output'] | null; - }; - readonly Feature: { - readonly priority: CodecTypes['pg/text@1']['output']; - readonly targetRelease: CodecTypes['pg/text@1']['output'] | null; - }; - readonly Post: { - readonly id: CodecTypes['pg/uuid@1']['output']; - readonly title: CodecTypes['pg/text@1']['output']; - readonly userId: CodecTypes['pg/uuid@1']['output']; - readonly priority: 'low' | 'high' | 'urgent'; - readonly createdAt: CodecTypes['pg/timestamptz-temporal@1']['output']; - readonly embedding: Vector<1536> | null; - readonly viewCount: CodecTypes['pg/int8number@1']['output'] | null; - readonly impressionCount: CodecTypes['pg/int8@1']['output'] | null; - readonly reachScore: CodecTypes['pg/unboundedint@1']['output'] | null; - }; - readonly PostTag: { - readonly postId: CodecTypes['pg/uuid@1']['output']; - readonly tagId: CodecTypes['pg/uuid@1']['output']; - }; - readonly Tag: { - readonly id: CodecTypes['pg/uuid@1']['output']; - readonly label: CodecTypes['pg/text@1']['output']; - }; - readonly Task: { - readonly id: CodecTypes['pg/uuid@1']['output']; - readonly title: CodecTypes['pg/text@1']['output']; - readonly description: CodecTypes['pg/text@1']['output'] | null; - readonly status: CodecTypes['pg/text@1']['output']; - readonly type: CodecTypes['pg/text@1']['output']; - readonly userId: CodecTypes['pg/uuid@1']['output']; - readonly createdAt: CodecTypes['pg/timestamptz-temporal@1']['output']; - }; - readonly User: { - readonly id: CodecTypes['pg/uuid@1']['output']; - readonly email: CodecTypes['pg/text@1']['output']; - readonly displayName: CodecTypes['pg/text@1']['output']; - readonly createdAt: CodecTypes['pg/timestamptz-temporal@1']['output']; - readonly kind: 'admin' | 'user'; - readonly address: AddressOutput | null; - }; - }; -}; -export type FieldInputTypes = { - readonly public: { - readonly Bug: { - readonly severity: CodecTypes['pg/text@1']['input']; - readonly stepsToRepro: CodecTypes['pg/text@1']['input'] | null; - }; - readonly Feature: { - readonly priority: CodecTypes['pg/text@1']['input']; - readonly targetRelease: CodecTypes['pg/text@1']['input'] | null; - }; - readonly Post: { - readonly id: CodecTypes['pg/uuid@1']['input']; - readonly title: CodecTypes['pg/text@1']['input']; - readonly userId: CodecTypes['pg/uuid@1']['input']; - readonly priority: 'low' | 'high' | 'urgent'; - readonly createdAt: CodecTypes['pg/timestamptz-temporal@1']['input']; - readonly embedding: CodecTypes['pg/vector@1']['input'] | null; - readonly viewCount: CodecTypes['pg/int8number@1']['input'] | null; - readonly impressionCount: CodecTypes['pg/int8@1']['input'] | null; - readonly reachScore: CodecTypes['pg/unboundedint@1']['input'] | null; - }; - readonly PostTag: { - readonly postId: CodecTypes['pg/uuid@1']['input']; - readonly tagId: CodecTypes['pg/uuid@1']['input']; - }; - readonly Tag: { - readonly id: CodecTypes['pg/uuid@1']['input']; - readonly label: CodecTypes['pg/text@1']['input']; - }; - readonly Task: { - readonly id: CodecTypes['pg/uuid@1']['input']; - readonly title: CodecTypes['pg/text@1']['input']; - readonly description: CodecTypes['pg/text@1']['input'] | null; - readonly status: CodecTypes['pg/text@1']['input']; - readonly type: CodecTypes['pg/text@1']['input']; - readonly userId: CodecTypes['pg/uuid@1']['input']; - readonly createdAt: CodecTypes['pg/timestamptz-temporal@1']['input']; - }; - readonly User: { - readonly id: CodecTypes['pg/uuid@1']['input']; - readonly email: CodecTypes['pg/text@1']['input']; - readonly displayName: CodecTypes['pg/text@1']['input']; - readonly createdAt: CodecTypes['pg/timestamptz-temporal@1']['input']; - readonly kind: 'admin' | 'user'; - readonly address: AddressInput | null; - }; - }; -}; -export type StorageColumnTypes = { - readonly public: { - readonly bug: { - readonly id: CodecTypes['pg/uuid@1']['output']; - readonly severity: CodecTypes['pg/text@1']['output']; - readonly stepsToRepro: CodecTypes['pg/text@1']['output'] | null; - }; - readonly feature: { - readonly id: CodecTypes['pg/uuid@1']['output']; - readonly priority: CodecTypes['pg/text@1']['output']; - readonly targetRelease: CodecTypes['pg/text@1']['output'] | null; - }; - readonly post: { - readonly createdAt: CodecTypes['pg/timestamptz-temporal@1']['output']; - readonly embedding: Vector<1536> | null; - readonly id: CodecTypes['pg/uuid@1']['output']; - readonly impressionCount: CodecTypes['pg/int8@1']['output'] | null; - readonly priority: 'low' | 'high' | 'urgent'; - readonly reachScore: CodecTypes['pg/unboundedint@1']['output'] | null; - readonly title: CodecTypes['pg/text@1']['output']; - readonly userId: CodecTypes['pg/uuid@1']['output']; - readonly viewCount: CodecTypes['pg/int8number@1']['output'] | null; - }; - readonly post_tag: { - readonly postId: CodecTypes['pg/uuid@1']['output']; - readonly tagId: CodecTypes['pg/uuid@1']['output']; - }; - readonly tag: { - readonly id: CodecTypes['pg/uuid@1']['output']; - readonly label: CodecTypes['pg/text@1']['output']; - }; - readonly task: { - readonly createdAt: CodecTypes['pg/timestamptz-temporal@1']['output']; - readonly description: CodecTypes['pg/text@1']['output'] | null; - readonly id: CodecTypes['pg/uuid@1']['output']; - readonly status: CodecTypes['pg/text@1']['output']; - readonly title: CodecTypes['pg/text@1']['output']; - readonly type: CodecTypes['pg/text@1']['output']; - readonly userId: CodecTypes['pg/uuid@1']['output']; - }; - readonly user: { - readonly address: CodecTypes['pg/jsonb@1']['output'] | null; - readonly createdAt: CodecTypes['pg/timestamptz-temporal@1']['output']; - readonly displayName: CodecTypes['pg/text@1']['output']; - readonly email: CodecTypes['pg/text@1']['output']; - readonly id: CodecTypes['pg/uuid@1']['output']; - readonly kind: 'admin' | 'user'; - }; - }; -}; -export type StorageColumnInputTypes = { - readonly public: { - readonly bug: { - readonly id: CodecTypes['pg/uuid@1']['input']; - readonly severity: CodecTypes['pg/text@1']['input']; - readonly stepsToRepro: CodecTypes['pg/text@1']['input'] | null; - }; - readonly feature: { - readonly id: CodecTypes['pg/uuid@1']['input']; - readonly priority: CodecTypes['pg/text@1']['input']; - readonly targetRelease: CodecTypes['pg/text@1']['input'] | null; - }; - readonly post: { - readonly createdAt: CodecTypes['pg/timestamptz-temporal@1']['input']; - readonly embedding: CodecTypes['pg/vector@1']['input'] | null; - readonly id: CodecTypes['pg/uuid@1']['input']; - readonly impressionCount: CodecTypes['pg/int8@1']['input'] | null; - readonly priority: 'low' | 'high' | 'urgent'; - readonly reachScore: CodecTypes['pg/unboundedint@1']['input'] | null; - readonly title: CodecTypes['pg/text@1']['input']; - readonly userId: CodecTypes['pg/uuid@1']['input']; - readonly viewCount: CodecTypes['pg/int8number@1']['input'] | null; - }; - readonly post_tag: { - readonly postId: CodecTypes['pg/uuid@1']['input']; - readonly tagId: CodecTypes['pg/uuid@1']['input']; - }; - readonly tag: { - readonly id: CodecTypes['pg/uuid@1']['input']; - readonly label: CodecTypes['pg/text@1']['input']; - }; - readonly task: { - readonly createdAt: CodecTypes['pg/timestamptz-temporal@1']['input']; - readonly description: CodecTypes['pg/text@1']['input'] | null; - readonly id: CodecTypes['pg/uuid@1']['input']; - readonly status: CodecTypes['pg/text@1']['input']; - readonly title: CodecTypes['pg/text@1']['input']; - readonly type: CodecTypes['pg/text@1']['input']; - readonly userId: CodecTypes['pg/uuid@1']['input']; - }; - readonly user: { - readonly address: CodecTypes['pg/jsonb@1']['input'] | null; - readonly createdAt: CodecTypes['pg/timestamptz-temporal@1']['input']; - readonly displayName: CodecTypes['pg/text@1']['input']; - readonly email: CodecTypes['pg/text@1']['input']; - readonly id: CodecTypes['pg/uuid@1']['input']; - readonly kind: 'admin' | 'user'; - }; - }; -}; - -export namespace Models { - export type public_User = { - id: CodecTypes['pg/uuid@1']['output']; - email: CodecTypes['pg/text@1']['output']; - displayName: CodecTypes['pg/text@1']['output']; - createdAt: CodecTypes['pg/timestamptz-temporal@1']['output']; - kind: 'admin' | 'user'; - address: AddressOutput | null; - posts: public_Post[]; - tasks: public_AnyTask[]; - readonly [RelationKeys]?: 'posts' | 'tasks'; - }; - export type public_Post = { - id: CodecTypes['pg/uuid@1']['output']; - title: CodecTypes['pg/text@1']['output']; - userId: CodecTypes['pg/uuid@1']['output']; - priority: 'low' | 'high' | 'urgent'; - createdAt: CodecTypes['pg/timestamptz-temporal@1']['output']; - embedding: Vector<1536> | null; - viewCount: CodecTypes['pg/int8number@1']['output'] | null; - impressionCount: CodecTypes['pg/int8@1']['output'] | null; - reachScore: CodecTypes['pg/unboundedint@1']['output'] | null; - tags: public_Tag[]; - user: public_User; - readonly [RelationKeys]?: 'tags' | 'user'; - }; - export type public_Tag = { - id: CodecTypes['pg/uuid@1']['output']; - label: CodecTypes['pg/text@1']['output']; - posts: public_Post[]; - readonly [RelationKeys]?: 'posts'; - }; - export type public_PostTag = { - postId: CodecTypes['pg/uuid@1']['output']; - tagId: CodecTypes['pg/uuid@1']['output']; - post: public_Post; - tag: public_Tag; - readonly [RelationKeys]?: 'post' | 'tag'; - }; - export type public_Task = { - id: CodecTypes['pg/uuid@1']['output']; - title: CodecTypes['pg/text@1']['output']; - description: CodecTypes['pg/text@1']['output'] | null; - status: CodecTypes['pg/text@1']['output']; - type: 'bug' | 'feature'; - userId: CodecTypes['pg/uuid@1']['output']; - createdAt: CodecTypes['pg/timestamptz-temporal@1']['output']; - user: public_User; - readonly [RelationKeys]?: 'user'; - }; - export type public_Bug = { - id: CodecTypes['pg/uuid@1']['output']; - title: CodecTypes['pg/text@1']['output']; - description: CodecTypes['pg/text@1']['output'] | null; - status: CodecTypes['pg/text@1']['output']; - type: 'bug'; - userId: CodecTypes['pg/uuid@1']['output']; - createdAt: CodecTypes['pg/timestamptz-temporal@1']['output']; - severity: CodecTypes['pg/text@1']['output']; - stepsToRepro: CodecTypes['pg/text@1']['output'] | null; - user: public_User; - readonly [RelationKeys]?: 'user'; - }; - export type public_Feature = { - id: CodecTypes['pg/uuid@1']['output']; - title: CodecTypes['pg/text@1']['output']; - description: CodecTypes['pg/text@1']['output'] | null; - status: CodecTypes['pg/text@1']['output']; - type: 'feature'; - userId: CodecTypes['pg/uuid@1']['output']; - createdAt: CodecTypes['pg/timestamptz-temporal@1']['output']; - priority: CodecTypes['pg/text@1']['output']; - targetRelease: CodecTypes['pg/text@1']['output'] | null; - user: public_User; - readonly [RelationKeys]?: 'user'; - }; - export type public_AnyTask = public_Bug | public_Feature; -} - -export declare const models: { - public: { - User: Models.public_User; - Post: Models.public_Post; - Tag: Models.public_Tag; - PostTag: Models.public_PostTag; - Task: Models.public_Task; - Bug: Models.public_Bug; - Feature: Models.public_Feature; - AnyTask: Models.public_AnyTask; - }; -}; - -export type TypeMaps = TypeMapsType< - CodecTypes, - QueryOperationTypes, - FieldOutputTypes, - FieldInputTypes, - StorageColumnTypes, - StorageColumnInputTypes, - AggregateTypes ->; - -type ContractBase = Omit< - ContractType<{ - readonly namespaces: { - readonly public: { - readonly id: 'public'; - readonly kind: 'postgres-schema'; - readonly entries: { - readonly table: { - readonly bug: { - columns: { - readonly id: { - readonly many: false; - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - }; - readonly severity: { - readonly many: false; - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - }; - readonly stepsToRepro: { - readonly many: false; - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: true; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly []; - foreignKeys: readonly [ - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'bug'; - readonly columns: readonly ['id']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'task'; - readonly columns: readonly ['id']; - }; - }, - ]; - }; - readonly feature: { - columns: { - readonly id: { - readonly many: false; - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - }; - readonly priority: { - readonly many: false; - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - }; - readonly targetRelease: { - readonly many: false; - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: true; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly []; - foreignKeys: readonly [ - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'feature'; - readonly columns: readonly ['id']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'task'; - readonly columns: readonly ['id']; - }; - }, - ]; - }; - readonly post: { - columns: { - readonly id: { - readonly many: false; - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - }; - readonly title: { - readonly many: false; - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - }; - readonly userId: { - readonly many: false; - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - }; - readonly priority: { - readonly many: false; - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly default: { - readonly kind: 'literal'; - readonly value: DefaultLiteralValue<'pg/text@1', 'low'>; - }; - }; - readonly createdAt: { - readonly many: false; - readonly dataType: 'pg/timestamptz'; - readonly codecId: 'pg/timestamptz-temporal@1'; - readonly nullable: false; - readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; - }; - readonly embedding: { - readonly many: false; - readonly dataType: 'pgvector/vector'; - readonly codecId: 'pg/vector@1'; - readonly nullable: true; - readonly typeParams: { readonly length: 1536 }; - }; - readonly viewCount: { - readonly many: false; - readonly dataType: 'pg/int8'; - readonly codecId: 'pg/int8number@1'; - readonly nullable: true; - }; - readonly impressionCount: { - readonly many: false; - readonly dataType: 'pg/int8'; - readonly codecId: 'pg/int8@1'; - readonly nullable: true; - }; - readonly reachScore: { - readonly many: false; - readonly dataType: 'pg/numeric'; - readonly codecId: 'pg/unboundedint@1'; - readonly nullable: true; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly [ - { - readonly name: 'post_userId_idx_a489d58a'; - readonly prefix: 'post_userId_idx'; - readonly columns: readonly ['userId']; - readonly unique: false; - }, - ]; - foreignKeys: readonly [ - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'post'; - readonly columns: readonly ['userId']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'user'; - readonly columns: readonly ['id']; - }; - }, - ]; - }; - readonly post_tag: { - columns: { - readonly postId: { - readonly many: false; - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - }; - readonly tagId: { - readonly many: false; - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - }; - }; - primaryKey: { readonly columns: readonly ['postId', 'tagId'] }; - uniques: readonly []; - indexes: readonly [ - { - readonly name: 'post_tag_postId_idx_a7a72715'; - readonly prefix: 'post_tag_postId_idx'; - readonly columns: readonly ['postId']; - readonly unique: false; - }, - { - readonly name: 'post_tag_tagId_idx_86854244'; - readonly prefix: 'post_tag_tagId_idx'; - readonly columns: readonly ['tagId']; - readonly unique: false; - }, - ]; - foreignKeys: readonly [ - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'post_tag'; - readonly columns: readonly ['postId']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'post'; - readonly columns: readonly ['id']; - }; - }, - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'post_tag'; - readonly columns: readonly ['tagId']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'tag'; - readonly columns: readonly ['id']; - }; - }, - ]; - }; - readonly tag: { - columns: { - readonly id: { - readonly many: false; - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - }; - readonly label: { - readonly many: false; - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly [{ readonly columns: readonly ['label'] }]; - indexes: readonly []; - foreignKeys: readonly []; - }; - readonly task: { - columns: { - readonly id: { - readonly many: false; - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - }; - readonly title: { - readonly many: false; - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - }; - readonly description: { - readonly many: false; - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: true; - }; - readonly status: { - readonly many: false; - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly default: { - readonly kind: 'literal'; - readonly value: DefaultLiteralValue<'pg/text@1', 'open'>; - }; - }; - readonly type: { - readonly many: false; - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - }; - readonly userId: { - readonly many: false; - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - }; - readonly createdAt: { - readonly many: false; - readonly dataType: 'pg/timestamptz'; - readonly codecId: 'pg/timestamptz-temporal@1'; - readonly nullable: false; - readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly [ - { - readonly name: 'task_userId_idx_a489d58a'; - readonly prefix: 'task_userId_idx'; - readonly columns: readonly ['userId']; - readonly unique: false; - }, - ]; - foreignKeys: readonly [ - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'task'; - readonly columns: readonly ['userId']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'user'; - readonly columns: readonly ['id']; - }; - }, - ]; - }; - readonly user: { - columns: { - readonly id: { - readonly many: false; - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - }; - readonly email: { - readonly many: false; - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - }; - readonly displayName: { - readonly many: false; - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - }; - readonly createdAt: { - readonly many: false; - readonly dataType: 'pg/timestamptz'; - readonly codecId: 'pg/timestamptz-temporal@1'; - readonly nullable: false; - readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; - }; - readonly kind: { - readonly many: false; - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - }; - readonly address: { - readonly many: false; - readonly dataType: 'pg/jsonb'; - readonly codecId: 'pg/jsonb@1'; - readonly nullable: true; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly []; - foreignKeys: readonly []; - }; - }; - readonly valueSet: { - readonly Priority: { - readonly kind: 'valueSet'; - readonly values: readonly ['low', 'high', 'urgent']; - }; - readonly user_type: { - readonly kind: 'valueSet'; - readonly values: readonly ['admin', 'user']; - }; - }; - }; - }; - }; - readonly storageHash: StorageHash; - }>, - 'roots' | 'domain' -> & { - readonly target: 'postgres'; - readonly targetFamily: 'sql'; - readonly roots: { - readonly user: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; - readonly post: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; - readonly tag: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; - readonly post_tag: { readonly namespace: 'public' & NamespaceId; readonly model: 'PostTag' }; - readonly task: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; - }; - readonly domain: { - readonly namespaces: { - readonly public: { - readonly models: { - readonly Bug: { - readonly fields: { - readonly severity: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly stepsToRepro: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - readonly relations: Record; - readonly storage: { - readonly table: 'bug'; - readonly namespaceId: 'public'; - readonly fields: { - readonly severity: { readonly column: 'severity' }; - readonly stepsToRepro: { readonly column: 'stepsToRepro' }; - }; - }; - readonly base: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; - }; - readonly Feature: { - readonly fields: { - readonly priority: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly targetRelease: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - readonly relations: Record; - readonly storage: { - readonly table: 'feature'; - readonly namespaceId: 'public'; - readonly fields: { - readonly priority: { readonly column: 'priority' }; - readonly targetRelease: { readonly column: 'targetRelease' }; - }; - }; - readonly base: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; - }; - readonly Post: { - readonly fields: { - readonly id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly title: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly priority: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly createdAt: { - readonly nullable: false; - readonly type: { - readonly kind: 'scalar'; - readonly codecId: 'pg/timestamptz-temporal@1'; - }; - }; - readonly embedding: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/vector@1' }; - }; - readonly viewCount: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int8number@1' }; - }; - readonly impressionCount: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int8@1' }; - }; - readonly reachScore: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/unboundedint@1' }; - }; - }; - readonly relations: { - readonly tags: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; - readonly cardinality: 'N:M'; - readonly on: { - readonly localFields: readonly ['id']; - readonly targetFields: readonly ['postId']; - }; - readonly through: { - readonly table: 'post_tag'; - readonly namespaceId: 'public'; - readonly parentColumns: readonly ['postId']; - readonly childColumns: readonly ['tagId']; - readonly targetColumns: readonly ['id']; - }; - }; - readonly user: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['userId']; - readonly targetFields: readonly ['id']; - }; - }; - }; - readonly storage: { - readonly table: 'post'; - readonly namespaceId: 'public'; - readonly fields: { - readonly id: { readonly column: 'id' }; - readonly title: { readonly column: 'title' }; - readonly userId: { readonly column: 'userId' }; - readonly priority: { readonly column: 'priority' }; - readonly createdAt: { readonly column: 'createdAt' }; - readonly embedding: { readonly column: 'embedding' }; - readonly viewCount: { readonly column: 'viewCount' }; - readonly impressionCount: { readonly column: 'impressionCount' }; - readonly reachScore: { readonly column: 'reachScore' }; - }; - }; - }; - readonly PostTag: { - readonly fields: { - readonly postId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly tagId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - }; - readonly relations: { - readonly post: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['postId']; - readonly targetFields: readonly ['id']; - }; - }; - readonly tag: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['tagId']; - readonly targetFields: readonly ['id']; - }; - }; - }; - readonly storage: { - readonly table: 'post_tag'; - readonly namespaceId: 'public'; - readonly fields: { - readonly postId: { readonly column: 'postId' }; - readonly tagId: { readonly column: 'tagId' }; - }; - }; - }; - readonly Tag: { - readonly fields: { - readonly id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly label: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - readonly relations: { - readonly posts: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; - readonly cardinality: 'N:M'; - readonly on: { - readonly localFields: readonly ['id']; - readonly targetFields: readonly ['tagId']; - }; - readonly through: { - readonly table: 'post_tag'; - readonly namespaceId: 'public'; - readonly parentColumns: readonly ['tagId']; - readonly childColumns: readonly ['postId']; - readonly targetColumns: readonly ['id']; - }; - }; - }; - readonly storage: { - readonly table: 'tag'; - readonly namespaceId: 'public'; - readonly fields: { - readonly id: { readonly column: 'id' }; - readonly label: { readonly column: 'label' }; - }; - }; - }; - readonly Task: { - readonly fields: { - readonly id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly title: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly description: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly status: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly type: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly createdAt: { - readonly nullable: false; - readonly type: { - readonly kind: 'scalar'; - readonly codecId: 'pg/timestamptz-temporal@1'; - }; - }; - }; - readonly relations: { - readonly user: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['userId']; - readonly targetFields: readonly ['id']; - }; - }; - }; - readonly storage: { - readonly table: 'task'; - readonly namespaceId: 'public'; - readonly fields: { - readonly id: { readonly column: 'id' }; - readonly title: { readonly column: 'title' }; - readonly description: { readonly column: 'description' }; - readonly status: { readonly column: 'status' }; - readonly type: { readonly column: 'type' }; - readonly userId: { readonly column: 'userId' }; - readonly createdAt: { readonly column: 'createdAt' }; - }; - }; - readonly discriminator: { readonly field: 'type' }; - readonly variants: { - readonly Bug: { readonly value: 'bug' }; - readonly Feature: { readonly value: 'feature' }; - }; - }; - readonly User: { - readonly fields: { - readonly id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly email: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly displayName: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly createdAt: { - readonly nullable: false; - readonly type: { - readonly kind: 'scalar'; - readonly codecId: 'pg/timestamptz-temporal@1'; - }; - }; - readonly kind: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly address: { - readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; - }; - }; - readonly relations: { - readonly posts: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; - readonly cardinality: '1:N'; - readonly on: { - readonly localFields: readonly ['id']; - readonly targetFields: readonly ['userId']; - }; - }; - readonly tasks: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; - readonly cardinality: '1:N'; - readonly on: { - readonly localFields: readonly ['id']; - readonly targetFields: readonly ['userId']; - }; - }; - }; - readonly storage: { - readonly table: 'user'; - readonly namespaceId: 'public'; - readonly fields: { - readonly id: { readonly column: 'id' }; - readonly email: { readonly column: 'email' }; - readonly displayName: { readonly column: 'displayName' }; - readonly createdAt: { readonly column: 'createdAt' }; - readonly kind: { readonly column: 'kind' }; - readonly address: { readonly column: 'address' }; - }; - }; - }; - }; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - }; - }; - readonly enum: { - readonly user_type: { - readonly codecId: 'pg/text@1'; - readonly members: readonly [ - { readonly name: 'admin'; readonly value: 'admin' }, - { readonly name: 'user'; readonly value: 'user' }, - ]; - }; - readonly Priority: { - readonly codecId: 'pg/text@1'; - readonly members: readonly [ - { readonly name: 'Low'; readonly value: 'low' }, - { readonly name: 'High'; readonly value: 'high' }, - { readonly name: 'Urgent'; readonly value: 'urgent' }, - ]; - }; - }; - }; - }; - }; - readonly capabilities: { - readonly postgres: { - readonly distinctOn: true; - readonly jsonAgg: true; - readonly lateral: true; - readonly limit: true; - readonly orderBy: true; - readonly 'pgvector.cosine': true; - readonly returning: true; - }; - readonly sql: { - readonly checkConstraint: true; - readonly defaultInInsert: true; - readonly enums: true; - readonly lateral: true; - readonly returning: true; - readonly scalarList: true; - }; - }; - readonly extensions: { - readonly 'demo/engagement-stats': { - readonly familyId: 'sql'; - readonly id: 'demo/engagement-stats'; - readonly kind: 'extension'; - readonly targetId: 'postgres'; - readonly types: { - readonly aggregateDescriptors: readonly [ - { - readonly input: { readonly kind: 'trait'; readonly trait: 'numeric' }; - readonly lower: unknown; - readonly nullable: true; - readonly operation: 'stddev'; - readonly output: { readonly codecId: 'pg/numeric@1'; readonly kind: 'codec' }; - }, - ]; - }; - readonly version: '0.0.1'; - }; - readonly pgvector: { - readonly capabilities: { readonly postgres: { readonly 'pgvector.cosine': true } }; - readonly familyId: 'sql'; - readonly id: 'pgvector'; - readonly kind: 'extension'; - readonly targetId: 'postgres'; - readonly types: { - readonly codecTypes: { - readonly import: { - readonly alias: 'PgVectorTypes'; - readonly named: 'CodecTypes'; - readonly package: '@internal/extension-pgvector/codec-types'; - }; - readonly typeImports: readonly [ - { - readonly alias: 'Vector'; - readonly named: 'Vector'; - readonly package: '@internal/extension-pgvector/codec-types'; - }, - ]; - }; - readonly operationTypes: { - readonly import: { - readonly alias: 'PgVectorOperationTypes'; - readonly named: 'OperationTypes'; - readonly package: '@internal/extension-pgvector/operation-types'; - }; - }; - readonly queryOperationTypes: { - readonly import: { - readonly alias: 'PgVectorQueryOperationTypes'; - readonly named: 'QueryOperationTypes'; - readonly package: '@internal/extension-pgvector/operation-types'; - }; - }; - }; - readonly version: '0.0.1'; - }; - }; - readonly execution: { - readonly executionHash: ExecutionHash; - readonly mutations: { - readonly defaults: readonly [ - { - readonly ref: { - readonly entry: 'post'; - readonly field: 'id'; - readonly namespace: 'public'; - }; - readonly onCreate: { readonly kind: 'generator'; readonly id: 'uuidv4' }; - }, - { - readonly ref: { - readonly entry: 'tag'; - readonly field: 'id'; - readonly namespace: 'public'; - }; - readonly onCreate: { readonly kind: 'generator'; readonly id: 'uuidv4' }; - }, - { - readonly ref: { - readonly entry: 'task'; - readonly field: 'id'; - readonly namespace: 'public'; - }; - readonly onCreate: { readonly kind: 'generator'; readonly id: 'uuidv4' }; - }, - { - readonly ref: { - readonly entry: 'user'; - readonly field: 'id'; - readonly namespace: 'public'; - }; - readonly onCreate: { readonly kind: 'generator'; readonly id: 'uuidv4' }; - }, - ]; - }; - }; - readonly meta: {}; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - }; - }; - readonly profileHash: ProfileHash; -}; - -export type Contract = ContractWithTypeMaps; - -export type Namespaces = Contract['storage']['namespaces']; diff --git a/examples/prisma-8-demo/migrations/snapshots/a6e86cb536b51ac3b7542cb02dbf9f5004139e8dc5ee1b33a554c436d8cd2b2a/contract.json b/examples/prisma-8-demo/migrations/snapshots/a6e86cb536b51ac3b7542cb02dbf9f5004139e8dc5ee1b33a554c436d8cd2b2a/contract.json deleted file mode 100644 index 705edd640296..000000000000 --- a/examples/prisma-8-demo/migrations/snapshots/a6e86cb536b51ac3b7542cb02dbf9f5004139e8dc5ee1b33a554c436d8cd2b2a/contract.json +++ /dev/null @@ -1 +0,0 @@ -{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{"postgres":{"distinctOn":true,"jsonAgg":true,"lateral":true,"limit":true,"orderBy":true,"pgvector.cosine":true,"returning":true},"sql":{"checkConstraint":true,"defaultInInsert":true,"enums":true,"lateral":true,"returning":true,"scalarList":true}},"domain":{"namespaces":{"public":{"enum":{"Priority":{"codecId":"pg/text@1","members":[{"name":"Low","value":"low"},{"name":"High","value":"high"},{"name":"Urgent","value":"urgent"}]},"user_type":{"codecId":"pg/text@1","members":[{"name":"admin","value":"admin"},{"name":"user","value":"user"}]}},"models":{"Bug":{"base":{"model":"Task","namespace":"public"},"fields":{"severity":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"stepsToRepro":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"severity":{"column":"severity"},"stepsToRepro":{"column":"stepsToRepro"}},"namespaceId":"public","table":"bug"}},"Feature":{"base":{"model":"Task","namespace":"public"},"fields":{"priority":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"targetRelease":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"priority":{"column":"priority"},"targetRelease":{"column":"targetRelease"}},"namespaceId":"public","table":"feature"}},"Post":{"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"embedding":{"nullable":true,"type":{"codecId":"pg/vector@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"impressionCount":{"nullable":true,"type":{"codecId":"pg/int8@1","kind":"scalar"}},"priority":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"Priority","namespaceId":"public","plane":"domain"}},"reachScore":{"nullable":true,"type":{"codecId":"pg/unboundedint@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"viewCount":{"nullable":true,"type":{"codecId":"pg/int8number@1","kind":"scalar"}}},"relations":{"tags":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["postId"]},"through":{"childColumns":["tagId"],"namespaceId":"public","parentColumns":["postId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Tag","namespace":"public"}},"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"embedding":{"column":"embedding"},"id":{"column":"id"},"impressionCount":{"column":"impressionCount"},"priority":{"column":"priority"},"reachScore":{"column":"reachScore"},"title":{"column":"title"},"userId":{"column":"userId"},"viewCount":{"column":"viewCount"}},"namespaceId":"public","table":"post"}},"PostTag":{"fields":{"postId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"tagId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"post":{"cardinality":"N:1","nullable":false,"on":{"localFields":["postId"],"targetFields":["id"]},"to":{"model":"Post","namespace":"public"}},"tag":{"cardinality":"N:1","nullable":false,"on":{"localFields":["tagId"],"targetFields":["id"]},"to":{"model":"Tag","namespace":"public"}}},"storage":{"fields":{"postId":{"column":"postId"},"tagId":{"column":"tagId"}},"namespaceId":"public","table":"post_tag"}},"Tag":{"fields":{"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"label":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{"posts":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["tagId"]},"through":{"childColumns":["postId"],"namespaceId":"public","parentColumns":["tagId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Post","namespace":"public"}}},"storage":{"fields":{"id":{"column":"id"},"label":{"column":"label"}},"namespaceId":"public","table":"tag"}},"Task":{"discriminator":{"field":"type"},"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"description":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"status":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"type":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"description":{"column":"description"},"id":{"column":"id"},"status":{"column":"status"},"title":{"column":"title"},"type":{"column":"type"},"userId":{"column":"userId"}},"namespaceId":"public","table":"task"},"variants":{"Bug":{"value":"bug"},"Feature":{"value":"feature"}}},"User":{"fields":{"address":{"nullable":true,"type":{"kind":"valueObject","name":"Address"}},"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"displayName":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"user_type","namespaceId":"public","plane":"domain"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Post","namespace":"public"}},"tasks":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Task","namespace":"public"}}},"storage":{"fields":{"address":{"column":"address"},"createdAt":{"column":"createdAt"},"displayName":{"column":"displayName"},"email":{"column":"email"},"id":{"column":"id"},"kind":{"column":"kind"}},"namespaceId":"public","table":"user"}}},"valueObjects":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}}}}}}},"execution":{"executionHash":"e592be8e097b3e2b4958da0b3ed54265d1bfa0523295fa03e03af2364cccbd08","mutations":{"defaults":[{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"post","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"tag","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"task","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"user","field":"id","namespace":"public"}}]}},"extensions":{"demo/engagement-stats":{"familyId":"sql","id":"demo/engagement-stats","kind":"extension","targetId":"postgres","types":{"aggregateDescriptors":[{"input":{"kind":"trait","trait":"numeric"},"nullable":true,"operation":"stddev","output":{"codecId":"pg/numeric@1","kind":"codec"}}]},"version":"0.0.1"},"pgvector":{"capabilities":{"postgres":{"pgvector.cosine":true}},"familyId":"sql","id":"pgvector","kind":"extension","targetId":"postgres","types":{"codecTypes":{"import":{"alias":"PgVectorTypes","named":"CodecTypes","package":"@internal/extension-pgvector/codec-types"},"typeImports":[{"alias":"Vector","named":"Vector","package":"@internal/extension-pgvector/codec-types"}]},"operationTypes":{"import":{"alias":"PgVectorOperationTypes","named":"OperationTypes","package":"@internal/extension-pgvector/operation-types"}},"queryOperationTypes":{"import":{"alias":"PgVectorQueryOperationTypes","named":"QueryOperationTypes","package":"@internal/extension-pgvector/operation-types"}}},"version":"0.0.1"}},"meta":{},"profileHash":"3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2","roots":{"post":{"model":"Post","namespace":"public"},"post_tag":{"model":"PostTag","namespace":"public"},"tag":{"model":"Tag","namespace":"public"},"task":{"model":"Task","namespace":"public"},"user":{"model":"User","namespace":"public"}},"schemaVersion":"1","storage":{"namespaces":{"public":{"entries":{"table":{"bug":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"severity":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"stepsToRepro":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"bug"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"feature":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"priority":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"targetRelease":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"feature"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"post":{"checks":[{"expression":"\"priority\" IN ('low', 'high', 'urgent')","name":"post_priority_check_09b41aa0","prefix":"post_priority_check"}],"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"embedding":{"codecId":"pg/vector@1","dataType":"pgvector/vector","nullable":true,"typeParams":{"length":1536}},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"impressionCount":{"codecId":"pg/int8@1","dataType":"pg/int8","nullable":true},"priority":{"codecId":"pg/text@1","dataType":"pg/text","default":{"kind":"literal","value":"low"},"nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Priority","namespaceId":"public","plane":"storage"}},"reachScore":{"codecId":"pg/unboundedint@1","dataType":"pg/numeric","nullable":true},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"viewCount":{"codecId":"pg/int8number@1","dataType":"pg/int8","nullable":true}},"foreignKeys":[{"source":{"columns":["userId"],"namespaceId":"public","tableName":"post"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"post_userId_idx_a489d58a","prefix":"post_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"post_tag":{"columns":{"postId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"tagId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"source":{"columns":["postId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"post"}},{"source":{"columns":["tagId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"tag"}}],"indexes":[{"columns":["postId"],"name":"post_tag_postId_idx_a7a72715","prefix":"post_tag_postId_idx","unique":false},{"columns":["tagId"],"name":"post_tag_tagId_idx_86854244","prefix":"post_tag_tagId_idx","unique":false}],"primaryKey":{"columns":["postId","tagId"]},"uniques":[]},"tag":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"label":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[{"columns":["label"]}]},"task":{"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"description":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"status":{"codecId":"pg/text@1","dataType":"pg/text","default":{"kind":"literal","value":"open"},"nullable":false},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"type":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"source":{"columns":["userId"],"namespaceId":"public","tableName":"task"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"task_userId_idx_a489d58a","prefix":"task_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"user":{"checks":[{"expression":"\"kind\" IN ('admin', 'user')","name":"user_kind_check_836d43ef","prefix":"user_kind_check"}],"columns":{"address":{"codecId":"pg/jsonb@1","dataType":"pg/jsonb","nullable":true},"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"displayName":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"email":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"kind":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"user_type","namespaceId":"public","plane":"storage"}}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]}},"valueSet":{"Priority":{"kind":"valueSet","values":["low","high","urgent"]},"user_type":{"kind":"valueSet","values":["admin","user"]}}},"id":"public","kind":"postgres-schema"}},"storageHash":"a6e86cb536b51ac3b7542cb02dbf9f5004139e8dc5ee1b33a554c436d8cd2b2a"},"target":"postgres","targetFamily":"sql"} diff --git a/examples/prisma-8-demo/migrations/snapshots/d44ab6f7caddcc384332c669737008aec7ec8c9f5b013cb502a03c2eb04be9c0/contract.json b/examples/prisma-8-demo/migrations/snapshots/d44ab6f7caddcc384332c669737008aec7ec8c9f5b013cb502a03c2eb04be9c0/contract.json deleted file mode 100644 index 133dfbc8f71f..000000000000 --- a/examples/prisma-8-demo/migrations/snapshots/d44ab6f7caddcc384332c669737008aec7ec8c9f5b013cb502a03c2eb04be9c0/contract.json +++ /dev/null @@ -1 +0,0 @@ -{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{"postgres":{"distinctOn":true,"forKeyShare":true,"forNoKeyUpdate":true,"jsonAgg":true,"lateral":true,"limit":true,"orderBy":true,"pgvector.cosine":true,"returning":true},"sql":{"checkConstraint":true,"defaultInInsert":true,"enums":true,"forShare":true,"forUpdate":true,"insertOnConflictSkip":true,"insertOnConflictWithoutTarget":true,"lateral":true,"lockNowait":true,"lockOf":true,"lockSkipLocked":true,"returning":true,"scalarList":true}},"domain":{"namespaces":{"public":{"enum":{"Priority":{"codecId":"pg/int4@1","members":[{"name":"Low","value":0},{"name":"High","value":1},{"name":"Urgent","value":2}]},"user_type":{"codecId":"pg/text@1","members":[{"name":"admin","value":"admin"},{"name":"user","value":"user"}]}},"models":{"Bug":{"base":{"model":"Task","namespace":"public"},"fields":{"severity":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"stepsToRepro":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"severity":{"column":"severity"},"stepsToRepro":{"column":"stepsToRepro"}},"namespaceId":"public","table":"bug"}},"Feature":{"base":{"model":"Task","namespace":"public"},"fields":{"priority":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"targetRelease":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"priority":{"column":"priority"},"targetRelease":{"column":"targetRelease"}},"namespaceId":"public","table":"feature"}},"Post":{"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"embedding":{"nullable":true,"type":{"codecId":"pg/vector@1","kind":"scalar","typeParams":{"length":1536}}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"impressionCount":{"nullable":true,"type":{"codecId":"pg/int8@1","kind":"scalar"}},"priority":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"Priority","namespaceId":"public","plane":"domain"}},"reachScore":{"nullable":true,"type":{"codecId":"pg/unboundedint@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"viewCount":{"nullable":true,"type":{"codecId":"pg/int8number@1","kind":"scalar"}}},"relations":{"tags":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["postId"]},"through":{"childColumns":["tagId"],"namespaceId":"public","parentColumns":["postId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Tag","namespace":"public"}},"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"embedding":{"column":"embedding"},"id":{"column":"id"},"impressionCount":{"column":"impressionCount"},"priority":{"column":"priority"},"reachScore":{"column":"reachScore"},"title":{"column":"title"},"userId":{"column":"userId"},"viewCount":{"column":"viewCount"}},"namespaceId":"public","table":"post"}},"PostTag":{"fields":{"postId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"tagId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"post":{"cardinality":"N:1","nullable":false,"on":{"localFields":["postId"],"targetFields":["id"]},"to":{"model":"Post","namespace":"public"}},"tag":{"cardinality":"N:1","nullable":false,"on":{"localFields":["tagId"],"targetFields":["id"]},"to":{"model":"Tag","namespace":"public"}}},"storage":{"fields":{"postId":{"column":"postId"},"tagId":{"column":"tagId"}},"namespaceId":"public","table":"post_tag"}},"Tag":{"fields":{"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"label":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{"posts":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["tagId"]},"through":{"childColumns":["postId"],"namespaceId":"public","parentColumns":["tagId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Post","namespace":"public"}}},"storage":{"fields":{"id":{"column":"id"},"label":{"column":"label"}},"namespaceId":"public","table":"tag"}},"Task":{"discriminator":{"field":"type"},"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"description":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"status":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"type":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"description":{"column":"description"},"id":{"column":"id"},"status":{"column":"status"},"title":{"column":"title"},"type":{"column":"type"},"userId":{"column":"userId"}},"namespaceId":"public","table":"task"},"variants":{"bug":{"model":"Bug","namespace":"public"},"feature":{"model":"Feature","namespace":"public"}}},"User":{"fields":{"address":{"nullable":true,"type":{"kind":"valueObject","name":"Address"}},"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"displayName":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"user_type","namespaceId":"public","plane":"domain"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Post","namespace":"public"}},"tasks":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Task","namespace":"public"}}},"storage":{"fields":{"address":{"column":"address"},"createdAt":{"column":"createdAt"},"displayName":{"column":"displayName"},"email":{"column":"email"},"id":{"column":"id"},"kind":{"column":"kind"}},"namespaceId":"public","table":"user"}}},"valueObjects":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}}}}}}},"execution":{"executionHash":"eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655","mutations":{"defaults":[{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"post","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"tag","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"task","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"user","field":"id","namespace":"public"}}]}},"extensions":{"demo/engagement-stats":{"familyId":"sql","id":"demo/engagement-stats","kind":"extension","targetId":"postgres","types":{"aggregateDescriptors":[{"input":{"kind":"trait","trait":"numeric"},"nullable":true,"operation":"stddev","output":{"codecId":"pg/numeric@1","kind":"codec"}}]},"version":"0.0.1"},"pgvector":{"capabilities":{"postgres":{"pgvector.cosine":true}},"familyId":"sql","id":"pgvector","kind":"extension","targetId":"postgres","types":{"codecTypes":{"import":{"alias":"PgVectorTypes","named":"CodecTypes","package":"@internal/extension-pgvector/codec-types"},"parameterizedOutputTypes":{"alias":"PgVectorParameterizedOutputTypes","named":"ParameterizedOutputTypesForTypeParams","package":"@internal/extension-pgvector/codec-types"}},"operationTypes":{"import":{"alias":"PgVectorOperationTypes","named":"OperationTypes","package":"@internal/extension-pgvector/operation-types"}},"queryOperationTypes":{"import":{"alias":"PgVectorQueryOperationTypes","named":"QueryOperationTypes","package":"@internal/extension-pgvector/operation-types"}}},"version":"0.0.1"}},"meta":{},"profileHash":"3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2","roots":{"post":{"model":"Post","namespace":"public"},"post_tag":{"model":"PostTag","namespace":"public"},"tag":{"model":"Tag","namespace":"public"},"task":{"model":"Task","namespace":"public"},"user":{"model":"User","namespace":"public"}},"schemaVersion":"1","storage":{"namespaces":{"public":{"entries":{"table":{"bug":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"severity":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"stepsToRepro":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"bug"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"feature":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"priority":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"targetRelease":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"feature"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"post":{"checks":[{"expression":"\"priority\" IN (0, 1, 2)","name":"post_priority_check_b236ace9","prefix":"post_priority_check"}],"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"embedding":{"codecId":"pg/vector@1","dataType":"pgvector/vector","nullable":true,"typeParams":{"length":1536}},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"impressionCount":{"codecId":"pg/int8@1","dataType":"pg/int8","nullable":true},"priority":{"codecId":"pg/int4@1","dataType":"pg/int4","default":{"kind":"literal","value":0},"nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Priority","namespaceId":"public","plane":"storage"}},"reachScore":{"codecId":"pg/unboundedint@1","dataType":"pg/numeric","nullable":true},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"viewCount":{"codecId":"pg/int8number@1","dataType":"pg/int8","nullable":true}},"foreignKeys":[{"index":{"name":"post_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"post"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"post_userId_idx_a489d58a","prefix":"post_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"post_tag":{"columns":{"postId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"tagId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"post_tag_postId_idx_a7a72715"},"source":{"columns":["postId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"post"}},{"index":{"name":"post_tag_tagId_idx_86854244"},"source":{"columns":["tagId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"tag"}}],"indexes":[{"columns":["postId"],"name":"post_tag_postId_idx_a7a72715","prefix":"post_tag_postId_idx","unique":false},{"columns":["tagId"],"name":"post_tag_tagId_idx_86854244","prefix":"post_tag_tagId_idx","unique":false}],"primaryKey":{"columns":["postId","tagId"]},"uniques":[]},"tag":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"label":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[{"columns":["label"]}]},"task":{"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"description":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"status":{"codecId":"pg/text@1","dataType":"pg/text","default":{"kind":"literal","value":"open"},"nullable":false},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"type":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"task_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"task"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"task_userId_idx_a489d58a","prefix":"task_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"user":{"checks":[{"expression":"\"kind\" IN ('admin', 'user')","name":"user_kind_check_836d43ef","prefix":"user_kind_check"}],"columns":{"address":{"codecId":"pg/jsonb@1","dataType":"pg/jsonb","nullable":true},"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"displayName":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"email":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"kind":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"user_type","namespaceId":"public","plane":"storage"}}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]}},"valueSet":{"Priority":{"kind":"valueSet","values":[0,1,2]},"user_type":{"kind":"valueSet","values":["admin","user"]}}},"id":"public","kind":"postgres-schema"}},"storageHash":"d44ab6f7caddcc384332c669737008aec7ec8c9f5b013cb502a03c2eb04be9c0"},"target":"postgres","targetFamily":"sql"} diff --git a/examples/prisma-8-demo/src/prisma/contract.d.ts b/examples/prisma-8-demo/src/prisma/contract.d.ts index 6e6569558199..89ebdece0c99 100644 --- a/examples/prisma-8-demo/src/prisma/contract.d.ts +++ b/examples/prisma-8-demo/src/prisma/contract.d.ts @@ -25,11 +25,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@prisma/orm-postgres/contract/types'; export type StorageHash = - StorageHashBase<'0ce1b6cf3e03ac1498f39eb8ef08d36d73ee2f0bc7fe368b97f1497ba050501a'>; + StorageHashBase<'99a4393ee9f877db228baee1164325543fa018be780fd1253250d7d81539f38e'>; export type ExecutionHash = ExecutionHashBase<'eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655'>; export type ProfileHash = @@ -261,15 +261,15 @@ type DefaultLiteralValue = CodecId extends keyo : Encoded; type ParameterizedOutputTypeFunctions = PgParameterizedOutputTypes | PgVectorParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -283,6 +283,9 @@ type FieldTypeOverrides = { readonly Post: { readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 }; }; + readonly Task: { + readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; + }; readonly User: { readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; }; @@ -294,18 +297,23 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = { readonly public: { readonly post: { readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 } }; + readonly task: { + readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; + }; readonly user: { readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; }; @@ -843,11 +851,37 @@ type ContractBase = Omit< }; readonly valueSet: { readonly Priority: { readonly kind: 'valueSet'; readonly values: readonly [0, 1, 2] }; + readonly 'Task.type': { + readonly kind: 'valueSet'; + readonly values: readonly ['bug', 'feature']; + }; readonly user_type: { readonly kind: 'valueSet'; readonly values: readonly ['admin', 'user']; }; }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + }; + }; }; }; }; @@ -1161,7 +1195,13 @@ type ContractBase = Omit< readonly fields: { readonly address: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: 'public'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; }; readonly createdAt: { readonly nullable: false; @@ -1219,28 +1259,6 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - }; - }; readonly enum: { readonly Priority: { readonly codecId: 'pg/int4@1'; @@ -1396,28 +1414,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/examples/prisma-8-demo/src/prisma/contract.json b/examples/prisma-8-demo/src/prisma/contract.json index 08c9ebb3e98a..11f4bc74989a 100644 --- a/examples/prisma-8-demo/src/prisma/contract.json +++ b/examples/prisma-8-demo/src/prisma/contract.json @@ -546,8 +546,11 @@ "address": { "nullable": true, "type": { + "entityKind": "valueObject", + "entityName": "Address", "kind": "valueObject", - "name": "Address" + "namespaceId": "public", + "plane": "storage" } }, "createdAt": { @@ -649,40 +652,6 @@ "table": "user" } } - }, - "valueObjects": { - "Address": { - "fields": { - "city": { - "nullable": false, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" - } - }, - "country": { - "nullable": false, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" - } - }, - "street": { - "nullable": false, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" - } - }, - "zip": { - "nullable": true, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" - } - } - } - } } } } @@ -1058,6 +1027,13 @@ ] }, "task": { + "checks": [ + { + "expression": "\"type\" IN ('bug', 'feature')", + "name": "task_type_check_cda3f5e7", + "prefix": "task_type_check" + } + ], "columns": { "createdAt": { "codecId": "pg/timestamptz-temporal@1", @@ -1095,7 +1071,13 @@ "type": { "codecId": "pg/text@1", "dataType": "pg/text", - "nullable": false + "nullable": false, + "valueSet": { + "entityKind": "valueSet", + "entityName": "Task.type", + "namespaceId": "public", + "plane": "storage" + } }, "userId": { "codecId": "pg/uuid@1", @@ -1143,6 +1125,11 @@ }, "user": { "checks": [ + { + "expression": "\"address\" IS NULL OR jsonb_path_match(\"address\", 'strict $.type() == \"null\" || ($.type() == \"object\" && exists($.keyvalue() ? (@.key == \"city\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"country\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"street\" && (@.value.type() == \"string\"))) && !exists($.keyvalue() ? (@.key == \"zip\" && !(@.value.type() == \"null\" || (@.value.type() == \"string\")))) && !exists($.keyvalue() ? (@.key != \"city\" && @.key != \"country\" && @.key != \"street\" && @.key != \"zip\")))') IS TRUE", + "name": "user_address_value_object_69432dc1", + "prefix": "user_address_value_object" + }, { "expression": "\"kind\" IN ('admin', 'user')", "name": "user_kind_check_836d43ef", @@ -1201,6 +1188,40 @@ "uniques": [] } }, + "valueObject": { + "Address": { + "fields": { + "city": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "country": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "street": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "zip": { + "nullable": true, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + } + } + } + }, "valueSet": { "Priority": { "kind": "valueSet", @@ -1210,6 +1231,13 @@ 2 ] }, + "Task.type": { + "kind": "valueSet", + "values": [ + "bug", + "feature" + ] + }, "user_type": { "kind": "valueSet", "values": [ @@ -1223,7 +1251,7 @@ "kind": "postgres-schema" } }, - "storageHash": "0ce1b6cf3e03ac1498f39eb8ef08d36d73ee2f0bc7fe368b97f1497ba050501a" + "storageHash": "99a4393ee9f877db228baee1164325543fa018be780fd1253250d7d81539f38e" }, "execution": { "executionHash": "eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655", diff --git a/examples/prisma-8-postgis-demo/migrations/snapshots/28b5e5f98ce0e148f03e345518c9c53cb7a04a82e8e58de74a87c1b4ae08cf65/contract.d.ts b/examples/prisma-8-postgis-demo/migrations/snapshots/28b5e5f98ce0e148f03e345518c9c53cb7a04a82e8e58de74a87c1b4ae08cf65/contract.d.ts index 742622ac8252..1761a8f7bbe7 100644 --- a/examples/prisma-8-postgis-demo/migrations/snapshots/28b5e5f98ce0e148f03e345518c9c53cb7a04a82e8e58de74a87c1b4ae08cf65/contract.d.ts +++ b/examples/prisma-8-postgis-demo/migrations/snapshots/28b5e5f98ce0e148f03e345518c9c53cb7a04a82e8e58de74a87c1b4ae08cf65/contract.d.ts @@ -617,7 +617,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/examples/prisma-8-postgis-demo/src/prisma/contract.d.ts b/examples/prisma-8-postgis-demo/src/prisma/contract.d.ts index 742622ac8252..1761a8f7bbe7 100644 --- a/examples/prisma-8-postgis-demo/src/prisma/contract.d.ts +++ b/examples/prisma-8-postgis-demo/src/prisma/contract.d.ts @@ -617,7 +617,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/examples/prisma7-adoption/generated/prisma8/contract.d.ts b/examples/prisma7-adoption/generated/prisma8/contract.d.ts index 24b7812562d6..58f14e3c8fb2 100644 --- a/examples/prisma7-adoption/generated/prisma8/contract.d.ts +++ b/examples/prisma7-adoption/generated/prisma8/contract.d.ts @@ -845,7 +845,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/examples/react-router-demo/src/prisma/contract.d.ts b/examples/react-router-demo/src/prisma/contract.d.ts index 7126509cc497..fa05c231760f 100644 --- a/examples/react-router-demo/src/prisma/contract.d.ts +++ b/examples/react-router-demo/src/prisma/contract.d.ts @@ -558,7 +558,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/examples/retail-store/migrations/app/20260513T0505_initial/migration.json b/examples/retail-store/migrations/app/20260513T0505_initial/migration.json index cfeabaf07388..40d95ab97a4e 100644 --- a/examples/retail-store/migrations/app/20260513T0505_initial/migration.json +++ b/examples/retail-store/migrations/app/20260513T0505_initial/migration.json @@ -1,7 +1,7 @@ { "from": null, - "to": "6969147309c36a2e1cef69c2077abfa7ddb568d171ad99f03506d9f428b0a595", + "to": "917a8cff4030fa9140b93367a663f33d66cd14e92547b51e23a9ff82ffabf27f", "providedInvariants": [], "createdAt": "2026-05-13T05:05:47.654Z", - "migrationHash": "cd39aeeef127de0031bdd02d0b9053b60fd9ada3593ff258229f7589187c636a" + "migrationHash": "886de0ddc5bc235badc57ae7ae88bf4b360c26823e57a2667ce4f9d5b151f12a" } diff --git a/examples/retail-store/migrations/app/20260513T0505_initial/migration.ts b/examples/retail-store/migrations/app/20260513T0505_initial/migration.ts index c3eca8e45f1b..f142f7d8f49a 100755 --- a/examples/retail-store/migrations/app/20260513T0505_initial/migration.ts +++ b/examples/retail-store/migrations/app/20260513T0505_initial/migration.ts @@ -5,8 +5,8 @@ import { Migration, MigrationCLI, } from '@prisma/orm-mongo/target/migration'; -import type { Contract as End } from '../../snapshots/6969147309c36a2e1cef69c2077abfa7ddb568d171ad99f03506d9f428b0a595/contract'; -import endContract from '../../snapshots/6969147309c36a2e1cef69c2077abfa7ddb568d171ad99f03506d9f428b0a595/contract.json' with { +import type { Contract as End } from '../../snapshots/917a8cff4030fa9140b93367a663f33d66cd14e92547b51e23a9ff82ffabf27f/contract'; +import endContract from '../../snapshots/917a8cff4030fa9140b93367a663f33d66cd14e92547b51e23a9ff82ffabf27f/contract.json' with { type: 'json', }; diff --git a/examples/retail-store/migrations/app/20260513T0507_add_product_category_index/migration.json b/examples/retail-store/migrations/app/20260513T0507_add_product_category_index/migration.json index 872485bfe627..dfa8eca04a51 100644 --- a/examples/retail-store/migrations/app/20260513T0507_add_product_category_index/migration.json +++ b/examples/retail-store/migrations/app/20260513T0507_add_product_category_index/migration.json @@ -1,7 +1,7 @@ { - "from": "6969147309c36a2e1cef69c2077abfa7ddb568d171ad99f03506d9f428b0a595", - "to": "977a060afe52c4a56f93f2f33d65b8b6b4cc4ded04d16fe6b4f8e7e9e61192d3", + "from": "917a8cff4030fa9140b93367a663f33d66cd14e92547b51e23a9ff82ffabf27f", + "to": "32b49a010a6bec06827848a71ade1588d7863e429644edc402becee50580be78", "providedInvariants": [], "createdAt": "2026-05-13T05:07:50.592Z", - "migrationHash": "0ec54ea9455e425ad0a4df92cf5c674aaac04dbcf81fcc6a64d35618ea08402b" + "migrationHash": "7b8a1159dfd91a4ca23ea45e5fb96f6b6ddfe3fc565f00811af1251963292486" } diff --git a/examples/retail-store/migrations/app/20260513T0507_add_product_category_index/migration.ts b/examples/retail-store/migrations/app/20260513T0507_add_product_category_index/migration.ts index dcee5d04670d..2c25737eda59 100755 --- a/examples/retail-store/migrations/app/20260513T0507_add_product_category_index/migration.ts +++ b/examples/retail-store/migrations/app/20260513T0507_add_product_category_index/migration.ts @@ -1,11 +1,11 @@ #!/usr/bin/env -S node import { createIndex, Migration, MigrationCLI } from '@prisma/orm-mongo/target/migration'; -import type { Contract as End } from '../../snapshots/977a060afe52c4a56f93f2f33d65b8b6b4cc4ded04d16fe6b4f8e7e9e61192d3/contract'; -import endContract from '../../snapshots/977a060afe52c4a56f93f2f33d65b8b6b4cc4ded04d16fe6b4f8e7e9e61192d3/contract.json' with { +import type { Contract as End } from '../../snapshots/32b49a010a6bec06827848a71ade1588d7863e429644edc402becee50580be78/contract'; +import endContract from '../../snapshots/32b49a010a6bec06827848a71ade1588d7863e429644edc402becee50580be78/contract.json' with { type: 'json', }; -import type { Contract as Start } from '../../snapshots/6969147309c36a2e1cef69c2077abfa7ddb568d171ad99f03506d9f428b0a595/contract'; -import startContract from '../../snapshots/6969147309c36a2e1cef69c2077abfa7ddb568d171ad99f03506d9f428b0a595/contract.json' with { +import type { Contract as Start } from '../../snapshots/917a8cff4030fa9140b93367a663f33d66cd14e92547b51e23a9ff82ffabf27f/contract'; +import startContract from '../../snapshots/917a8cff4030fa9140b93367a663f33d66cd14e92547b51e23a9ff82ffabf27f/contract.json' with { type: 'json', }; diff --git a/examples/retail-store/migrations/app/20260513T0508_backfill_product_status/migration.json b/examples/retail-store/migrations/app/20260513T0508_backfill_product_status/migration.json index f882b213f3f2..115dcae34373 100644 --- a/examples/retail-store/migrations/app/20260513T0508_backfill_product_status/migration.json +++ b/examples/retail-store/migrations/app/20260513T0508_backfill_product_status/migration.json @@ -1,7 +1,7 @@ { - "from": "977a060afe52c4a56f93f2f33d65b8b6b4cc4ded04d16fe6b4f8e7e9e61192d3", - "to": "9af6d90aaa4cd03bf7eb2040a1c1b5c1225a2b33883ea3d0552f312f4684f8dc", + "from": "32b49a010a6bec06827848a71ade1588d7863e429644edc402becee50580be78", + "to": "c529f34639ba48ec978d133093cd95ac2a5a76196ea39b3fa71179a9ef2c3467", "providedInvariants": [], "createdAt": "2026-05-13T05:08:23.957Z", - "migrationHash": "2bce4ded744ff835ac51e0c815c7567047ccc1acc25069e0bebadaf72fd5f7d7" + "migrationHash": "21376cd04dd2ac1a87eea87af86728fce737c346bb0acfd066ff3842525daa0e" } diff --git a/examples/retail-store/migrations/app/20260513T0508_backfill_product_status/migration.ts b/examples/retail-store/migrations/app/20260513T0508_backfill_product_status/migration.ts index ecc6f3fa9f55..11167ec9355b 100755 --- a/examples/retail-store/migrations/app/20260513T0508_backfill_product_status/migration.ts +++ b/examples/retail-store/migrations/app/20260513T0508_backfill_product_status/migration.ts @@ -13,12 +13,12 @@ import { MigrationCLI, setValidation, } from '@prisma/orm-mongo/target/migration'; -import type { Contract as End } from '../../snapshots/9af6d90aaa4cd03bf7eb2040a1c1b5c1225a2b33883ea3d0552f312f4684f8dc/contract'; -import endContract from '../../snapshots/9af6d90aaa4cd03bf7eb2040a1c1b5c1225a2b33883ea3d0552f312f4684f8dc/contract.json' with { +import type { Contract as Start } from '../../snapshots/32b49a010a6bec06827848a71ade1588d7863e429644edc402becee50580be78/contract'; +import startContract from '../../snapshots/32b49a010a6bec06827848a71ade1588d7863e429644edc402becee50580be78/contract.json' with { type: 'json', }; -import type { Contract as Start } from '../../snapshots/977a060afe52c4a56f93f2f33d65b8b6b4cc4ded04d16fe6b4f8e7e9e61192d3/contract'; -import startContract from '../../snapshots/977a060afe52c4a56f93f2f33d65b8b6b4cc4ded04d16fe6b4f8e7e9e61192d3/contract.json' with { +import type { Contract as End } from '../../snapshots/c529f34639ba48ec978d133093cd95ac2a5a76196ea39b3fa71179a9ef2c3467/contract'; +import endContract from '../../snapshots/c529f34639ba48ec978d133093cd95ac2a5a76196ea39b3fa71179a9ef2c3467/contract.json' with { type: 'json', }; diff --git a/examples/retail-store/migrations/app/20260513T0508_backfill_product_status/ops.json b/examples/retail-store/migrations/app/20260513T0508_backfill_product_status/ops.json index 52e7218845ac..662bc61d3ce0 100644 --- a/examples/retail-store/migrations/app/20260513T0508_backfill_product_status/ops.json +++ b/examples/retail-store/migrations/app/20260513T0508_backfill_product_status/ops.json @@ -124,7 +124,7 @@ }, "meta": { "target": "mongo", - "storageHash": "9af6d90aaa4cd03bf7eb2040a1c1b5c1225a2b33883ea3d0552f312f4684f8dc", + "storageHash": "c529f34639ba48ec978d133093cd95ac2a5a76196ea39b3fa71179a9ef2c3467", "lane": "mongo-pipeline" } }, @@ -155,7 +155,7 @@ }, "meta": { "target": "mongo", - "storageHash": "9af6d90aaa4cd03bf7eb2040a1c1b5c1225a2b33883ea3d0552f312f4684f8dc", + "storageHash": "c529f34639ba48ec978d133093cd95ac2a5a76196ea39b3fa71179a9ef2c3467", "lane": "mongo-raw" } } @@ -185,7 +185,7 @@ }, "meta": { "target": "mongo", - "storageHash": "9af6d90aaa4cd03bf7eb2040a1c1b5c1225a2b33883ea3d0552f312f4684f8dc", + "storageHash": "c529f34639ba48ec978d133093cd95ac2a5a76196ea39b3fa71179a9ef2c3467", "lane": "mongo-pipeline" } }, diff --git a/examples/retail-store/migrations/app/20260628T0931_add_product_status_order_type_enums/migration.json b/examples/retail-store/migrations/app/20260628T0931_add_product_status_order_type_enums/migration.json index 8a6b19d15a71..a4b442280405 100644 --- a/examples/retail-store/migrations/app/20260628T0931_add_product_status_order_type_enums/migration.json +++ b/examples/retail-store/migrations/app/20260628T0931_add_product_status_order_type_enums/migration.json @@ -1,7 +1,7 @@ { - "from": "9af6d90aaa4cd03bf7eb2040a1c1b5c1225a2b33883ea3d0552f312f4684f8dc", - "to": "41bd5540ea833c009b5eacf6db7d489e88bdd5062554398ba8aa2657fa19d27e", + "from": "c529f34639ba48ec978d133093cd95ac2a5a76196ea39b3fa71179a9ef2c3467", + "to": "2f0e67cbc2a193e280f07c26972a484af4b62d3472c018c73bfd095749d4d0f5", "providedInvariants": [], "createdAt": "2026-06-28T09:31:36.433Z", - "migrationHash": "951cb0076901c679918a5b9029595c0abec72768ebe216ddb327d9066b3e3650" + "migrationHash": "057aed8cf407d30641357e655e7efc330ae7c6cf5b4352e9edf6f20e5c18e487" } diff --git a/examples/retail-store/migrations/app/20260628T0931_add_product_status_order_type_enums/migration.ts b/examples/retail-store/migrations/app/20260628T0931_add_product_status_order_type_enums/migration.ts index 70a5adeadb6f..317b0871a34c 100755 --- a/examples/retail-store/migrations/app/20260628T0931_add_product_status_order_type_enums/migration.ts +++ b/examples/retail-store/migrations/app/20260628T0931_add_product_status_order_type_enums/migration.ts @@ -1,11 +1,11 @@ #!/usr/bin/env -S node import { collMod, Migration, MigrationCLI } from '@prisma/orm-mongo/target/migration'; -import type { Contract as Start } from '../../snapshots/9af6d90aaa4cd03bf7eb2040a1c1b5c1225a2b33883ea3d0552f312f4684f8dc/contract'; -import startContract from '../../snapshots/9af6d90aaa4cd03bf7eb2040a1c1b5c1225a2b33883ea3d0552f312f4684f8dc/contract.json' with { +import type { Contract as End } from '../../snapshots/2f0e67cbc2a193e280f07c26972a484af4b62d3472c018c73bfd095749d4d0f5/contract'; +import endContract from '../../snapshots/2f0e67cbc2a193e280f07c26972a484af4b62d3472c018c73bfd095749d4d0f5/contract.json' with { type: 'json', }; -import type { Contract as End } from '../../snapshots/41bd5540ea833c009b5eacf6db7d489e88bdd5062554398ba8aa2657fa19d27e/contract'; -import endContract from '../../snapshots/41bd5540ea833c009b5eacf6db7d489e88bdd5062554398ba8aa2657fa19d27e/contract.json' with { +import type { Contract as Start } from '../../snapshots/c529f34639ba48ec978d133093cd95ac2a5a76196ea39b3fa71179a9ef2c3467/contract'; +import startContract from '../../snapshots/c529f34639ba48ec978d133093cd95ac2a5a76196ea39b3fa71179a9ef2c3467/contract.json' with { type: 'json', }; diff --git a/examples/retail-store/migrations/snapshots/41bd5540ea833c009b5eacf6db7d489e88bdd5062554398ba8aa2657fa19d27e/contract.d.ts b/examples/retail-store/migrations/snapshots/2f0e67cbc2a193e280f07c26972a484af4b62d3472c018c73bfd095749d4d0f5/contract.d.ts similarity index 82% rename from examples/retail-store/migrations/snapshots/41bd5540ea833c009b5eacf6db7d489e88bdd5062554398ba8aa2657fa19d27e/contract.d.ts rename to examples/retail-store/migrations/snapshots/2f0e67cbc2a193e280f07c26972a484af4b62d3472c018c73bfd095749d4d0f5/contract.d.ts index 4bf316302d46..1d1e6c05a177 100644 --- a/examples/retail-store/migrations/snapshots/41bd5540ea833c009b5eacf6db7d489e88bdd5062554398ba8aa2657fa19d27e/contract.d.ts +++ b/examples/retail-store/migrations/snapshots/2f0e67cbc2a193e280f07c26972a484af4b62d3472c018c73bfd095749d4d0f5/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@prisma/orm-mongo/contract/types'; export type StorageHash = - StorageHashBase<'41bd5540ea833c009b5eacf6db7d489e88bdd5062554398ba8aa2657fa19d27e'>; + StorageHashBase<'2f0e67cbc2a193e280f07c26972a484af4b62d3472c018c73bfd095749d4d0f5'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -82,14 +82,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -669,6 +671,160 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly postalCode: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly streetAndNumber: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + readonly CartItem: { + readonly fields: { + readonly amount: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; + }; + readonly brand: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly image: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Image'; + }; + }; + readonly name: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly price: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Price'; + }; + }; + readonly productId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + readonly Image: { + readonly fields: { + readonly url: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + readonly InvoiceLineItem: { + readonly fields: { + readonly amount: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; + }; + readonly lineTotal: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; + }; + readonly name: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly unitPrice: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; + }; + }; + }; + readonly OrderLineItem: { + readonly fields: { + readonly amount: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; + }; + readonly brand: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly image: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Image'; + }; + }; + readonly name: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly price: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Price'; + }; + }; + readonly productId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + readonly Price: { + readonly fields: { + readonly amount: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; + }; + readonly currency: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + readonly StatusEntry: { + readonly fields: { + readonly status: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly timestamp: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; + }; + }; + }; + }; }; }; }; @@ -732,7 +888,13 @@ type ContractBase = Omit< }; readonly items: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CartItem' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CartItem'; + }; readonly many: { readonly elementNullable: false }; }; readonly userId: { @@ -825,7 +987,13 @@ type ContractBase = Omit< }; readonly items: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'InvoiceLineItem' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'InvoiceLineItem'; + }; readonly many: { readonly elementNullable: false }; }; readonly orderId: { @@ -920,7 +1088,13 @@ type ContractBase = Omit< }; readonly items: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'OrderLineItem' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'OrderLineItem'; + }; readonly many: { readonly elementNullable: false }; }; readonly shippingAddress: { @@ -929,7 +1103,13 @@ type ContractBase = Omit< }; readonly statusHistory: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'StatusEntry' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'StatusEntry'; + }; readonly many: { readonly elementNullable: false }; }; readonly type: { @@ -1007,7 +1187,13 @@ type ContractBase = Omit< }; readonly image: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Image'; + }; }; readonly name: { readonly nullable: false; @@ -1015,7 +1201,13 @@ type ContractBase = Omit< }; readonly price: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Price'; + }; }; readonly primaryCategory: { readonly nullable: false; @@ -1070,7 +1262,13 @@ type ContractBase = Omit< readonly fields: { readonly address: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; }; readonly email: { readonly nullable: false; @@ -1154,136 +1352,6 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly postalCode: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly streetAndNumber: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly CartItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly image: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly price: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Image: { - readonly fields: { - readonly url: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly InvoiceLineItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly lineTotal: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly unitPrice: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - }; - }; - readonly OrderLineItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly image: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly price: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Price: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly currency: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly StatusEntry: { - readonly fields: { - readonly status: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly timestamp: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; - }; - }; - }; - }; readonly enum: { readonly OrderType: { readonly codecId: 'mongo/string@1'; @@ -1318,136 +1386,6 @@ type ContractBase = Omit< readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly postalCode: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly streetAndNumber: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly CartItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly image: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly price: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Image: { - readonly fields: { - readonly url: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly InvoiceLineItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly lineTotal: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly unitPrice: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - }; - }; - readonly OrderLineItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly image: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly price: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Price: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly currency: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly StatusEntry: { - readonly fields: { - readonly status: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly timestamp: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/examples/retail-store/migrations/snapshots/2f0e67cbc2a193e280f07c26972a484af4b62d3472c018c73bfd095749d4d0f5/contract.json b/examples/retail-store/migrations/snapshots/2f0e67cbc2a193e280f07c26972a484af4b62d3472c018c73bfd095749d4d0f5/contract.json new file mode 100644 index 000000000000..14ba6239f9d8 --- /dev/null +++ b/examples/retail-store/migrations/snapshots/2f0e67cbc2a193e280f07c26972a484af4b62d3472c018c73bfd095749d4d0f5/contract.json @@ -0,0 +1 @@ +{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{},"domain":{"namespaces":{"__unbound__":{"enum":{"OrderType":{"codecId":"mongo/string@1","members":[{"name":"Delivery","value":"delivery"},{"name":"Pickup","value":"pickup"}]},"ProductStatus":{"codecId":"mongo/string@1","members":[{"name":"Active","value":"active"},{"name":"Discontinued","value":"discontinued"},{"name":"OutOfStock","value":"out-of-stock"}]}},"models":{"AddToCartEvent":{"base":{"model":"Event","namespace":"__unbound__"},"fields":{"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events","fields":{"brand":{"field":"brand"},"productId":{"field":"productId"}}}},"Cart":{"fields":{"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"items":{"many":{"elementNullable":false},"nullable":false,"type":{"entityKind":"valueObject","entityName":"CartItem","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"userId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}}},"relations":{"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"__unbound__"}}},"storage":{"collection":"carts","fields":{"id":{"field":"_id"},"items":{"field":"items"},"userId":{"field":"userId"}}}},"Event":{"discriminator":{"field":"type"},"fields":{"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"sessionId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"timestamp":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}},"type":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events","fields":{"id":{"field":"_id"},"sessionId":{"field":"sessionId"},"timestamp":{"field":"timestamp"},"type":{"field":"type"},"userId":{"field":"userId"}}},"variants":{"add-to-cart":{"model":"AddToCartEvent","namespace":"__unbound__"},"search":{"model":"SearchEvent","namespace":"__unbound__"},"view-product":{"model":"ViewProductEvent","namespace":"__unbound__"}}},"Invoice":{"fields":{"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"issuedAt":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}},"items":{"many":{"elementNullable":false},"nullable":false,"type":{"entityKind":"valueObject","entityName":"InvoiceLineItem","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"orderId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"subtotal":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"tax":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"total":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}}},"relations":{"order":{"cardinality":"N:1","nullable":false,"on":{"localFields":["orderId"],"targetFields":["id"]},"to":{"model":"Order","namespace":"__unbound__"}}},"storage":{"collection":"invoices","fields":{"id":{"field":"_id"},"issuedAt":{"field":"issuedAt"},"items":{"field":"items"},"orderId":{"field":"orderId"},"subtotal":{"field":"subtotal"},"tax":{"field":"tax"},"total":{"field":"total"}}}},"Location":{"fields":{"city":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"postalCode":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"streetAndNumber":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"locations","fields":{"city":{"field":"city"},"country":{"field":"country"},"id":{"field":"_id"},"name":{"field":"name"},"postalCode":{"field":"postalCode"},"streetAndNumber":{"field":"streetAndNumber"}}}},"Order":{"fields":{"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"items":{"many":{"elementNullable":false},"nullable":false,"type":{"entityKind":"valueObject","entityName":"OrderLineItem","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"shippingAddress":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"statusHistory":{"many":{"elementNullable":false},"nullable":false,"type":{"entityKind":"valueObject","entityName":"StatusEntry","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"type":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"OrderType","namespaceId":"__unbound__","plane":"domain"}},"userId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}}},"relations":{"invoices":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["orderId"]},"to":{"model":"Invoice","namespace":"__unbound__"}},"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"__unbound__"}}},"storage":{"collection":"orders","fields":{"id":{"field":"_id"},"items":{"field":"items"},"shippingAddress":{"field":"shippingAddress"},"statusHistory":{"field":"statusHistory"},"type":{"field":"type"},"userId":{"field":"userId"}}}},"Product":{"fields":{"articleType":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"code":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"description":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"embedding":{"many":{"elementNullable":false},"nullable":true,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"image":{"nullable":false,"type":{"entityKind":"valueObject","entityName":"Image","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"price":{"nullable":false,"type":{"entityKind":"valueObject","entityName":"Price","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"primaryCategory":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"status":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"ProductStatus","namespaceId":"__unbound__","plane":"domain"}},"subCategory":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"products","fields":{"articleType":{"field":"articleType"},"brand":{"field":"brand"},"code":{"field":"code"},"description":{"field":"description"},"embedding":{"field":"embedding"},"id":{"field":"_id"},"image":{"field":"image"},"name":{"field":"name"},"price":{"field":"price"},"primaryCategory":{"field":"primaryCategory"},"status":{"field":"status"},"subCategory":{"field":"subCategory"}}}},"SearchEvent":{"base":{"model":"Event","namespace":"__unbound__"},"fields":{"query":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events","fields":{"query":{"field":"query"}}}},"User":{"fields":{"address":{"nullable":true,"type":{"entityKind":"valueObject","entityName":"Address","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"email":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{"carts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Cart","namespace":"__unbound__"}},"orders":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Order","namespace":"__unbound__"}}},"storage":{"collection":"users","fields":{"address":{"field":"address"},"email":{"field":"email"},"id":{"field":"_id"},"name":{"field":"name"}}}},"ViewProductEvent":{"base":{"model":"Event","namespace":"__unbound__"},"fields":{"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"exitMethod":{"nullable":true,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"subCategory":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events","fields":{"brand":{"field":"brand"},"exitMethod":{"field":"exitMethod"},"productId":{"field":"productId"},"subCategory":{"field":"subCategory"}}}}}}}},"extensions":{},"meta":{},"profileHash":"251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3","roots":{"carts":{"model":"Cart","namespace":"__unbound__"},"events":{"model":"Event","namespace":"__unbound__"},"invoices":{"model":"Invoice","namespace":"__unbound__"},"locations":{"model":"Location","namespace":"__unbound__"},"orders":{"model":"Order","namespace":"__unbound__"},"products":{"model":"Product","namespace":"__unbound__"},"users":{"model":"User","namespace":"__unbound__"}},"schemaVersion":"1","storage":{"namespaces":{"__unbound__":{"entries":{"collection":{"carts":{"indexes":[{"keys":[{"direction":1,"field":"userId"}],"kind":"mongo-index","unique":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"items":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"int"},"brand":{"bsonType":"string"},"image":{"additionalProperties":false,"bsonType":"object","properties":{"url":{"bsonType":"string"}},"required":["url"]},"name":{"bsonType":"string"},"price":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"double"},"currency":{"bsonType":"string"}},"required":["amount","currency"]},"productId":{"bsonType":"string"}},"required":["amount","brand","image","name","price","productId"]}},"userId":{"bsonType":"objectId"}},"required":["_id","items","userId"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"events":{"indexes":[{"keys":[{"direction":1,"field":"userId"},{"direction":-1,"field":"timestamp"}],"kind":"mongo-index"},{"expireAfterSeconds":7776000,"keys":[{"direction":1,"field":"timestamp"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"bsonType":"object","oneOf":[{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"brand":{"bsonType":"string"},"exitMethod":{"bsonType":["null","string"]},"productId":{"bsonType":"string"},"sessionId":{"bsonType":"string"},"subCategory":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"enum":["view-product"]},"userId":{"bsonType":"string"}},"required":["brand","productId","subCategory","type"]},{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"query":{"bsonType":"string"},"sessionId":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"enum":["search"]},"userId":{"bsonType":"string"}},"required":["query","type"]},{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"brand":{"bsonType":"string"},"productId":{"bsonType":"string"},"sessionId":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"enum":["add-to-cart"]},"userId":{"bsonType":"string"}},"required":["brand","productId","type"]}],"properties":{"_id":{"bsonType":"objectId"},"sessionId":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"bsonType":"string"},"userId":{"bsonType":"string"}},"required":["_id","sessionId","timestamp","type","userId"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"invoices":{"indexes":[{"keys":[{"direction":1,"field":"orderId"}],"kind":"mongo-index"},{"keys":[{"direction":-1,"field":"issuedAt"}],"kind":"mongo-index","sparse":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"issuedAt":{"bsonType":"date"},"items":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"int"},"lineTotal":{"bsonType":"double"},"name":{"bsonType":"string"},"unitPrice":{"bsonType":"double"}},"required":["amount","lineTotal","name","unitPrice"]}},"orderId":{"bsonType":"objectId"},"subtotal":{"bsonType":"double"},"tax":{"bsonType":"double"},"total":{"bsonType":"double"}},"required":["_id","issuedAt","items","orderId","subtotal","tax","total"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"locations":{"indexes":[{"collation":{"locale":"en","strength":2},"keys":[{"direction":1,"field":"city"},{"direction":1,"field":"country"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"city":{"bsonType":"string"},"country":{"bsonType":"string"},"name":{"bsonType":"string"},"postalCode":{"bsonType":"string"},"streetAndNumber":{"bsonType":"string"}},"required":["_id","city","country","name","postalCode","streetAndNumber"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"orders":{"indexes":[{"keys":[{"direction":1,"field":"userId"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"items":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"int"},"brand":{"bsonType":"string"},"image":{"additionalProperties":false,"bsonType":"object","properties":{"url":{"bsonType":"string"}},"required":["url"]},"name":{"bsonType":"string"},"price":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"double"},"currency":{"bsonType":"string"}},"required":["amount","currency"]},"productId":{"bsonType":"string"}},"required":["amount","brand","image","name","price","productId"]}},"shippingAddress":{"bsonType":"string"},"statusHistory":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"status":{"bsonType":"string"},"timestamp":{"bsonType":"date"}},"required":["status","timestamp"]}},"type":{"bsonType":"string","enum":["delivery","pickup"]},"userId":{"bsonType":"objectId"}},"required":["_id","items","shippingAddress","statusHistory","type","userId"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"products":{"indexes":[{"keys":[{"direction":"text","field":"name"},{"direction":"text","field":"description"}],"kind":"mongo-index","weights":{"description":1,"name":10}},{"keys":[{"direction":1,"field":"brand"},{"direction":1,"field":"subCategory"}],"kind":"mongo-index"},{"keys":[{"direction":1,"field":"primaryCategory"},{"direction":1,"field":"articleType"}],"kind":"mongo-index"},{"keys":[{"direction":"hashed","field":"code"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"articleType":{"bsonType":"string"},"brand":{"bsonType":"string"},"code":{"bsonType":"string"},"description":{"bsonType":"string"},"embedding":{"bsonType":["null","array"],"items":{"bsonType":"double"}},"image":{"additionalProperties":false,"bsonType":"object","properties":{"url":{"bsonType":"string"}},"required":["url"]},"name":{"bsonType":"string"},"price":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"double"},"currency":{"bsonType":"string"}},"required":["amount","currency"]},"primaryCategory":{"bsonType":"string"},"status":{"bsonType":"string","enum":["active","discontinued","out-of-stock"]},"subCategory":{"bsonType":"string"}},"required":["_id","articleType","brand","code","description","image","name","price","primaryCategory","status","subCategory"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"users":{"indexes":[{"keys":[{"direction":1,"field":"email"}],"kind":"mongo-index","unique":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"address":{"oneOf":[{"bsonType":"null"},{"additionalProperties":false,"bsonType":"object","properties":{"city":{"bsonType":"string"},"country":{"bsonType":"string"},"postalCode":{"bsonType":"string"},"streetAndNumber":{"bsonType":"string"}},"required":["city","country","postalCode","streetAndNumber"]}]},"email":{"bsonType":"string"},"name":{"bsonType":"string"}},"required":["_id","email","name"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}}},"valueObject":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"postalCode":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"streetAndNumber":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"CartItem":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}},"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"image":{"nullable":false,"type":{"entityKind":"valueObject","entityName":"Image","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"price":{"nullable":false,"type":{"entityKind":"valueObject","entityName":"Price","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"Image":{"fields":{"url":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"InvoiceLineItem":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}},"lineTotal":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"unitPrice":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}}}},"OrderLineItem":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}},"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"image":{"nullable":false,"type":{"entityKind":"valueObject","entityName":"Image","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"price":{"nullable":false,"type":{"entityKind":"valueObject","entityName":"Price","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"Price":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"currency":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"StatusEntry":{"fields":{"status":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"timestamp":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}}}}},"valueSet":{"OrderType":{"kind":"valueSet","values":["delivery","pickup"]},"ProductStatus":{"kind":"valueSet","values":["active","discontinued","out-of-stock"]}}},"id":"__unbound__","kind":"mongo-database"}},"storageHash":"2f0e67cbc2a193e280f07c26972a484af4b62d3472c018c73bfd095749d4d0f5"},"target":"mongo","targetFamily":"mongo"} diff --git a/examples/retail-store/migrations/snapshots/6969147309c36a2e1cef69c2077abfa7ddb568d171ad99f03506d9f428b0a595/contract.d.ts b/examples/retail-store/migrations/snapshots/32b49a010a6bec06827848a71ade1588d7863e429644edc402becee50580be78/contract.d.ts similarity index 81% rename from examples/retail-store/migrations/snapshots/6969147309c36a2e1cef69c2077abfa7ddb568d171ad99f03506d9f428b0a595/contract.d.ts rename to examples/retail-store/migrations/snapshots/32b49a010a6bec06827848a71ade1588d7863e429644edc402becee50580be78/contract.d.ts index a0c8f0590190..65c76ed39bf5 100644 --- a/examples/retail-store/migrations/snapshots/6969147309c36a2e1cef69c2077abfa7ddb568d171ad99f03506d9f428b0a595/contract.d.ts +++ b/examples/retail-store/migrations/snapshots/32b49a010a6bec06827848a71ade1588d7863e429644edc402becee50580be78/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@prisma/orm-mongo/contract/types'; export type StorageHash = - StorageHashBase<'6969147309c36a2e1cef69c2077abfa7ddb568d171ad99f03506d9f428b0a595'>; + StorageHashBase<'32b49a010a6bec06827848a71ade1588d7863e429644edc402becee50580be78'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -65,14 +65,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -529,6 +531,13 @@ type ContractBase = Omit< ]; readonly kind: 'mongo-index'; }, + { + readonly keys: readonly [ + { readonly direction: 1; readonly field: 'primaryCategory' }, + { readonly direction: 1; readonly field: 'articleType' }, + ]; + readonly kind: 'mongo-index'; + }, { readonly keys: readonly [ { readonly direction: 'hashed'; readonly field: 'code' }, @@ -631,6 +640,160 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly postalCode: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly streetAndNumber: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + readonly CartItem: { + readonly fields: { + readonly amount: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; + }; + readonly brand: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly image: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Image'; + }; + }; + readonly name: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly price: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Price'; + }; + }; + readonly productId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + readonly Image: { + readonly fields: { + readonly url: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + readonly InvoiceLineItem: { + readonly fields: { + readonly amount: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; + }; + readonly lineTotal: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; + }; + readonly name: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly unitPrice: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; + }; + }; + }; + readonly OrderLineItem: { + readonly fields: { + readonly amount: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; + }; + readonly brand: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly image: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Image'; + }; + }; + readonly name: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly price: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Price'; + }; + }; + readonly productId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + readonly Price: { + readonly fields: { + readonly amount: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; + }; + readonly currency: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + readonly StatusEntry: { + readonly fields: { + readonly status: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly timestamp: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; + }; + }; + }; + }; }; }; }; @@ -694,7 +857,13 @@ type ContractBase = Omit< }; readonly items: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CartItem' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CartItem'; + }; readonly many: { readonly elementNullable: false }; }; readonly userId: { @@ -787,7 +956,13 @@ type ContractBase = Omit< }; readonly items: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'InvoiceLineItem' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'InvoiceLineItem'; + }; readonly many: { readonly elementNullable: false }; }; readonly orderId: { @@ -882,7 +1057,13 @@ type ContractBase = Omit< }; readonly items: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'OrderLineItem' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'OrderLineItem'; + }; readonly many: { readonly elementNullable: false }; }; readonly shippingAddress: { @@ -891,7 +1072,13 @@ type ContractBase = Omit< }; readonly statusHistory: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'StatusEntry' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'StatusEntry'; + }; readonly many: { readonly elementNullable: false }; }; readonly type: { @@ -964,7 +1151,13 @@ type ContractBase = Omit< }; readonly image: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Image'; + }; }; readonly name: { readonly nullable: false; @@ -972,7 +1165,13 @@ type ContractBase = Omit< }; readonly price: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Price'; + }; }; readonly primaryCategory: { readonly nullable: false; @@ -1021,7 +1220,13 @@ type ContractBase = Omit< readonly fields: { readonly address: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; }; readonly email: { readonly nullable: false; @@ -1105,272 +1310,12 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly postalCode: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly streetAndNumber: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly CartItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly image: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly price: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Image: { - readonly fields: { - readonly url: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly InvoiceLineItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly lineTotal: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly unitPrice: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - }; - }; - readonly OrderLineItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly image: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly price: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Price: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly currency: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly StatusEntry: { - readonly fields: { - readonly status: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly timestamp: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; - }; - }; - }; - }; }; }; }; readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly postalCode: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly streetAndNumber: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly CartItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly image: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly price: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Image: { - readonly fields: { - readonly url: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly InvoiceLineItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly lineTotal: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly unitPrice: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - }; - }; - readonly OrderLineItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly image: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly price: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Price: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly currency: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly StatusEntry: { - readonly fields: { - readonly status: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly timestamp: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/examples/retail-store/migrations/snapshots/32b49a010a6bec06827848a71ade1588d7863e429644edc402becee50580be78/contract.json b/examples/retail-store/migrations/snapshots/32b49a010a6bec06827848a71ade1588d7863e429644edc402becee50580be78/contract.json new file mode 100644 index 000000000000..efc89272ddd9 --- /dev/null +++ b/examples/retail-store/migrations/snapshots/32b49a010a6bec06827848a71ade1588d7863e429644edc402becee50580be78/contract.json @@ -0,0 +1 @@ +{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{},"domain":{"namespaces":{"__unbound__":{"models":{"AddToCartEvent":{"base":{"model":"Event","namespace":"__unbound__"},"fields":{"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events","fields":{"brand":{"field":"brand"},"productId":{"field":"productId"}}}},"Cart":{"fields":{"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"items":{"many":{"elementNullable":false},"nullable":false,"type":{"entityKind":"valueObject","entityName":"CartItem","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"userId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}}},"relations":{"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"__unbound__"}}},"storage":{"collection":"carts","fields":{"id":{"field":"_id"},"items":{"field":"items"},"userId":{"field":"userId"}}}},"Event":{"discriminator":{"field":"type"},"fields":{"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"sessionId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"timestamp":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}},"type":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events","fields":{"id":{"field":"_id"},"sessionId":{"field":"sessionId"},"timestamp":{"field":"timestamp"},"type":{"field":"type"},"userId":{"field":"userId"}}},"variants":{"add-to-cart":{"model":"AddToCartEvent","namespace":"__unbound__"},"search":{"model":"SearchEvent","namespace":"__unbound__"},"view-product":{"model":"ViewProductEvent","namespace":"__unbound__"}}},"Invoice":{"fields":{"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"issuedAt":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}},"items":{"many":{"elementNullable":false},"nullable":false,"type":{"entityKind":"valueObject","entityName":"InvoiceLineItem","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"orderId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"subtotal":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"tax":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"total":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}}},"relations":{"order":{"cardinality":"N:1","nullable":false,"on":{"localFields":["orderId"],"targetFields":["id"]},"to":{"model":"Order","namespace":"__unbound__"}}},"storage":{"collection":"invoices","fields":{"id":{"field":"_id"},"issuedAt":{"field":"issuedAt"},"items":{"field":"items"},"orderId":{"field":"orderId"},"subtotal":{"field":"subtotal"},"tax":{"field":"tax"},"total":{"field":"total"}}}},"Location":{"fields":{"city":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"postalCode":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"streetAndNumber":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"locations","fields":{"city":{"field":"city"},"country":{"field":"country"},"id":{"field":"_id"},"name":{"field":"name"},"postalCode":{"field":"postalCode"},"streetAndNumber":{"field":"streetAndNumber"}}}},"Order":{"fields":{"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"items":{"many":{"elementNullable":false},"nullable":false,"type":{"entityKind":"valueObject","entityName":"OrderLineItem","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"shippingAddress":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"statusHistory":{"many":{"elementNullable":false},"nullable":false,"type":{"entityKind":"valueObject","entityName":"StatusEntry","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"type":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}}},"relations":{"invoices":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["orderId"]},"to":{"model":"Invoice","namespace":"__unbound__"}},"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"__unbound__"}}},"storage":{"collection":"orders","fields":{"id":{"field":"_id"},"items":{"field":"items"},"shippingAddress":{"field":"shippingAddress"},"statusHistory":{"field":"statusHistory"},"type":{"field":"type"},"userId":{"field":"userId"}}}},"Product":{"fields":{"articleType":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"code":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"description":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"image":{"nullable":false,"type":{"entityKind":"valueObject","entityName":"Image","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"price":{"nullable":false,"type":{"entityKind":"valueObject","entityName":"Price","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"primaryCategory":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"subCategory":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"products","fields":{"articleType":{"field":"articleType"},"brand":{"field":"brand"},"code":{"field":"code"},"description":{"field":"description"},"id":{"field":"_id"},"image":{"field":"image"},"name":{"field":"name"},"price":{"field":"price"},"primaryCategory":{"field":"primaryCategory"},"subCategory":{"field":"subCategory"}}}},"SearchEvent":{"base":{"model":"Event","namespace":"__unbound__"},"fields":{"query":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events","fields":{"query":{"field":"query"}}}},"User":{"fields":{"address":{"nullable":true,"type":{"entityKind":"valueObject","entityName":"Address","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"email":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{"carts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Cart","namespace":"__unbound__"}},"orders":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Order","namespace":"__unbound__"}}},"storage":{"collection":"users","fields":{"address":{"field":"address"},"email":{"field":"email"},"id":{"field":"_id"},"name":{"field":"name"}}}},"ViewProductEvent":{"base":{"model":"Event","namespace":"__unbound__"},"fields":{"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"exitMethod":{"nullable":true,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"subCategory":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events","fields":{"brand":{"field":"brand"},"exitMethod":{"field":"exitMethod"},"productId":{"field":"productId"},"subCategory":{"field":"subCategory"}}}}}}}},"extensions":{},"meta":{},"profileHash":"251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3","roots":{"carts":{"model":"Cart","namespace":"__unbound__"},"events":{"model":"Event","namespace":"__unbound__"},"invoices":{"model":"Invoice","namespace":"__unbound__"},"locations":{"model":"Location","namespace":"__unbound__"},"orders":{"model":"Order","namespace":"__unbound__"},"products":{"model":"Product","namespace":"__unbound__"},"users":{"model":"User","namespace":"__unbound__"}},"schemaVersion":"1","storage":{"namespaces":{"__unbound__":{"entries":{"collection":{"carts":{"indexes":[{"keys":[{"direction":1,"field":"userId"}],"kind":"mongo-index","unique":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"items":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"int"},"brand":{"bsonType":"string"},"image":{"additionalProperties":false,"bsonType":"object","properties":{"url":{"bsonType":"string"}},"required":["url"]},"name":{"bsonType":"string"},"price":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"double"},"currency":{"bsonType":"string"}},"required":["amount","currency"]},"productId":{"bsonType":"string"}},"required":["amount","brand","image","name","price","productId"]}},"userId":{"bsonType":"objectId"}},"required":["_id","items","userId"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"events":{"indexes":[{"keys":[{"direction":1,"field":"userId"},{"direction":-1,"field":"timestamp"}],"kind":"mongo-index"},{"expireAfterSeconds":7776000,"keys":[{"direction":1,"field":"timestamp"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"bsonType":"object","oneOf":[{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"brand":{"bsonType":"string"},"exitMethod":{"bsonType":["null","string"]},"productId":{"bsonType":"string"},"sessionId":{"bsonType":"string"},"subCategory":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"enum":["view-product"]},"userId":{"bsonType":"string"}},"required":["brand","productId","subCategory","type"]},{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"query":{"bsonType":"string"},"sessionId":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"enum":["search"]},"userId":{"bsonType":"string"}},"required":["query","type"]},{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"brand":{"bsonType":"string"},"productId":{"bsonType":"string"},"sessionId":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"enum":["add-to-cart"]},"userId":{"bsonType":"string"}},"required":["brand","productId","type"]}],"properties":{"_id":{"bsonType":"objectId"},"sessionId":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"bsonType":"string"},"userId":{"bsonType":"string"}},"required":["_id","sessionId","timestamp","type","userId"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"invoices":{"indexes":[{"keys":[{"direction":1,"field":"orderId"}],"kind":"mongo-index"},{"keys":[{"direction":-1,"field":"issuedAt"}],"kind":"mongo-index","sparse":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"issuedAt":{"bsonType":"date"},"items":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"int"},"lineTotal":{"bsonType":"double"},"name":{"bsonType":"string"},"unitPrice":{"bsonType":"double"}},"required":["amount","lineTotal","name","unitPrice"]}},"orderId":{"bsonType":"objectId"},"subtotal":{"bsonType":"double"},"tax":{"bsonType":"double"},"total":{"bsonType":"double"}},"required":["_id","issuedAt","items","orderId","subtotal","tax","total"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"locations":{"indexes":[{"collation":{"locale":"en","strength":2},"keys":[{"direction":1,"field":"city"},{"direction":1,"field":"country"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"city":{"bsonType":"string"},"country":{"bsonType":"string"},"name":{"bsonType":"string"},"postalCode":{"bsonType":"string"},"streetAndNumber":{"bsonType":"string"}},"required":["_id","city","country","name","postalCode","streetAndNumber"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"orders":{"indexes":[{"keys":[{"direction":1,"field":"userId"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"items":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"int"},"brand":{"bsonType":"string"},"image":{"additionalProperties":false,"bsonType":"object","properties":{"url":{"bsonType":"string"}},"required":["url"]},"name":{"bsonType":"string"},"price":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"double"},"currency":{"bsonType":"string"}},"required":["amount","currency"]},"productId":{"bsonType":"string"}},"required":["amount","brand","image","name","price","productId"]}},"shippingAddress":{"bsonType":"string"},"statusHistory":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"status":{"bsonType":"string"},"timestamp":{"bsonType":"date"}},"required":["status","timestamp"]}},"type":{"bsonType":"string"},"userId":{"bsonType":"objectId"}},"required":["_id","items","shippingAddress","statusHistory","type","userId"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"products":{"indexes":[{"keys":[{"direction":"text","field":"name"},{"direction":"text","field":"description"}],"kind":"mongo-index","weights":{"description":1,"name":10}},{"keys":[{"direction":1,"field":"brand"},{"direction":1,"field":"subCategory"}],"kind":"mongo-index"},{"keys":[{"direction":1,"field":"primaryCategory"},{"direction":1,"field":"articleType"}],"kind":"mongo-index"},{"keys":[{"direction":"hashed","field":"code"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"articleType":{"bsonType":"string"},"brand":{"bsonType":"string"},"code":{"bsonType":"string"},"description":{"bsonType":"string"},"image":{"additionalProperties":false,"bsonType":"object","properties":{"url":{"bsonType":"string"}},"required":["url"]},"name":{"bsonType":"string"},"price":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"double"},"currency":{"bsonType":"string"}},"required":["amount","currency"]},"primaryCategory":{"bsonType":"string"},"subCategory":{"bsonType":"string"}},"required":["_id","articleType","brand","code","description","image","name","price","primaryCategory","subCategory"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"users":{"indexes":[{"keys":[{"direction":1,"field":"email"}],"kind":"mongo-index","unique":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"address":{"oneOf":[{"bsonType":"null"},{"additionalProperties":false,"bsonType":"object","properties":{"city":{"bsonType":"string"},"country":{"bsonType":"string"},"postalCode":{"bsonType":"string"},"streetAndNumber":{"bsonType":"string"}},"required":["city","country","postalCode","streetAndNumber"]}]},"email":{"bsonType":"string"},"name":{"bsonType":"string"}},"required":["_id","email","name"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}}},"valueObject":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"postalCode":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"streetAndNumber":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"CartItem":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}},"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"image":{"nullable":false,"type":{"entityKind":"valueObject","entityName":"Image","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"price":{"nullable":false,"type":{"entityKind":"valueObject","entityName":"Price","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"Image":{"fields":{"url":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"InvoiceLineItem":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}},"lineTotal":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"unitPrice":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}}}},"OrderLineItem":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}},"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"image":{"nullable":false,"type":{"entityKind":"valueObject","entityName":"Image","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"price":{"nullable":false,"type":{"entityKind":"valueObject","entityName":"Price","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"Price":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"currency":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"StatusEntry":{"fields":{"status":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"timestamp":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}}}}}},"id":"__unbound__","kind":"mongo-database"}},"storageHash":"32b49a010a6bec06827848a71ade1588d7863e429644edc402becee50580be78"},"target":"mongo","targetFamily":"mongo"} diff --git a/examples/retail-store/migrations/snapshots/41bd5540ea833c009b5eacf6db7d489e88bdd5062554398ba8aa2657fa19d27e/contract.json b/examples/retail-store/migrations/snapshots/41bd5540ea833c009b5eacf6db7d489e88bdd5062554398ba8aa2657fa19d27e/contract.json deleted file mode 100644 index 8f4dbd2ca7cf..000000000000 --- a/examples/retail-store/migrations/snapshots/41bd5540ea833c009b5eacf6db7d489e88bdd5062554398ba8aa2657fa19d27e/contract.json +++ /dev/null @@ -1 +0,0 @@ -{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{},"domain":{"namespaces":{"__unbound__":{"enum":{"OrderType":{"codecId":"mongo/string@1","members":[{"name":"Delivery","value":"delivery"},{"name":"Pickup","value":"pickup"}]},"ProductStatus":{"codecId":"mongo/string@1","members":[{"name":"Active","value":"active"},{"name":"Discontinued","value":"discontinued"},{"name":"OutOfStock","value":"out-of-stock"}]}},"models":{"AddToCartEvent":{"base":{"model":"Event","namespace":"__unbound__"},"fields":{"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events","fields":{"brand":{"field":"brand"},"productId":{"field":"productId"}}}},"Cart":{"fields":{"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"items":{"many":{"elementNullable":false},"nullable":false,"type":{"kind":"valueObject","name":"CartItem"}},"userId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}}},"relations":{"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"__unbound__"}}},"storage":{"collection":"carts","fields":{"id":{"field":"_id"},"items":{"field":"items"},"userId":{"field":"userId"}}}},"Event":{"discriminator":{"field":"type"},"fields":{"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"sessionId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"timestamp":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}},"type":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events","fields":{"id":{"field":"_id"},"sessionId":{"field":"sessionId"},"timestamp":{"field":"timestamp"},"type":{"field":"type"},"userId":{"field":"userId"}}},"variants":{"add-to-cart":{"model":"AddToCartEvent","namespace":"__unbound__"},"search":{"model":"SearchEvent","namespace":"__unbound__"},"view-product":{"model":"ViewProductEvent","namespace":"__unbound__"}}},"Invoice":{"fields":{"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"issuedAt":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}},"items":{"many":{"elementNullable":false},"nullable":false,"type":{"kind":"valueObject","name":"InvoiceLineItem"}},"orderId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"subtotal":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"tax":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"total":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}}},"relations":{"order":{"cardinality":"N:1","nullable":false,"on":{"localFields":["orderId"],"targetFields":["id"]},"to":{"model":"Order","namespace":"__unbound__"}}},"storage":{"collection":"invoices","fields":{"id":{"field":"_id"},"issuedAt":{"field":"issuedAt"},"items":{"field":"items"},"orderId":{"field":"orderId"},"subtotal":{"field":"subtotal"},"tax":{"field":"tax"},"total":{"field":"total"}}}},"Location":{"fields":{"city":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"postalCode":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"streetAndNumber":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"locations","fields":{"city":{"field":"city"},"country":{"field":"country"},"id":{"field":"_id"},"name":{"field":"name"},"postalCode":{"field":"postalCode"},"streetAndNumber":{"field":"streetAndNumber"}}}},"Order":{"fields":{"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"items":{"many":{"elementNullable":false},"nullable":false,"type":{"kind":"valueObject","name":"OrderLineItem"}},"shippingAddress":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"statusHistory":{"many":{"elementNullable":false},"nullable":false,"type":{"kind":"valueObject","name":"StatusEntry"}},"type":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"OrderType","namespaceId":"__unbound__","plane":"domain"}},"userId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}}},"relations":{"invoices":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["orderId"]},"to":{"model":"Invoice","namespace":"__unbound__"}},"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"__unbound__"}}},"storage":{"collection":"orders","fields":{"id":{"field":"_id"},"items":{"field":"items"},"shippingAddress":{"field":"shippingAddress"},"statusHistory":{"field":"statusHistory"},"type":{"field":"type"},"userId":{"field":"userId"}}}},"Product":{"fields":{"articleType":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"code":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"description":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"embedding":{"many":{"elementNullable":false},"nullable":true,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"image":{"nullable":false,"type":{"kind":"valueObject","name":"Image"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"price":{"nullable":false,"type":{"kind":"valueObject","name":"Price"}},"primaryCategory":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"status":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"ProductStatus","namespaceId":"__unbound__","plane":"domain"}},"subCategory":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"products","fields":{"articleType":{"field":"articleType"},"brand":{"field":"brand"},"code":{"field":"code"},"description":{"field":"description"},"embedding":{"field":"embedding"},"id":{"field":"_id"},"image":{"field":"image"},"name":{"field":"name"},"price":{"field":"price"},"primaryCategory":{"field":"primaryCategory"},"status":{"field":"status"},"subCategory":{"field":"subCategory"}}}},"SearchEvent":{"base":{"model":"Event","namespace":"__unbound__"},"fields":{"query":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events","fields":{"query":{"field":"query"}}}},"User":{"fields":{"address":{"nullable":true,"type":{"kind":"valueObject","name":"Address"}},"email":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{"carts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Cart","namespace":"__unbound__"}},"orders":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Order","namespace":"__unbound__"}}},"storage":{"collection":"users","fields":{"address":{"field":"address"},"email":{"field":"email"},"id":{"field":"_id"},"name":{"field":"name"}}}},"ViewProductEvent":{"base":{"model":"Event","namespace":"__unbound__"},"fields":{"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"exitMethod":{"nullable":true,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"subCategory":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events","fields":{"brand":{"field":"brand"},"exitMethod":{"field":"exitMethod"},"productId":{"field":"productId"},"subCategory":{"field":"subCategory"}}}}},"valueObjects":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"postalCode":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"streetAndNumber":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"CartItem":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}},"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"image":{"nullable":false,"type":{"kind":"valueObject","name":"Image"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"price":{"nullable":false,"type":{"kind":"valueObject","name":"Price"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"Image":{"fields":{"url":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"InvoiceLineItem":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}},"lineTotal":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"unitPrice":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}}}},"OrderLineItem":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}},"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"image":{"nullable":false,"type":{"kind":"valueObject","name":"Image"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"price":{"nullable":false,"type":{"kind":"valueObject","name":"Price"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"Price":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"currency":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"StatusEntry":{"fields":{"status":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"timestamp":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}}}}}}}},"extensions":{},"meta":{},"profileHash":"251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3","roots":{"carts":{"model":"Cart","namespace":"__unbound__"},"events":{"model":"Event","namespace":"__unbound__"},"invoices":{"model":"Invoice","namespace":"__unbound__"},"locations":{"model":"Location","namespace":"__unbound__"},"orders":{"model":"Order","namespace":"__unbound__"},"products":{"model":"Product","namespace":"__unbound__"},"users":{"model":"User","namespace":"__unbound__"}},"schemaVersion":"1","storage":{"namespaces":{"__unbound__":{"entries":{"collection":{"carts":{"indexes":[{"keys":[{"direction":1,"field":"userId"}],"kind":"mongo-index","unique":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"items":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"int"},"brand":{"bsonType":"string"},"image":{"additionalProperties":false,"bsonType":"object","properties":{"url":{"bsonType":"string"}},"required":["url"]},"name":{"bsonType":"string"},"price":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"double"},"currency":{"bsonType":"string"}},"required":["amount","currency"]},"productId":{"bsonType":"string"}},"required":["amount","brand","image","name","price","productId"]}},"userId":{"bsonType":"objectId"}},"required":["_id","items","userId"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"events":{"indexes":[{"keys":[{"direction":1,"field":"userId"},{"direction":-1,"field":"timestamp"}],"kind":"mongo-index"},{"expireAfterSeconds":7776000,"keys":[{"direction":1,"field":"timestamp"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"bsonType":"object","oneOf":[{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"brand":{"bsonType":"string"},"exitMethod":{"bsonType":["null","string"]},"productId":{"bsonType":"string"},"sessionId":{"bsonType":"string"},"subCategory":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"enum":["view-product"]},"userId":{"bsonType":"string"}},"required":["brand","productId","subCategory","type"]},{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"query":{"bsonType":"string"},"sessionId":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"enum":["search"]},"userId":{"bsonType":"string"}},"required":["query","type"]},{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"brand":{"bsonType":"string"},"productId":{"bsonType":"string"},"sessionId":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"enum":["add-to-cart"]},"userId":{"bsonType":"string"}},"required":["brand","productId","type"]}],"properties":{"_id":{"bsonType":"objectId"},"sessionId":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"bsonType":"string"},"userId":{"bsonType":"string"}},"required":["_id","sessionId","timestamp","type","userId"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"invoices":{"indexes":[{"keys":[{"direction":1,"field":"orderId"}],"kind":"mongo-index"},{"keys":[{"direction":-1,"field":"issuedAt"}],"kind":"mongo-index","sparse":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"issuedAt":{"bsonType":"date"},"items":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"int"},"lineTotal":{"bsonType":"double"},"name":{"bsonType":"string"},"unitPrice":{"bsonType":"double"}},"required":["amount","lineTotal","name","unitPrice"]}},"orderId":{"bsonType":"objectId"},"subtotal":{"bsonType":"double"},"tax":{"bsonType":"double"},"total":{"bsonType":"double"}},"required":["_id","issuedAt","items","orderId","subtotal","tax","total"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"locations":{"indexes":[{"collation":{"locale":"en","strength":2},"keys":[{"direction":1,"field":"city"},{"direction":1,"field":"country"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"city":{"bsonType":"string"},"country":{"bsonType":"string"},"name":{"bsonType":"string"},"postalCode":{"bsonType":"string"},"streetAndNumber":{"bsonType":"string"}},"required":["_id","city","country","name","postalCode","streetAndNumber"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"orders":{"indexes":[{"keys":[{"direction":1,"field":"userId"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"items":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"int"},"brand":{"bsonType":"string"},"image":{"additionalProperties":false,"bsonType":"object","properties":{"url":{"bsonType":"string"}},"required":["url"]},"name":{"bsonType":"string"},"price":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"double"},"currency":{"bsonType":"string"}},"required":["amount","currency"]},"productId":{"bsonType":"string"}},"required":["amount","brand","image","name","price","productId"]}},"shippingAddress":{"bsonType":"string"},"statusHistory":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"status":{"bsonType":"string"},"timestamp":{"bsonType":"date"}},"required":["status","timestamp"]}},"type":{"bsonType":"string","enum":["delivery","pickup"]},"userId":{"bsonType":"objectId"}},"required":["_id","items","shippingAddress","statusHistory","type","userId"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"products":{"indexes":[{"keys":[{"direction":"text","field":"name"},{"direction":"text","field":"description"}],"kind":"mongo-index","weights":{"description":1,"name":10}},{"keys":[{"direction":1,"field":"brand"},{"direction":1,"field":"subCategory"}],"kind":"mongo-index"},{"keys":[{"direction":1,"field":"primaryCategory"},{"direction":1,"field":"articleType"}],"kind":"mongo-index"},{"keys":[{"direction":"hashed","field":"code"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"articleType":{"bsonType":"string"},"brand":{"bsonType":"string"},"code":{"bsonType":"string"},"description":{"bsonType":"string"},"embedding":{"bsonType":["null","array"],"items":{"bsonType":"double"}},"image":{"additionalProperties":false,"bsonType":"object","properties":{"url":{"bsonType":"string"}},"required":["url"]},"name":{"bsonType":"string"},"price":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"double"},"currency":{"bsonType":"string"}},"required":["amount","currency"]},"primaryCategory":{"bsonType":"string"},"status":{"bsonType":"string","enum":["active","discontinued","out-of-stock"]},"subCategory":{"bsonType":"string"}},"required":["_id","articleType","brand","code","description","image","name","price","primaryCategory","status","subCategory"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"users":{"indexes":[{"keys":[{"direction":1,"field":"email"}],"kind":"mongo-index","unique":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"address":{"oneOf":[{"bsonType":"null"},{"additionalProperties":false,"bsonType":"object","properties":{"city":{"bsonType":"string"},"country":{"bsonType":"string"},"postalCode":{"bsonType":"string"},"streetAndNumber":{"bsonType":"string"}},"required":["city","country","postalCode","streetAndNumber"]}]},"email":{"bsonType":"string"},"name":{"bsonType":"string"}},"required":["_id","email","name"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}}},"valueSet":{"OrderType":{"kind":"valueSet","values":["delivery","pickup"]},"ProductStatus":{"kind":"valueSet","values":["active","discontinued","out-of-stock"]}}},"id":"__unbound__","kind":"mongo-database"}},"storageHash":"41bd5540ea833c009b5eacf6db7d489e88bdd5062554398ba8aa2657fa19d27e"},"target":"mongo","targetFamily":"mongo"} diff --git a/examples/retail-store/migrations/snapshots/6969147309c36a2e1cef69c2077abfa7ddb568d171ad99f03506d9f428b0a595/contract.json b/examples/retail-store/migrations/snapshots/6969147309c36a2e1cef69c2077abfa7ddb568d171ad99f03506d9f428b0a595/contract.json deleted file mode 100644 index 78264e5b0e0c..000000000000 --- a/examples/retail-store/migrations/snapshots/6969147309c36a2e1cef69c2077abfa7ddb568d171ad99f03506d9f428b0a595/contract.json +++ /dev/null @@ -1 +0,0 @@ -{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{},"domain":{"namespaces":{"__unbound__":{"models":{"AddToCartEvent":{"base":{"model":"Event","namespace":"__unbound__"},"fields":{"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events","fields":{"brand":{"field":"brand"},"productId":{"field":"productId"}}}},"Cart":{"fields":{"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"items":{"many":{"elementNullable":false},"nullable":false,"type":{"kind":"valueObject","name":"CartItem"}},"userId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}}},"relations":{"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"__unbound__"}}},"storage":{"collection":"carts","fields":{"id":{"field":"_id"},"items":{"field":"items"},"userId":{"field":"userId"}}}},"Event":{"discriminator":{"field":"type"},"fields":{"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"sessionId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"timestamp":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}},"type":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events","fields":{"id":{"field":"_id"},"sessionId":{"field":"sessionId"},"timestamp":{"field":"timestamp"},"type":{"field":"type"},"userId":{"field":"userId"}}},"variants":{"add-to-cart":{"model":"AddToCartEvent","namespace":"__unbound__"},"search":{"model":"SearchEvent","namespace":"__unbound__"},"view-product":{"model":"ViewProductEvent","namespace":"__unbound__"}}},"Invoice":{"fields":{"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"issuedAt":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}},"items":{"many":{"elementNullable":false},"nullable":false,"type":{"kind":"valueObject","name":"InvoiceLineItem"}},"orderId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"subtotal":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"tax":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"total":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}}},"relations":{"order":{"cardinality":"N:1","nullable":false,"on":{"localFields":["orderId"],"targetFields":["id"]},"to":{"model":"Order","namespace":"__unbound__"}}},"storage":{"collection":"invoices","fields":{"id":{"field":"_id"},"issuedAt":{"field":"issuedAt"},"items":{"field":"items"},"orderId":{"field":"orderId"},"subtotal":{"field":"subtotal"},"tax":{"field":"tax"},"total":{"field":"total"}}}},"Location":{"fields":{"city":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"postalCode":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"streetAndNumber":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"locations","fields":{"city":{"field":"city"},"country":{"field":"country"},"id":{"field":"_id"},"name":{"field":"name"},"postalCode":{"field":"postalCode"},"streetAndNumber":{"field":"streetAndNumber"}}}},"Order":{"fields":{"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"items":{"many":{"elementNullable":false},"nullable":false,"type":{"kind":"valueObject","name":"OrderLineItem"}},"shippingAddress":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"statusHistory":{"many":{"elementNullable":false},"nullable":false,"type":{"kind":"valueObject","name":"StatusEntry"}},"type":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}}},"relations":{"invoices":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["orderId"]},"to":{"model":"Invoice","namespace":"__unbound__"}},"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"__unbound__"}}},"storage":{"collection":"orders","fields":{"id":{"field":"_id"},"items":{"field":"items"},"shippingAddress":{"field":"shippingAddress"},"statusHistory":{"field":"statusHistory"},"type":{"field":"type"},"userId":{"field":"userId"}}}},"Product":{"fields":{"articleType":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"code":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"description":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"image":{"nullable":false,"type":{"kind":"valueObject","name":"Image"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"price":{"nullable":false,"type":{"kind":"valueObject","name":"Price"}},"primaryCategory":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"subCategory":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"products","fields":{"articleType":{"field":"articleType"},"brand":{"field":"brand"},"code":{"field":"code"},"description":{"field":"description"},"id":{"field":"_id"},"image":{"field":"image"},"name":{"field":"name"},"price":{"field":"price"},"primaryCategory":{"field":"primaryCategory"},"subCategory":{"field":"subCategory"}}}},"SearchEvent":{"base":{"model":"Event","namespace":"__unbound__"},"fields":{"query":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events","fields":{"query":{"field":"query"}}}},"User":{"fields":{"address":{"nullable":true,"type":{"kind":"valueObject","name":"Address"}},"email":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{"carts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Cart","namespace":"__unbound__"}},"orders":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Order","namespace":"__unbound__"}}},"storage":{"collection":"users","fields":{"address":{"field":"address"},"email":{"field":"email"},"id":{"field":"_id"},"name":{"field":"name"}}}},"ViewProductEvent":{"base":{"model":"Event","namespace":"__unbound__"},"fields":{"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"exitMethod":{"nullable":true,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"subCategory":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events","fields":{"brand":{"field":"brand"},"exitMethod":{"field":"exitMethod"},"productId":{"field":"productId"},"subCategory":{"field":"subCategory"}}}}},"valueObjects":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"postalCode":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"streetAndNumber":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"CartItem":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}},"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"image":{"nullable":false,"type":{"kind":"valueObject","name":"Image"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"price":{"nullable":false,"type":{"kind":"valueObject","name":"Price"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"Image":{"fields":{"url":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"InvoiceLineItem":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}},"lineTotal":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"unitPrice":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}}}},"OrderLineItem":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}},"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"image":{"nullable":false,"type":{"kind":"valueObject","name":"Image"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"price":{"nullable":false,"type":{"kind":"valueObject","name":"Price"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"Price":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"currency":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"StatusEntry":{"fields":{"status":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"timestamp":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}}}}}}}},"extensions":{},"meta":{},"profileHash":"251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3","roots":{"carts":{"model":"Cart","namespace":"__unbound__"},"events":{"model":"Event","namespace":"__unbound__"},"invoices":{"model":"Invoice","namespace":"__unbound__"},"locations":{"model":"Location","namespace":"__unbound__"},"orders":{"model":"Order","namespace":"__unbound__"},"products":{"model":"Product","namespace":"__unbound__"},"users":{"model":"User","namespace":"__unbound__"}},"schemaVersion":"1","storage":{"namespaces":{"__unbound__":{"entries":{"collection":{"carts":{"indexes":[{"keys":[{"direction":1,"field":"userId"}],"kind":"mongo-index","unique":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"items":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"int"},"brand":{"bsonType":"string"},"image":{"additionalProperties":false,"bsonType":"object","properties":{"url":{"bsonType":"string"}},"required":["url"]},"name":{"bsonType":"string"},"price":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"double"},"currency":{"bsonType":"string"}},"required":["amount","currency"]},"productId":{"bsonType":"string"}},"required":["amount","brand","image","name","price","productId"]}},"userId":{"bsonType":"objectId"}},"required":["_id","items","userId"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"events":{"indexes":[{"keys":[{"direction":1,"field":"userId"},{"direction":-1,"field":"timestamp"}],"kind":"mongo-index"},{"expireAfterSeconds":7776000,"keys":[{"direction":1,"field":"timestamp"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"bsonType":"object","oneOf":[{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"brand":{"bsonType":"string"},"exitMethod":{"bsonType":["null","string"]},"productId":{"bsonType":"string"},"sessionId":{"bsonType":"string"},"subCategory":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"enum":["view-product"]},"userId":{"bsonType":"string"}},"required":["brand","productId","subCategory","type"]},{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"query":{"bsonType":"string"},"sessionId":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"enum":["search"]},"userId":{"bsonType":"string"}},"required":["query","type"]},{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"brand":{"bsonType":"string"},"productId":{"bsonType":"string"},"sessionId":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"enum":["add-to-cart"]},"userId":{"bsonType":"string"}},"required":["brand","productId","type"]}],"properties":{"_id":{"bsonType":"objectId"},"sessionId":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"bsonType":"string"},"userId":{"bsonType":"string"}},"required":["_id","sessionId","timestamp","type","userId"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"invoices":{"indexes":[{"keys":[{"direction":1,"field":"orderId"}],"kind":"mongo-index"},{"keys":[{"direction":-1,"field":"issuedAt"}],"kind":"mongo-index","sparse":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"issuedAt":{"bsonType":"date"},"items":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"int"},"lineTotal":{"bsonType":"double"},"name":{"bsonType":"string"},"unitPrice":{"bsonType":"double"}},"required":["amount","lineTotal","name","unitPrice"]}},"orderId":{"bsonType":"objectId"},"subtotal":{"bsonType":"double"},"tax":{"bsonType":"double"},"total":{"bsonType":"double"}},"required":["_id","issuedAt","items","orderId","subtotal","tax","total"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"locations":{"indexes":[{"collation":{"locale":"en","strength":2},"keys":[{"direction":1,"field":"city"},{"direction":1,"field":"country"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"city":{"bsonType":"string"},"country":{"bsonType":"string"},"name":{"bsonType":"string"},"postalCode":{"bsonType":"string"},"streetAndNumber":{"bsonType":"string"}},"required":["_id","city","country","name","postalCode","streetAndNumber"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"orders":{"indexes":[{"keys":[{"direction":1,"field":"userId"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"items":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"int"},"brand":{"bsonType":"string"},"image":{"additionalProperties":false,"bsonType":"object","properties":{"url":{"bsonType":"string"}},"required":["url"]},"name":{"bsonType":"string"},"price":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"double"},"currency":{"bsonType":"string"}},"required":["amount","currency"]},"productId":{"bsonType":"string"}},"required":["amount","brand","image","name","price","productId"]}},"shippingAddress":{"bsonType":"string"},"statusHistory":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"status":{"bsonType":"string"},"timestamp":{"bsonType":"date"}},"required":["status","timestamp"]}},"type":{"bsonType":"string"},"userId":{"bsonType":"objectId"}},"required":["_id","items","shippingAddress","statusHistory","type","userId"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"products":{"indexes":[{"keys":[{"direction":"text","field":"name"},{"direction":"text","field":"description"}],"kind":"mongo-index","weights":{"description":1,"name":10}},{"keys":[{"direction":1,"field":"brand"},{"direction":1,"field":"subCategory"}],"kind":"mongo-index"},{"keys":[{"direction":"hashed","field":"code"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"articleType":{"bsonType":"string"},"brand":{"bsonType":"string"},"code":{"bsonType":"string"},"description":{"bsonType":"string"},"image":{"additionalProperties":false,"bsonType":"object","properties":{"url":{"bsonType":"string"}},"required":["url"]},"name":{"bsonType":"string"},"price":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"double"},"currency":{"bsonType":"string"}},"required":["amount","currency"]},"primaryCategory":{"bsonType":"string"},"subCategory":{"bsonType":"string"}},"required":["_id","articleType","brand","code","description","image","name","price","primaryCategory","subCategory"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"users":{"indexes":[{"keys":[{"direction":1,"field":"email"}],"kind":"mongo-index","unique":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"address":{"oneOf":[{"bsonType":"null"},{"additionalProperties":false,"bsonType":"object","properties":{"city":{"bsonType":"string"},"country":{"bsonType":"string"},"postalCode":{"bsonType":"string"},"streetAndNumber":{"bsonType":"string"}},"required":["city","country","postalCode","streetAndNumber"]}]},"email":{"bsonType":"string"},"name":{"bsonType":"string"}},"required":["_id","email","name"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}}}},"id":"__unbound__","kind":"mongo-database"}},"storageHash":"6969147309c36a2e1cef69c2077abfa7ddb568d171ad99f03506d9f428b0a595"},"target":"mongo","targetFamily":"mongo"} diff --git a/examples/retail-store/migrations/snapshots/9af6d90aaa4cd03bf7eb2040a1c1b5c1225a2b33883ea3d0552f312f4684f8dc/contract.d.ts b/examples/retail-store/migrations/snapshots/917a8cff4030fa9140b93367a663f33d66cd14e92547b51e23a9ff82ffabf27f/contract.d.ts similarity index 80% rename from examples/retail-store/migrations/snapshots/9af6d90aaa4cd03bf7eb2040a1c1b5c1225a2b33883ea3d0552f312f4684f8dc/contract.d.ts rename to examples/retail-store/migrations/snapshots/917a8cff4030fa9140b93367a663f33d66cd14e92547b51e23a9ff82ffabf27f/contract.d.ts index bd59ba46526a..2d1156267de5 100644 --- a/examples/retail-store/migrations/snapshots/9af6d90aaa4cd03bf7eb2040a1c1b5c1225a2b33883ea3d0552f312f4684f8dc/contract.d.ts +++ b/examples/retail-store/migrations/snapshots/917a8cff4030fa9140b93367a663f33d66cd14e92547b51e23a9ff82ffabf27f/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@prisma/orm-mongo/contract/types'; export type StorageHash = - StorageHashBase<'9af6d90aaa4cd03bf7eb2040a1c1b5c1225a2b33883ea3d0552f312f4684f8dc'>; + StorageHashBase<'917a8cff4030fa9140b93367a663f33d66cd14e92547b51e23a9ff82ffabf27f'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -65,14 +65,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -137,13 +139,11 @@ export namespace Models { brand: FieldOutputTypes['__unbound__']['Product']['brand']; code: FieldOutputTypes['__unbound__']['Product']['code']; description: FieldOutputTypes['__unbound__']['Product']['description']; - embedding: FieldOutputTypes['__unbound__']['Product']['embedding']; id: FieldOutputTypes['__unbound__']['Product']['id']; image: FieldOutputTypes['__unbound__']['Product']['image']; name: FieldOutputTypes['__unbound__']['Product']['name']; price: FieldOutputTypes['__unbound__']['Product']['price']; primaryCategory: FieldOutputTypes['__unbound__']['Product']['primaryCategory']; - status: FieldOutputTypes['__unbound__']['Product']['status']; subCategory: FieldOutputTypes['__unbound__']['Product']['subCategory']; readonly [RelationKeys]?: never; }; @@ -531,13 +531,6 @@ type ContractBase = Omit< ]; readonly kind: 'mongo-index'; }, - { - readonly keys: readonly [ - { readonly direction: 1; readonly field: 'primaryCategory' }, - { readonly direction: 1; readonly field: 'articleType' }, - ]; - readonly kind: 'mongo-index'; - }, { readonly keys: readonly [ { readonly direction: 'hashed'; readonly field: 'code' }, @@ -556,10 +549,6 @@ type ContractBase = Omit< readonly brand: { readonly bsonType: 'string' }; readonly code: { readonly bsonType: 'string' }; readonly description: { readonly bsonType: 'string' }; - readonly embedding: { - readonly bsonType: readonly ['null', 'array']; - readonly items: { readonly bsonType: 'double' }; - }; readonly image: { readonly additionalProperties: false; readonly bsonType: 'object'; @@ -577,7 +566,6 @@ type ContractBase = Omit< readonly required: readonly ['amount', 'currency']; }; readonly primaryCategory: { readonly bsonType: 'string' }; - readonly status: { readonly bsonType: 'string' }; readonly subCategory: { readonly bsonType: 'string' }; }; readonly required: readonly [ @@ -590,7 +578,6 @@ type ContractBase = Omit< 'name', 'price', 'primaryCategory', - 'status', 'subCategory', ]; }; @@ -646,6 +633,160 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly postalCode: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly streetAndNumber: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + readonly CartItem: { + readonly fields: { + readonly amount: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; + }; + readonly brand: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly image: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Image'; + }; + }; + readonly name: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly price: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Price'; + }; + }; + readonly productId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + readonly Image: { + readonly fields: { + readonly url: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + readonly InvoiceLineItem: { + readonly fields: { + readonly amount: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; + }; + readonly lineTotal: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; + }; + readonly name: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly unitPrice: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; + }; + }; + }; + readonly OrderLineItem: { + readonly fields: { + readonly amount: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; + }; + readonly brand: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly image: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Image'; + }; + }; + readonly name: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly price: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Price'; + }; + }; + readonly productId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + readonly Price: { + readonly fields: { + readonly amount: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; + }; + readonly currency: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + readonly StatusEntry: { + readonly fields: { + readonly status: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly timestamp: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; + }; + }; + }; + }; }; }; }; @@ -709,7 +850,13 @@ type ContractBase = Omit< }; readonly items: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CartItem' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CartItem'; + }; readonly many: { readonly elementNullable: false }; }; readonly userId: { @@ -802,7 +949,13 @@ type ContractBase = Omit< }; readonly items: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'InvoiceLineItem' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'InvoiceLineItem'; + }; readonly many: { readonly elementNullable: false }; }; readonly orderId: { @@ -897,7 +1050,13 @@ type ContractBase = Omit< }; readonly items: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'OrderLineItem' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'OrderLineItem'; + }; readonly many: { readonly elementNullable: false }; }; readonly shippingAddress: { @@ -906,7 +1065,13 @@ type ContractBase = Omit< }; readonly statusHistory: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'StatusEntry' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'StatusEntry'; + }; readonly many: { readonly elementNullable: false }; }; readonly type: { @@ -973,18 +1138,19 @@ type ContractBase = Omit< readonly nullable: false; readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; }; - readonly embedding: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - readonly many: { readonly elementNullable: false }; - }; readonly id: { readonly nullable: false; readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; }; readonly image: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Image'; + }; }; readonly name: { readonly nullable: false; @@ -992,16 +1158,18 @@ type ContractBase = Omit< }; readonly price: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Price'; + }; }; readonly primaryCategory: { readonly nullable: false; readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; }; - readonly status: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; readonly subCategory: { readonly nullable: false; readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; @@ -1015,13 +1183,11 @@ type ContractBase = Omit< readonly brand: { readonly field: 'brand' }; readonly code: { readonly field: 'code' }; readonly description: { readonly field: 'description' }; - readonly embedding: { readonly field: 'embedding' }; readonly id: { readonly field: '_id' }; readonly image: { readonly field: 'image' }; readonly name: { readonly field: 'name' }; readonly price: { readonly field: 'price' }; readonly primaryCategory: { readonly field: 'primaryCategory' }; - readonly status: { readonly field: 'status' }; readonly subCategory: { readonly field: 'subCategory' }; }; }; @@ -1047,7 +1213,13 @@ type ContractBase = Omit< readonly fields: { readonly address: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; }; readonly email: { readonly nullable: false; @@ -1131,272 +1303,12 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly postalCode: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly streetAndNumber: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly CartItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly image: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly price: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Image: { - readonly fields: { - readonly url: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly InvoiceLineItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly lineTotal: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly unitPrice: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - }; - }; - readonly OrderLineItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly image: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly price: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Price: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly currency: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly StatusEntry: { - readonly fields: { - readonly status: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly timestamp: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; - }; - }; - }; - }; }; }; }; readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly postalCode: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly streetAndNumber: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly CartItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly image: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly price: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Image: { - readonly fields: { - readonly url: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly InvoiceLineItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly lineTotal: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly unitPrice: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - }; - }; - readonly OrderLineItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly image: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly price: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Price: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly currency: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly StatusEntry: { - readonly fields: { - readonly status: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly timestamp: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/examples/retail-store/migrations/snapshots/917a8cff4030fa9140b93367a663f33d66cd14e92547b51e23a9ff82ffabf27f/contract.json b/examples/retail-store/migrations/snapshots/917a8cff4030fa9140b93367a663f33d66cd14e92547b51e23a9ff82ffabf27f/contract.json new file mode 100644 index 000000000000..199fa0bca020 --- /dev/null +++ b/examples/retail-store/migrations/snapshots/917a8cff4030fa9140b93367a663f33d66cd14e92547b51e23a9ff82ffabf27f/contract.json @@ -0,0 +1 @@ +{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{},"domain":{"namespaces":{"__unbound__":{"models":{"AddToCartEvent":{"base":{"model":"Event","namespace":"__unbound__"},"fields":{"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events","fields":{"brand":{"field":"brand"},"productId":{"field":"productId"}}}},"Cart":{"fields":{"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"items":{"many":{"elementNullable":false},"nullable":false,"type":{"entityKind":"valueObject","entityName":"CartItem","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"userId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}}},"relations":{"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"__unbound__"}}},"storage":{"collection":"carts","fields":{"id":{"field":"_id"},"items":{"field":"items"},"userId":{"field":"userId"}}}},"Event":{"discriminator":{"field":"type"},"fields":{"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"sessionId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"timestamp":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}},"type":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events","fields":{"id":{"field":"_id"},"sessionId":{"field":"sessionId"},"timestamp":{"field":"timestamp"},"type":{"field":"type"},"userId":{"field":"userId"}}},"variants":{"add-to-cart":{"model":"AddToCartEvent","namespace":"__unbound__"},"search":{"model":"SearchEvent","namespace":"__unbound__"},"view-product":{"model":"ViewProductEvent","namespace":"__unbound__"}}},"Invoice":{"fields":{"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"issuedAt":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}},"items":{"many":{"elementNullable":false},"nullable":false,"type":{"entityKind":"valueObject","entityName":"InvoiceLineItem","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"orderId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"subtotal":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"tax":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"total":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}}},"relations":{"order":{"cardinality":"N:1","nullable":false,"on":{"localFields":["orderId"],"targetFields":["id"]},"to":{"model":"Order","namespace":"__unbound__"}}},"storage":{"collection":"invoices","fields":{"id":{"field":"_id"},"issuedAt":{"field":"issuedAt"},"items":{"field":"items"},"orderId":{"field":"orderId"},"subtotal":{"field":"subtotal"},"tax":{"field":"tax"},"total":{"field":"total"}}}},"Location":{"fields":{"city":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"postalCode":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"streetAndNumber":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"locations","fields":{"city":{"field":"city"},"country":{"field":"country"},"id":{"field":"_id"},"name":{"field":"name"},"postalCode":{"field":"postalCode"},"streetAndNumber":{"field":"streetAndNumber"}}}},"Order":{"fields":{"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"items":{"many":{"elementNullable":false},"nullable":false,"type":{"entityKind":"valueObject","entityName":"OrderLineItem","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"shippingAddress":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"statusHistory":{"many":{"elementNullable":false},"nullable":false,"type":{"entityKind":"valueObject","entityName":"StatusEntry","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"type":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}}},"relations":{"invoices":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["orderId"]},"to":{"model":"Invoice","namespace":"__unbound__"}},"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"__unbound__"}}},"storage":{"collection":"orders","fields":{"id":{"field":"_id"},"items":{"field":"items"},"shippingAddress":{"field":"shippingAddress"},"statusHistory":{"field":"statusHistory"},"type":{"field":"type"},"userId":{"field":"userId"}}}},"Product":{"fields":{"articleType":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"code":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"description":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"image":{"nullable":false,"type":{"entityKind":"valueObject","entityName":"Image","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"price":{"nullable":false,"type":{"entityKind":"valueObject","entityName":"Price","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"primaryCategory":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"subCategory":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"products","fields":{"articleType":{"field":"articleType"},"brand":{"field":"brand"},"code":{"field":"code"},"description":{"field":"description"},"id":{"field":"_id"},"image":{"field":"image"},"name":{"field":"name"},"price":{"field":"price"},"primaryCategory":{"field":"primaryCategory"},"subCategory":{"field":"subCategory"}}}},"SearchEvent":{"base":{"model":"Event","namespace":"__unbound__"},"fields":{"query":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events","fields":{"query":{"field":"query"}}}},"User":{"fields":{"address":{"nullable":true,"type":{"entityKind":"valueObject","entityName":"Address","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"email":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{"carts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Cart","namespace":"__unbound__"}},"orders":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Order","namespace":"__unbound__"}}},"storage":{"collection":"users","fields":{"address":{"field":"address"},"email":{"field":"email"},"id":{"field":"_id"},"name":{"field":"name"}}}},"ViewProductEvent":{"base":{"model":"Event","namespace":"__unbound__"},"fields":{"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"exitMethod":{"nullable":true,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"subCategory":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events","fields":{"brand":{"field":"brand"},"exitMethod":{"field":"exitMethod"},"productId":{"field":"productId"},"subCategory":{"field":"subCategory"}}}}}}}},"extensions":{},"meta":{},"profileHash":"251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3","roots":{"carts":{"model":"Cart","namespace":"__unbound__"},"events":{"model":"Event","namespace":"__unbound__"},"invoices":{"model":"Invoice","namespace":"__unbound__"},"locations":{"model":"Location","namespace":"__unbound__"},"orders":{"model":"Order","namespace":"__unbound__"},"products":{"model":"Product","namespace":"__unbound__"},"users":{"model":"User","namespace":"__unbound__"}},"schemaVersion":"1","storage":{"namespaces":{"__unbound__":{"entries":{"collection":{"carts":{"indexes":[{"keys":[{"direction":1,"field":"userId"}],"kind":"mongo-index","unique":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"items":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"int"},"brand":{"bsonType":"string"},"image":{"additionalProperties":false,"bsonType":"object","properties":{"url":{"bsonType":"string"}},"required":["url"]},"name":{"bsonType":"string"},"price":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"double"},"currency":{"bsonType":"string"}},"required":["amount","currency"]},"productId":{"bsonType":"string"}},"required":["amount","brand","image","name","price","productId"]}},"userId":{"bsonType":"objectId"}},"required":["_id","items","userId"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"events":{"indexes":[{"keys":[{"direction":1,"field":"userId"},{"direction":-1,"field":"timestamp"}],"kind":"mongo-index"},{"expireAfterSeconds":7776000,"keys":[{"direction":1,"field":"timestamp"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"bsonType":"object","oneOf":[{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"brand":{"bsonType":"string"},"exitMethod":{"bsonType":["null","string"]},"productId":{"bsonType":"string"},"sessionId":{"bsonType":"string"},"subCategory":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"enum":["view-product"]},"userId":{"bsonType":"string"}},"required":["brand","productId","subCategory","type"]},{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"query":{"bsonType":"string"},"sessionId":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"enum":["search"]},"userId":{"bsonType":"string"}},"required":["query","type"]},{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"brand":{"bsonType":"string"},"productId":{"bsonType":"string"},"sessionId":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"enum":["add-to-cart"]},"userId":{"bsonType":"string"}},"required":["brand","productId","type"]}],"properties":{"_id":{"bsonType":"objectId"},"sessionId":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"bsonType":"string"},"userId":{"bsonType":"string"}},"required":["_id","sessionId","timestamp","type","userId"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"invoices":{"indexes":[{"keys":[{"direction":1,"field":"orderId"}],"kind":"mongo-index"},{"keys":[{"direction":-1,"field":"issuedAt"}],"kind":"mongo-index","sparse":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"issuedAt":{"bsonType":"date"},"items":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"int"},"lineTotal":{"bsonType":"double"},"name":{"bsonType":"string"},"unitPrice":{"bsonType":"double"}},"required":["amount","lineTotal","name","unitPrice"]}},"orderId":{"bsonType":"objectId"},"subtotal":{"bsonType":"double"},"tax":{"bsonType":"double"},"total":{"bsonType":"double"}},"required":["_id","issuedAt","items","orderId","subtotal","tax","total"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"locations":{"indexes":[{"collation":{"locale":"en","strength":2},"keys":[{"direction":1,"field":"city"},{"direction":1,"field":"country"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"city":{"bsonType":"string"},"country":{"bsonType":"string"},"name":{"bsonType":"string"},"postalCode":{"bsonType":"string"},"streetAndNumber":{"bsonType":"string"}},"required":["_id","city","country","name","postalCode","streetAndNumber"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"orders":{"indexes":[{"keys":[{"direction":1,"field":"userId"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"items":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"int"},"brand":{"bsonType":"string"},"image":{"additionalProperties":false,"bsonType":"object","properties":{"url":{"bsonType":"string"}},"required":["url"]},"name":{"bsonType":"string"},"price":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"double"},"currency":{"bsonType":"string"}},"required":["amount","currency"]},"productId":{"bsonType":"string"}},"required":["amount","brand","image","name","price","productId"]}},"shippingAddress":{"bsonType":"string"},"statusHistory":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"status":{"bsonType":"string"},"timestamp":{"bsonType":"date"}},"required":["status","timestamp"]}},"type":{"bsonType":"string"},"userId":{"bsonType":"objectId"}},"required":["_id","items","shippingAddress","statusHistory","type","userId"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"products":{"indexes":[{"keys":[{"direction":"text","field":"name"},{"direction":"text","field":"description"}],"kind":"mongo-index","weights":{"description":1,"name":10}},{"keys":[{"direction":1,"field":"brand"},{"direction":1,"field":"subCategory"}],"kind":"mongo-index"},{"keys":[{"direction":"hashed","field":"code"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"articleType":{"bsonType":"string"},"brand":{"bsonType":"string"},"code":{"bsonType":"string"},"description":{"bsonType":"string"},"image":{"additionalProperties":false,"bsonType":"object","properties":{"url":{"bsonType":"string"}},"required":["url"]},"name":{"bsonType":"string"},"price":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"double"},"currency":{"bsonType":"string"}},"required":["amount","currency"]},"primaryCategory":{"bsonType":"string"},"subCategory":{"bsonType":"string"}},"required":["_id","articleType","brand","code","description","image","name","price","primaryCategory","subCategory"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"users":{"indexes":[{"keys":[{"direction":1,"field":"email"}],"kind":"mongo-index","unique":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"address":{"oneOf":[{"bsonType":"null"},{"additionalProperties":false,"bsonType":"object","properties":{"city":{"bsonType":"string"},"country":{"bsonType":"string"},"postalCode":{"bsonType":"string"},"streetAndNumber":{"bsonType":"string"}},"required":["city","country","postalCode","streetAndNumber"]}]},"email":{"bsonType":"string"},"name":{"bsonType":"string"}},"required":["_id","email","name"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}}},"valueObject":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"postalCode":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"streetAndNumber":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"CartItem":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}},"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"image":{"nullable":false,"type":{"entityKind":"valueObject","entityName":"Image","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"price":{"nullable":false,"type":{"entityKind":"valueObject","entityName":"Price","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"Image":{"fields":{"url":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"InvoiceLineItem":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}},"lineTotal":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"unitPrice":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}}}},"OrderLineItem":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}},"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"image":{"nullable":false,"type":{"entityKind":"valueObject","entityName":"Image","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"price":{"nullable":false,"type":{"entityKind":"valueObject","entityName":"Price","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"Price":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"currency":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"StatusEntry":{"fields":{"status":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"timestamp":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}}}}}},"id":"__unbound__","kind":"mongo-database"}},"storageHash":"917a8cff4030fa9140b93367a663f33d66cd14e92547b51e23a9ff82ffabf27f"},"target":"mongo","targetFamily":"mongo"} diff --git a/examples/retail-store/migrations/snapshots/9414a8f88a64f9decc0e019967459e49da083f011cf91898094b7bccab6c1810/contract.d.ts b/examples/retail-store/migrations/snapshots/9414a8f88a64f9decc0e019967459e49da083f011cf91898094b7bccab6c1810/contract.d.ts deleted file mode 100644 index 57f5e63aac57..000000000000 --- a/examples/retail-store/migrations/snapshots/9414a8f88a64f9decc0e019967459e49da083f011cf91898094b7bccab6c1810/contract.d.ts +++ /dev/null @@ -1,1475 +0,0 @@ -// ⚠️ GENERATED FILE - DO NOT EDIT -// This file is automatically generated by 'prisma contract emit'. -// To regenerate, run: prisma contract emit -import type { CodecTypes as MongoCodecTypes } from '@prisma/orm-mongo/target/codec-types'; - -import type { - MongoCollection, - MongoContractWithTypeMaps, - MongoTypeMaps, - RelationKeys, -} from '@prisma/orm-mongo/family-contract'; -import type { - Contract as ContractType, - ExecutionHashBase, - NamespaceId, - ProfileHashBase, - StorageHashBase, -} from '@prisma/orm-mongo/contract/types'; - -export type StorageHash = - StorageHashBase<'9414a8f88a64f9decc0e019967459e49da083f011cf91898094b7bccab6c1810'>; -export type ExecutionHash = ExecutionHashBase; -export type ProfileHash = - ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; - -export type CodecTypes = MongoCodecTypes; - -export type AddressOutput = { - readonly city: CodecTypes['mongo/string@1']['output']; - readonly country: CodecTypes['mongo/string@1']['output']; - readonly postalCode: CodecTypes['mongo/string@1']['output']; - readonly streetAndNumber: CodecTypes['mongo/string@1']['output']; -}; -export type AddressInput = { - readonly city: CodecTypes['mongo/string@1']['input']; - readonly country: CodecTypes['mongo/string@1']['input']; - readonly postalCode: CodecTypes['mongo/string@1']['input']; - readonly streetAndNumber: CodecTypes['mongo/string@1']['input']; -}; -export type CartItemOutput = { - readonly amount: CodecTypes['mongo/int32@1']['output']; - readonly brand: CodecTypes['mongo/string@1']['output']; - readonly image: ImageOutput; - readonly name: CodecTypes['mongo/string@1']['output']; - readonly price: PriceOutput; - readonly productId: CodecTypes['mongo/string@1']['output']; -}; -export type CartItemInput = { - readonly amount: CodecTypes['mongo/int32@1']['input']; - readonly brand: CodecTypes['mongo/string@1']['input']; - readonly image: ImageInput; - readonly name: CodecTypes['mongo/string@1']['input']; - readonly price: PriceInput; - readonly productId: CodecTypes['mongo/string@1']['input']; -}; -export type ImageOutput = { readonly url: CodecTypes['mongo/string@1']['output'] }; -export type ImageInput = { readonly url: CodecTypes['mongo/string@1']['input'] }; -export type InvoiceLineItemOutput = { - readonly amount: CodecTypes['mongo/int32@1']['output']; - readonly lineTotal: CodecTypes['mongo/double@1']['output']; - readonly name: CodecTypes['mongo/string@1']['output']; - readonly unitPrice: CodecTypes['mongo/double@1']['output']; -}; -export type InvoiceLineItemInput = { - readonly amount: CodecTypes['mongo/int32@1']['input']; - readonly lineTotal: CodecTypes['mongo/double@1']['input']; - readonly name: CodecTypes['mongo/string@1']['input']; - readonly unitPrice: CodecTypes['mongo/double@1']['input']; -}; -export type OrderLineItemOutput = { - readonly amount: CodecTypes['mongo/int32@1']['output']; - readonly brand: CodecTypes['mongo/string@1']['output']; - readonly image: ImageOutput; - readonly name: CodecTypes['mongo/string@1']['output']; - readonly price: PriceOutput; - readonly productId: CodecTypes['mongo/string@1']['output']; -}; -export type OrderLineItemInput = { - readonly amount: CodecTypes['mongo/int32@1']['input']; - readonly brand: CodecTypes['mongo/string@1']['input']; - readonly image: ImageInput; - readonly name: CodecTypes['mongo/string@1']['input']; - readonly price: PriceInput; - readonly productId: CodecTypes['mongo/string@1']['input']; -}; -export type PriceOutput = { - readonly amount: CodecTypes['mongo/double@1']['output']; - readonly currency: CodecTypes['mongo/string@1']['output']; -}; -export type PriceInput = { - readonly amount: CodecTypes['mongo/double@1']['input']; - readonly currency: CodecTypes['mongo/string@1']['input']; -}; -export type StatusEntryOutput = { - readonly status: CodecTypes['mongo/string@1']['output']; - readonly timestamp: CodecTypes['mongo/date@1']['output']; -}; -export type StatusEntryInput = { - readonly status: CodecTypes['mongo/string@1']['input']; - readonly timestamp: CodecTypes['mongo/date@1']['input']; -}; -export type FieldOutputTypes = { - readonly __unbound__: { - readonly AddToCartEvent: { - readonly brand: CodecTypes['mongo/string@1']['output']; - readonly productId: CodecTypes['mongo/string@1']['output']; - }; - readonly Cart: { - readonly _id: CodecTypes['mongo/objectId@1']['output']; - readonly items: ReadonlyArray; - readonly userId: CodecTypes['mongo/objectId@1']['output']; - }; - readonly Event: { - readonly _id: CodecTypes['mongo/objectId@1']['output']; - readonly sessionId: CodecTypes['mongo/string@1']['output']; - readonly timestamp: CodecTypes['mongo/date@1']['output']; - readonly type: CodecTypes['mongo/string@1']['output']; - readonly userId: CodecTypes['mongo/string@1']['output']; - }; - readonly Invoice: { - readonly _id: CodecTypes['mongo/objectId@1']['output']; - readonly issuedAt: CodecTypes['mongo/date@1']['output']; - readonly items: ReadonlyArray; - readonly orderId: CodecTypes['mongo/objectId@1']['output']; - readonly subtotal: CodecTypes['mongo/double@1']['output']; - readonly tax: CodecTypes['mongo/double@1']['output']; - readonly total: CodecTypes['mongo/double@1']['output']; - }; - readonly Location: { - readonly _id: CodecTypes['mongo/objectId@1']['output']; - readonly city: CodecTypes['mongo/string@1']['output']; - readonly country: CodecTypes['mongo/string@1']['output']; - readonly name: CodecTypes['mongo/string@1']['output']; - readonly postalCode: CodecTypes['mongo/string@1']['output']; - readonly streetAndNumber: CodecTypes['mongo/string@1']['output']; - }; - readonly Order: { - readonly _id: CodecTypes['mongo/objectId@1']['output']; - readonly items: ReadonlyArray; - readonly shippingAddress: CodecTypes['mongo/string@1']['output']; - readonly statusHistory: ReadonlyArray; - readonly type: CodecTypes['mongo/string@1']['output']; - readonly userId: CodecTypes['mongo/objectId@1']['output']; - }; - readonly Product: { - readonly _id: CodecTypes['mongo/objectId@1']['output']; - readonly articleType: CodecTypes['mongo/string@1']['output']; - readonly brand: CodecTypes['mongo/string@1']['output']; - readonly code: CodecTypes['mongo/string@1']['output']; - readonly description: CodecTypes['mongo/string@1']['output']; - readonly embedding: ReadonlyArray | null; - readonly image: ImageOutput; - readonly name: CodecTypes['mongo/string@1']['output']; - readonly price: PriceOutput; - readonly primaryCategory: CodecTypes['mongo/string@1']['output']; - readonly status: CodecTypes['mongo/string@1']['output']; - readonly subCategory: CodecTypes['mongo/string@1']['output']; - }; - readonly SearchEvent: { readonly query: CodecTypes['mongo/string@1']['output'] }; - readonly User: { - readonly _id: CodecTypes['mongo/objectId@1']['output']; - readonly address: AddressOutput | null; - readonly email: CodecTypes['mongo/string@1']['output']; - readonly name: CodecTypes['mongo/string@1']['output']; - }; - readonly ViewProductEvent: { - readonly brand: CodecTypes['mongo/string@1']['output']; - readonly exitMethod: CodecTypes['mongo/string@1']['output'] | null; - readonly productId: CodecTypes['mongo/string@1']['output']; - readonly subCategory: CodecTypes['mongo/string@1']['output']; - }; - }; -}; -export type FieldInputTypes = { - readonly __unbound__: { - readonly AddToCartEvent: { - readonly brand: CodecTypes['mongo/string@1']['input']; - readonly productId: CodecTypes['mongo/string@1']['input']; - }; - readonly Cart: { - readonly _id: CodecTypes['mongo/objectId@1']['input']; - readonly items: ReadonlyArray; - readonly userId: CodecTypes['mongo/objectId@1']['input']; - }; - readonly Event: { - readonly _id: CodecTypes['mongo/objectId@1']['input']; - readonly sessionId: CodecTypes['mongo/string@1']['input']; - readonly timestamp: CodecTypes['mongo/date@1']['input']; - readonly type: CodecTypes['mongo/string@1']['input']; - readonly userId: CodecTypes['mongo/string@1']['input']; - }; - readonly Invoice: { - readonly _id: CodecTypes['mongo/objectId@1']['input']; - readonly issuedAt: CodecTypes['mongo/date@1']['input']; - readonly items: ReadonlyArray; - readonly orderId: CodecTypes['mongo/objectId@1']['input']; - readonly subtotal: CodecTypes['mongo/double@1']['input']; - readonly tax: CodecTypes['mongo/double@1']['input']; - readonly total: CodecTypes['mongo/double@1']['input']; - }; - readonly Location: { - readonly _id: CodecTypes['mongo/objectId@1']['input']; - readonly city: CodecTypes['mongo/string@1']['input']; - readonly country: CodecTypes['mongo/string@1']['input']; - readonly name: CodecTypes['mongo/string@1']['input']; - readonly postalCode: CodecTypes['mongo/string@1']['input']; - readonly streetAndNumber: CodecTypes['mongo/string@1']['input']; - }; - readonly Order: { - readonly _id: CodecTypes['mongo/objectId@1']['input']; - readonly items: ReadonlyArray; - readonly shippingAddress: CodecTypes['mongo/string@1']['input']; - readonly statusHistory: ReadonlyArray; - readonly type: CodecTypes['mongo/string@1']['input']; - readonly userId: CodecTypes['mongo/objectId@1']['input']; - }; - readonly Product: { - readonly _id: CodecTypes['mongo/objectId@1']['input']; - readonly articleType: CodecTypes['mongo/string@1']['input']; - readonly brand: CodecTypes['mongo/string@1']['input']; - readonly code: CodecTypes['mongo/string@1']['input']; - readonly description: CodecTypes['mongo/string@1']['input']; - readonly embedding: ReadonlyArray | null; - readonly image: ImageInput; - readonly name: CodecTypes['mongo/string@1']['input']; - readonly price: PriceInput; - readonly primaryCategory: CodecTypes['mongo/string@1']['input']; - readonly status: CodecTypes['mongo/string@1']['input']; - readonly subCategory: CodecTypes['mongo/string@1']['input']; - }; - readonly SearchEvent: { readonly query: CodecTypes['mongo/string@1']['input'] }; - readonly User: { - readonly _id: CodecTypes['mongo/objectId@1']['input']; - readonly address: AddressInput | null; - readonly email: CodecTypes['mongo/string@1']['input']; - readonly name: CodecTypes['mongo/string@1']['input']; - }; - readonly ViewProductEvent: { - readonly brand: CodecTypes['mongo/string@1']['input']; - readonly exitMethod: CodecTypes['mongo/string@1']['input'] | null; - readonly productId: CodecTypes['mongo/string@1']['input']; - readonly subCategory: CodecTypes['mongo/string@1']['input']; - }; - }; -}; - -export namespace Models { - export type unbound_AddToCartEvent = { - _id: CodecTypes['mongo/objectId@1']['output']; - sessionId: CodecTypes['mongo/string@1']['output']; - timestamp: CodecTypes['mongo/date@1']['output']; - type: 'add-to-cart'; - userId: CodecTypes['mongo/string@1']['output']; - brand: CodecTypes['mongo/string@1']['output']; - productId: CodecTypes['mongo/string@1']['output']; - readonly [RelationKeys]?: never; - }; - export type unbound_Cart = { - _id: CodecTypes['mongo/objectId@1']['output']; - items: ReadonlyArray; - userId: CodecTypes['mongo/objectId@1']['output']; - user: unbound_User; - readonly [RelationKeys]?: 'user'; - }; - export type unbound_Event = { - _id: CodecTypes['mongo/objectId@1']['output']; - sessionId: CodecTypes['mongo/string@1']['output']; - timestamp: CodecTypes['mongo/date@1']['output']; - type: 'add-to-cart' | 'search' | 'view-product'; - userId: CodecTypes['mongo/string@1']['output']; - readonly [RelationKeys]?: never; - }; - export type unbound_Invoice = { - _id: CodecTypes['mongo/objectId@1']['output']; - issuedAt: CodecTypes['mongo/date@1']['output']; - items: ReadonlyArray; - orderId: CodecTypes['mongo/objectId@1']['output']; - subtotal: CodecTypes['mongo/double@1']['output']; - tax: CodecTypes['mongo/double@1']['output']; - total: CodecTypes['mongo/double@1']['output']; - order: unbound_Order; - readonly [RelationKeys]?: 'order'; - }; - export type unbound_Location = { - _id: CodecTypes['mongo/objectId@1']['output']; - city: CodecTypes['mongo/string@1']['output']; - country: CodecTypes['mongo/string@1']['output']; - name: CodecTypes['mongo/string@1']['output']; - postalCode: CodecTypes['mongo/string@1']['output']; - streetAndNumber: CodecTypes['mongo/string@1']['output']; - readonly [RelationKeys]?: never; - }; - export type unbound_Order = { - _id: CodecTypes['mongo/objectId@1']['output']; - items: ReadonlyArray; - shippingAddress: CodecTypes['mongo/string@1']['output']; - statusHistory: ReadonlyArray; - type: CodecTypes['mongo/string@1']['output']; - userId: CodecTypes['mongo/objectId@1']['output']; - invoices: unbound_Invoice[]; - user: unbound_User; - readonly [RelationKeys]?: 'invoices' | 'user'; - }; - export type unbound_Product = { - _id: CodecTypes['mongo/objectId@1']['output']; - articleType: CodecTypes['mongo/string@1']['output']; - brand: CodecTypes['mongo/string@1']['output']; - code: CodecTypes['mongo/string@1']['output']; - description: CodecTypes['mongo/string@1']['output']; - embedding: ReadonlyArray | null; - image: ImageOutput; - name: CodecTypes['mongo/string@1']['output']; - price: PriceOutput; - primaryCategory: CodecTypes['mongo/string@1']['output']; - status: CodecTypes['mongo/string@1']['output']; - subCategory: CodecTypes['mongo/string@1']['output']; - readonly [RelationKeys]?: never; - }; - export type unbound_SearchEvent = { - _id: CodecTypes['mongo/objectId@1']['output']; - sessionId: CodecTypes['mongo/string@1']['output']; - timestamp: CodecTypes['mongo/date@1']['output']; - type: 'search'; - userId: CodecTypes['mongo/string@1']['output']; - query: CodecTypes['mongo/string@1']['output']; - readonly [RelationKeys]?: never; - }; - export type unbound_User = { - _id: CodecTypes['mongo/objectId@1']['output']; - address: AddressOutput | null; - email: CodecTypes['mongo/string@1']['output']; - name: CodecTypes['mongo/string@1']['output']; - carts: unbound_Cart[]; - orders: unbound_Order[]; - readonly [RelationKeys]?: 'carts' | 'orders'; - }; - export type unbound_ViewProductEvent = { - _id: CodecTypes['mongo/objectId@1']['output']; - sessionId: CodecTypes['mongo/string@1']['output']; - timestamp: CodecTypes['mongo/date@1']['output']; - type: 'view-product'; - userId: CodecTypes['mongo/string@1']['output']; - brand: CodecTypes['mongo/string@1']['output']; - exitMethod: CodecTypes['mongo/string@1']['output'] | null; - productId: CodecTypes['mongo/string@1']['output']; - subCategory: CodecTypes['mongo/string@1']['output']; - readonly [RelationKeys]?: never; - }; - export type unbound_AnyEvent = - unbound_AddToCartEvent | unbound_SearchEvent | unbound_ViewProductEvent; -} - -export declare const models: { - __unbound__: { - AddToCartEvent: Models.unbound_AddToCartEvent; - Cart: Models.unbound_Cart; - Event: Models.unbound_Event; - Invoice: Models.unbound_Invoice; - Location: Models.unbound_Location; - Order: Models.unbound_Order; - Product: Models.unbound_Product; - SearchEvent: Models.unbound_SearchEvent; - User: Models.unbound_User; - ViewProductEvent: Models.unbound_ViewProductEvent; - AnyEvent: Models.unbound_AnyEvent; - }; -}; - -export type TypeMaps = MongoTypeMaps; - -type ContractBase = Omit< - ContractType<{ - readonly namespaces: { - readonly __unbound__: { - readonly id: '__unbound__'; - readonly kind: 'mongo-database'; - readonly entries: { - readonly collection: { - readonly carts: { - readonly indexes: readonly [ - { - readonly keys: readonly [{ readonly direction: 1; readonly field: 'userId' }]; - readonly kind: 'mongo-index'; - readonly unique: true; - }, - ]; - readonly kind: 'mongo-collection'; - readonly validator: { - readonly jsonSchema: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly _id: { readonly bsonType: 'objectId' }; - readonly items: { - readonly bsonType: 'array'; - readonly items: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly amount: { readonly bsonType: 'int' }; - readonly brand: { readonly bsonType: 'string' }; - readonly image: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { readonly url: { readonly bsonType: 'string' } }; - readonly required: readonly ['url']; - }; - readonly name: { readonly bsonType: 'string' }; - readonly price: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly amount: { readonly bsonType: 'double' }; - readonly currency: { readonly bsonType: 'string' }; - }; - readonly required: readonly ['amount', 'currency']; - }; - readonly productId: { readonly bsonType: 'string' }; - }; - readonly required: readonly [ - 'amount', - 'brand', - 'image', - 'name', - 'price', - 'productId', - ]; - }; - }; - readonly userId: { readonly bsonType: 'objectId' }; - }; - readonly required: readonly ['_id', 'items', 'userId']; - }; - readonly kind: 'mongo-validator'; - readonly validationAction: 'error'; - readonly validationLevel: 'strict'; - }; - }; - readonly events: { - readonly indexes: readonly [ - { - readonly keys: readonly [ - { readonly direction: 1; readonly field: 'userId' }, - { readonly direction: -1; readonly field: 'timestamp' }, - ]; - readonly kind: 'mongo-index'; - }, - { - readonly expireAfterSeconds: 7776000; - readonly keys: readonly [{ readonly direction: 1; readonly field: 'timestamp' }]; - readonly kind: 'mongo-index'; - }, - ]; - readonly kind: 'mongo-collection'; - readonly validator: { - readonly jsonSchema: { - readonly bsonType: 'object'; - readonly oneOf: readonly [ - { - readonly additionalProperties: false; - readonly properties: { - readonly _id: { readonly bsonType: 'objectId' }; - readonly brand: { readonly bsonType: 'string' }; - readonly exitMethod: { readonly bsonType: readonly ['null', 'string'] }; - readonly productId: { readonly bsonType: 'string' }; - readonly sessionId: { readonly bsonType: 'string' }; - readonly subCategory: { readonly bsonType: 'string' }; - readonly timestamp: { readonly bsonType: 'date' }; - readonly type: { readonly enum: readonly ['view-product'] }; - readonly userId: { readonly bsonType: 'string' }; - }; - readonly required: readonly ['brand', 'productId', 'subCategory', 'type']; - }, - { - readonly additionalProperties: false; - readonly properties: { - readonly _id: { readonly bsonType: 'objectId' }; - readonly query: { readonly bsonType: 'string' }; - readonly sessionId: { readonly bsonType: 'string' }; - readonly timestamp: { readonly bsonType: 'date' }; - readonly type: { readonly enum: readonly ['search'] }; - readonly userId: { readonly bsonType: 'string' }; - }; - readonly required: readonly ['query', 'type']; - }, - { - readonly additionalProperties: false; - readonly properties: { - readonly _id: { readonly bsonType: 'objectId' }; - readonly brand: { readonly bsonType: 'string' }; - readonly productId: { readonly bsonType: 'string' }; - readonly sessionId: { readonly bsonType: 'string' }; - readonly timestamp: { readonly bsonType: 'date' }; - readonly type: { readonly enum: readonly ['add-to-cart'] }; - readonly userId: { readonly bsonType: 'string' }; - }; - readonly required: readonly ['brand', 'productId', 'type']; - }, - ]; - readonly properties: { - readonly _id: { readonly bsonType: 'objectId' }; - readonly sessionId: { readonly bsonType: 'string' }; - readonly timestamp: { readonly bsonType: 'date' }; - readonly type: { readonly bsonType: 'string' }; - readonly userId: { readonly bsonType: 'string' }; - }; - readonly required: readonly ['_id', 'sessionId', 'timestamp', 'type', 'userId']; - }; - readonly kind: 'mongo-validator'; - readonly validationAction: 'error'; - readonly validationLevel: 'strict'; - }; - }; - readonly invoices: { - readonly indexes: readonly [ - { - readonly keys: readonly [{ readonly direction: 1; readonly field: 'orderId' }]; - readonly kind: 'mongo-index'; - }, - { - readonly keys: readonly [{ readonly direction: -1; readonly field: 'issuedAt' }]; - readonly kind: 'mongo-index'; - readonly sparse: true; - }, - ]; - readonly kind: 'mongo-collection'; - readonly validator: { - readonly jsonSchema: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly _id: { readonly bsonType: 'objectId' }; - readonly issuedAt: { readonly bsonType: 'date' }; - readonly items: { - readonly bsonType: 'array'; - readonly items: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly amount: { readonly bsonType: 'int' }; - readonly lineTotal: { readonly bsonType: 'double' }; - readonly name: { readonly bsonType: 'string' }; - readonly unitPrice: { readonly bsonType: 'double' }; - }; - readonly required: readonly ['amount', 'lineTotal', 'name', 'unitPrice']; - }; - }; - readonly orderId: { readonly bsonType: 'objectId' }; - readonly subtotal: { readonly bsonType: 'double' }; - readonly tax: { readonly bsonType: 'double' }; - readonly total: { readonly bsonType: 'double' }; - }; - readonly required: readonly [ - '_id', - 'issuedAt', - 'items', - 'orderId', - 'subtotal', - 'tax', - 'total', - ]; - }; - readonly kind: 'mongo-validator'; - readonly validationAction: 'error'; - readonly validationLevel: 'strict'; - }; - }; - readonly locations: { - readonly indexes: readonly [ - { - readonly collation: { readonly locale: 'en'; readonly strength: 2 }; - readonly keys: readonly [ - { readonly direction: 1; readonly field: 'city' }, - { readonly direction: 1; readonly field: 'country' }, - ]; - readonly kind: 'mongo-index'; - }, - ]; - readonly kind: 'mongo-collection'; - readonly validator: { - readonly jsonSchema: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly _id: { readonly bsonType: 'objectId' }; - readonly city: { readonly bsonType: 'string' }; - readonly country: { readonly bsonType: 'string' }; - readonly name: { readonly bsonType: 'string' }; - readonly postalCode: { readonly bsonType: 'string' }; - readonly streetAndNumber: { readonly bsonType: 'string' }; - }; - readonly required: readonly [ - '_id', - 'city', - 'country', - 'name', - 'postalCode', - 'streetAndNumber', - ]; - }; - readonly kind: 'mongo-validator'; - readonly validationAction: 'error'; - readonly validationLevel: 'strict'; - }; - }; - readonly orders: { - readonly indexes: readonly [ - { - readonly keys: readonly [{ readonly direction: 1; readonly field: 'userId' }]; - readonly kind: 'mongo-index'; - }, - ]; - readonly kind: 'mongo-collection'; - readonly validator: { - readonly jsonSchema: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly _id: { readonly bsonType: 'objectId' }; - readonly items: { - readonly bsonType: 'array'; - readonly items: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly amount: { readonly bsonType: 'int' }; - readonly brand: { readonly bsonType: 'string' }; - readonly image: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { readonly url: { readonly bsonType: 'string' } }; - readonly required: readonly ['url']; - }; - readonly name: { readonly bsonType: 'string' }; - readonly price: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly amount: { readonly bsonType: 'double' }; - readonly currency: { readonly bsonType: 'string' }; - }; - readonly required: readonly ['amount', 'currency']; - }; - readonly productId: { readonly bsonType: 'string' }; - }; - readonly required: readonly [ - 'amount', - 'brand', - 'image', - 'name', - 'price', - 'productId', - ]; - }; - }; - readonly shippingAddress: { readonly bsonType: 'string' }; - readonly statusHistory: { - readonly bsonType: 'array'; - readonly items: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly status: { readonly bsonType: 'string' }; - readonly timestamp: { readonly bsonType: 'date' }; - }; - readonly required: readonly ['status', 'timestamp']; - }; - }; - readonly type: { readonly bsonType: 'string' }; - readonly userId: { readonly bsonType: 'objectId' }; - }; - readonly required: readonly [ - '_id', - 'items', - 'shippingAddress', - 'statusHistory', - 'type', - 'userId', - ]; - }; - readonly kind: 'mongo-validator'; - readonly validationAction: 'error'; - readonly validationLevel: 'strict'; - }; - }; - readonly products: { - readonly indexes: readonly [ - { - readonly keys: readonly [ - { readonly direction: 'text'; readonly field: 'name' }, - { readonly direction: 'text'; readonly field: 'description' }, - ]; - readonly kind: 'mongo-index'; - readonly weights: { readonly description: 1; readonly name: 10 }; - }, - { - readonly keys: readonly [ - { readonly direction: 1; readonly field: 'brand' }, - { readonly direction: 1; readonly field: 'subCategory' }, - ]; - readonly kind: 'mongo-index'; - }, - { - readonly keys: readonly [ - { readonly direction: 1; readonly field: 'primaryCategory' }, - { readonly direction: 1; readonly field: 'articleType' }, - ]; - readonly kind: 'mongo-index'; - }, - { - readonly keys: readonly [ - { readonly direction: 'hashed'; readonly field: 'code' }, - ]; - readonly kind: 'mongo-index'; - }, - ]; - readonly kind: 'mongo-collection'; - readonly validator: { - readonly jsonSchema: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly _id: { readonly bsonType: 'objectId' }; - readonly articleType: { readonly bsonType: 'string' }; - readonly brand: { readonly bsonType: 'string' }; - readonly code: { readonly bsonType: 'string' }; - readonly description: { readonly bsonType: 'string' }; - readonly embedding: { - readonly bsonType: 'array'; - readonly items: { readonly bsonType: 'double' }; - }; - readonly image: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { readonly url: { readonly bsonType: 'string' } }; - readonly required: readonly ['url']; - }; - readonly name: { readonly bsonType: 'string' }; - readonly price: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly amount: { readonly bsonType: 'double' }; - readonly currency: { readonly bsonType: 'string' }; - }; - readonly required: readonly ['amount', 'currency']; - }; - readonly primaryCategory: { readonly bsonType: 'string' }; - readonly status: { readonly bsonType: 'string' }; - readonly subCategory: { readonly bsonType: 'string' }; - }; - readonly required: readonly [ - '_id', - 'articleType', - 'brand', - 'code', - 'description', - 'image', - 'name', - 'price', - 'primaryCategory', - 'status', - 'subCategory', - ]; - }; - readonly kind: 'mongo-validator'; - readonly validationAction: 'error'; - readonly validationLevel: 'strict'; - }; - }; - readonly users: { - readonly indexes: readonly [ - { - readonly keys: readonly [{ readonly direction: 1; readonly field: 'email' }]; - readonly kind: 'mongo-index'; - readonly unique: true; - }, - ]; - readonly kind: 'mongo-collection'; - readonly validator: { - readonly jsonSchema: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly _id: { readonly bsonType: 'objectId' }; - readonly address: { - readonly oneOf: readonly [ - { readonly bsonType: 'null' }, - { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly city: { readonly bsonType: 'string' }; - readonly country: { readonly bsonType: 'string' }; - readonly postalCode: { readonly bsonType: 'string' }; - readonly streetAndNumber: { readonly bsonType: 'string' }; - }; - readonly required: readonly [ - 'city', - 'country', - 'postalCode', - 'streetAndNumber', - ]; - }, - ]; - }; - readonly email: { readonly bsonType: 'string' }; - readonly name: { readonly bsonType: 'string' }; - }; - readonly required: readonly ['_id', 'email', 'name']; - }; - readonly kind: 'mongo-validator'; - readonly validationAction: 'error'; - readonly validationLevel: 'strict'; - }; - }; - }; - }; - }; - }; - readonly storageHash: StorageHash; - }>, - 'roots' | 'domain' -> & { - readonly target: 'mongo'; - readonly targetFamily: 'mongo'; - readonly roots: { - readonly carts: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'Cart' }; - readonly events: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'Event' }; - readonly invoices: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Invoice'; - }; - readonly locations: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Location'; - }; - readonly orders: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'Order' }; - readonly products: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Product'; - }; - readonly users: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'User' }; - }; - readonly domain: { - readonly namespaces: { - readonly __unbound__: { - readonly models: { - readonly AddToCartEvent: { - readonly fields: { - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - readonly relations: Record; - readonly storage: { readonly collection: 'events' }; - readonly base: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Event'; - }; - }; - readonly Cart: { - readonly fields: { - readonly _id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - readonly items: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CartItem' }; - readonly many: { readonly elementNullable: false }; - }; - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - }; - readonly relations: { - readonly user: { - readonly to: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'User'; - }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['userId']; - readonly targetFields: readonly ['_id']; - }; - }; - }; - readonly storage: { readonly collection: 'carts' }; - }; - readonly Event: { - readonly fields: { - readonly _id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - readonly sessionId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly timestamp: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; - }; - readonly type: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - readonly relations: Record; - readonly storage: { readonly collection: 'events' }; - readonly discriminator: { readonly field: 'type' }; - readonly variants: { - readonly AddToCartEvent: { readonly value: 'add-to-cart' }; - readonly SearchEvent: { readonly value: 'search' }; - readonly ViewProductEvent: { readonly value: 'view-product' }; - }; - }; - readonly Invoice: { - readonly fields: { - readonly _id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - readonly issuedAt: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; - }; - readonly items: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'InvoiceLineItem' }; - readonly many: { readonly elementNullable: false }; - }; - readonly orderId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - readonly subtotal: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly tax: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly total: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - }; - readonly relations: { - readonly order: { - readonly to: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Order'; - }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['orderId']; - readonly targetFields: readonly ['_id']; - }; - }; - }; - readonly storage: { readonly collection: 'invoices' }; - }; - readonly Location: { - readonly fields: { - readonly _id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly postalCode: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly streetAndNumber: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - readonly relations: Record; - readonly storage: { readonly collection: 'locations' }; - }; - readonly Order: { - readonly fields: { - readonly _id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - readonly items: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'OrderLineItem' }; - readonly many: { readonly elementNullable: false }; - }; - readonly shippingAddress: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly statusHistory: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'StatusEntry' }; - readonly many: { readonly elementNullable: false }; - }; - readonly type: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - }; - readonly relations: { - readonly invoices: { - readonly to: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Invoice'; - }; - readonly cardinality: '1:N'; - readonly on: { - readonly localFields: readonly ['_id']; - readonly targetFields: readonly ['orderId']; - }; - }; - readonly user: { - readonly to: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'User'; - }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['userId']; - readonly targetFields: readonly ['_id']; - }; - }; - }; - readonly storage: { readonly collection: 'orders' }; - }; - readonly Product: { - readonly fields: { - readonly _id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - readonly articleType: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly code: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly description: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly embedding: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - readonly many: { readonly elementNullable: false }; - }; - readonly image: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly price: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; - }; - readonly primaryCategory: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly status: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly subCategory: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - readonly relations: Record; - readonly storage: { readonly collection: 'products' }; - }; - readonly SearchEvent: { - readonly fields: { - readonly query: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - readonly relations: Record; - readonly storage: { readonly collection: 'events' }; - readonly base: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Event'; - }; - }; - readonly User: { - readonly fields: { - readonly _id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - readonly address: { - readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; - }; - readonly email: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - readonly relations: { - readonly carts: { - readonly to: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Cart'; - }; - readonly cardinality: '1:N'; - readonly on: { - readonly localFields: readonly ['_id']; - readonly targetFields: readonly ['userId']; - }; - }; - readonly orders: { - readonly to: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Order'; - }; - readonly cardinality: '1:N'; - readonly on: { - readonly localFields: readonly ['_id']; - readonly targetFields: readonly ['userId']; - }; - }; - }; - readonly storage: { readonly collection: 'users' }; - }; - readonly ViewProductEvent: { - readonly fields: { - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly exitMethod: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly subCategory: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - readonly relations: Record; - readonly storage: { readonly collection: 'events' }; - readonly base: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Event'; - }; - }; - }; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly postalCode: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly streetAndNumber: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly CartItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly image: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly price: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Image: { - readonly fields: { - readonly url: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly InvoiceLineItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly lineTotal: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly unitPrice: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - }; - }; - readonly OrderLineItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly image: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly price: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Price: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly currency: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly StatusEntry: { - readonly fields: { - readonly status: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly timestamp: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; - }; - }; - }; - }; - }; - }; - }; - readonly capabilities: {}; - readonly extensions: {}; - readonly meta: {}; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly postalCode: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly streetAndNumber: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly CartItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly image: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly price: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Image: { - readonly fields: { - readonly url: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly InvoiceLineItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly lineTotal: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly unitPrice: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - }; - }; - readonly OrderLineItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly image: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly price: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Price: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly currency: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly StatusEntry: { - readonly fields: { - readonly status: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly timestamp: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; - }; - }; - }; - }; - readonly profileHash: ProfileHash; -}; - -export type Contract = MongoContractWithTypeMaps; diff --git a/examples/retail-store/migrations/snapshots/9414a8f88a64f9decc0e019967459e49da083f011cf91898094b7bccab6c1810/contract.json b/examples/retail-store/migrations/snapshots/9414a8f88a64f9decc0e019967459e49da083f011cf91898094b7bccab6c1810/contract.json deleted file mode 100644 index c5b7c73eb437..000000000000 --- a/examples/retail-store/migrations/snapshots/9414a8f88a64f9decc0e019967459e49da083f011cf91898094b7bccab6c1810/contract.json +++ /dev/null @@ -1 +0,0 @@ -{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{},"domain":{"namespaces":{"__unbound__":{"models":{"AddToCartEvent":{"base":{"model":"Event","namespace":"__unbound__"},"fields":{"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events"}},"Cart":{"fields":{"_id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"items":{"many":{"elementNullable":false},"nullable":false,"type":{"kind":"valueObject","name":"CartItem"}},"userId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}}},"relations":{"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["_id"]},"to":{"model":"User","namespace":"__unbound__"}}},"storage":{"collection":"carts"}},"Event":{"discriminator":{"field":"type"},"fields":{"_id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"sessionId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"timestamp":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}},"type":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events"},"variants":{"AddToCartEvent":{"value":"add-to-cart"},"SearchEvent":{"value":"search"},"ViewProductEvent":{"value":"view-product"}}},"Invoice":{"fields":{"_id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"issuedAt":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}},"items":{"many":{"elementNullable":false},"nullable":false,"type":{"kind":"valueObject","name":"InvoiceLineItem"}},"orderId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"subtotal":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"tax":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"total":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}}},"relations":{"order":{"cardinality":"N:1","nullable":false,"on":{"localFields":["orderId"],"targetFields":["_id"]},"to":{"model":"Order","namespace":"__unbound__"}}},"storage":{"collection":"invoices"}},"Location":{"fields":{"_id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"city":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"postalCode":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"streetAndNumber":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"locations"}},"Order":{"fields":{"_id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"items":{"many":{"elementNullable":false},"nullable":false,"type":{"kind":"valueObject","name":"OrderLineItem"}},"shippingAddress":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"statusHistory":{"many":{"elementNullable":false},"nullable":false,"type":{"kind":"valueObject","name":"StatusEntry"}},"type":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}}},"relations":{"invoices":{"cardinality":"1:N","on":{"localFields":["_id"],"targetFields":["orderId"]},"to":{"model":"Invoice","namespace":"__unbound__"}},"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["_id"]},"to":{"model":"User","namespace":"__unbound__"}}},"storage":{"collection":"orders"}},"Product":{"fields":{"_id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"articleType":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"code":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"description":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"embedding":{"many":{"elementNullable":false},"nullable":true,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"image":{"nullable":false,"type":{"kind":"valueObject","name":"Image"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"price":{"nullable":false,"type":{"kind":"valueObject","name":"Price"}},"primaryCategory":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"status":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"subCategory":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"products"}},"SearchEvent":{"base":{"model":"Event","namespace":"__unbound__"},"fields":{"query":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events"}},"User":{"fields":{"_id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"address":{"nullable":true,"type":{"kind":"valueObject","name":"Address"}},"email":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{"carts":{"cardinality":"1:N","on":{"localFields":["_id"],"targetFields":["userId"]},"to":{"model":"Cart","namespace":"__unbound__"}},"orders":{"cardinality":"1:N","on":{"localFields":["_id"],"targetFields":["userId"]},"to":{"model":"Order","namespace":"__unbound__"}}},"storage":{"collection":"users"}},"ViewProductEvent":{"base":{"model":"Event","namespace":"__unbound__"},"fields":{"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"exitMethod":{"nullable":true,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"subCategory":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events"}}},"valueObjects":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"postalCode":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"streetAndNumber":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"CartItem":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}},"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"image":{"nullable":false,"type":{"kind":"valueObject","name":"Image"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"price":{"nullable":false,"type":{"kind":"valueObject","name":"Price"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"Image":{"fields":{"url":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"InvoiceLineItem":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}},"lineTotal":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"unitPrice":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}}}},"OrderLineItem":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}},"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"image":{"nullable":false,"type":{"kind":"valueObject","name":"Image"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"price":{"nullable":false,"type":{"kind":"valueObject","name":"Price"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"Price":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"currency":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"StatusEntry":{"fields":{"status":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"timestamp":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}}}}}}}},"extensions":{},"meta":{},"profileHash":"251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3","roots":{"carts":{"model":"Cart","namespace":"__unbound__"},"events":{"model":"Event","namespace":"__unbound__"},"invoices":{"model":"Invoice","namespace":"__unbound__"},"locations":{"model":"Location","namespace":"__unbound__"},"orders":{"model":"Order","namespace":"__unbound__"},"products":{"model":"Product","namespace":"__unbound__"},"users":{"model":"User","namespace":"__unbound__"}},"schemaVersion":"1","storage":{"namespaces":{"__unbound__":{"entries":{"collection":{"carts":{"indexes":[{"keys":[{"direction":1,"field":"userId"}],"kind":"mongo-index","unique":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"items":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"int"},"brand":{"bsonType":"string"},"image":{"additionalProperties":false,"bsonType":"object","properties":{"url":{"bsonType":"string"}},"required":["url"]},"name":{"bsonType":"string"},"price":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"double"},"currency":{"bsonType":"string"}},"required":["amount","currency"]},"productId":{"bsonType":"string"}},"required":["amount","brand","image","name","price","productId"]}},"userId":{"bsonType":"objectId"}},"required":["_id","items","userId"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"events":{"indexes":[{"keys":[{"direction":1,"field":"userId"},{"direction":-1,"field":"timestamp"}],"kind":"mongo-index"},{"expireAfterSeconds":7776000,"keys":[{"direction":1,"field":"timestamp"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"bsonType":"object","oneOf":[{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"brand":{"bsonType":"string"},"exitMethod":{"bsonType":["null","string"]},"productId":{"bsonType":"string"},"sessionId":{"bsonType":"string"},"subCategory":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"enum":["view-product"]},"userId":{"bsonType":"string"}},"required":["brand","productId","subCategory","type"]},{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"query":{"bsonType":"string"},"sessionId":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"enum":["search"]},"userId":{"bsonType":"string"}},"required":["query","type"]},{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"brand":{"bsonType":"string"},"productId":{"bsonType":"string"},"sessionId":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"enum":["add-to-cart"]},"userId":{"bsonType":"string"}},"required":["brand","productId","type"]}],"properties":{"_id":{"bsonType":"objectId"},"sessionId":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"bsonType":"string"},"userId":{"bsonType":"string"}},"required":["_id","sessionId","timestamp","type","userId"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"invoices":{"indexes":[{"keys":[{"direction":1,"field":"orderId"}],"kind":"mongo-index"},{"keys":[{"direction":-1,"field":"issuedAt"}],"kind":"mongo-index","sparse":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"issuedAt":{"bsonType":"date"},"items":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"int"},"lineTotal":{"bsonType":"double"},"name":{"bsonType":"string"},"unitPrice":{"bsonType":"double"}},"required":["amount","lineTotal","name","unitPrice"]}},"orderId":{"bsonType":"objectId"},"subtotal":{"bsonType":"double"},"tax":{"bsonType":"double"},"total":{"bsonType":"double"}},"required":["_id","issuedAt","items","orderId","subtotal","tax","total"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"locations":{"indexes":[{"collation":{"locale":"en","strength":2},"keys":[{"direction":1,"field":"city"},{"direction":1,"field":"country"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"city":{"bsonType":"string"},"country":{"bsonType":"string"},"name":{"bsonType":"string"},"postalCode":{"bsonType":"string"},"streetAndNumber":{"bsonType":"string"}},"required":["_id","city","country","name","postalCode","streetAndNumber"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"orders":{"indexes":[{"keys":[{"direction":1,"field":"userId"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"items":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"int"},"brand":{"bsonType":"string"},"image":{"additionalProperties":false,"bsonType":"object","properties":{"url":{"bsonType":"string"}},"required":["url"]},"name":{"bsonType":"string"},"price":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"double"},"currency":{"bsonType":"string"}},"required":["amount","currency"]},"productId":{"bsonType":"string"}},"required":["amount","brand","image","name","price","productId"]}},"shippingAddress":{"bsonType":"string"},"statusHistory":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"status":{"bsonType":"string"},"timestamp":{"bsonType":"date"}},"required":["status","timestamp"]}},"type":{"bsonType":"string"},"userId":{"bsonType":"objectId"}},"required":["_id","items","shippingAddress","statusHistory","type","userId"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"products":{"indexes":[{"keys":[{"direction":"text","field":"name"},{"direction":"text","field":"description"}],"kind":"mongo-index","weights":{"description":1,"name":10}},{"keys":[{"direction":1,"field":"brand"},{"direction":1,"field":"subCategory"}],"kind":"mongo-index"},{"keys":[{"direction":1,"field":"primaryCategory"},{"direction":1,"field":"articleType"}],"kind":"mongo-index"},{"keys":[{"direction":"hashed","field":"code"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"articleType":{"bsonType":"string"},"brand":{"bsonType":"string"},"code":{"bsonType":"string"},"description":{"bsonType":"string"},"embedding":{"bsonType":"array","items":{"bsonType":"double"}},"image":{"additionalProperties":false,"bsonType":"object","properties":{"url":{"bsonType":"string"}},"required":["url"]},"name":{"bsonType":"string"},"price":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"double"},"currency":{"bsonType":"string"}},"required":["amount","currency"]},"primaryCategory":{"bsonType":"string"},"status":{"bsonType":"string"},"subCategory":{"bsonType":"string"}},"required":["_id","articleType","brand","code","description","image","name","price","primaryCategory","status","subCategory"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"users":{"indexes":[{"keys":[{"direction":1,"field":"email"}],"kind":"mongo-index","unique":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"address":{"oneOf":[{"bsonType":"null"},{"additionalProperties":false,"bsonType":"object","properties":{"city":{"bsonType":"string"},"country":{"bsonType":"string"},"postalCode":{"bsonType":"string"},"streetAndNumber":{"bsonType":"string"}},"required":["city","country","postalCode","streetAndNumber"]}]},"email":{"bsonType":"string"},"name":{"bsonType":"string"}},"required":["_id","email","name"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}}}},"id":"__unbound__","kind":"mongo-database"}},"storageHash":"9414a8f88a64f9decc0e019967459e49da083f011cf91898094b7bccab6c1810"},"target":"mongo","targetFamily":"mongo"} diff --git a/examples/retail-store/migrations/snapshots/977a060afe52c4a56f93f2f33d65b8b6b4cc4ded04d16fe6b4f8e7e9e61192d3/contract.json b/examples/retail-store/migrations/snapshots/977a060afe52c4a56f93f2f33d65b8b6b4cc4ded04d16fe6b4f8e7e9e61192d3/contract.json deleted file mode 100644 index 44c00bb96821..000000000000 --- a/examples/retail-store/migrations/snapshots/977a060afe52c4a56f93f2f33d65b8b6b4cc4ded04d16fe6b4f8e7e9e61192d3/contract.json +++ /dev/null @@ -1 +0,0 @@ -{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{},"domain":{"namespaces":{"__unbound__":{"models":{"AddToCartEvent":{"base":{"model":"Event","namespace":"__unbound__"},"fields":{"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events","fields":{"brand":{"field":"brand"},"productId":{"field":"productId"}}}},"Cart":{"fields":{"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"items":{"many":{"elementNullable":false},"nullable":false,"type":{"kind":"valueObject","name":"CartItem"}},"userId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}}},"relations":{"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"__unbound__"}}},"storage":{"collection":"carts","fields":{"id":{"field":"_id"},"items":{"field":"items"},"userId":{"field":"userId"}}}},"Event":{"discriminator":{"field":"type"},"fields":{"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"sessionId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"timestamp":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}},"type":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events","fields":{"id":{"field":"_id"},"sessionId":{"field":"sessionId"},"timestamp":{"field":"timestamp"},"type":{"field":"type"},"userId":{"field":"userId"}}},"variants":{"add-to-cart":{"model":"AddToCartEvent","namespace":"__unbound__"},"search":{"model":"SearchEvent","namespace":"__unbound__"},"view-product":{"model":"ViewProductEvent","namespace":"__unbound__"}}},"Invoice":{"fields":{"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"issuedAt":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}},"items":{"many":{"elementNullable":false},"nullable":false,"type":{"kind":"valueObject","name":"InvoiceLineItem"}},"orderId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"subtotal":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"tax":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"total":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}}},"relations":{"order":{"cardinality":"N:1","nullable":false,"on":{"localFields":["orderId"],"targetFields":["id"]},"to":{"model":"Order","namespace":"__unbound__"}}},"storage":{"collection":"invoices","fields":{"id":{"field":"_id"},"issuedAt":{"field":"issuedAt"},"items":{"field":"items"},"orderId":{"field":"orderId"},"subtotal":{"field":"subtotal"},"tax":{"field":"tax"},"total":{"field":"total"}}}},"Location":{"fields":{"city":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"postalCode":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"streetAndNumber":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"locations","fields":{"city":{"field":"city"},"country":{"field":"country"},"id":{"field":"_id"},"name":{"field":"name"},"postalCode":{"field":"postalCode"},"streetAndNumber":{"field":"streetAndNumber"}}}},"Order":{"fields":{"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"items":{"many":{"elementNullable":false},"nullable":false,"type":{"kind":"valueObject","name":"OrderLineItem"}},"shippingAddress":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"statusHistory":{"many":{"elementNullable":false},"nullable":false,"type":{"kind":"valueObject","name":"StatusEntry"}},"type":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}}},"relations":{"invoices":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["orderId"]},"to":{"model":"Invoice","namespace":"__unbound__"}},"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"__unbound__"}}},"storage":{"collection":"orders","fields":{"id":{"field":"_id"},"items":{"field":"items"},"shippingAddress":{"field":"shippingAddress"},"statusHistory":{"field":"statusHistory"},"type":{"field":"type"},"userId":{"field":"userId"}}}},"Product":{"fields":{"articleType":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"code":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"description":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"image":{"nullable":false,"type":{"kind":"valueObject","name":"Image"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"price":{"nullable":false,"type":{"kind":"valueObject","name":"Price"}},"primaryCategory":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"subCategory":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"products","fields":{"articleType":{"field":"articleType"},"brand":{"field":"brand"},"code":{"field":"code"},"description":{"field":"description"},"id":{"field":"_id"},"image":{"field":"image"},"name":{"field":"name"},"price":{"field":"price"},"primaryCategory":{"field":"primaryCategory"},"subCategory":{"field":"subCategory"}}}},"SearchEvent":{"base":{"model":"Event","namespace":"__unbound__"},"fields":{"query":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events","fields":{"query":{"field":"query"}}}},"User":{"fields":{"address":{"nullable":true,"type":{"kind":"valueObject","name":"Address"}},"email":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{"carts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Cart","namespace":"__unbound__"}},"orders":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Order","namespace":"__unbound__"}}},"storage":{"collection":"users","fields":{"address":{"field":"address"},"email":{"field":"email"},"id":{"field":"_id"},"name":{"field":"name"}}}},"ViewProductEvent":{"base":{"model":"Event","namespace":"__unbound__"},"fields":{"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"exitMethod":{"nullable":true,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"subCategory":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events","fields":{"brand":{"field":"brand"},"exitMethod":{"field":"exitMethod"},"productId":{"field":"productId"},"subCategory":{"field":"subCategory"}}}}},"valueObjects":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"postalCode":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"streetAndNumber":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"CartItem":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}},"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"image":{"nullable":false,"type":{"kind":"valueObject","name":"Image"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"price":{"nullable":false,"type":{"kind":"valueObject","name":"Price"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"Image":{"fields":{"url":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"InvoiceLineItem":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}},"lineTotal":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"unitPrice":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}}}},"OrderLineItem":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}},"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"image":{"nullable":false,"type":{"kind":"valueObject","name":"Image"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"price":{"nullable":false,"type":{"kind":"valueObject","name":"Price"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"Price":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"currency":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"StatusEntry":{"fields":{"status":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"timestamp":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}}}}}}}},"extensions":{},"meta":{},"profileHash":"251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3","roots":{"carts":{"model":"Cart","namespace":"__unbound__"},"events":{"model":"Event","namespace":"__unbound__"},"invoices":{"model":"Invoice","namespace":"__unbound__"},"locations":{"model":"Location","namespace":"__unbound__"},"orders":{"model":"Order","namespace":"__unbound__"},"products":{"model":"Product","namespace":"__unbound__"},"users":{"model":"User","namespace":"__unbound__"}},"schemaVersion":"1","storage":{"namespaces":{"__unbound__":{"entries":{"collection":{"carts":{"indexes":[{"keys":[{"direction":1,"field":"userId"}],"kind":"mongo-index","unique":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"items":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"int"},"brand":{"bsonType":"string"},"image":{"additionalProperties":false,"bsonType":"object","properties":{"url":{"bsonType":"string"}},"required":["url"]},"name":{"bsonType":"string"},"price":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"double"},"currency":{"bsonType":"string"}},"required":["amount","currency"]},"productId":{"bsonType":"string"}},"required":["amount","brand","image","name","price","productId"]}},"userId":{"bsonType":"objectId"}},"required":["_id","items","userId"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"events":{"indexes":[{"keys":[{"direction":1,"field":"userId"},{"direction":-1,"field":"timestamp"}],"kind":"mongo-index"},{"expireAfterSeconds":7776000,"keys":[{"direction":1,"field":"timestamp"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"bsonType":"object","oneOf":[{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"brand":{"bsonType":"string"},"exitMethod":{"bsonType":["null","string"]},"productId":{"bsonType":"string"},"sessionId":{"bsonType":"string"},"subCategory":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"enum":["view-product"]},"userId":{"bsonType":"string"}},"required":["brand","productId","subCategory","type"]},{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"query":{"bsonType":"string"},"sessionId":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"enum":["search"]},"userId":{"bsonType":"string"}},"required":["query","type"]},{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"brand":{"bsonType":"string"},"productId":{"bsonType":"string"},"sessionId":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"enum":["add-to-cart"]},"userId":{"bsonType":"string"}},"required":["brand","productId","type"]}],"properties":{"_id":{"bsonType":"objectId"},"sessionId":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"bsonType":"string"},"userId":{"bsonType":"string"}},"required":["_id","sessionId","timestamp","type","userId"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"invoices":{"indexes":[{"keys":[{"direction":1,"field":"orderId"}],"kind":"mongo-index"},{"keys":[{"direction":-1,"field":"issuedAt"}],"kind":"mongo-index","sparse":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"issuedAt":{"bsonType":"date"},"items":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"int"},"lineTotal":{"bsonType":"double"},"name":{"bsonType":"string"},"unitPrice":{"bsonType":"double"}},"required":["amount","lineTotal","name","unitPrice"]}},"orderId":{"bsonType":"objectId"},"subtotal":{"bsonType":"double"},"tax":{"bsonType":"double"},"total":{"bsonType":"double"}},"required":["_id","issuedAt","items","orderId","subtotal","tax","total"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"locations":{"indexes":[{"collation":{"locale":"en","strength":2},"keys":[{"direction":1,"field":"city"},{"direction":1,"field":"country"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"city":{"bsonType":"string"},"country":{"bsonType":"string"},"name":{"bsonType":"string"},"postalCode":{"bsonType":"string"},"streetAndNumber":{"bsonType":"string"}},"required":["_id","city","country","name","postalCode","streetAndNumber"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"orders":{"indexes":[{"keys":[{"direction":1,"field":"userId"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"items":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"int"},"brand":{"bsonType":"string"},"image":{"additionalProperties":false,"bsonType":"object","properties":{"url":{"bsonType":"string"}},"required":["url"]},"name":{"bsonType":"string"},"price":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"double"},"currency":{"bsonType":"string"}},"required":["amount","currency"]},"productId":{"bsonType":"string"}},"required":["amount","brand","image","name","price","productId"]}},"shippingAddress":{"bsonType":"string"},"statusHistory":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"status":{"bsonType":"string"},"timestamp":{"bsonType":"date"}},"required":["status","timestamp"]}},"type":{"bsonType":"string"},"userId":{"bsonType":"objectId"}},"required":["_id","items","shippingAddress","statusHistory","type","userId"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"products":{"indexes":[{"keys":[{"direction":"text","field":"name"},{"direction":"text","field":"description"}],"kind":"mongo-index","weights":{"description":1,"name":10}},{"keys":[{"direction":1,"field":"brand"},{"direction":1,"field":"subCategory"}],"kind":"mongo-index"},{"keys":[{"direction":1,"field":"primaryCategory"},{"direction":1,"field":"articleType"}],"kind":"mongo-index"},{"keys":[{"direction":"hashed","field":"code"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"articleType":{"bsonType":"string"},"brand":{"bsonType":"string"},"code":{"bsonType":"string"},"description":{"bsonType":"string"},"image":{"additionalProperties":false,"bsonType":"object","properties":{"url":{"bsonType":"string"}},"required":["url"]},"name":{"bsonType":"string"},"price":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"double"},"currency":{"bsonType":"string"}},"required":["amount","currency"]},"primaryCategory":{"bsonType":"string"},"subCategory":{"bsonType":"string"}},"required":["_id","articleType","brand","code","description","image","name","price","primaryCategory","subCategory"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"users":{"indexes":[{"keys":[{"direction":1,"field":"email"}],"kind":"mongo-index","unique":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"address":{"oneOf":[{"bsonType":"null"},{"additionalProperties":false,"bsonType":"object","properties":{"city":{"bsonType":"string"},"country":{"bsonType":"string"},"postalCode":{"bsonType":"string"},"streetAndNumber":{"bsonType":"string"}},"required":["city","country","postalCode","streetAndNumber"]}]},"email":{"bsonType":"string"},"name":{"bsonType":"string"}},"required":["_id","email","name"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}}}},"id":"__unbound__","kind":"mongo-database"}},"storageHash":"977a060afe52c4a56f93f2f33d65b8b6b4cc4ded04d16fe6b4f8e7e9e61192d3"},"target":"mongo","targetFamily":"mongo"} diff --git a/examples/retail-store/migrations/snapshots/9af6d90aaa4cd03bf7eb2040a1c1b5c1225a2b33883ea3d0552f312f4684f8dc/contract.json b/examples/retail-store/migrations/snapshots/9af6d90aaa4cd03bf7eb2040a1c1b5c1225a2b33883ea3d0552f312f4684f8dc/contract.json deleted file mode 100644 index b78ef490a2de..000000000000 --- a/examples/retail-store/migrations/snapshots/9af6d90aaa4cd03bf7eb2040a1c1b5c1225a2b33883ea3d0552f312f4684f8dc/contract.json +++ /dev/null @@ -1 +0,0 @@ -{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{},"domain":{"namespaces":{"__unbound__":{"models":{"AddToCartEvent":{"base":{"model":"Event","namespace":"__unbound__"},"fields":{"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events","fields":{"brand":{"field":"brand"},"productId":{"field":"productId"}}}},"Cart":{"fields":{"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"items":{"many":{"elementNullable":false},"nullable":false,"type":{"kind":"valueObject","name":"CartItem"}},"userId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}}},"relations":{"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"__unbound__"}}},"storage":{"collection":"carts","fields":{"id":{"field":"_id"},"items":{"field":"items"},"userId":{"field":"userId"}}}},"Event":{"discriminator":{"field":"type"},"fields":{"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"sessionId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"timestamp":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}},"type":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events","fields":{"id":{"field":"_id"},"sessionId":{"field":"sessionId"},"timestamp":{"field":"timestamp"},"type":{"field":"type"},"userId":{"field":"userId"}}},"variants":{"add-to-cart":{"model":"AddToCartEvent","namespace":"__unbound__"},"search":{"model":"SearchEvent","namespace":"__unbound__"},"view-product":{"model":"ViewProductEvent","namespace":"__unbound__"}}},"Invoice":{"fields":{"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"issuedAt":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}},"items":{"many":{"elementNullable":false},"nullable":false,"type":{"kind":"valueObject","name":"InvoiceLineItem"}},"orderId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"subtotal":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"tax":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"total":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}}},"relations":{"order":{"cardinality":"N:1","nullable":false,"on":{"localFields":["orderId"],"targetFields":["id"]},"to":{"model":"Order","namespace":"__unbound__"}}},"storage":{"collection":"invoices","fields":{"id":{"field":"_id"},"issuedAt":{"field":"issuedAt"},"items":{"field":"items"},"orderId":{"field":"orderId"},"subtotal":{"field":"subtotal"},"tax":{"field":"tax"},"total":{"field":"total"}}}},"Location":{"fields":{"city":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"postalCode":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"streetAndNumber":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"locations","fields":{"city":{"field":"city"},"country":{"field":"country"},"id":{"field":"_id"},"name":{"field":"name"},"postalCode":{"field":"postalCode"},"streetAndNumber":{"field":"streetAndNumber"}}}},"Order":{"fields":{"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"items":{"many":{"elementNullable":false},"nullable":false,"type":{"kind":"valueObject","name":"OrderLineItem"}},"shippingAddress":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"statusHistory":{"many":{"elementNullable":false},"nullable":false,"type":{"kind":"valueObject","name":"StatusEntry"}},"type":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}}},"relations":{"invoices":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["orderId"]},"to":{"model":"Invoice","namespace":"__unbound__"}},"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"__unbound__"}}},"storage":{"collection":"orders","fields":{"id":{"field":"_id"},"items":{"field":"items"},"shippingAddress":{"field":"shippingAddress"},"statusHistory":{"field":"statusHistory"},"type":{"field":"type"},"userId":{"field":"userId"}}}},"Product":{"fields":{"articleType":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"code":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"description":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"embedding":{"many":{"elementNullable":false},"nullable":true,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"image":{"nullable":false,"type":{"kind":"valueObject","name":"Image"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"price":{"nullable":false,"type":{"kind":"valueObject","name":"Price"}},"primaryCategory":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"status":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"subCategory":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"products","fields":{"articleType":{"field":"articleType"},"brand":{"field":"brand"},"code":{"field":"code"},"description":{"field":"description"},"embedding":{"field":"embedding"},"id":{"field":"_id"},"image":{"field":"image"},"name":{"field":"name"},"price":{"field":"price"},"primaryCategory":{"field":"primaryCategory"},"status":{"field":"status"},"subCategory":{"field":"subCategory"}}}},"SearchEvent":{"base":{"model":"Event","namespace":"__unbound__"},"fields":{"query":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events","fields":{"query":{"field":"query"}}}},"User":{"fields":{"address":{"nullable":true,"type":{"kind":"valueObject","name":"Address"}},"email":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{"carts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Cart","namespace":"__unbound__"}},"orders":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Order","namespace":"__unbound__"}}},"storage":{"collection":"users","fields":{"address":{"field":"address"},"email":{"field":"email"},"id":{"field":"_id"},"name":{"field":"name"}}}},"ViewProductEvent":{"base":{"model":"Event","namespace":"__unbound__"},"fields":{"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"exitMethod":{"nullable":true,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"subCategory":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events","fields":{"brand":{"field":"brand"},"exitMethod":{"field":"exitMethod"},"productId":{"field":"productId"},"subCategory":{"field":"subCategory"}}}}},"valueObjects":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"postalCode":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"streetAndNumber":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"CartItem":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}},"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"image":{"nullable":false,"type":{"kind":"valueObject","name":"Image"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"price":{"nullable":false,"type":{"kind":"valueObject","name":"Price"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"Image":{"fields":{"url":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"InvoiceLineItem":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}},"lineTotal":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"unitPrice":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}}}},"OrderLineItem":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}},"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"image":{"nullable":false,"type":{"kind":"valueObject","name":"Image"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"price":{"nullable":false,"type":{"kind":"valueObject","name":"Price"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"Price":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"currency":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"StatusEntry":{"fields":{"status":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"timestamp":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}}}}}}}},"extensions":{},"meta":{},"profileHash":"251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3","roots":{"carts":{"model":"Cart","namespace":"__unbound__"},"events":{"model":"Event","namespace":"__unbound__"},"invoices":{"model":"Invoice","namespace":"__unbound__"},"locations":{"model":"Location","namespace":"__unbound__"},"orders":{"model":"Order","namespace":"__unbound__"},"products":{"model":"Product","namespace":"__unbound__"},"users":{"model":"User","namespace":"__unbound__"}},"schemaVersion":"1","storage":{"namespaces":{"__unbound__":{"entries":{"collection":{"carts":{"indexes":[{"keys":[{"direction":1,"field":"userId"}],"kind":"mongo-index","unique":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"items":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"int"},"brand":{"bsonType":"string"},"image":{"additionalProperties":false,"bsonType":"object","properties":{"url":{"bsonType":"string"}},"required":["url"]},"name":{"bsonType":"string"},"price":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"double"},"currency":{"bsonType":"string"}},"required":["amount","currency"]},"productId":{"bsonType":"string"}},"required":["amount","brand","image","name","price","productId"]}},"userId":{"bsonType":"objectId"}},"required":["_id","items","userId"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"events":{"indexes":[{"keys":[{"direction":1,"field":"userId"},{"direction":-1,"field":"timestamp"}],"kind":"mongo-index"},{"expireAfterSeconds":7776000,"keys":[{"direction":1,"field":"timestamp"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"bsonType":"object","oneOf":[{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"brand":{"bsonType":"string"},"exitMethod":{"bsonType":["null","string"]},"productId":{"bsonType":"string"},"sessionId":{"bsonType":"string"},"subCategory":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"enum":["view-product"]},"userId":{"bsonType":"string"}},"required":["brand","productId","subCategory","type"]},{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"query":{"bsonType":"string"},"sessionId":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"enum":["search"]},"userId":{"bsonType":"string"}},"required":["query","type"]},{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"brand":{"bsonType":"string"},"productId":{"bsonType":"string"},"sessionId":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"enum":["add-to-cart"]},"userId":{"bsonType":"string"}},"required":["brand","productId","type"]}],"properties":{"_id":{"bsonType":"objectId"},"sessionId":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"bsonType":"string"},"userId":{"bsonType":"string"}},"required":["_id","sessionId","timestamp","type","userId"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"invoices":{"indexes":[{"keys":[{"direction":1,"field":"orderId"}],"kind":"mongo-index"},{"keys":[{"direction":-1,"field":"issuedAt"}],"kind":"mongo-index","sparse":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"issuedAt":{"bsonType":"date"},"items":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"int"},"lineTotal":{"bsonType":"double"},"name":{"bsonType":"string"},"unitPrice":{"bsonType":"double"}},"required":["amount","lineTotal","name","unitPrice"]}},"orderId":{"bsonType":"objectId"},"subtotal":{"bsonType":"double"},"tax":{"bsonType":"double"},"total":{"bsonType":"double"}},"required":["_id","issuedAt","items","orderId","subtotal","tax","total"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"locations":{"indexes":[{"collation":{"locale":"en","strength":2},"keys":[{"direction":1,"field":"city"},{"direction":1,"field":"country"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"city":{"bsonType":"string"},"country":{"bsonType":"string"},"name":{"bsonType":"string"},"postalCode":{"bsonType":"string"},"streetAndNumber":{"bsonType":"string"}},"required":["_id","city","country","name","postalCode","streetAndNumber"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"orders":{"indexes":[{"keys":[{"direction":1,"field":"userId"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"items":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"int"},"brand":{"bsonType":"string"},"image":{"additionalProperties":false,"bsonType":"object","properties":{"url":{"bsonType":"string"}},"required":["url"]},"name":{"bsonType":"string"},"price":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"double"},"currency":{"bsonType":"string"}},"required":["amount","currency"]},"productId":{"bsonType":"string"}},"required":["amount","brand","image","name","price","productId"]}},"shippingAddress":{"bsonType":"string"},"statusHistory":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"status":{"bsonType":"string"},"timestamp":{"bsonType":"date"}},"required":["status","timestamp"]}},"type":{"bsonType":"string"},"userId":{"bsonType":"objectId"}},"required":["_id","items","shippingAddress","statusHistory","type","userId"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"products":{"indexes":[{"keys":[{"direction":"text","field":"name"},{"direction":"text","field":"description"}],"kind":"mongo-index","weights":{"description":1,"name":10}},{"keys":[{"direction":1,"field":"brand"},{"direction":1,"field":"subCategory"}],"kind":"mongo-index"},{"keys":[{"direction":1,"field":"primaryCategory"},{"direction":1,"field":"articleType"}],"kind":"mongo-index"},{"keys":[{"direction":"hashed","field":"code"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"articleType":{"bsonType":"string"},"brand":{"bsonType":"string"},"code":{"bsonType":"string"},"description":{"bsonType":"string"},"embedding":{"bsonType":["null","array"],"items":{"bsonType":"double"}},"image":{"additionalProperties":false,"bsonType":"object","properties":{"url":{"bsonType":"string"}},"required":["url"]},"name":{"bsonType":"string"},"price":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"double"},"currency":{"bsonType":"string"}},"required":["amount","currency"]},"primaryCategory":{"bsonType":"string"},"status":{"bsonType":"string"},"subCategory":{"bsonType":"string"}},"required":["_id","articleType","brand","code","description","image","name","price","primaryCategory","status","subCategory"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"users":{"indexes":[{"keys":[{"direction":1,"field":"email"}],"kind":"mongo-index","unique":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"address":{"oneOf":[{"bsonType":"null"},{"additionalProperties":false,"bsonType":"object","properties":{"city":{"bsonType":"string"},"country":{"bsonType":"string"},"postalCode":{"bsonType":"string"},"streetAndNumber":{"bsonType":"string"}},"required":["city","country","postalCode","streetAndNumber"]}]},"email":{"bsonType":"string"},"name":{"bsonType":"string"}},"required":["_id","email","name"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}}}},"id":"__unbound__","kind":"mongo-database"}},"storageHash":"9af6d90aaa4cd03bf7eb2040a1c1b5c1225a2b33883ea3d0552f312f4684f8dc"},"target":"mongo","targetFamily":"mongo"} diff --git a/examples/retail-store/migrations/snapshots/bd938b4f8a10c688bd32dc61ec1dd808dcf34e725f08505b39ce365a39c97e1b/contract.d.ts b/examples/retail-store/migrations/snapshots/bd938b4f8a10c688bd32dc61ec1dd808dcf34e725f08505b39ce365a39c97e1b/contract.d.ts deleted file mode 100644 index b6382c0be504..000000000000 --- a/examples/retail-store/migrations/snapshots/bd938b4f8a10c688bd32dc61ec1dd808dcf34e725f08505b39ce365a39c97e1b/contract.d.ts +++ /dev/null @@ -1,1498 +0,0 @@ -// ⚠️ GENERATED FILE - DO NOT EDIT -// This file is automatically generated by 'prisma contract emit'. -// To regenerate, run: prisma contract emit -import type { CodecTypes as MongoCodecTypes } from '@prisma/orm-mongo/target/codec-types'; - -import type { - MongoCollection, - MongoContractWithTypeMaps, - MongoTypeMaps, - RelationKeys, -} from '@prisma/orm-mongo/family-contract'; -import type { - Contract as ContractType, - ExecutionHashBase, - NamespaceId, - ProfileHashBase, - StorageHashBase, -} from '@prisma/orm-mongo/contract/types'; - -export type StorageHash = - StorageHashBase<'bd938b4f8a10c688bd32dc61ec1dd808dcf34e725f08505b39ce365a39c97e1b'>; -export type ExecutionHash = ExecutionHashBase; -export type ProfileHash = - ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; - -export type CodecTypes = MongoCodecTypes; - -export type AddressOutput = { - readonly city: CodecTypes['mongo/string@1']['output']; - readonly country: CodecTypes['mongo/string@1']['output']; - readonly postalCode: CodecTypes['mongo/string@1']['output']; - readonly streetAndNumber: CodecTypes['mongo/string@1']['output']; -}; -export type AddressInput = { - readonly city: CodecTypes['mongo/string@1']['input']; - readonly country: CodecTypes['mongo/string@1']['input']; - readonly postalCode: CodecTypes['mongo/string@1']['input']; - readonly streetAndNumber: CodecTypes['mongo/string@1']['input']; -}; -export type CartItemOutput = { - readonly amount: CodecTypes['mongo/int32@1']['output']; - readonly brand: CodecTypes['mongo/string@1']['output']; - readonly image: ImageOutput; - readonly name: CodecTypes['mongo/string@1']['output']; - readonly price: PriceOutput; - readonly productId: CodecTypes['mongo/string@1']['output']; -}; -export type CartItemInput = { - readonly amount: CodecTypes['mongo/int32@1']['input']; - readonly brand: CodecTypes['mongo/string@1']['input']; - readonly image: ImageInput; - readonly name: CodecTypes['mongo/string@1']['input']; - readonly price: PriceInput; - readonly productId: CodecTypes['mongo/string@1']['input']; -}; -export type ImageOutput = { readonly url: CodecTypes['mongo/string@1']['output'] }; -export type ImageInput = { readonly url: CodecTypes['mongo/string@1']['input'] }; -export type InvoiceLineItemOutput = { - readonly amount: CodecTypes['mongo/int32@1']['output']; - readonly lineTotal: CodecTypes['mongo/double@1']['output']; - readonly name: CodecTypes['mongo/string@1']['output']; - readonly unitPrice: CodecTypes['mongo/double@1']['output']; -}; -export type InvoiceLineItemInput = { - readonly amount: CodecTypes['mongo/int32@1']['input']; - readonly lineTotal: CodecTypes['mongo/double@1']['input']; - readonly name: CodecTypes['mongo/string@1']['input']; - readonly unitPrice: CodecTypes['mongo/double@1']['input']; -}; -export type OrderLineItemOutput = { - readonly amount: CodecTypes['mongo/int32@1']['output']; - readonly brand: CodecTypes['mongo/string@1']['output']; - readonly image: ImageOutput; - readonly name: CodecTypes['mongo/string@1']['output']; - readonly price: PriceOutput; - readonly productId: CodecTypes['mongo/string@1']['output']; -}; -export type OrderLineItemInput = { - readonly amount: CodecTypes['mongo/int32@1']['input']; - readonly brand: CodecTypes['mongo/string@1']['input']; - readonly image: ImageInput; - readonly name: CodecTypes['mongo/string@1']['input']; - readonly price: PriceInput; - readonly productId: CodecTypes['mongo/string@1']['input']; -}; -export type PriceOutput = { - readonly amount: CodecTypes['mongo/double@1']['output']; - readonly currency: CodecTypes['mongo/string@1']['output']; -}; -export type PriceInput = { - readonly amount: CodecTypes['mongo/double@1']['input']; - readonly currency: CodecTypes['mongo/string@1']['input']; -}; -export type StatusEntryOutput = { - readonly status: CodecTypes['mongo/string@1']['output']; - readonly timestamp: CodecTypes['mongo/date@1']['output']; -}; -export type StatusEntryInput = { - readonly status: CodecTypes['mongo/string@1']['input']; - readonly timestamp: CodecTypes['mongo/date@1']['input']; -}; -export type FieldOutputTypes = { - readonly __unbound__: { - readonly AddToCartEvent: { - readonly brand: CodecTypes['mongo/string@1']['output']; - readonly productId: CodecTypes['mongo/string@1']['output']; - }; - readonly Cart: { - readonly _id: CodecTypes['mongo/objectId@1']['output']; - readonly items: ReadonlyArray; - readonly userId: CodecTypes['mongo/objectId@1']['output']; - }; - readonly Event: { - readonly _id: CodecTypes['mongo/objectId@1']['output']; - readonly sessionId: CodecTypes['mongo/string@1']['output']; - readonly timestamp: CodecTypes['mongo/date@1']['output']; - readonly type: CodecTypes['mongo/string@1']['output']; - readonly userId: CodecTypes['mongo/string@1']['output']; - }; - readonly Invoice: { - readonly _id: CodecTypes['mongo/objectId@1']['output']; - readonly issuedAt: CodecTypes['mongo/date@1']['output']; - readonly items: ReadonlyArray; - readonly orderId: CodecTypes['mongo/objectId@1']['output']; - readonly subtotal: CodecTypes['mongo/double@1']['output']; - readonly tax: CodecTypes['mongo/double@1']['output']; - readonly total: CodecTypes['mongo/double@1']['output']; - }; - readonly Location: { - readonly _id: CodecTypes['mongo/objectId@1']['output']; - readonly city: CodecTypes['mongo/string@1']['output']; - readonly country: CodecTypes['mongo/string@1']['output']; - readonly name: CodecTypes['mongo/string@1']['output']; - readonly postalCode: CodecTypes['mongo/string@1']['output']; - readonly streetAndNumber: CodecTypes['mongo/string@1']['output']; - }; - readonly Order: { - readonly _id: CodecTypes['mongo/objectId@1']['output']; - readonly items: ReadonlyArray; - readonly shippingAddress: CodecTypes['mongo/string@1']['output']; - readonly statusHistory: ReadonlyArray; - readonly type: 'delivery' | 'pickup'; - readonly userId: CodecTypes['mongo/objectId@1']['output']; - }; - readonly Product: { - readonly _id: CodecTypes['mongo/objectId@1']['output']; - readonly articleType: CodecTypes['mongo/string@1']['output']; - readonly brand: CodecTypes['mongo/string@1']['output']; - readonly code: CodecTypes['mongo/string@1']['output']; - readonly description: CodecTypes['mongo/string@1']['output']; - readonly embedding: ReadonlyArray | null; - readonly image: ImageOutput; - readonly name: CodecTypes['mongo/string@1']['output']; - readonly price: PriceOutput; - readonly primaryCategory: CodecTypes['mongo/string@1']['output']; - readonly status: 'active' | 'discontinued' | 'out-of-stock'; - readonly subCategory: CodecTypes['mongo/string@1']['output']; - }; - readonly SearchEvent: { readonly query: CodecTypes['mongo/string@1']['output'] }; - readonly User: { - readonly _id: CodecTypes['mongo/objectId@1']['output']; - readonly address: AddressOutput | null; - readonly email: CodecTypes['mongo/string@1']['output']; - readonly name: CodecTypes['mongo/string@1']['output']; - }; - readonly ViewProductEvent: { - readonly brand: CodecTypes['mongo/string@1']['output']; - readonly exitMethod: CodecTypes['mongo/string@1']['output'] | null; - readonly productId: CodecTypes['mongo/string@1']['output']; - readonly subCategory: CodecTypes['mongo/string@1']['output']; - }; - }; -}; -export type FieldInputTypes = { - readonly __unbound__: { - readonly AddToCartEvent: { - readonly brand: CodecTypes['mongo/string@1']['input']; - readonly productId: CodecTypes['mongo/string@1']['input']; - }; - readonly Cart: { - readonly _id: CodecTypes['mongo/objectId@1']['input']; - readonly items: ReadonlyArray; - readonly userId: CodecTypes['mongo/objectId@1']['input']; - }; - readonly Event: { - readonly _id: CodecTypes['mongo/objectId@1']['input']; - readonly sessionId: CodecTypes['mongo/string@1']['input']; - readonly timestamp: CodecTypes['mongo/date@1']['input']; - readonly type: CodecTypes['mongo/string@1']['input']; - readonly userId: CodecTypes['mongo/string@1']['input']; - }; - readonly Invoice: { - readonly _id: CodecTypes['mongo/objectId@1']['input']; - readonly issuedAt: CodecTypes['mongo/date@1']['input']; - readonly items: ReadonlyArray; - readonly orderId: CodecTypes['mongo/objectId@1']['input']; - readonly subtotal: CodecTypes['mongo/double@1']['input']; - readonly tax: CodecTypes['mongo/double@1']['input']; - readonly total: CodecTypes['mongo/double@1']['input']; - }; - readonly Location: { - readonly _id: CodecTypes['mongo/objectId@1']['input']; - readonly city: CodecTypes['mongo/string@1']['input']; - readonly country: CodecTypes['mongo/string@1']['input']; - readonly name: CodecTypes['mongo/string@1']['input']; - readonly postalCode: CodecTypes['mongo/string@1']['input']; - readonly streetAndNumber: CodecTypes['mongo/string@1']['input']; - }; - readonly Order: { - readonly _id: CodecTypes['mongo/objectId@1']['input']; - readonly items: ReadonlyArray; - readonly shippingAddress: CodecTypes['mongo/string@1']['input']; - readonly statusHistory: ReadonlyArray; - readonly type: 'delivery' | 'pickup'; - readonly userId: CodecTypes['mongo/objectId@1']['input']; - }; - readonly Product: { - readonly _id: CodecTypes['mongo/objectId@1']['input']; - readonly articleType: CodecTypes['mongo/string@1']['input']; - readonly brand: CodecTypes['mongo/string@1']['input']; - readonly code: CodecTypes['mongo/string@1']['input']; - readonly description: CodecTypes['mongo/string@1']['input']; - readonly embedding: ReadonlyArray | null; - readonly image: ImageInput; - readonly name: CodecTypes['mongo/string@1']['input']; - readonly price: PriceInput; - readonly primaryCategory: CodecTypes['mongo/string@1']['input']; - readonly status: 'active' | 'discontinued' | 'out-of-stock'; - readonly subCategory: CodecTypes['mongo/string@1']['input']; - }; - readonly SearchEvent: { readonly query: CodecTypes['mongo/string@1']['input'] }; - readonly User: { - readonly _id: CodecTypes['mongo/objectId@1']['input']; - readonly address: AddressInput | null; - readonly email: CodecTypes['mongo/string@1']['input']; - readonly name: CodecTypes['mongo/string@1']['input']; - }; - readonly ViewProductEvent: { - readonly brand: CodecTypes['mongo/string@1']['input']; - readonly exitMethod: CodecTypes['mongo/string@1']['input'] | null; - readonly productId: CodecTypes['mongo/string@1']['input']; - readonly subCategory: CodecTypes['mongo/string@1']['input']; - }; - }; -}; - -export namespace Models { - export type unbound_AddToCartEvent = { - _id: CodecTypes['mongo/objectId@1']['output']; - sessionId: CodecTypes['mongo/string@1']['output']; - timestamp: CodecTypes['mongo/date@1']['output']; - type: 'add-to-cart'; - userId: CodecTypes['mongo/string@1']['output']; - brand: CodecTypes['mongo/string@1']['output']; - productId: CodecTypes['mongo/string@1']['output']; - readonly [RelationKeys]?: never; - }; - export type unbound_Cart = { - _id: CodecTypes['mongo/objectId@1']['output']; - items: ReadonlyArray; - userId: CodecTypes['mongo/objectId@1']['output']; - user: unbound_User; - readonly [RelationKeys]?: 'user'; - }; - export type unbound_Event = { - _id: CodecTypes['mongo/objectId@1']['output']; - sessionId: CodecTypes['mongo/string@1']['output']; - timestamp: CodecTypes['mongo/date@1']['output']; - type: 'add-to-cart' | 'search' | 'view-product'; - userId: CodecTypes['mongo/string@1']['output']; - readonly [RelationKeys]?: never; - }; - export type unbound_Invoice = { - _id: CodecTypes['mongo/objectId@1']['output']; - issuedAt: CodecTypes['mongo/date@1']['output']; - items: ReadonlyArray; - orderId: CodecTypes['mongo/objectId@1']['output']; - subtotal: CodecTypes['mongo/double@1']['output']; - tax: CodecTypes['mongo/double@1']['output']; - total: CodecTypes['mongo/double@1']['output']; - order: unbound_Order; - readonly [RelationKeys]?: 'order'; - }; - export type unbound_Location = { - _id: CodecTypes['mongo/objectId@1']['output']; - city: CodecTypes['mongo/string@1']['output']; - country: CodecTypes['mongo/string@1']['output']; - name: CodecTypes['mongo/string@1']['output']; - postalCode: CodecTypes['mongo/string@1']['output']; - streetAndNumber: CodecTypes['mongo/string@1']['output']; - readonly [RelationKeys]?: never; - }; - export type unbound_Order = { - _id: CodecTypes['mongo/objectId@1']['output']; - items: ReadonlyArray; - shippingAddress: CodecTypes['mongo/string@1']['output']; - statusHistory: ReadonlyArray; - type: 'delivery' | 'pickup'; - userId: CodecTypes['mongo/objectId@1']['output']; - invoices: unbound_Invoice[]; - user: unbound_User; - readonly [RelationKeys]?: 'invoices' | 'user'; - }; - export type unbound_Product = { - _id: CodecTypes['mongo/objectId@1']['output']; - articleType: CodecTypes['mongo/string@1']['output']; - brand: CodecTypes['mongo/string@1']['output']; - code: CodecTypes['mongo/string@1']['output']; - description: CodecTypes['mongo/string@1']['output']; - embedding: ReadonlyArray | null; - image: ImageOutput; - name: CodecTypes['mongo/string@1']['output']; - price: PriceOutput; - primaryCategory: CodecTypes['mongo/string@1']['output']; - status: 'active' | 'discontinued' | 'out-of-stock'; - subCategory: CodecTypes['mongo/string@1']['output']; - readonly [RelationKeys]?: never; - }; - export type unbound_SearchEvent = { - _id: CodecTypes['mongo/objectId@1']['output']; - sessionId: CodecTypes['mongo/string@1']['output']; - timestamp: CodecTypes['mongo/date@1']['output']; - type: 'search'; - userId: CodecTypes['mongo/string@1']['output']; - query: CodecTypes['mongo/string@1']['output']; - readonly [RelationKeys]?: never; - }; - export type unbound_User = { - _id: CodecTypes['mongo/objectId@1']['output']; - address: AddressOutput | null; - email: CodecTypes['mongo/string@1']['output']; - name: CodecTypes['mongo/string@1']['output']; - carts: unbound_Cart[]; - orders: unbound_Order[]; - readonly [RelationKeys]?: 'carts' | 'orders'; - }; - export type unbound_ViewProductEvent = { - _id: CodecTypes['mongo/objectId@1']['output']; - sessionId: CodecTypes['mongo/string@1']['output']; - timestamp: CodecTypes['mongo/date@1']['output']; - type: 'view-product'; - userId: CodecTypes['mongo/string@1']['output']; - brand: CodecTypes['mongo/string@1']['output']; - exitMethod: CodecTypes['mongo/string@1']['output'] | null; - productId: CodecTypes['mongo/string@1']['output']; - subCategory: CodecTypes['mongo/string@1']['output']; - readonly [RelationKeys]?: never; - }; - export type unbound_AnyEvent = - unbound_AddToCartEvent | unbound_SearchEvent | unbound_ViewProductEvent; -} - -export declare const models: { - __unbound__: { - AddToCartEvent: Models.unbound_AddToCartEvent; - Cart: Models.unbound_Cart; - Event: Models.unbound_Event; - Invoice: Models.unbound_Invoice; - Location: Models.unbound_Location; - Order: Models.unbound_Order; - Product: Models.unbound_Product; - SearchEvent: Models.unbound_SearchEvent; - User: Models.unbound_User; - ViewProductEvent: Models.unbound_ViewProductEvent; - AnyEvent: Models.unbound_AnyEvent; - }; -}; - -export type TypeMaps = MongoTypeMaps; - -type ContractBase = Omit< - ContractType<{ - readonly namespaces: { - readonly __unbound__: { - readonly id: '__unbound__'; - readonly kind: 'mongo-database'; - readonly entries: { - readonly collection: { - readonly carts: { - readonly indexes: readonly [ - { - readonly keys: readonly [{ readonly direction: 1; readonly field: 'userId' }]; - readonly kind: 'mongo-index'; - readonly unique: true; - }, - ]; - readonly kind: 'mongo-collection'; - readonly validator: { - readonly jsonSchema: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly _id: { readonly bsonType: 'objectId' }; - readonly items: { - readonly bsonType: 'array'; - readonly items: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly amount: { readonly bsonType: 'int' }; - readonly brand: { readonly bsonType: 'string' }; - readonly image: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { readonly url: { readonly bsonType: 'string' } }; - readonly required: readonly ['url']; - }; - readonly name: { readonly bsonType: 'string' }; - readonly price: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly amount: { readonly bsonType: 'double' }; - readonly currency: { readonly bsonType: 'string' }; - }; - readonly required: readonly ['amount', 'currency']; - }; - readonly productId: { readonly bsonType: 'string' }; - }; - readonly required: readonly [ - 'amount', - 'brand', - 'image', - 'name', - 'price', - 'productId', - ]; - }; - }; - readonly userId: { readonly bsonType: 'objectId' }; - }; - readonly required: readonly ['_id', 'items', 'userId']; - }; - readonly kind: 'mongo-validator'; - readonly validationAction: 'error'; - readonly validationLevel: 'strict'; - }; - }; - readonly events: { - readonly indexes: readonly [ - { - readonly keys: readonly [ - { readonly direction: 1; readonly field: 'userId' }, - { readonly direction: -1; readonly field: 'timestamp' }, - ]; - readonly kind: 'mongo-index'; - }, - { - readonly expireAfterSeconds: 7776000; - readonly keys: readonly [{ readonly direction: 1; readonly field: 'timestamp' }]; - readonly kind: 'mongo-index'; - }, - ]; - readonly kind: 'mongo-collection'; - readonly validator: { - readonly jsonSchema: { - readonly bsonType: 'object'; - readonly oneOf: readonly [ - { - readonly additionalProperties: false; - readonly properties: { - readonly _id: { readonly bsonType: 'objectId' }; - readonly brand: { readonly bsonType: 'string' }; - readonly exitMethod: { readonly bsonType: readonly ['null', 'string'] }; - readonly productId: { readonly bsonType: 'string' }; - readonly sessionId: { readonly bsonType: 'string' }; - readonly subCategory: { readonly bsonType: 'string' }; - readonly timestamp: { readonly bsonType: 'date' }; - readonly type: { readonly enum: readonly ['view-product'] }; - readonly userId: { readonly bsonType: 'string' }; - }; - readonly required: readonly ['brand', 'productId', 'subCategory', 'type']; - }, - { - readonly additionalProperties: false; - readonly properties: { - readonly _id: { readonly bsonType: 'objectId' }; - readonly query: { readonly bsonType: 'string' }; - readonly sessionId: { readonly bsonType: 'string' }; - readonly timestamp: { readonly bsonType: 'date' }; - readonly type: { readonly enum: readonly ['search'] }; - readonly userId: { readonly bsonType: 'string' }; - }; - readonly required: readonly ['query', 'type']; - }, - { - readonly additionalProperties: false; - readonly properties: { - readonly _id: { readonly bsonType: 'objectId' }; - readonly brand: { readonly bsonType: 'string' }; - readonly productId: { readonly bsonType: 'string' }; - readonly sessionId: { readonly bsonType: 'string' }; - readonly timestamp: { readonly bsonType: 'date' }; - readonly type: { readonly enum: readonly ['add-to-cart'] }; - readonly userId: { readonly bsonType: 'string' }; - }; - readonly required: readonly ['brand', 'productId', 'type']; - }, - ]; - readonly properties: { - readonly _id: { readonly bsonType: 'objectId' }; - readonly sessionId: { readonly bsonType: 'string' }; - readonly timestamp: { readonly bsonType: 'date' }; - readonly type: { readonly bsonType: 'string' }; - readonly userId: { readonly bsonType: 'string' }; - }; - readonly required: readonly ['_id', 'sessionId', 'timestamp', 'type', 'userId']; - }; - readonly kind: 'mongo-validator'; - readonly validationAction: 'error'; - readonly validationLevel: 'strict'; - }; - }; - readonly invoices: { - readonly indexes: readonly [ - { - readonly keys: readonly [{ readonly direction: 1; readonly field: 'orderId' }]; - readonly kind: 'mongo-index'; - }, - { - readonly keys: readonly [{ readonly direction: -1; readonly field: 'issuedAt' }]; - readonly kind: 'mongo-index'; - readonly sparse: true; - }, - ]; - readonly kind: 'mongo-collection'; - readonly validator: { - readonly jsonSchema: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly _id: { readonly bsonType: 'objectId' }; - readonly issuedAt: { readonly bsonType: 'date' }; - readonly items: { - readonly bsonType: 'array'; - readonly items: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly amount: { readonly bsonType: 'int' }; - readonly lineTotal: { readonly bsonType: 'double' }; - readonly name: { readonly bsonType: 'string' }; - readonly unitPrice: { readonly bsonType: 'double' }; - }; - readonly required: readonly ['amount', 'lineTotal', 'name', 'unitPrice']; - }; - }; - readonly orderId: { readonly bsonType: 'objectId' }; - readonly subtotal: { readonly bsonType: 'double' }; - readonly tax: { readonly bsonType: 'double' }; - readonly total: { readonly bsonType: 'double' }; - }; - readonly required: readonly [ - '_id', - 'issuedAt', - 'items', - 'orderId', - 'subtotal', - 'tax', - 'total', - ]; - }; - readonly kind: 'mongo-validator'; - readonly validationAction: 'error'; - readonly validationLevel: 'strict'; - }; - }; - readonly locations: { - readonly indexes: readonly [ - { - readonly collation: { readonly locale: 'en'; readonly strength: 2 }; - readonly keys: readonly [ - { readonly direction: 1; readonly field: 'city' }, - { readonly direction: 1; readonly field: 'country' }, - ]; - readonly kind: 'mongo-index'; - }, - ]; - readonly kind: 'mongo-collection'; - readonly validator: { - readonly jsonSchema: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly _id: { readonly bsonType: 'objectId' }; - readonly city: { readonly bsonType: 'string' }; - readonly country: { readonly bsonType: 'string' }; - readonly name: { readonly bsonType: 'string' }; - readonly postalCode: { readonly bsonType: 'string' }; - readonly streetAndNumber: { readonly bsonType: 'string' }; - }; - readonly required: readonly [ - '_id', - 'city', - 'country', - 'name', - 'postalCode', - 'streetAndNumber', - ]; - }; - readonly kind: 'mongo-validator'; - readonly validationAction: 'error'; - readonly validationLevel: 'strict'; - }; - }; - readonly orders: { - readonly indexes: readonly [ - { - readonly keys: readonly [{ readonly direction: 1; readonly field: 'userId' }]; - readonly kind: 'mongo-index'; - }, - ]; - readonly kind: 'mongo-collection'; - readonly validator: { - readonly jsonSchema: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly _id: { readonly bsonType: 'objectId' }; - readonly items: { - readonly bsonType: 'array'; - readonly items: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly amount: { readonly bsonType: 'int' }; - readonly brand: { readonly bsonType: 'string' }; - readonly image: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { readonly url: { readonly bsonType: 'string' } }; - readonly required: readonly ['url']; - }; - readonly name: { readonly bsonType: 'string' }; - readonly price: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly amount: { readonly bsonType: 'double' }; - readonly currency: { readonly bsonType: 'string' }; - }; - readonly required: readonly ['amount', 'currency']; - }; - readonly productId: { readonly bsonType: 'string' }; - }; - readonly required: readonly [ - 'amount', - 'brand', - 'image', - 'name', - 'price', - 'productId', - ]; - }; - }; - readonly shippingAddress: { readonly bsonType: 'string' }; - readonly statusHistory: { - readonly bsonType: 'array'; - readonly items: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly status: { readonly bsonType: 'string' }; - readonly timestamp: { readonly bsonType: 'date' }; - }; - readonly required: readonly ['status', 'timestamp']; - }; - }; - readonly type: { - readonly bsonType: 'string'; - readonly enum: readonly ['delivery', 'pickup']; - }; - readonly userId: { readonly bsonType: 'objectId' }; - }; - readonly required: readonly [ - '_id', - 'items', - 'shippingAddress', - 'statusHistory', - 'type', - 'userId', - ]; - }; - readonly kind: 'mongo-validator'; - readonly validationAction: 'error'; - readonly validationLevel: 'strict'; - }; - }; - readonly products: { - readonly indexes: readonly [ - { - readonly keys: readonly [ - { readonly direction: 'text'; readonly field: 'name' }, - { readonly direction: 'text'; readonly field: 'description' }, - ]; - readonly kind: 'mongo-index'; - readonly weights: { readonly description: 1; readonly name: 10 }; - }, - { - readonly keys: readonly [ - { readonly direction: 1; readonly field: 'brand' }, - { readonly direction: 1; readonly field: 'subCategory' }, - ]; - readonly kind: 'mongo-index'; - }, - { - readonly keys: readonly [ - { readonly direction: 1; readonly field: 'primaryCategory' }, - { readonly direction: 1; readonly field: 'articleType' }, - ]; - readonly kind: 'mongo-index'; - }, - { - readonly keys: readonly [ - { readonly direction: 'hashed'; readonly field: 'code' }, - ]; - readonly kind: 'mongo-index'; - }, - ]; - readonly kind: 'mongo-collection'; - readonly validator: { - readonly jsonSchema: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly _id: { readonly bsonType: 'objectId' }; - readonly articleType: { readonly bsonType: 'string' }; - readonly brand: { readonly bsonType: 'string' }; - readonly code: { readonly bsonType: 'string' }; - readonly description: { readonly bsonType: 'string' }; - readonly embedding: { - readonly bsonType: 'array'; - readonly items: { readonly bsonType: 'double' }; - }; - readonly image: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { readonly url: { readonly bsonType: 'string' } }; - readonly required: readonly ['url']; - }; - readonly name: { readonly bsonType: 'string' }; - readonly price: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly amount: { readonly bsonType: 'double' }; - readonly currency: { readonly bsonType: 'string' }; - }; - readonly required: readonly ['amount', 'currency']; - }; - readonly primaryCategory: { readonly bsonType: 'string' }; - readonly status: { - readonly bsonType: 'string'; - readonly enum: readonly ['active', 'discontinued', 'out-of-stock']; - }; - readonly subCategory: { readonly bsonType: 'string' }; - }; - readonly required: readonly [ - '_id', - 'articleType', - 'brand', - 'code', - 'description', - 'image', - 'name', - 'price', - 'primaryCategory', - 'status', - 'subCategory', - ]; - }; - readonly kind: 'mongo-validator'; - readonly validationAction: 'error'; - readonly validationLevel: 'strict'; - }; - }; - readonly users: { - readonly indexes: readonly [ - { - readonly keys: readonly [{ readonly direction: 1; readonly field: 'email' }]; - readonly kind: 'mongo-index'; - readonly unique: true; - }, - ]; - readonly kind: 'mongo-collection'; - readonly validator: { - readonly jsonSchema: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly _id: { readonly bsonType: 'objectId' }; - readonly address: { - readonly oneOf: readonly [ - { readonly bsonType: 'null' }, - { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly city: { readonly bsonType: 'string' }; - readonly country: { readonly bsonType: 'string' }; - readonly postalCode: { readonly bsonType: 'string' }; - readonly streetAndNumber: { readonly bsonType: 'string' }; - }; - readonly required: readonly [ - 'city', - 'country', - 'postalCode', - 'streetAndNumber', - ]; - }, - ]; - }; - readonly email: { readonly bsonType: 'string' }; - readonly name: { readonly bsonType: 'string' }; - }; - readonly required: readonly ['_id', 'email', 'name']; - }; - readonly kind: 'mongo-validator'; - readonly validationAction: 'error'; - readonly validationLevel: 'strict'; - }; - }; - }; - }; - }; - }; - readonly storageHash: StorageHash; - }>, - 'roots' | 'domain' -> & { - readonly target: 'mongo'; - readonly targetFamily: 'mongo'; - readonly roots: { - readonly carts: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'Cart' }; - readonly events: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'Event' }; - readonly invoices: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Invoice'; - }; - readonly locations: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Location'; - }; - readonly orders: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'Order' }; - readonly products: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Product'; - }; - readonly users: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'User' }; - }; - readonly domain: { - readonly namespaces: { - readonly __unbound__: { - readonly models: { - readonly AddToCartEvent: { - readonly fields: { - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - readonly relations: Record; - readonly storage: { readonly collection: 'events' }; - readonly base: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Event'; - }; - }; - readonly Cart: { - readonly fields: { - readonly _id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - readonly items: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CartItem' }; - readonly many: { readonly elementNullable: false }; - }; - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - }; - readonly relations: { - readonly user: { - readonly to: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'User'; - }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['userId']; - readonly targetFields: readonly ['_id']; - }; - }; - }; - readonly storage: { readonly collection: 'carts' }; - }; - readonly Event: { - readonly fields: { - readonly _id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - readonly sessionId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly timestamp: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; - }; - readonly type: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - readonly relations: Record; - readonly storage: { readonly collection: 'events' }; - readonly discriminator: { readonly field: 'type' }; - readonly variants: { - readonly AddToCartEvent: { readonly value: 'add-to-cart' }; - readonly SearchEvent: { readonly value: 'search' }; - readonly ViewProductEvent: { readonly value: 'view-product' }; - }; - }; - readonly Invoice: { - readonly fields: { - readonly _id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - readonly issuedAt: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; - }; - readonly items: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'InvoiceLineItem' }; - readonly many: { readonly elementNullable: false }; - }; - readonly orderId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - readonly subtotal: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly tax: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly total: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - }; - readonly relations: { - readonly order: { - readonly to: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Order'; - }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['orderId']; - readonly targetFields: readonly ['_id']; - }; - }; - }; - readonly storage: { readonly collection: 'invoices' }; - }; - readonly Location: { - readonly fields: { - readonly _id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly postalCode: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly streetAndNumber: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - readonly relations: Record; - readonly storage: { readonly collection: 'locations' }; - }; - readonly Order: { - readonly fields: { - readonly _id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - readonly items: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'OrderLineItem' }; - readonly many: { readonly elementNullable: false }; - }; - readonly shippingAddress: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly statusHistory: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'StatusEntry' }; - readonly many: { readonly elementNullable: false }; - }; - readonly type: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - }; - readonly relations: { - readonly invoices: { - readonly to: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Invoice'; - }; - readonly cardinality: '1:N'; - readonly on: { - readonly localFields: readonly ['_id']; - readonly targetFields: readonly ['orderId']; - }; - }; - readonly user: { - readonly to: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'User'; - }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['userId']; - readonly targetFields: readonly ['_id']; - }; - }; - }; - readonly storage: { readonly collection: 'orders' }; - }; - readonly Product: { - readonly fields: { - readonly _id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - readonly articleType: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly code: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly description: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly embedding: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - readonly many: { readonly elementNullable: false }; - }; - readonly image: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly price: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; - }; - readonly primaryCategory: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly status: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly subCategory: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - readonly relations: Record; - readonly storage: { readonly collection: 'products' }; - }; - readonly SearchEvent: { - readonly fields: { - readonly query: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - readonly relations: Record; - readonly storage: { readonly collection: 'events' }; - readonly base: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Event'; - }; - }; - readonly User: { - readonly fields: { - readonly _id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - readonly address: { - readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; - }; - readonly email: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - readonly relations: { - readonly carts: { - readonly to: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Cart'; - }; - readonly cardinality: '1:N'; - readonly on: { - readonly localFields: readonly ['_id']; - readonly targetFields: readonly ['userId']; - }; - }; - readonly orders: { - readonly to: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Order'; - }; - readonly cardinality: '1:N'; - readonly on: { - readonly localFields: readonly ['_id']; - readonly targetFields: readonly ['userId']; - }; - }; - }; - readonly storage: { readonly collection: 'users' }; - }; - readonly ViewProductEvent: { - readonly fields: { - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly exitMethod: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly subCategory: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - readonly relations: Record; - readonly storage: { readonly collection: 'events' }; - readonly base: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Event'; - }; - }; - }; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly postalCode: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly streetAndNumber: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly CartItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly image: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly price: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Image: { - readonly fields: { - readonly url: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly InvoiceLineItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly lineTotal: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly unitPrice: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - }; - }; - readonly OrderLineItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly image: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly price: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Price: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly currency: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly StatusEntry: { - readonly fields: { - readonly status: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly timestamp: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; - }; - }; - }; - }; - readonly enum: { - readonly OrderType: { - readonly codecId: 'mongo/string@1'; - readonly members: readonly [ - { readonly name: 'Delivery'; readonly value: 'delivery' }, - { readonly name: 'Pickup'; readonly value: 'pickup' }, - ]; - }; - readonly ProductStatus: { - readonly codecId: 'mongo/string@1'; - readonly members: readonly [ - { readonly name: 'Active'; readonly value: 'active' }, - { readonly name: 'Discontinued'; readonly value: 'discontinued' }, - { readonly name: 'OutOfStock'; readonly value: 'out-of-stock' }, - ]; - }; - }; - }; - }; - }; - readonly capabilities: {}; - readonly extensions: {}; - readonly meta: {}; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly postalCode: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly streetAndNumber: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly CartItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly image: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly price: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Image: { - readonly fields: { - readonly url: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly InvoiceLineItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly lineTotal: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly unitPrice: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - }; - }; - readonly OrderLineItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly image: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly price: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Price: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly currency: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly StatusEntry: { - readonly fields: { - readonly status: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly timestamp: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; - }; - }; - }; - }; - readonly profileHash: ProfileHash; -}; - -export type Contract = MongoContractWithTypeMaps; diff --git a/examples/retail-store/migrations/snapshots/bd938b4f8a10c688bd32dc61ec1dd808dcf34e725f08505b39ce365a39c97e1b/contract.json b/examples/retail-store/migrations/snapshots/bd938b4f8a10c688bd32dc61ec1dd808dcf34e725f08505b39ce365a39c97e1b/contract.json deleted file mode 100644 index 4f3461da3fe3..000000000000 --- a/examples/retail-store/migrations/snapshots/bd938b4f8a10c688bd32dc61ec1dd808dcf34e725f08505b39ce365a39c97e1b/contract.json +++ /dev/null @@ -1 +0,0 @@ -{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{},"domain":{"namespaces":{"__unbound__":{"enum":{"OrderType":{"codecId":"mongo/string@1","members":[{"name":"Delivery","value":"delivery"},{"name":"Pickup","value":"pickup"}]},"ProductStatus":{"codecId":"mongo/string@1","members":[{"name":"Active","value":"active"},{"name":"Discontinued","value":"discontinued"},{"name":"OutOfStock","value":"out-of-stock"}]}},"models":{"AddToCartEvent":{"base":{"model":"Event","namespace":"__unbound__"},"fields":{"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events"}},"Cart":{"fields":{"_id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"items":{"many":{"elementNullable":false},"nullable":false,"type":{"kind":"valueObject","name":"CartItem"}},"userId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}}},"relations":{"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["_id"]},"to":{"model":"User","namespace":"__unbound__"}}},"storage":{"collection":"carts"}},"Event":{"discriminator":{"field":"type"},"fields":{"_id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"sessionId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"timestamp":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}},"type":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events"},"variants":{"AddToCartEvent":{"value":"add-to-cart"},"SearchEvent":{"value":"search"},"ViewProductEvent":{"value":"view-product"}}},"Invoice":{"fields":{"_id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"issuedAt":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}},"items":{"many":{"elementNullable":false},"nullable":false,"type":{"kind":"valueObject","name":"InvoiceLineItem"}},"orderId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"subtotal":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"tax":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"total":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}}},"relations":{"order":{"cardinality":"N:1","nullable":false,"on":{"localFields":["orderId"],"targetFields":["_id"]},"to":{"model":"Order","namespace":"__unbound__"}}},"storage":{"collection":"invoices"}},"Location":{"fields":{"_id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"city":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"postalCode":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"streetAndNumber":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"locations"}},"Order":{"fields":{"_id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"items":{"many":{"elementNullable":false},"nullable":false,"type":{"kind":"valueObject","name":"OrderLineItem"}},"shippingAddress":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"statusHistory":{"many":{"elementNullable":false},"nullable":false,"type":{"kind":"valueObject","name":"StatusEntry"}},"type":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"OrderType","namespaceId":"__unbound__","plane":"domain"}},"userId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}}},"relations":{"invoices":{"cardinality":"1:N","on":{"localFields":["_id"],"targetFields":["orderId"]},"to":{"model":"Invoice","namespace":"__unbound__"}},"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["_id"]},"to":{"model":"User","namespace":"__unbound__"}}},"storage":{"collection":"orders"}},"Product":{"fields":{"_id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"articleType":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"code":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"description":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"embedding":{"many":{"elementNullable":false},"nullable":true,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"image":{"nullable":false,"type":{"kind":"valueObject","name":"Image"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"price":{"nullable":false,"type":{"kind":"valueObject","name":"Price"}},"primaryCategory":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"status":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"ProductStatus","namespaceId":"__unbound__","plane":"domain"}},"subCategory":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"products"}},"SearchEvent":{"base":{"model":"Event","namespace":"__unbound__"},"fields":{"query":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events"}},"User":{"fields":{"_id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"address":{"nullable":true,"type":{"kind":"valueObject","name":"Address"}},"email":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{"carts":{"cardinality":"1:N","on":{"localFields":["_id"],"targetFields":["userId"]},"to":{"model":"Cart","namespace":"__unbound__"}},"orders":{"cardinality":"1:N","on":{"localFields":["_id"],"targetFields":["userId"]},"to":{"model":"Order","namespace":"__unbound__"}}},"storage":{"collection":"users"}},"ViewProductEvent":{"base":{"model":"Event","namespace":"__unbound__"},"fields":{"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"exitMethod":{"nullable":true,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"subCategory":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events"}}},"valueObjects":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"postalCode":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"streetAndNumber":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"CartItem":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}},"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"image":{"nullable":false,"type":{"kind":"valueObject","name":"Image"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"price":{"nullable":false,"type":{"kind":"valueObject","name":"Price"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"Image":{"fields":{"url":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"InvoiceLineItem":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}},"lineTotal":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"unitPrice":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}}}},"OrderLineItem":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}},"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"image":{"nullable":false,"type":{"kind":"valueObject","name":"Image"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"price":{"nullable":false,"type":{"kind":"valueObject","name":"Price"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"Price":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"currency":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"StatusEntry":{"fields":{"status":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"timestamp":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}}}}}}}},"extensions":{},"meta":{},"profileHash":"251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3","roots":{"carts":{"model":"Cart","namespace":"__unbound__"},"events":{"model":"Event","namespace":"__unbound__"},"invoices":{"model":"Invoice","namespace":"__unbound__"},"locations":{"model":"Location","namespace":"__unbound__"},"orders":{"model":"Order","namespace":"__unbound__"},"products":{"model":"Product","namespace":"__unbound__"},"users":{"model":"User","namespace":"__unbound__"}},"schemaVersion":"1","storage":{"namespaces":{"__unbound__":{"entries":{"collection":{"carts":{"indexes":[{"keys":[{"direction":1,"field":"userId"}],"kind":"mongo-index","unique":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"items":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"int"},"brand":{"bsonType":"string"},"image":{"additionalProperties":false,"bsonType":"object","properties":{"url":{"bsonType":"string"}},"required":["url"]},"name":{"bsonType":"string"},"price":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"double"},"currency":{"bsonType":"string"}},"required":["amount","currency"]},"productId":{"bsonType":"string"}},"required":["amount","brand","image","name","price","productId"]}},"userId":{"bsonType":"objectId"}},"required":["_id","items","userId"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"events":{"indexes":[{"keys":[{"direction":1,"field":"userId"},{"direction":-1,"field":"timestamp"}],"kind":"mongo-index"},{"expireAfterSeconds":7776000,"keys":[{"direction":1,"field":"timestamp"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"bsonType":"object","oneOf":[{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"brand":{"bsonType":"string"},"exitMethod":{"bsonType":["null","string"]},"productId":{"bsonType":"string"},"sessionId":{"bsonType":"string"},"subCategory":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"enum":["view-product"]},"userId":{"bsonType":"string"}},"required":["brand","productId","subCategory","type"]},{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"query":{"bsonType":"string"},"sessionId":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"enum":["search"]},"userId":{"bsonType":"string"}},"required":["query","type"]},{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"brand":{"bsonType":"string"},"productId":{"bsonType":"string"},"sessionId":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"enum":["add-to-cart"]},"userId":{"bsonType":"string"}},"required":["brand","productId","type"]}],"properties":{"_id":{"bsonType":"objectId"},"sessionId":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"bsonType":"string"},"userId":{"bsonType":"string"}},"required":["_id","sessionId","timestamp","type","userId"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"invoices":{"indexes":[{"keys":[{"direction":1,"field":"orderId"}],"kind":"mongo-index"},{"keys":[{"direction":-1,"field":"issuedAt"}],"kind":"mongo-index","sparse":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"issuedAt":{"bsonType":"date"},"items":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"int"},"lineTotal":{"bsonType":"double"},"name":{"bsonType":"string"},"unitPrice":{"bsonType":"double"}},"required":["amount","lineTotal","name","unitPrice"]}},"orderId":{"bsonType":"objectId"},"subtotal":{"bsonType":"double"},"tax":{"bsonType":"double"},"total":{"bsonType":"double"}},"required":["_id","issuedAt","items","orderId","subtotal","tax","total"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"locations":{"indexes":[{"collation":{"locale":"en","strength":2},"keys":[{"direction":1,"field":"city"},{"direction":1,"field":"country"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"city":{"bsonType":"string"},"country":{"bsonType":"string"},"name":{"bsonType":"string"},"postalCode":{"bsonType":"string"},"streetAndNumber":{"bsonType":"string"}},"required":["_id","city","country","name","postalCode","streetAndNumber"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"orders":{"indexes":[{"keys":[{"direction":1,"field":"userId"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"items":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"int"},"brand":{"bsonType":"string"},"image":{"additionalProperties":false,"bsonType":"object","properties":{"url":{"bsonType":"string"}},"required":["url"]},"name":{"bsonType":"string"},"price":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"double"},"currency":{"bsonType":"string"}},"required":["amount","currency"]},"productId":{"bsonType":"string"}},"required":["amount","brand","image","name","price","productId"]}},"shippingAddress":{"bsonType":"string"},"statusHistory":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"status":{"bsonType":"string"},"timestamp":{"bsonType":"date"}},"required":["status","timestamp"]}},"type":{"bsonType":"string","enum":["delivery","pickup"]},"userId":{"bsonType":"objectId"}},"required":["_id","items","shippingAddress","statusHistory","type","userId"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"products":{"indexes":[{"keys":[{"direction":"text","field":"name"},{"direction":"text","field":"description"}],"kind":"mongo-index","weights":{"description":1,"name":10}},{"keys":[{"direction":1,"field":"brand"},{"direction":1,"field":"subCategory"}],"kind":"mongo-index"},{"keys":[{"direction":1,"field":"primaryCategory"},{"direction":1,"field":"articleType"}],"kind":"mongo-index"},{"keys":[{"direction":"hashed","field":"code"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"articleType":{"bsonType":"string"},"brand":{"bsonType":"string"},"code":{"bsonType":"string"},"description":{"bsonType":"string"},"embedding":{"bsonType":"array","items":{"bsonType":"double"}},"image":{"additionalProperties":false,"bsonType":"object","properties":{"url":{"bsonType":"string"}},"required":["url"]},"name":{"bsonType":"string"},"price":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"double"},"currency":{"bsonType":"string"}},"required":["amount","currency"]},"primaryCategory":{"bsonType":"string"},"status":{"bsonType":"string","enum":["active","discontinued","out-of-stock"]},"subCategory":{"bsonType":"string"}},"required":["_id","articleType","brand","code","description","image","name","price","primaryCategory","status","subCategory"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"users":{"indexes":[{"keys":[{"direction":1,"field":"email"}],"kind":"mongo-index","unique":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"address":{"oneOf":[{"bsonType":"null"},{"additionalProperties":false,"bsonType":"object","properties":{"city":{"bsonType":"string"},"country":{"bsonType":"string"},"postalCode":{"bsonType":"string"},"streetAndNumber":{"bsonType":"string"}},"required":["city","country","postalCode","streetAndNumber"]}]},"email":{"bsonType":"string"},"name":{"bsonType":"string"}},"required":["_id","email","name"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}}},"valueSet":{"OrderType":{"kind":"valueSet","values":["delivery","pickup"]},"ProductStatus":{"kind":"valueSet","values":["active","discontinued","out-of-stock"]}}},"id":"__unbound__","kind":"mongo-database"}},"storageHash":"bd938b4f8a10c688bd32dc61ec1dd808dcf34e725f08505b39ce365a39c97e1b"},"target":"mongo","targetFamily":"mongo"} diff --git a/examples/retail-store/migrations/snapshots/977a060afe52c4a56f93f2f33d65b8b6b4cc4ded04d16fe6b4f8e7e9e61192d3/contract.d.ts b/examples/retail-store/migrations/snapshots/c529f34639ba48ec978d133093cd95ac2a5a76196ea39b3fa71179a9ef2c3467/contract.d.ts similarity index 82% rename from examples/retail-store/migrations/snapshots/977a060afe52c4a56f93f2f33d65b8b6b4cc4ded04d16fe6b4f8e7e9e61192d3/contract.d.ts rename to examples/retail-store/migrations/snapshots/c529f34639ba48ec978d133093cd95ac2a5a76196ea39b3fa71179a9ef2c3467/contract.d.ts index 87331f9dc03d..419f49b78331 100644 --- a/examples/retail-store/migrations/snapshots/977a060afe52c4a56f93f2f33d65b8b6b4cc4ded04d16fe6b4f8e7e9e61192d3/contract.d.ts +++ b/examples/retail-store/migrations/snapshots/c529f34639ba48ec978d133093cd95ac2a5a76196ea39b3fa71179a9ef2c3467/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@prisma/orm-mongo/contract/types'; export type StorageHash = - StorageHashBase<'977a060afe52c4a56f93f2f33d65b8b6b4cc4ded04d16fe6b4f8e7e9e61192d3'>; + StorageHashBase<'c529f34639ba48ec978d133093cd95ac2a5a76196ea39b3fa71179a9ef2c3467'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -65,14 +65,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -137,11 +139,13 @@ export namespace Models { brand: FieldOutputTypes['__unbound__']['Product']['brand']; code: FieldOutputTypes['__unbound__']['Product']['code']; description: FieldOutputTypes['__unbound__']['Product']['description']; + embedding: FieldOutputTypes['__unbound__']['Product']['embedding']; id: FieldOutputTypes['__unbound__']['Product']['id']; image: FieldOutputTypes['__unbound__']['Product']['image']; name: FieldOutputTypes['__unbound__']['Product']['name']; price: FieldOutputTypes['__unbound__']['Product']['price']; primaryCategory: FieldOutputTypes['__unbound__']['Product']['primaryCategory']; + status: FieldOutputTypes['__unbound__']['Product']['status']; subCategory: FieldOutputTypes['__unbound__']['Product']['subCategory']; readonly [RelationKeys]?: never; }; @@ -554,6 +558,10 @@ type ContractBase = Omit< readonly brand: { readonly bsonType: 'string' }; readonly code: { readonly bsonType: 'string' }; readonly description: { readonly bsonType: 'string' }; + readonly embedding: { + readonly bsonType: readonly ['null', 'array']; + readonly items: { readonly bsonType: 'double' }; + }; readonly image: { readonly additionalProperties: false; readonly bsonType: 'object'; @@ -571,6 +579,7 @@ type ContractBase = Omit< readonly required: readonly ['amount', 'currency']; }; readonly primaryCategory: { readonly bsonType: 'string' }; + readonly status: { readonly bsonType: 'string' }; readonly subCategory: { readonly bsonType: 'string' }; }; readonly required: readonly [ @@ -583,6 +592,7 @@ type ContractBase = Omit< 'name', 'price', 'primaryCategory', + 'status', 'subCategory', ]; }; @@ -638,6 +648,160 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly postalCode: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly streetAndNumber: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + readonly CartItem: { + readonly fields: { + readonly amount: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; + }; + readonly brand: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly image: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Image'; + }; + }; + readonly name: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly price: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Price'; + }; + }; + readonly productId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + readonly Image: { + readonly fields: { + readonly url: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + readonly InvoiceLineItem: { + readonly fields: { + readonly amount: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; + }; + readonly lineTotal: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; + }; + readonly name: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly unitPrice: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; + }; + }; + }; + readonly OrderLineItem: { + readonly fields: { + readonly amount: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; + }; + readonly brand: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly image: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Image'; + }; + }; + readonly name: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly price: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Price'; + }; + }; + readonly productId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + readonly Price: { + readonly fields: { + readonly amount: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; + }; + readonly currency: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + readonly StatusEntry: { + readonly fields: { + readonly status: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly timestamp: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; + }; + }; + }; + }; }; }; }; @@ -701,7 +865,13 @@ type ContractBase = Omit< }; readonly items: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CartItem' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CartItem'; + }; readonly many: { readonly elementNullable: false }; }; readonly userId: { @@ -794,7 +964,13 @@ type ContractBase = Omit< }; readonly items: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'InvoiceLineItem' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'InvoiceLineItem'; + }; readonly many: { readonly elementNullable: false }; }; readonly orderId: { @@ -889,7 +1065,13 @@ type ContractBase = Omit< }; readonly items: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'OrderLineItem' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'OrderLineItem'; + }; readonly many: { readonly elementNullable: false }; }; readonly shippingAddress: { @@ -898,7 +1080,13 @@ type ContractBase = Omit< }; readonly statusHistory: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'StatusEntry' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'StatusEntry'; + }; readonly many: { readonly elementNullable: false }; }; readonly type: { @@ -965,13 +1153,24 @@ type ContractBase = Omit< readonly nullable: false; readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; }; + readonly embedding: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; + readonly many: { readonly elementNullable: false }; + }; readonly id: { readonly nullable: false; readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; }; readonly image: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Image'; + }; }; readonly name: { readonly nullable: false; @@ -979,12 +1178,22 @@ type ContractBase = Omit< }; readonly price: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Price'; + }; }; readonly primaryCategory: { readonly nullable: false; readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; }; + readonly status: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; readonly subCategory: { readonly nullable: false; readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; @@ -998,11 +1207,13 @@ type ContractBase = Omit< readonly brand: { readonly field: 'brand' }; readonly code: { readonly field: 'code' }; readonly description: { readonly field: 'description' }; + readonly embedding: { readonly field: 'embedding' }; readonly id: { readonly field: '_id' }; readonly image: { readonly field: 'image' }; readonly name: { readonly field: 'name' }; readonly price: { readonly field: 'price' }; readonly primaryCategory: { readonly field: 'primaryCategory' }; + readonly status: { readonly field: 'status' }; readonly subCategory: { readonly field: 'subCategory' }; }; }; @@ -1028,7 +1239,13 @@ type ContractBase = Omit< readonly fields: { readonly address: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; }; readonly email: { readonly nullable: false; @@ -1112,272 +1329,12 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly postalCode: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly streetAndNumber: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly CartItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly image: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly price: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Image: { - readonly fields: { - readonly url: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly InvoiceLineItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly lineTotal: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly unitPrice: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - }; - }; - readonly OrderLineItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly image: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly price: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Price: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly currency: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly StatusEntry: { - readonly fields: { - readonly status: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly timestamp: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; - }; - }; - }; - }; }; }; }; readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly postalCode: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly streetAndNumber: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly CartItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly image: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly price: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Image: { - readonly fields: { - readonly url: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly InvoiceLineItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly lineTotal: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly unitPrice: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - }; - }; - readonly OrderLineItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly image: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly price: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Price: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly currency: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly StatusEntry: { - readonly fields: { - readonly status: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly timestamp: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/examples/retail-store/migrations/snapshots/c529f34639ba48ec978d133093cd95ac2a5a76196ea39b3fa71179a9ef2c3467/contract.json b/examples/retail-store/migrations/snapshots/c529f34639ba48ec978d133093cd95ac2a5a76196ea39b3fa71179a9ef2c3467/contract.json new file mode 100644 index 000000000000..ece0d2834161 --- /dev/null +++ b/examples/retail-store/migrations/snapshots/c529f34639ba48ec978d133093cd95ac2a5a76196ea39b3fa71179a9ef2c3467/contract.json @@ -0,0 +1 @@ +{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{},"domain":{"namespaces":{"__unbound__":{"models":{"AddToCartEvent":{"base":{"model":"Event","namespace":"__unbound__"},"fields":{"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events","fields":{"brand":{"field":"brand"},"productId":{"field":"productId"}}}},"Cart":{"fields":{"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"items":{"many":{"elementNullable":false},"nullable":false,"type":{"entityKind":"valueObject","entityName":"CartItem","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"userId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}}},"relations":{"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"__unbound__"}}},"storage":{"collection":"carts","fields":{"id":{"field":"_id"},"items":{"field":"items"},"userId":{"field":"userId"}}}},"Event":{"discriminator":{"field":"type"},"fields":{"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"sessionId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"timestamp":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}},"type":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events","fields":{"id":{"field":"_id"},"sessionId":{"field":"sessionId"},"timestamp":{"field":"timestamp"},"type":{"field":"type"},"userId":{"field":"userId"}}},"variants":{"add-to-cart":{"model":"AddToCartEvent","namespace":"__unbound__"},"search":{"model":"SearchEvent","namespace":"__unbound__"},"view-product":{"model":"ViewProductEvent","namespace":"__unbound__"}}},"Invoice":{"fields":{"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"issuedAt":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}},"items":{"many":{"elementNullable":false},"nullable":false,"type":{"entityKind":"valueObject","entityName":"InvoiceLineItem","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"orderId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"subtotal":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"tax":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"total":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}}},"relations":{"order":{"cardinality":"N:1","nullable":false,"on":{"localFields":["orderId"],"targetFields":["id"]},"to":{"model":"Order","namespace":"__unbound__"}}},"storage":{"collection":"invoices","fields":{"id":{"field":"_id"},"issuedAt":{"field":"issuedAt"},"items":{"field":"items"},"orderId":{"field":"orderId"},"subtotal":{"field":"subtotal"},"tax":{"field":"tax"},"total":{"field":"total"}}}},"Location":{"fields":{"city":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"postalCode":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"streetAndNumber":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"locations","fields":{"city":{"field":"city"},"country":{"field":"country"},"id":{"field":"_id"},"name":{"field":"name"},"postalCode":{"field":"postalCode"},"streetAndNumber":{"field":"streetAndNumber"}}}},"Order":{"fields":{"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"items":{"many":{"elementNullable":false},"nullable":false,"type":{"entityKind":"valueObject","entityName":"OrderLineItem","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"shippingAddress":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"statusHistory":{"many":{"elementNullable":false},"nullable":false,"type":{"entityKind":"valueObject","entityName":"StatusEntry","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"type":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}}},"relations":{"invoices":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["orderId"]},"to":{"model":"Invoice","namespace":"__unbound__"}},"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"__unbound__"}}},"storage":{"collection":"orders","fields":{"id":{"field":"_id"},"items":{"field":"items"},"shippingAddress":{"field":"shippingAddress"},"statusHistory":{"field":"statusHistory"},"type":{"field":"type"},"userId":{"field":"userId"}}}},"Product":{"fields":{"articleType":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"code":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"description":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"embedding":{"many":{"elementNullable":false},"nullable":true,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"image":{"nullable":false,"type":{"entityKind":"valueObject","entityName":"Image","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"price":{"nullable":false,"type":{"entityKind":"valueObject","entityName":"Price","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"primaryCategory":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"status":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"subCategory":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"products","fields":{"articleType":{"field":"articleType"},"brand":{"field":"brand"},"code":{"field":"code"},"description":{"field":"description"},"embedding":{"field":"embedding"},"id":{"field":"_id"},"image":{"field":"image"},"name":{"field":"name"},"price":{"field":"price"},"primaryCategory":{"field":"primaryCategory"},"status":{"field":"status"},"subCategory":{"field":"subCategory"}}}},"SearchEvent":{"base":{"model":"Event","namespace":"__unbound__"},"fields":{"query":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events","fields":{"query":{"field":"query"}}}},"User":{"fields":{"address":{"nullable":true,"type":{"entityKind":"valueObject","entityName":"Address","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"email":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{"carts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Cart","namespace":"__unbound__"}},"orders":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Order","namespace":"__unbound__"}}},"storage":{"collection":"users","fields":{"address":{"field":"address"},"email":{"field":"email"},"id":{"field":"_id"},"name":{"field":"name"}}}},"ViewProductEvent":{"base":{"model":"Event","namespace":"__unbound__"},"fields":{"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"exitMethod":{"nullable":true,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"subCategory":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"events","fields":{"brand":{"field":"brand"},"exitMethod":{"field":"exitMethod"},"productId":{"field":"productId"},"subCategory":{"field":"subCategory"}}}}}}}},"extensions":{},"meta":{},"profileHash":"251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3","roots":{"carts":{"model":"Cart","namespace":"__unbound__"},"events":{"model":"Event","namespace":"__unbound__"},"invoices":{"model":"Invoice","namespace":"__unbound__"},"locations":{"model":"Location","namespace":"__unbound__"},"orders":{"model":"Order","namespace":"__unbound__"},"products":{"model":"Product","namespace":"__unbound__"},"users":{"model":"User","namespace":"__unbound__"}},"schemaVersion":"1","storage":{"namespaces":{"__unbound__":{"entries":{"collection":{"carts":{"indexes":[{"keys":[{"direction":1,"field":"userId"}],"kind":"mongo-index","unique":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"items":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"int"},"brand":{"bsonType":"string"},"image":{"additionalProperties":false,"bsonType":"object","properties":{"url":{"bsonType":"string"}},"required":["url"]},"name":{"bsonType":"string"},"price":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"double"},"currency":{"bsonType":"string"}},"required":["amount","currency"]},"productId":{"bsonType":"string"}},"required":["amount","brand","image","name","price","productId"]}},"userId":{"bsonType":"objectId"}},"required":["_id","items","userId"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"events":{"indexes":[{"keys":[{"direction":1,"field":"userId"},{"direction":-1,"field":"timestamp"}],"kind":"mongo-index"},{"expireAfterSeconds":7776000,"keys":[{"direction":1,"field":"timestamp"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"bsonType":"object","oneOf":[{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"brand":{"bsonType":"string"},"exitMethod":{"bsonType":["null","string"]},"productId":{"bsonType":"string"},"sessionId":{"bsonType":"string"},"subCategory":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"enum":["view-product"]},"userId":{"bsonType":"string"}},"required":["brand","productId","subCategory","type"]},{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"query":{"bsonType":"string"},"sessionId":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"enum":["search"]},"userId":{"bsonType":"string"}},"required":["query","type"]},{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"brand":{"bsonType":"string"},"productId":{"bsonType":"string"},"sessionId":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"enum":["add-to-cart"]},"userId":{"bsonType":"string"}},"required":["brand","productId","type"]}],"properties":{"_id":{"bsonType":"objectId"},"sessionId":{"bsonType":"string"},"timestamp":{"bsonType":"date"},"type":{"bsonType":"string"},"userId":{"bsonType":"string"}},"required":["_id","sessionId","timestamp","type","userId"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"invoices":{"indexes":[{"keys":[{"direction":1,"field":"orderId"}],"kind":"mongo-index"},{"keys":[{"direction":-1,"field":"issuedAt"}],"kind":"mongo-index","sparse":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"issuedAt":{"bsonType":"date"},"items":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"int"},"lineTotal":{"bsonType":"double"},"name":{"bsonType":"string"},"unitPrice":{"bsonType":"double"}},"required":["amount","lineTotal","name","unitPrice"]}},"orderId":{"bsonType":"objectId"},"subtotal":{"bsonType":"double"},"tax":{"bsonType":"double"},"total":{"bsonType":"double"}},"required":["_id","issuedAt","items","orderId","subtotal","tax","total"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"locations":{"indexes":[{"collation":{"locale":"en","strength":2},"keys":[{"direction":1,"field":"city"},{"direction":1,"field":"country"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"city":{"bsonType":"string"},"country":{"bsonType":"string"},"name":{"bsonType":"string"},"postalCode":{"bsonType":"string"},"streetAndNumber":{"bsonType":"string"}},"required":["_id","city","country","name","postalCode","streetAndNumber"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"orders":{"indexes":[{"keys":[{"direction":1,"field":"userId"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"items":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"int"},"brand":{"bsonType":"string"},"image":{"additionalProperties":false,"bsonType":"object","properties":{"url":{"bsonType":"string"}},"required":["url"]},"name":{"bsonType":"string"},"price":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"double"},"currency":{"bsonType":"string"}},"required":["amount","currency"]},"productId":{"bsonType":"string"}},"required":["amount","brand","image","name","price","productId"]}},"shippingAddress":{"bsonType":"string"},"statusHistory":{"bsonType":"array","items":{"additionalProperties":false,"bsonType":"object","properties":{"status":{"bsonType":"string"},"timestamp":{"bsonType":"date"}},"required":["status","timestamp"]}},"type":{"bsonType":"string"},"userId":{"bsonType":"objectId"}},"required":["_id","items","shippingAddress","statusHistory","type","userId"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"products":{"indexes":[{"keys":[{"direction":"text","field":"name"},{"direction":"text","field":"description"}],"kind":"mongo-index","weights":{"description":1,"name":10}},{"keys":[{"direction":1,"field":"brand"},{"direction":1,"field":"subCategory"}],"kind":"mongo-index"},{"keys":[{"direction":1,"field":"primaryCategory"},{"direction":1,"field":"articleType"}],"kind":"mongo-index"},{"keys":[{"direction":"hashed","field":"code"}],"kind":"mongo-index"}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"articleType":{"bsonType":"string"},"brand":{"bsonType":"string"},"code":{"bsonType":"string"},"description":{"bsonType":"string"},"embedding":{"bsonType":["null","array"],"items":{"bsonType":"double"}},"image":{"additionalProperties":false,"bsonType":"object","properties":{"url":{"bsonType":"string"}},"required":["url"]},"name":{"bsonType":"string"},"price":{"additionalProperties":false,"bsonType":"object","properties":{"amount":{"bsonType":"double"},"currency":{"bsonType":"string"}},"required":["amount","currency"]},"primaryCategory":{"bsonType":"string"},"status":{"bsonType":"string"},"subCategory":{"bsonType":"string"}},"required":["_id","articleType","brand","code","description","image","name","price","primaryCategory","status","subCategory"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"users":{"indexes":[{"keys":[{"direction":1,"field":"email"}],"kind":"mongo-index","unique":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"address":{"oneOf":[{"bsonType":"null"},{"additionalProperties":false,"bsonType":"object","properties":{"city":{"bsonType":"string"},"country":{"bsonType":"string"},"postalCode":{"bsonType":"string"},"streetAndNumber":{"bsonType":"string"}},"required":["city","country","postalCode","streetAndNumber"]}]},"email":{"bsonType":"string"},"name":{"bsonType":"string"}},"required":["_id","email","name"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}}},"valueObject":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"postalCode":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"streetAndNumber":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"CartItem":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}},"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"image":{"nullable":false,"type":{"entityKind":"valueObject","entityName":"Image","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"price":{"nullable":false,"type":{"entityKind":"valueObject","entityName":"Price","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"Image":{"fields":{"url":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"InvoiceLineItem":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}},"lineTotal":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"unitPrice":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}}}},"OrderLineItem":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}},"brand":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"image":{"nullable":false,"type":{"entityKind":"valueObject","entityName":"Image","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"price":{"nullable":false,"type":{"entityKind":"valueObject","entityName":"Price","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"productId":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"Price":{"fields":{"amount":{"nullable":false,"type":{"codecId":"mongo/double@1","kind":"scalar"}},"currency":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}},"StatusEntry":{"fields":{"status":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"timestamp":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}}}}}},"id":"__unbound__","kind":"mongo-database"}},"storageHash":"c529f34639ba48ec978d133093cd95ac2a5a76196ea39b3fa71179a9ef2c3467"},"target":"mongo","targetFamily":"mongo"} diff --git a/examples/retail-store/src/contract.d.ts b/examples/retail-store/src/contract.d.ts index 4bf316302d46..1d1e6c05a177 100644 --- a/examples/retail-store/src/contract.d.ts +++ b/examples/retail-store/src/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@prisma/orm-mongo/contract/types'; export type StorageHash = - StorageHashBase<'41bd5540ea833c009b5eacf6db7d489e88bdd5062554398ba8aa2657fa19d27e'>; + StorageHashBase<'2f0e67cbc2a193e280f07c26972a484af4b62d3472c018c73bfd095749d4d0f5'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -82,14 +82,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -669,6 +671,160 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly postalCode: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly streetAndNumber: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + readonly CartItem: { + readonly fields: { + readonly amount: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; + }; + readonly brand: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly image: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Image'; + }; + }; + readonly name: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly price: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Price'; + }; + }; + readonly productId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + readonly Image: { + readonly fields: { + readonly url: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + readonly InvoiceLineItem: { + readonly fields: { + readonly amount: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; + }; + readonly lineTotal: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; + }; + readonly name: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly unitPrice: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; + }; + }; + }; + readonly OrderLineItem: { + readonly fields: { + readonly amount: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; + }; + readonly brand: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly image: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Image'; + }; + }; + readonly name: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly price: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Price'; + }; + }; + readonly productId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + readonly Price: { + readonly fields: { + readonly amount: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; + }; + readonly currency: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + readonly StatusEntry: { + readonly fields: { + readonly status: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly timestamp: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; + }; + }; + }; + }; }; }; }; @@ -732,7 +888,13 @@ type ContractBase = Omit< }; readonly items: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CartItem' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CartItem'; + }; readonly many: { readonly elementNullable: false }; }; readonly userId: { @@ -825,7 +987,13 @@ type ContractBase = Omit< }; readonly items: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'InvoiceLineItem' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'InvoiceLineItem'; + }; readonly many: { readonly elementNullable: false }; }; readonly orderId: { @@ -920,7 +1088,13 @@ type ContractBase = Omit< }; readonly items: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'OrderLineItem' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'OrderLineItem'; + }; readonly many: { readonly elementNullable: false }; }; readonly shippingAddress: { @@ -929,7 +1103,13 @@ type ContractBase = Omit< }; readonly statusHistory: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'StatusEntry' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'StatusEntry'; + }; readonly many: { readonly elementNullable: false }; }; readonly type: { @@ -1007,7 +1187,13 @@ type ContractBase = Omit< }; readonly image: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Image'; + }; }; readonly name: { readonly nullable: false; @@ -1015,7 +1201,13 @@ type ContractBase = Omit< }; readonly price: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Price'; + }; }; readonly primaryCategory: { readonly nullable: false; @@ -1070,7 +1262,13 @@ type ContractBase = Omit< readonly fields: { readonly address: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; }; readonly email: { readonly nullable: false; @@ -1154,136 +1352,6 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly postalCode: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly streetAndNumber: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly CartItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly image: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly price: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Image: { - readonly fields: { - readonly url: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly InvoiceLineItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly lineTotal: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly unitPrice: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - }; - }; - readonly OrderLineItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly image: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly price: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Price: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly currency: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly StatusEntry: { - readonly fields: { - readonly status: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly timestamp: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; - }; - }; - }; - }; readonly enum: { readonly OrderType: { readonly codecId: 'mongo/string@1'; @@ -1318,136 +1386,6 @@ type ContractBase = Omit< readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly postalCode: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly streetAndNumber: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly CartItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly image: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly price: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Image: { - readonly fields: { - readonly url: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly InvoiceLineItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly lineTotal: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly unitPrice: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - }; - }; - readonly OrderLineItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly image: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly price: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Price: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly currency: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly StatusEntry: { - readonly fields: { - readonly status: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly timestamp: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/examples/retail-store/src/contract.json b/examples/retail-store/src/contract.json index bbd2f0a2193b..0b7f73438f44 100644 --- a/examples/retail-store/src/contract.json +++ b/examples/retail-store/src/contract.json @@ -118,8 +118,11 @@ }, "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "CartItem", "kind": "valueObject", - "name": "CartItem" + "namespaceId": "__unbound__", + "plane": "storage" } }, "userId": { @@ -262,8 +265,11 @@ }, "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "InvoiceLineItem", "kind": "valueObject", - "name": "InvoiceLineItem" + "namespaceId": "__unbound__", + "plane": "storage" } }, "orderId": { @@ -425,8 +431,11 @@ }, "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "OrderLineItem", "kind": "valueObject", - "name": "OrderLineItem" + "namespaceId": "__unbound__", + "plane": "storage" } }, "shippingAddress": { @@ -442,8 +451,11 @@ }, "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "StatusEntry", "kind": "valueObject", - "name": "StatusEntry" + "namespaceId": "__unbound__", + "plane": "storage" } }, "type": { @@ -574,8 +586,11 @@ "image": { "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "Image", "kind": "valueObject", - "name": "Image" + "namespaceId": "__unbound__", + "plane": "storage" } }, "name": { @@ -588,8 +603,11 @@ "price": { "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "Price", "kind": "valueObject", - "name": "Price" + "namespaceId": "__unbound__", + "plane": "storage" } }, "primaryCategory": { @@ -692,8 +710,11 @@ "address": { "nullable": true, "type": { + "entityKind": "valueObject", + "entityName": "Address", "kind": "valueObject", - "name": "Address" + "namespaceId": "__unbound__", + "plane": "storage" } }, "email": { @@ -822,211 +843,6 @@ } } } - }, - "valueObjects": { - "Address": { - "fields": { - "city": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "country": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "postalCode": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "streetAndNumber": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - } - } - }, - "CartItem": { - "fields": { - "amount": { - "nullable": false, - "type": { - "codecId": "mongo/int32@1", - "kind": "scalar" - } - }, - "brand": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "image": { - "nullable": false, - "type": { - "kind": "valueObject", - "name": "Image" - } - }, - "name": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "price": { - "nullable": false, - "type": { - "kind": "valueObject", - "name": "Price" - } - }, - "productId": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - } - } - }, - "Image": { - "fields": { - "url": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - } - } - }, - "InvoiceLineItem": { - "fields": { - "amount": { - "nullable": false, - "type": { - "codecId": "mongo/int32@1", - "kind": "scalar" - } - }, - "lineTotal": { - "nullable": false, - "type": { - "codecId": "mongo/double@1", - "kind": "scalar" - } - }, - "name": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "unitPrice": { - "nullable": false, - "type": { - "codecId": "mongo/double@1", - "kind": "scalar" - } - } - } - }, - "OrderLineItem": { - "fields": { - "amount": { - "nullable": false, - "type": { - "codecId": "mongo/int32@1", - "kind": "scalar" - } - }, - "brand": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "image": { - "nullable": false, - "type": { - "kind": "valueObject", - "name": "Image" - } - }, - "name": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "price": { - "nullable": false, - "type": { - "kind": "valueObject", - "name": "Price" - } - }, - "productId": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - } - } - }, - "Price": { - "fields": { - "amount": { - "nullable": false, - "type": { - "codecId": "mongo/double@1", - "kind": "scalar" - } - }, - "currency": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - } - } - }, - "StatusEntry": { - "fields": { - "status": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "timestamp": { - "nullable": false, - "type": { - "codecId": "mongo/date@1", - "kind": "scalar" - } - } - } - } } } } @@ -1792,6 +1608,223 @@ } } }, + "valueObject": { + "Address": { + "fields": { + "city": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "country": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "postalCode": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "streetAndNumber": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + } + } + }, + "CartItem": { + "fields": { + "amount": { + "nullable": false, + "type": { + "codecId": "mongo/int32@1", + "kind": "scalar" + } + }, + "brand": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "image": { + "nullable": false, + "type": { + "entityKind": "valueObject", + "entityName": "Image", + "kind": "valueObject", + "namespaceId": "__unbound__", + "plane": "storage" + } + }, + "name": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "price": { + "nullable": false, + "type": { + "entityKind": "valueObject", + "entityName": "Price", + "kind": "valueObject", + "namespaceId": "__unbound__", + "plane": "storage" + } + }, + "productId": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + } + } + }, + "Image": { + "fields": { + "url": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + } + } + }, + "InvoiceLineItem": { + "fields": { + "amount": { + "nullable": false, + "type": { + "codecId": "mongo/int32@1", + "kind": "scalar" + } + }, + "lineTotal": { + "nullable": false, + "type": { + "codecId": "mongo/double@1", + "kind": "scalar" + } + }, + "name": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "unitPrice": { + "nullable": false, + "type": { + "codecId": "mongo/double@1", + "kind": "scalar" + } + } + } + }, + "OrderLineItem": { + "fields": { + "amount": { + "nullable": false, + "type": { + "codecId": "mongo/int32@1", + "kind": "scalar" + } + }, + "brand": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "image": { + "nullable": false, + "type": { + "entityKind": "valueObject", + "entityName": "Image", + "kind": "valueObject", + "namespaceId": "__unbound__", + "plane": "storage" + } + }, + "name": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "price": { + "nullable": false, + "type": { + "entityKind": "valueObject", + "entityName": "Price", + "kind": "valueObject", + "namespaceId": "__unbound__", + "plane": "storage" + } + }, + "productId": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + } + } + }, + "Price": { + "fields": { + "amount": { + "nullable": false, + "type": { + "codecId": "mongo/double@1", + "kind": "scalar" + } + }, + "currency": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + } + } + }, + "StatusEntry": { + "fields": { + "status": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "timestamp": { + "nullable": false, + "type": { + "codecId": "mongo/date@1", + "kind": "scalar" + } + } + } + } + }, "valueSet": { "OrderType": { "kind": "valueSet", @@ -1814,7 +1847,7 @@ "kind": "mongo-database" } }, - "storageHash": "41bd5540ea833c009b5eacf6db7d489e88bdd5062554398ba8aa2657fa19d27e" + "storageHash": "2f0e67cbc2a193e280f07c26972a484af4b62d3472c018c73bfd095749d4d0f5" }, "capabilities": {}, "extensions": {}, diff --git a/examples/supabase/src/contract.d.ts b/examples/supabase/src/contract.d.ts index 3173043eec06..db1c24bd8d8a 100644 --- a/examples/supabase/src/contract.d.ts +++ b/examples/supabase/src/contract.d.ts @@ -428,7 +428,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/test/fixtures/orm-contract.d.ts b/packages/2-mongo-family/1-foundation/mongo-contract/test/fixtures/orm-contract.d.ts index 84f2acd997e1..10e3d96664d2 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/test/fixtures/orm-contract.d.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/test/fixtures/orm-contract.d.ts @@ -1,7 +1,7 @@ // ⚠️ GENERATED FILE - DO NOT EDIT // This file is automatically generated by 'prisma contract emit'. // To regenerate, run: prisma contract emit -import type { CodecTypes as MongoCodecTypes } from '@internal/target-mongo/codec-types'; +import type { CodecTypes as MongoCodecTypes } from '../../../../../3-targets/mongo/1-target/src/exports/codec-types'; import type { MongoCollection, @@ -12,143 +12,79 @@ import type { import type { Contract as ContractType, ExecutionHashBase, + FieldTypesFromDomain, NamespaceId, ProfileHashBase, StorageHashBase, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; -export type StorageHash = StorageHashBase<'orm-storage'>; +export type StorageHash = StorageHashBase<"orm-storage">; export type ExecutionHash = ExecutionHashBase; -export type ProfileHash = ProfileHashBase<'orm-profile'>; +export type ProfileHash = ProfileHashBase<"orm-profile">; export type CodecTypes = MongoCodecTypes; -export type HomeAddressOutput = { - readonly city: CodecTypes['mongo/string@1']['output']; - readonly country: CodecTypes['mongo/string@1']['output']; -}; -export type HomeAddressInput = { - readonly city: CodecTypes['mongo/string@1']['input']; - readonly country: CodecTypes['mongo/string@1']['input']; -}; -export type FieldOutputTypes = { - readonly __unbound__: { - readonly Address: { - readonly street: CodecTypes['mongo/string@1']['output']; - readonly city: CodecTypes['mongo/string@1']['output']; - readonly zip: CodecTypes['mongo/string@1']['output']; - }; - readonly Bug: { readonly severity: CodecTypes['mongo/string@1']['output'] }; - readonly Comment: { - readonly _id: CodecTypes['mongo/objectId@1']['output']; - readonly text: CodecTypes['mongo/string@1']['output']; - readonly createdAt: CodecTypes['mongo/date@1']['output']; - }; - readonly Feature: { - readonly priority: CodecTypes['mongo/string@1']['output']; - readonly targetRelease: CodecTypes['mongo/string@1']['output']; - }; - readonly Task: { - readonly _id: CodecTypes['mongo/objectId@1']['output']; - readonly title: CodecTypes['mongo/string@1']['output']; - readonly type: CodecTypes['mongo/string@1']['output']; - readonly assigneeId: CodecTypes['mongo/objectId@1']['output']; - }; - readonly User: { - readonly _id: CodecTypes['mongo/objectId@1']['output']; - readonly name: CodecTypes['mongo/string@1']['output']; - readonly email: CodecTypes['mongo/string@1']['output']; - readonly loginCount: CodecTypes['mongo/int32@1']['output']; - readonly tags: ReadonlyArray; - readonly homeAddress: HomeAddressOutput | null; - }; - }; -}; -export type FieldInputTypes = { - readonly __unbound__: { - readonly Address: { - readonly street: CodecTypes['mongo/string@1']['input']; - readonly city: CodecTypes['mongo/string@1']['input']; - readonly zip: CodecTypes['mongo/string@1']['input']; - }; - readonly Bug: { readonly severity: CodecTypes['mongo/string@1']['input'] }; - readonly Comment: { - readonly _id: CodecTypes['mongo/objectId@1']['input']; - readonly text: CodecTypes['mongo/string@1']['input']; - readonly createdAt: CodecTypes['mongo/date@1']['input']; - }; - readonly Feature: { - readonly priority: CodecTypes['mongo/string@1']['input']; - readonly targetRelease: CodecTypes['mongo/string@1']['input']; - }; - readonly Task: { - readonly _id: CodecTypes['mongo/objectId@1']['input']; - readonly title: CodecTypes['mongo/string@1']['input']; - readonly type: CodecTypes['mongo/string@1']['input']; - readonly assigneeId: CodecTypes['mongo/objectId@1']['input']; - }; - readonly User: { - readonly _id: CodecTypes['mongo/objectId@1']['input']; - readonly name: CodecTypes['mongo/string@1']['input']; - readonly email: CodecTypes['mongo/string@1']['input']; - readonly loginCount: CodecTypes['mongo/int32@1']['input']; - readonly tags: ReadonlyArray; - readonly homeAddress: HomeAddressInput | null; - }; - }; -}; +type ParameterizedOutputTypeFunctions = never; +type ValueObjectOutputTypes = ValueObjectTypesFromStorage; +type ValueObjectInputTypes = ValueObjectTypesFromStorage; +export type HomeAddressOutput = ValueObjectOutputTypes["__unbound__"]["HomeAddress"]; +export type HomeAddressInput = ValueObjectInputTypes["__unbound__"]["HomeAddress"]; +type FieldTypeOverrides = { }; +export type FieldOutputTypes = FieldTypesFromDomain; +export type FieldInputTypes = FieldTypesFromDomain; export namespace Models { export type unbound_Task = { - _id: CodecTypes['mongo/objectId@1']['output']; - title: CodecTypes['mongo/string@1']['output']; - type: 'bug' | 'feature'; - assigneeId: CodecTypes['mongo/objectId@1']['output']; + _id: FieldOutputTypes["__unbound__"]["Task"]["_id"]; + title: FieldOutputTypes["__unbound__"]["Task"]["title"]; + type: "bug" | "feature"; + assigneeId: FieldOutputTypes["__unbound__"]["Task"]["assigneeId"]; comments: unbound_Comment[]; assignee: unbound_User; - readonly [RelationKeys]?: 'assignee'; + readonly [RelationKeys]?: "assignee"; }; export type unbound_Bug = { - _id: CodecTypes['mongo/objectId@1']['output']; - title: CodecTypes['mongo/string@1']['output']; - type: 'bug'; - assigneeId: CodecTypes['mongo/objectId@1']['output']; - severity: CodecTypes['mongo/string@1']['output']; + _id: FieldOutputTypes["__unbound__"]["Task"]["_id"]; + title: FieldOutputTypes["__unbound__"]["Task"]["title"]; + type: "bug"; + assigneeId: FieldOutputTypes["__unbound__"]["Task"]["assigneeId"]; + severity: FieldOutputTypes["__unbound__"]["Bug"]["severity"]; comments: unbound_Comment[]; assignee: unbound_User; - readonly [RelationKeys]?: 'assignee'; + readonly [RelationKeys]?: "assignee"; }; export type unbound_Feature = { - _id: CodecTypes['mongo/objectId@1']['output']; - title: CodecTypes['mongo/string@1']['output']; - type: 'feature'; - assigneeId: CodecTypes['mongo/objectId@1']['output']; - priority: CodecTypes['mongo/string@1']['output']; - targetRelease: CodecTypes['mongo/string@1']['output']; + _id: FieldOutputTypes["__unbound__"]["Task"]["_id"]; + title: FieldOutputTypes["__unbound__"]["Task"]["title"]; + type: "feature"; + assigneeId: FieldOutputTypes["__unbound__"]["Task"]["assigneeId"]; + priority: FieldOutputTypes["__unbound__"]["Feature"]["priority"]; + targetRelease: FieldOutputTypes["__unbound__"]["Feature"]["targetRelease"]; comments: unbound_Comment[]; assignee: unbound_User; - readonly [RelationKeys]?: 'assignee'; + readonly [RelationKeys]?: "assignee"; }; export type unbound_User = { - _id: CodecTypes['mongo/objectId@1']['output']; - name: CodecTypes['mongo/string@1']['output']; - email: CodecTypes['mongo/string@1']['output']; - loginCount: CodecTypes['mongo/int32@1']['output']; - tags: ReadonlyArray; - homeAddress: HomeAddressOutput | null; + _id: FieldOutputTypes["__unbound__"]["User"]["_id"]; + name: FieldOutputTypes["__unbound__"]["User"]["name"]; + email: FieldOutputTypes["__unbound__"]["User"]["email"]; + loginCount: FieldOutputTypes["__unbound__"]["User"]["loginCount"]; + tags: FieldOutputTypes["__unbound__"]["User"]["tags"]; + homeAddress: FieldOutputTypes["__unbound__"]["User"]["homeAddress"]; addresses: unbound_Address[]; tasks: unbound_AnyTask[]; - readonly [RelationKeys]?: 'tasks'; + readonly [RelationKeys]?: "tasks"; }; export type unbound_Address = { - street: CodecTypes['mongo/string@1']['output']; - city: CodecTypes['mongo/string@1']['output']; - zip: CodecTypes['mongo/string@1']['output']; + street: FieldOutputTypes["__unbound__"]["Address"]["street"]; + city: FieldOutputTypes["__unbound__"]["Address"]["city"]; + zip: FieldOutputTypes["__unbound__"]["Address"]["zip"]; }; export type unbound_Comment = { - _id: CodecTypes['mongo/objectId@1']['output']; - text: CodecTypes['mongo/string@1']['output']; - createdAt: CodecTypes['mongo/date@1']['output']; + _id: FieldOutputTypes["__unbound__"]["Comment"]["_id"]; + text: FieldOutputTypes["__unbound__"]["Comment"]["text"]; + createdAt: FieldOutputTypes["__unbound__"]["Comment"]["createdAt"]; }; export type unbound_AnyTask = unbound_Bug | unbound_Feature; } @@ -168,230 +104,22 @@ export declare const models: { export type TypeMaps = MongoTypeMaps; type ContractBase = Omit< - ContractType<{ - readonly namespaces: { - readonly __unbound__: { - readonly id: '__unbound__'; - readonly kind: 'mongo-namespace'; - readonly entries: { - readonly collection: { readonly tasks: MongoCollection; readonly users: MongoCollection }; - readonly valueObject: { - readonly HomeAddress: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - }; - }; - }; - }; - readonly storageHash: StorageHash; - }>, + ContractType<{ readonly namespaces: { readonly __unbound__: { readonly id: "__unbound__"; readonly kind: "mongo-namespace"; readonly entries: { readonly collection: { readonly tasks: MongoCollection; readonly users: MongoCollection }; readonly valueObject: { readonly HomeAddress: { readonly fields: { readonly city: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly country: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } } } } } } } }; readonly storageHash: StorageHash }>, 'roots' | 'domain' > & { - readonly target: 'mongo'; - readonly targetFamily: 'mongo'; - readonly roots: { - readonly tasks: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'Task' }; - readonly users: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'User' }; - }; + readonly target: "mongo"; + readonly targetFamily: "mongo"; + readonly roots: { readonly tasks: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Task" }; readonly users: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "User" } }; readonly domain: { readonly namespaces: { readonly __unbound__: { - readonly models: { - readonly Address: { - readonly fields: { - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly zip: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - readonly relations: Record; - readonly storage: { readonly fields: { readonly street: { readonly field: 'street' }; readonly city: { readonly field: 'city' }; readonly zip: { readonly field: 'zip' } } }; - readonly owner: 'User'; - }; - readonly Bug: { - readonly fields: { - readonly severity: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - readonly relations: Record; - readonly storage: { readonly collection: 'tasks'; readonly fields: { readonly severity: { readonly field: 'severity' } } }; - readonly base: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Task'; - }; - }; - readonly Comment: { - readonly fields: { - readonly _id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly createdAt: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; - }; - }; - readonly relations: Record; - readonly storage: { readonly fields: { readonly _id: { readonly field: '_id' }; readonly text: { readonly field: 'text' }; readonly createdAt: { readonly field: 'createdAt' } } }; - readonly owner: 'Task'; - }; - readonly Feature: { - readonly fields: { - readonly priority: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly targetRelease: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - readonly relations: Record; - readonly storage: { readonly collection: 'tasks'; readonly fields: { readonly priority: { readonly field: 'priority' }; readonly targetRelease: { readonly field: 'targetRelease' } } }; - readonly base: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Task'; - }; - }; - readonly Task: { - readonly fields: { - readonly _id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - readonly title: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly type: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly assigneeId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - }; - readonly relations: { - readonly assignee: { - readonly to: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'User'; - }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['assigneeId']; - readonly targetFields: readonly ['_id']; - }; - }; - readonly comments: { - readonly to: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Comment'; - }; - readonly cardinality: '1:N'; - }; - }; - readonly storage: { readonly collection: 'tasks'; readonly fields: { readonly _id: { readonly field: '_id' }; readonly title: { readonly field: 'title' }; readonly type: { readonly field: 'type' }; readonly assigneeId: { readonly field: 'assigneeId' } }; readonly relations: { readonly comments: { readonly field: 'comments' } } }; - readonly discriminator: { readonly field: 'type' }; - readonly variants: { - readonly bug: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Bug'; - }; - readonly feature: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Feature'; - }; - }; - }; - readonly User: { - readonly fields: { - readonly _id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly email: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly loginCount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly tags: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - readonly many: { readonly elementNullable: false }; - }; - readonly homeAddress: { - readonly nullable: true; - readonly type: { - readonly kind: 'valueObject'; - readonly plane: 'storage'; - readonly namespaceId: '__unbound__'; - readonly entityKind: 'valueObject'; - readonly entityName: 'HomeAddress'; - }; - }; - }; - readonly relations: { - readonly addresses: { - readonly to: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Address'; - }; - readonly cardinality: '1:N'; - }; - readonly tasks: { - readonly to: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Task'; - }; - readonly cardinality: '1:N'; - readonly on: { - readonly localFields: readonly ['_id']; - readonly targetFields: readonly ['assigneeId']; - }; - }; - }; - readonly storage: { readonly collection: 'users'; readonly fields: { readonly _id: { readonly field: '_id' }; readonly name: { readonly field: 'name' }; readonly email: { readonly field: 'email' }; readonly loginCount: { readonly field: 'loginCount' }; readonly tags: { readonly field: 'tags' }; readonly homeAddress: { readonly field: 'homeAddress' } }; readonly relations: { readonly addresses: { readonly field: 'addresses' } } }; - }; - }; + readonly models: { readonly Address: { readonly fields: { readonly street: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly city: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly zip: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } } }; readonly relations: Record; readonly storage: { readonly fields: { readonly street: { readonly field: "street" }; readonly city: { readonly field: "city" }; readonly zip: { readonly field: "zip" } } }; readonly owner: "User" }; readonly Bug: { readonly fields: { readonly severity: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } } }; readonly relations: Record; readonly storage: { readonly collection: "tasks"; readonly fields: { readonly severity: { readonly field: "severity" } } }; readonly base: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Task" } }; readonly Comment: { readonly fields: { readonly _id: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/objectId@1" } }; readonly text: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly createdAt: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/date@1" } } }; readonly relations: Record; readonly storage: { readonly fields: { readonly _id: { readonly field: "_id" }; readonly text: { readonly field: "text" }; readonly createdAt: { readonly field: "createdAt" } } }; readonly owner: "Task" }; readonly Feature: { readonly fields: { readonly priority: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly targetRelease: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } } }; readonly relations: Record; readonly storage: { readonly collection: "tasks"; readonly fields: { readonly priority: { readonly field: "priority" }; readonly targetRelease: { readonly field: "targetRelease" } } }; readonly base: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Task" } }; readonly Task: { readonly fields: { readonly _id: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/objectId@1" } }; readonly title: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly type: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly assigneeId: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/objectId@1" } } }; readonly relations: { readonly assignee: { readonly to: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "User" }; readonly cardinality: "N:1"; readonly nullable: false; readonly on: { readonly localFields: readonly ["assigneeId"]; readonly targetFields: readonly ["_id"] } }; readonly comments: { readonly to: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Comment" }; readonly cardinality: "1:N" } }; readonly storage: { readonly collection: "tasks"; readonly fields: { readonly _id: { readonly field: "_id" }; readonly title: { readonly field: "title" }; readonly type: { readonly field: "type" }; readonly assigneeId: { readonly field: "assigneeId" } }; readonly relations: { readonly comments: { readonly field: "comments" } } }; readonly discriminator: { readonly field: "type" }; readonly variants: { readonly bug: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Bug" }; readonly feature: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Feature" } } }; readonly User: { readonly fields: { readonly _id: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/objectId@1" } }; readonly name: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly email: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly loginCount: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/int32@1" } }; readonly tags: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" }; readonly many: { readonly elementNullable: false } }; readonly homeAddress: { readonly nullable: true; readonly type: { readonly kind: "valueObject"; readonly plane: "storage"; readonly namespaceId: "__unbound__"; readonly entityKind: "valueObject"; readonly entityName: "HomeAddress" } } }; readonly relations: { readonly addresses: { readonly to: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Address" }; readonly cardinality: "1:N" }; readonly tasks: { readonly to: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Task" }; readonly cardinality: "1:N"; readonly on: { readonly localFields: readonly ["_id"]; readonly targetFields: readonly ["assigneeId"] } } }; readonly storage: { readonly collection: "users"; readonly fields: { readonly _id: { readonly field: "_id" }; readonly name: { readonly field: "name" }; readonly email: { readonly field: "email" }; readonly loginCount: { readonly field: "loginCount" }; readonly tags: { readonly field: "tags" }; readonly homeAddress: { readonly field: "homeAddress" } }; readonly relations: { readonly addresses: { readonly field: "addresses" } } } } }; }; }; }; - readonly capabilities: {}; - readonly extensions: {}; - readonly meta: {}; + readonly capabilities: { }; + readonly extensions: { }; + readonly meta: { }; readonly profileHash: ProfileHash; }; diff --git a/packages/2-mongo-family/5-query-builders/orm/test/fixtures/stored-names-contract.d.ts b/packages/2-mongo-family/5-query-builders/orm/test/fixtures/stored-names-contract.d.ts index 989f1dd4760f..9d80ed3c8616 100644 --- a/packages/2-mongo-family/5-query-builders/orm/test/fixtures/stored-names-contract.d.ts +++ b/packages/2-mongo-family/5-query-builders/orm/test/fixtures/stored-names-contract.d.ts @@ -12,9 +12,11 @@ import type { import type { Contract as ContractType, ExecutionHashBase, + FieldTypesFromDomain, NamespaceId, ProfileHashBase, StorageHashBase, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; export type StorageHash = StorageHashBase<"stored-names-storage">; @@ -23,44 +25,48 @@ export type ProfileHash = ProfileHashBase<"stored-names-profile">; export type CodecTypes = MongoCodecTypes; -export type HomeAddressOutput = { readonly city: CodecTypes["mongo/string@1"]["output"] }; -export type HomeAddressInput = { readonly city: CodecTypes["mongo/string@1"]["input"] }; -export type FieldOutputTypes = { readonly __unbound__: { readonly Address: { readonly street: CodecTypes["mongo/string@1"]["output"]; readonly city: CodecTypes["mongo/string@1"]["output"] }; readonly Bug: { readonly severity: CodecTypes["mongo/string@1"]["output"] }; readonly Post: { readonly id: CodecTypes["mongo/objectId@1"]["output"]; readonly authorId: CodecTypes["mongo/objectId@1"]["output"]; readonly title: CodecTypes["mongo/string@1"]["output"] }; readonly Task: { readonly id: CodecTypes["mongo/objectId@1"]["output"]; readonly kind: CodecTypes["mongo/string@1"]["output"]; readonly title: CodecTypes["mongo/string@1"]["output"] }; readonly User: { readonly id: CodecTypes["mongo/objectId@1"]["output"]; readonly displayName: CodecTypes["mongo/string@1"]["output"]; readonly email: CodecTypes["mongo/string@1"]["output"] | null; readonly createdAt: CodecTypes["mongo/date@1"]["output"]; readonly home: HomeAddressOutput | null } } }; -export type FieldInputTypes = { readonly __unbound__: { readonly Address: { readonly street: CodecTypes["mongo/string@1"]["input"]; readonly city: CodecTypes["mongo/string@1"]["input"] }; readonly Bug: { readonly severity: CodecTypes["mongo/string@1"]["input"] }; readonly Post: { readonly id: CodecTypes["mongo/objectId@1"]["input"]; readonly authorId: CodecTypes["mongo/objectId@1"]["input"]; readonly title: CodecTypes["mongo/string@1"]["input"] }; readonly Task: { readonly id: CodecTypes["mongo/objectId@1"]["input"]; readonly kind: CodecTypes["mongo/string@1"]["input"]; readonly title: CodecTypes["mongo/string@1"]["input"] }; readonly User: { readonly id: CodecTypes["mongo/objectId@1"]["input"]; readonly displayName: CodecTypes["mongo/string@1"]["input"]; readonly email: CodecTypes["mongo/string@1"]["input"] | null; readonly createdAt: CodecTypes["mongo/date@1"]["input"]; readonly home: HomeAddressInput | null } } }; +type ParameterizedOutputTypeFunctions = never; +type ValueObjectOutputTypes = ValueObjectTypesFromStorage; +type ValueObjectInputTypes = ValueObjectTypesFromStorage; +export type HomeAddressOutput = ValueObjectOutputTypes["__unbound__"]["HomeAddress"]; +export type HomeAddressInput = ValueObjectInputTypes["__unbound__"]["HomeAddress"]; +type FieldTypeOverrides = { }; +export type FieldOutputTypes = FieldTypesFromDomain; +export type FieldInputTypes = FieldTypesFromDomain; export namespace Models { export type unbound_User = { - id: CodecTypes["mongo/objectId@1"]["output"]; - displayName: CodecTypes["mongo/string@1"]["output"]; - email: CodecTypes["mongo/string@1"]["output"] | null; - createdAt: CodecTypes["mongo/date@1"]["output"]; - home: HomeAddressOutput | null; + id: FieldOutputTypes["__unbound__"]["User"]["id"]; + displayName: FieldOutputTypes["__unbound__"]["User"]["displayName"]; + email: FieldOutputTypes["__unbound__"]["User"]["email"]; + createdAt: FieldOutputTypes["__unbound__"]["User"]["createdAt"]; + home: FieldOutputTypes["__unbound__"]["User"]["home"]; addresses: unbound_Address[]; posts: unbound_Post[]; readonly [RelationKeys]?: "posts"; }; export type unbound_Address = { - street: CodecTypes["mongo/string@1"]["output"]; - city: CodecTypes["mongo/string@1"]["output"]; + street: FieldOutputTypes["__unbound__"]["Address"]["street"]; + city: FieldOutputTypes["__unbound__"]["Address"]["city"]; }; export type unbound_Post = { - id: CodecTypes["mongo/objectId@1"]["output"]; - authorId: CodecTypes["mongo/objectId@1"]["output"]; - title: CodecTypes["mongo/string@1"]["output"]; + id: FieldOutputTypes["__unbound__"]["Post"]["id"]; + authorId: FieldOutputTypes["__unbound__"]["Post"]["authorId"]; + title: FieldOutputTypes["__unbound__"]["Post"]["title"]; author: unbound_User; readonly [RelationKeys]?: "author"; }; export type unbound_Task = { - id: CodecTypes["mongo/objectId@1"]["output"]; + id: FieldOutputTypes["__unbound__"]["Task"]["id"]; kind: "bug"; - title: CodecTypes["mongo/string@1"]["output"]; + title: FieldOutputTypes["__unbound__"]["Task"]["title"]; readonly [RelationKeys]?: never; }; export type unbound_Bug = { - id: CodecTypes["mongo/objectId@1"]["output"]; + id: FieldOutputTypes["__unbound__"]["Task"]["id"]; kind: "bug"; - title: CodecTypes["mongo/string@1"]["output"]; - severity: CodecTypes["mongo/string@1"]["output"]; + title: FieldOutputTypes["__unbound__"]["Task"]["title"]; + severity: FieldOutputTypes["__unbound__"]["Bug"]["severity"]; readonly [RelationKeys]?: never; }; export type unbound_AnyTask = unbound_Bug; @@ -80,7 +86,7 @@ export declare const models: { export type TypeMaps = MongoTypeMaps; type ContractBase = Omit< - ContractType<{ readonly namespaces: { readonly __unbound__: { readonly id: "__unbound__"; readonly kind: "mongo-namespace"; readonly entries: { readonly collection: { readonly posts: MongoCollection; readonly tasks: MongoCollection; readonly users: MongoCollection } } } }; readonly storageHash: StorageHash }>, + ContractType<{ readonly namespaces: { readonly __unbound__: { readonly id: "__unbound__"; readonly kind: "mongo-namespace"; readonly entries: { readonly collection: { readonly posts: MongoCollection; readonly tasks: MongoCollection; readonly users: MongoCollection }; readonly valueObject: { readonly HomeAddress: { readonly fields: { readonly city: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } } } } } } } }; readonly storageHash: StorageHash }>, 'roots' | 'domain' > & { readonly target: "mongo"; @@ -89,8 +95,7 @@ type ContractBase = Omit< readonly domain: { readonly namespaces: { readonly __unbound__: { - readonly models: { readonly Address: { readonly fields: { readonly street: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly city: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } } }; readonly relations: Record; readonly storage: { readonly fields: { readonly street: { readonly field: "street_name" }; readonly city: { readonly field: "city" } } }; readonly owner: "User" }; readonly Bug: { readonly fields: { readonly severity: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } } }; readonly relations: Record; readonly storage: { readonly collection: "tasks"; readonly fields: { readonly severity: { readonly field: "bug_severity" } } }; readonly base: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Task" } }; readonly Post: { readonly fields: { readonly id: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/objectId@1" } }; readonly authorId: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/objectId@1" } }; readonly title: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } } }; readonly relations: { readonly author: { readonly to: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "User" }; readonly cardinality: "N:1"; readonly nullable: false; readonly on: { readonly localFields: readonly ["authorId"]; readonly targetFields: readonly ["id"] } } }; readonly storage: { readonly collection: "posts"; readonly fields: { readonly id: { readonly field: "_id" }; readonly authorId: { readonly field: "author_id" }; readonly title: { readonly field: "title" } } } }; readonly Task: { readonly fields: { readonly id: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/objectId@1" } }; readonly kind: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly title: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } } }; readonly relations: Record; readonly storage: { readonly collection: "tasks"; readonly fields: { readonly id: { readonly field: "_id" }; readonly kind: { readonly field: "_kind" }; readonly title: { readonly field: "task_title" } } }; readonly discriminator: { readonly field: "kind" }; readonly variants: { readonly bug: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Bug" } } }; readonly User: { readonly fields: { readonly id: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/objectId@1" } }; readonly displayName: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly email: { readonly nullable: true; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly createdAt: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/date@1" } }; readonly home: { readonly nullable: true; readonly type: { readonly kind: "valueObject"; readonly name: "HomeAddress" } } }; readonly relations: { readonly posts: { readonly to: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Post" }; readonly cardinality: "1:N"; readonly on: { readonly localFields: readonly ["id"]; readonly targetFields: readonly ["authorId"] } }; readonly addresses: { readonly to: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Address" }; readonly cardinality: "1:N" } }; readonly storage: { readonly collection: "users"; readonly fields: { readonly id: { readonly field: "_id" }; readonly displayName: { readonly field: "display_name" }; readonly email: { readonly field: "email" }; readonly createdAt: { readonly field: "created_at" }; readonly home: { readonly field: "home_address" } }; readonly relations: { readonly addresses: { readonly field: "user_addresses" } } } } }; - readonly valueObjects: { readonly HomeAddress: { readonly fields: { readonly city: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } } } } }; + readonly models: { readonly Address: { readonly fields: { readonly street: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly city: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } } }; readonly relations: Record; readonly storage: { readonly fields: { readonly street: { readonly field: "street_name" }; readonly city: { readonly field: "city" } } }; readonly owner: "User" }; readonly Bug: { readonly fields: { readonly severity: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } } }; readonly relations: Record; readonly storage: { readonly collection: "tasks"; readonly fields: { readonly severity: { readonly field: "bug_severity" } } }; readonly base: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Task" } }; readonly Post: { readonly fields: { readonly id: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/objectId@1" } }; readonly authorId: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/objectId@1" } }; readonly title: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } } }; readonly relations: { readonly author: { readonly to: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "User" }; readonly cardinality: "N:1"; readonly nullable: false; readonly on: { readonly localFields: readonly ["authorId"]; readonly targetFields: readonly ["id"] } } }; readonly storage: { readonly collection: "posts"; readonly fields: { readonly id: { readonly field: "_id" }; readonly authorId: { readonly field: "author_id" }; readonly title: { readonly field: "title" } } } }; readonly Task: { readonly fields: { readonly id: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/objectId@1" } }; readonly kind: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly title: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } } }; readonly relations: Record; readonly storage: { readonly collection: "tasks"; readonly fields: { readonly id: { readonly field: "_id" }; readonly kind: { readonly field: "_kind" }; readonly title: { readonly field: "task_title" } } }; readonly discriminator: { readonly field: "kind" }; readonly variants: { readonly bug: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Bug" } } }; readonly User: { readonly fields: { readonly id: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/objectId@1" } }; readonly displayName: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly email: { readonly nullable: true; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly createdAt: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/date@1" } }; readonly home: { readonly nullable: true; readonly type: { readonly kind: "valueObject"; readonly plane: "storage"; readonly namespaceId: "__unbound__"; readonly entityKind: "valueObject"; readonly entityName: "HomeAddress" } } }; readonly relations: { readonly posts: { readonly to: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Post" }; readonly cardinality: "1:N"; readonly on: { readonly localFields: readonly ["id"]; readonly targetFields: readonly ["authorId"] } }; readonly addresses: { readonly to: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Address" }; readonly cardinality: "1:N" } }; readonly storage: { readonly collection: "users"; readonly fields: { readonly id: { readonly field: "_id" }; readonly displayName: { readonly field: "display_name" }; readonly email: { readonly field: "email" }; readonly createdAt: { readonly field: "created_at" }; readonly home: { readonly field: "home_address" } }; readonly relations: { readonly addresses: { readonly field: "user_addresses" } } } } }; }; }; }; @@ -98,7 +103,6 @@ type ContractBase = Omit< readonly extensions: { }; readonly execution: { readonly executionHash: ExecutionHash; readonly mutations: { readonly defaults: readonly [{ readonly onCreate: { readonly id: "timestampNow"; readonly kind: "generator" }; readonly ref: { readonly entry: "users"; readonly field: "created_at"; readonly namespace: "__unbound__" } }] } }; readonly meta: { }; - readonly valueObjects: { readonly HomeAddress: { readonly fields: { readonly city: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } } } } }; readonly profileHash: ProfileHash; }; diff --git a/packages/2-mongo-family/9-family/test/fixtures/migration-contract.d.ts b/packages/2-mongo-family/9-family/test/fixtures/migration-contract.d.ts index fb0ae579ef53..8af46af010b8 100644 --- a/packages/2-mongo-family/9-family/test/fixtures/migration-contract.d.ts +++ b/packages/2-mongo-family/9-family/test/fixtures/migration-contract.d.ts @@ -1,7 +1,7 @@ // ⚠️ GENERATED FILE - DO NOT EDIT // This file is automatically generated by 'prisma contract emit'. // To regenerate, run: prisma contract emit -import type { CodecTypes as MongoCodecTypes, Vector } from '@internal/target-mongo/codec-types'; +import type { CodecTypes as MongoCodecTypes } from '../../../../3-targets/mongo/1-target/src/exports/codec-types'; import type { MongoCollection, @@ -12,342 +12,143 @@ import type { import type { Contract as ContractType, ExecutionHashBase, + FieldTypesFromDomain, NamespaceId, ProfileHashBase, StorageHashBase, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; -export type StorageHash = - StorageHashBase<'bd938b4f8a10c688bd32dc61ec1dd808dcf34e725f08505b39ce365a39c97e1b'>; +export type StorageHash = StorageHashBase<"migration-storage">; export type ExecutionHash = ExecutionHashBase; -export type ProfileHash = - ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; +export type ProfileHash = ProfileHashBase<"migration-profile">; export type CodecTypes = MongoCodecTypes; -export type AddressOutput = { - readonly city: CodecTypes['mongo/string@1']['output']; - readonly country: CodecTypes['mongo/string@1']['output']; - readonly postalCode: CodecTypes['mongo/string@1']['output']; - readonly streetAndNumber: CodecTypes['mongo/string@1']['output']; -}; -export type AddressInput = { - readonly city: CodecTypes['mongo/string@1']['input']; - readonly country: CodecTypes['mongo/string@1']['input']; - readonly postalCode: CodecTypes['mongo/string@1']['input']; - readonly streetAndNumber: CodecTypes['mongo/string@1']['input']; -}; -export type CartItemOutput = { - readonly amount: CodecTypes['mongo/int32@1']['output']; - readonly brand: CodecTypes['mongo/string@1']['output']; - readonly image: ImageOutput; - readonly name: CodecTypes['mongo/string@1']['output']; - readonly price: PriceOutput; - readonly productId: CodecTypes['mongo/string@1']['output']; -}; -export type CartItemInput = { - readonly amount: CodecTypes['mongo/int32@1']['input']; - readonly brand: CodecTypes['mongo/string@1']['input']; - readonly image: ImageInput; - readonly name: CodecTypes['mongo/string@1']['input']; - readonly price: PriceInput; - readonly productId: CodecTypes['mongo/string@1']['input']; -}; -export type ImageOutput = { readonly url: CodecTypes['mongo/string@1']['output'] }; -export type ImageInput = { readonly url: CodecTypes['mongo/string@1']['input'] }; -export type InvoiceLineItemOutput = { - readonly amount: CodecTypes['mongo/int32@1']['output']; - readonly lineTotal: CodecTypes['mongo/double@1']['output']; - readonly name: CodecTypes['mongo/string@1']['output']; - readonly unitPrice: CodecTypes['mongo/double@1']['output']; -}; -export type InvoiceLineItemInput = { - readonly amount: CodecTypes['mongo/int32@1']['input']; - readonly lineTotal: CodecTypes['mongo/double@1']['input']; - readonly name: CodecTypes['mongo/string@1']['input']; - readonly unitPrice: CodecTypes['mongo/double@1']['input']; -}; -export type OrderLineItemOutput = { - readonly amount: CodecTypes['mongo/int32@1']['output']; - readonly brand: CodecTypes['mongo/string@1']['output']; - readonly image: ImageOutput; - readonly name: CodecTypes['mongo/string@1']['output']; - readonly price: PriceOutput; - readonly productId: CodecTypes['mongo/string@1']['output']; -}; -export type OrderLineItemInput = { - readonly amount: CodecTypes['mongo/int32@1']['input']; - readonly brand: CodecTypes['mongo/string@1']['input']; - readonly image: ImageInput; - readonly name: CodecTypes['mongo/string@1']['input']; - readonly price: PriceInput; - readonly productId: CodecTypes['mongo/string@1']['input']; -}; -export type PriceOutput = { - readonly amount: CodecTypes['mongo/double@1']['output']; - readonly currency: CodecTypes['mongo/string@1']['output']; -}; -export type PriceInput = { - readonly amount: CodecTypes['mongo/double@1']['input']; - readonly currency: CodecTypes['mongo/string@1']['input']; -}; -export type StatusEntryOutput = { - readonly status: CodecTypes['mongo/string@1']['output']; - readonly timestamp: CodecTypes['mongo/date@1']['output']; -}; -export type StatusEntryInput = { - readonly status: CodecTypes['mongo/string@1']['input']; - readonly timestamp: CodecTypes['mongo/date@1']['input']; -}; -export type FieldOutputTypes = { - readonly __unbound__: { - readonly AddToCartEvent: { - readonly brand: CodecTypes['mongo/string@1']['output']; - readonly productId: CodecTypes['mongo/string@1']['output']; - }; - readonly Cart: { - readonly _id: CodecTypes['mongo/objectId@1']['output']; - readonly items: ReadonlyArray; - readonly userId: CodecTypes['mongo/objectId@1']['output']; - }; - readonly Event: { - readonly _id: CodecTypes['mongo/objectId@1']['output']; - readonly sessionId: CodecTypes['mongo/string@1']['output']; - readonly timestamp: CodecTypes['mongo/date@1']['output']; - readonly type: CodecTypes['mongo/string@1']['output']; - readonly userId: CodecTypes['mongo/string@1']['output']; - }; - readonly Invoice: { - readonly _id: CodecTypes['mongo/objectId@1']['output']; - readonly issuedAt: CodecTypes['mongo/date@1']['output']; - readonly items: ReadonlyArray; - readonly orderId: CodecTypes['mongo/objectId@1']['output']; - readonly subtotal: CodecTypes['mongo/double@1']['output']; - readonly tax: CodecTypes['mongo/double@1']['output']; - readonly total: CodecTypes['mongo/double@1']['output']; - }; - readonly Location: { - readonly _id: CodecTypes['mongo/objectId@1']['output']; - readonly city: CodecTypes['mongo/string@1']['output']; - readonly country: CodecTypes['mongo/string@1']['output']; - readonly name: CodecTypes['mongo/string@1']['output']; - readonly postalCode: CodecTypes['mongo/string@1']['output']; - readonly streetAndNumber: CodecTypes['mongo/string@1']['output']; - }; - readonly Order: { - readonly _id: CodecTypes['mongo/objectId@1']['output']; - readonly items: ReadonlyArray; - readonly shippingAddress: CodecTypes['mongo/string@1']['output']; - readonly statusHistory: ReadonlyArray; - readonly type: 'delivery' | 'pickup'; - readonly userId: CodecTypes['mongo/objectId@1']['output']; - }; - readonly Product: { - readonly _id: CodecTypes['mongo/objectId@1']['output']; - readonly articleType: CodecTypes['mongo/string@1']['output']; - readonly brand: CodecTypes['mongo/string@1']['output']; - readonly code: CodecTypes['mongo/string@1']['output']; - readonly description: CodecTypes['mongo/string@1']['output']; - readonly embedding: ReadonlyArray | null; - readonly image: ImageOutput; - readonly name: CodecTypes['mongo/string@1']['output']; - readonly price: PriceOutput; - readonly primaryCategory: CodecTypes['mongo/string@1']['output']; - readonly status: 'active' | 'discontinued' | 'out-of-stock'; - readonly subCategory: CodecTypes['mongo/string@1']['output']; - }; - readonly SearchEvent: { readonly query: CodecTypes['mongo/string@1']['output'] }; - readonly User: { - readonly _id: CodecTypes['mongo/objectId@1']['output']; - readonly address: AddressOutput | null; - readonly email: CodecTypes['mongo/string@1']['output']; - readonly name: CodecTypes['mongo/string@1']['output']; - }; - readonly ViewProductEvent: { - readonly brand: CodecTypes['mongo/string@1']['output']; - readonly exitMethod: CodecTypes['mongo/string@1']['output'] | null; - readonly productId: CodecTypes['mongo/string@1']['output']; - readonly subCategory: CodecTypes['mongo/string@1']['output']; - }; - }; -}; -export type FieldInputTypes = { - readonly __unbound__: { - readonly AddToCartEvent: { - readonly brand: CodecTypes['mongo/string@1']['input']; - readonly productId: CodecTypes['mongo/string@1']['input']; - }; - readonly Cart: { - readonly _id: CodecTypes['mongo/objectId@1']['input']; - readonly items: ReadonlyArray; - readonly userId: CodecTypes['mongo/objectId@1']['input']; - }; - readonly Event: { - readonly _id: CodecTypes['mongo/objectId@1']['input']; - readonly sessionId: CodecTypes['mongo/string@1']['input']; - readonly timestamp: CodecTypes['mongo/date@1']['input']; - readonly type: CodecTypes['mongo/string@1']['input']; - readonly userId: CodecTypes['mongo/string@1']['input']; - }; - readonly Invoice: { - readonly _id: CodecTypes['mongo/objectId@1']['input']; - readonly issuedAt: CodecTypes['mongo/date@1']['input']; - readonly items: ReadonlyArray; - readonly orderId: CodecTypes['mongo/objectId@1']['input']; - readonly subtotal: CodecTypes['mongo/double@1']['input']; - readonly tax: CodecTypes['mongo/double@1']['input']; - readonly total: CodecTypes['mongo/double@1']['input']; - }; - readonly Location: { - readonly _id: CodecTypes['mongo/objectId@1']['input']; - readonly city: CodecTypes['mongo/string@1']['input']; - readonly country: CodecTypes['mongo/string@1']['input']; - readonly name: CodecTypes['mongo/string@1']['input']; - readonly postalCode: CodecTypes['mongo/string@1']['input']; - readonly streetAndNumber: CodecTypes['mongo/string@1']['input']; - }; - readonly Order: { - readonly _id: CodecTypes['mongo/objectId@1']['input']; - readonly items: ReadonlyArray; - readonly shippingAddress: CodecTypes['mongo/string@1']['input']; - readonly statusHistory: ReadonlyArray; - readonly type: 'delivery' | 'pickup'; - readonly userId: CodecTypes['mongo/objectId@1']['input']; - }; - readonly Product: { - readonly _id: CodecTypes['mongo/objectId@1']['input']; - readonly articleType: CodecTypes['mongo/string@1']['input']; - readonly brand: CodecTypes['mongo/string@1']['input']; - readonly code: CodecTypes['mongo/string@1']['input']; - readonly description: CodecTypes['mongo/string@1']['input']; - readonly embedding: ReadonlyArray | null; - readonly image: ImageInput; - readonly name: CodecTypes['mongo/string@1']['input']; - readonly price: PriceInput; - readonly primaryCategory: CodecTypes['mongo/string@1']['input']; - readonly status: 'active' | 'discontinued' | 'out-of-stock'; - readonly subCategory: CodecTypes['mongo/string@1']['input']; - }; - readonly SearchEvent: { readonly query: CodecTypes['mongo/string@1']['input'] }; - readonly User: { - readonly _id: CodecTypes['mongo/objectId@1']['input']; - readonly address: AddressInput | null; - readonly email: CodecTypes['mongo/string@1']['input']; - readonly name: CodecTypes['mongo/string@1']['input']; - }; - readonly ViewProductEvent: { - readonly brand: CodecTypes['mongo/string@1']['input']; - readonly exitMethod: CodecTypes['mongo/string@1']['input'] | null; - readonly productId: CodecTypes['mongo/string@1']['input']; - readonly subCategory: CodecTypes['mongo/string@1']['input']; - }; - }; -}; +type ParameterizedOutputTypeFunctions = never; +type ValueObjectOutputTypes = ValueObjectTypesFromStorage; +type ValueObjectInputTypes = ValueObjectTypesFromStorage; +export type AddressOutput = ValueObjectOutputTypes["__unbound__"]["Address"]; +export type AddressInput = ValueObjectInputTypes["__unbound__"]["Address"]; +export type CartItemOutput = ValueObjectOutputTypes["__unbound__"]["CartItem"]; +export type CartItemInput = ValueObjectInputTypes["__unbound__"]["CartItem"]; +export type ImageOutput = ValueObjectOutputTypes["__unbound__"]["Image"]; +export type ImageInput = ValueObjectInputTypes["__unbound__"]["Image"]; +export type InvoiceLineItemOutput = ValueObjectOutputTypes["__unbound__"]["InvoiceLineItem"]; +export type InvoiceLineItemInput = ValueObjectInputTypes["__unbound__"]["InvoiceLineItem"]; +export type OrderLineItemOutput = ValueObjectOutputTypes["__unbound__"]["OrderLineItem"]; +export type OrderLineItemInput = ValueObjectInputTypes["__unbound__"]["OrderLineItem"]; +export type PriceOutput = ValueObjectOutputTypes["__unbound__"]["Price"]; +export type PriceInput = ValueObjectInputTypes["__unbound__"]["Price"]; +export type StatusEntryOutput = ValueObjectOutputTypes["__unbound__"]["StatusEntry"]; +export type StatusEntryInput = ValueObjectInputTypes["__unbound__"]["StatusEntry"]; +type FieldTypeOverrides = { }; +export type FieldOutputTypes = FieldTypesFromDomain; +export type FieldInputTypes = FieldTypesFromDomain; export namespace Models { export type unbound_AddToCartEvent = { - _id: CodecTypes['mongo/objectId@1']['output']; - sessionId: CodecTypes['mongo/string@1']['output']; - timestamp: CodecTypes['mongo/date@1']['output']; - type: 'add-to-cart'; - userId: CodecTypes['mongo/string@1']['output']; - brand: CodecTypes['mongo/string@1']['output']; - productId: CodecTypes['mongo/string@1']['output']; + _id: FieldOutputTypes["__unbound__"]["Event"]["_id"]; + sessionId: FieldOutputTypes["__unbound__"]["Event"]["sessionId"]; + timestamp: FieldOutputTypes["__unbound__"]["Event"]["timestamp"]; + type: "add-to-cart"; + userId: FieldOutputTypes["__unbound__"]["Event"]["userId"]; + brand: FieldOutputTypes["__unbound__"]["AddToCartEvent"]["brand"]; + productId: FieldOutputTypes["__unbound__"]["AddToCartEvent"]["productId"]; readonly [RelationKeys]?: never; }; export type unbound_Cart = { - _id: CodecTypes['mongo/objectId@1']['output']; - items: ReadonlyArray; - userId: CodecTypes['mongo/objectId@1']['output']; + _id: FieldOutputTypes["__unbound__"]["Cart"]["_id"]; + items: FieldOutputTypes["__unbound__"]["Cart"]["items"]; + userId: FieldOutputTypes["__unbound__"]["Cart"]["userId"]; user: unbound_User; - readonly [RelationKeys]?: 'user'; + readonly [RelationKeys]?: "user"; }; export type unbound_Event = { - _id: CodecTypes['mongo/objectId@1']['output']; - sessionId: CodecTypes['mongo/string@1']['output']; - timestamp: CodecTypes['mongo/date@1']['output']; - type: 'add-to-cart' | 'search' | 'view-product'; - userId: CodecTypes['mongo/string@1']['output']; + _id: FieldOutputTypes["__unbound__"]["Event"]["_id"]; + sessionId: FieldOutputTypes["__unbound__"]["Event"]["sessionId"]; + timestamp: FieldOutputTypes["__unbound__"]["Event"]["timestamp"]; + type: "add-to-cart" | "search" | "view-product"; + userId: FieldOutputTypes["__unbound__"]["Event"]["userId"]; readonly [RelationKeys]?: never; }; export type unbound_Invoice = { - _id: CodecTypes['mongo/objectId@1']['output']; - issuedAt: CodecTypes['mongo/date@1']['output']; - items: ReadonlyArray; - orderId: CodecTypes['mongo/objectId@1']['output']; - subtotal: CodecTypes['mongo/double@1']['output']; - tax: CodecTypes['mongo/double@1']['output']; - total: CodecTypes['mongo/double@1']['output']; + _id: FieldOutputTypes["__unbound__"]["Invoice"]["_id"]; + issuedAt: FieldOutputTypes["__unbound__"]["Invoice"]["issuedAt"]; + items: FieldOutputTypes["__unbound__"]["Invoice"]["items"]; + orderId: FieldOutputTypes["__unbound__"]["Invoice"]["orderId"]; + subtotal: FieldOutputTypes["__unbound__"]["Invoice"]["subtotal"]; + tax: FieldOutputTypes["__unbound__"]["Invoice"]["tax"]; + total: FieldOutputTypes["__unbound__"]["Invoice"]["total"]; order: unbound_Order; - readonly [RelationKeys]?: 'order'; + readonly [RelationKeys]?: "order"; }; export type unbound_Location = { - _id: CodecTypes['mongo/objectId@1']['output']; - city: CodecTypes['mongo/string@1']['output']; - country: CodecTypes['mongo/string@1']['output']; - name: CodecTypes['mongo/string@1']['output']; - postalCode: CodecTypes['mongo/string@1']['output']; - streetAndNumber: CodecTypes['mongo/string@1']['output']; + _id: FieldOutputTypes["__unbound__"]["Location"]["_id"]; + city: FieldOutputTypes["__unbound__"]["Location"]["city"]; + country: FieldOutputTypes["__unbound__"]["Location"]["country"]; + name: FieldOutputTypes["__unbound__"]["Location"]["name"]; + postalCode: FieldOutputTypes["__unbound__"]["Location"]["postalCode"]; + streetAndNumber: FieldOutputTypes["__unbound__"]["Location"]["streetAndNumber"]; readonly [RelationKeys]?: never; }; export type unbound_Order = { - _id: CodecTypes['mongo/objectId@1']['output']; - items: ReadonlyArray; - shippingAddress: CodecTypes['mongo/string@1']['output']; - statusHistory: ReadonlyArray; - type: 'delivery' | 'pickup'; - userId: CodecTypes['mongo/objectId@1']['output']; + _id: FieldOutputTypes["__unbound__"]["Order"]["_id"]; + items: FieldOutputTypes["__unbound__"]["Order"]["items"]; + shippingAddress: FieldOutputTypes["__unbound__"]["Order"]["shippingAddress"]; + statusHistory: FieldOutputTypes["__unbound__"]["Order"]["statusHistory"]; + type: FieldOutputTypes["__unbound__"]["Order"]["type"]; + userId: FieldOutputTypes["__unbound__"]["Order"]["userId"]; invoices: unbound_Invoice[]; user: unbound_User; - readonly [RelationKeys]?: 'invoices' | 'user'; + readonly [RelationKeys]?: "invoices" | "user"; }; export type unbound_Product = { - _id: CodecTypes['mongo/objectId@1']['output']; - articleType: CodecTypes['mongo/string@1']['output']; - brand: CodecTypes['mongo/string@1']['output']; - code: CodecTypes['mongo/string@1']['output']; - description: CodecTypes['mongo/string@1']['output']; - embedding: ReadonlyArray | null; - image: ImageOutput; - name: CodecTypes['mongo/string@1']['output']; - price: PriceOutput; - primaryCategory: CodecTypes['mongo/string@1']['output']; - status: 'active' | 'discontinued' | 'out-of-stock'; - subCategory: CodecTypes['mongo/string@1']['output']; + _id: FieldOutputTypes["__unbound__"]["Product"]["_id"]; + articleType: FieldOutputTypes["__unbound__"]["Product"]["articleType"]; + brand: FieldOutputTypes["__unbound__"]["Product"]["brand"]; + code: FieldOutputTypes["__unbound__"]["Product"]["code"]; + description: FieldOutputTypes["__unbound__"]["Product"]["description"]; + embedding: FieldOutputTypes["__unbound__"]["Product"]["embedding"]; + image: FieldOutputTypes["__unbound__"]["Product"]["image"]; + name: FieldOutputTypes["__unbound__"]["Product"]["name"]; + price: FieldOutputTypes["__unbound__"]["Product"]["price"]; + primaryCategory: FieldOutputTypes["__unbound__"]["Product"]["primaryCategory"]; + status: FieldOutputTypes["__unbound__"]["Product"]["status"]; + subCategory: FieldOutputTypes["__unbound__"]["Product"]["subCategory"]; readonly [RelationKeys]?: never; }; export type unbound_SearchEvent = { - _id: CodecTypes['mongo/objectId@1']['output']; - sessionId: CodecTypes['mongo/string@1']['output']; - timestamp: CodecTypes['mongo/date@1']['output']; - type: 'search'; - userId: CodecTypes['mongo/string@1']['output']; - query: CodecTypes['mongo/string@1']['output']; + _id: FieldOutputTypes["__unbound__"]["Event"]["_id"]; + sessionId: FieldOutputTypes["__unbound__"]["Event"]["sessionId"]; + timestamp: FieldOutputTypes["__unbound__"]["Event"]["timestamp"]; + type: "search"; + userId: FieldOutputTypes["__unbound__"]["Event"]["userId"]; + query: FieldOutputTypes["__unbound__"]["SearchEvent"]["query"]; readonly [RelationKeys]?: never; }; export type unbound_User = { - _id: CodecTypes['mongo/objectId@1']['output']; - address: AddressOutput | null; - email: CodecTypes['mongo/string@1']['output']; - name: CodecTypes['mongo/string@1']['output']; + _id: FieldOutputTypes["__unbound__"]["User"]["_id"]; + address: FieldOutputTypes["__unbound__"]["User"]["address"]; + email: FieldOutputTypes["__unbound__"]["User"]["email"]; + name: FieldOutputTypes["__unbound__"]["User"]["name"]; carts: unbound_Cart[]; orders: unbound_Order[]; - readonly [RelationKeys]?: 'carts' | 'orders'; + readonly [RelationKeys]?: "carts" | "orders"; }; export type unbound_ViewProductEvent = { - _id: CodecTypes['mongo/objectId@1']['output']; - sessionId: CodecTypes['mongo/string@1']['output']; - timestamp: CodecTypes['mongo/date@1']['output']; - type: 'view-product'; - userId: CodecTypes['mongo/string@1']['output']; - brand: CodecTypes['mongo/string@1']['output']; - exitMethod: CodecTypes['mongo/string@1']['output'] | null; - productId: CodecTypes['mongo/string@1']['output']; - subCategory: CodecTypes['mongo/string@1']['output']; + _id: FieldOutputTypes["__unbound__"]["Event"]["_id"]; + sessionId: FieldOutputTypes["__unbound__"]["Event"]["sessionId"]; + timestamp: FieldOutputTypes["__unbound__"]["Event"]["timestamp"]; + type: "view-product"; + userId: FieldOutputTypes["__unbound__"]["Event"]["userId"]; + brand: FieldOutputTypes["__unbound__"]["ViewProductEvent"]["brand"]; + exitMethod: FieldOutputTypes["__unbound__"]["ViewProductEvent"]["exitMethod"]; + productId: FieldOutputTypes["__unbound__"]["ViewProductEvent"]["productId"]; + subCategory: FieldOutputTypes["__unbound__"]["ViewProductEvent"]["subCategory"]; readonly [RelationKeys]?: never; }; - export type unbound_AnyEvent = - unbound_AddToCartEvent | unbound_SearchEvent | unbound_ViewProductEvent; + export type unbound_AnyEvent = unbound_AddToCartEvent | unbound_SearchEvent | unbound_ViewProductEvent; } export declare const models: { @@ -369,1138 +170,24 @@ export declare const models: { export type TypeMaps = MongoTypeMaps; type ContractBase = Omit< - ContractType<{ - readonly namespaces: { - readonly __unbound__: { - readonly id: '__unbound__'; - readonly kind: 'mongo-database'; - readonly entries: { - readonly collection: { - readonly carts: { - readonly indexes: readonly [ - { - readonly keys: readonly [{ readonly direction: 1; readonly field: 'userId' }]; - readonly kind: 'mongo-index'; - readonly unique: true; - }, - ]; - readonly kind: 'mongo-collection'; - readonly validator: { - readonly jsonSchema: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly _id: { readonly bsonType: 'objectId' }; - readonly items: { - readonly bsonType: 'array'; - readonly items: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly amount: { readonly bsonType: 'int' }; - readonly brand: { readonly bsonType: 'string' }; - readonly image: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { readonly url: { readonly bsonType: 'string' } }; - readonly required: readonly ['url']; - }; - readonly name: { readonly bsonType: 'string' }; - readonly price: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly amount: { readonly bsonType: 'double' }; - readonly currency: { readonly bsonType: 'string' }; - }; - readonly required: readonly ['amount', 'currency']; - }; - readonly productId: { readonly bsonType: 'string' }; - }; - readonly required: readonly [ - 'amount', - 'brand', - 'image', - 'name', - 'price', - 'productId', - ]; - }; - }; - readonly userId: { readonly bsonType: 'objectId' }; - }; - readonly required: readonly ['_id', 'items', 'userId']; - }; - readonly kind: 'mongo-validator'; - readonly validationAction: 'error'; - readonly validationLevel: 'strict'; - }; - }; - readonly events: { - readonly indexes: readonly [ - { - readonly keys: readonly [ - { readonly direction: 1; readonly field: 'userId' }, - { readonly direction: -1; readonly field: 'timestamp' }, - ]; - readonly kind: 'mongo-index'; - }, - { - readonly expireAfterSeconds: 7776000; - readonly keys: readonly [{ readonly direction: 1; readonly field: 'timestamp' }]; - readonly kind: 'mongo-index'; - }, - ]; - readonly kind: 'mongo-collection'; - readonly validator: { - readonly jsonSchema: { - readonly bsonType: 'object'; - readonly oneOf: readonly [ - { - readonly additionalProperties: false; - readonly properties: { - readonly _id: { readonly bsonType: 'objectId' }; - readonly brand: { readonly bsonType: 'string' }; - readonly exitMethod: { readonly bsonType: readonly ['null', 'string'] }; - readonly productId: { readonly bsonType: 'string' }; - readonly sessionId: { readonly bsonType: 'string' }; - readonly subCategory: { readonly bsonType: 'string' }; - readonly timestamp: { readonly bsonType: 'date' }; - readonly type: { readonly enum: readonly ['view-product'] }; - readonly userId: { readonly bsonType: 'string' }; - }; - readonly required: readonly ['brand', 'productId', 'subCategory', 'type']; - }, - { - readonly additionalProperties: false; - readonly properties: { - readonly _id: { readonly bsonType: 'objectId' }; - readonly query: { readonly bsonType: 'string' }; - readonly sessionId: { readonly bsonType: 'string' }; - readonly timestamp: { readonly bsonType: 'date' }; - readonly type: { readonly enum: readonly ['search'] }; - readonly userId: { readonly bsonType: 'string' }; - }; - readonly required: readonly ['query', 'type']; - }, - { - readonly additionalProperties: false; - readonly properties: { - readonly _id: { readonly bsonType: 'objectId' }; - readonly brand: { readonly bsonType: 'string' }; - readonly productId: { readonly bsonType: 'string' }; - readonly sessionId: { readonly bsonType: 'string' }; - readonly timestamp: { readonly bsonType: 'date' }; - readonly type: { readonly enum: readonly ['add-to-cart'] }; - readonly userId: { readonly bsonType: 'string' }; - }; - readonly required: readonly ['brand', 'productId', 'type']; - }, - ]; - readonly properties: { - readonly _id: { readonly bsonType: 'objectId' }; - readonly sessionId: { readonly bsonType: 'string' }; - readonly timestamp: { readonly bsonType: 'date' }; - readonly type: { readonly bsonType: 'string' }; - readonly userId: { readonly bsonType: 'string' }; - }; - readonly required: readonly ['_id', 'sessionId', 'timestamp', 'type', 'userId']; - }; - readonly kind: 'mongo-validator'; - readonly validationAction: 'error'; - readonly validationLevel: 'strict'; - }; - }; - readonly invoices: { - readonly indexes: readonly [ - { - readonly keys: readonly [{ readonly direction: 1; readonly field: 'orderId' }]; - readonly kind: 'mongo-index'; - }, - { - readonly keys: readonly [{ readonly direction: -1; readonly field: 'issuedAt' }]; - readonly kind: 'mongo-index'; - readonly sparse: true; - }, - ]; - readonly kind: 'mongo-collection'; - readonly validator: { - readonly jsonSchema: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly _id: { readonly bsonType: 'objectId' }; - readonly issuedAt: { readonly bsonType: 'date' }; - readonly items: { - readonly bsonType: 'array'; - readonly items: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly amount: { readonly bsonType: 'int' }; - readonly lineTotal: { readonly bsonType: 'double' }; - readonly name: { readonly bsonType: 'string' }; - readonly unitPrice: { readonly bsonType: 'double' }; - }; - readonly required: readonly ['amount', 'lineTotal', 'name', 'unitPrice']; - }; - }; - readonly orderId: { readonly bsonType: 'objectId' }; - readonly subtotal: { readonly bsonType: 'double' }; - readonly tax: { readonly bsonType: 'double' }; - readonly total: { readonly bsonType: 'double' }; - }; - readonly required: readonly [ - '_id', - 'issuedAt', - 'items', - 'orderId', - 'subtotal', - 'tax', - 'total', - ]; - }; - readonly kind: 'mongo-validator'; - readonly validationAction: 'error'; - readonly validationLevel: 'strict'; - }; - }; - readonly locations: { - readonly indexes: readonly [ - { - readonly collation: { readonly locale: 'en'; readonly strength: 2 }; - readonly keys: readonly [ - { readonly direction: 1; readonly field: 'city' }, - { readonly direction: 1; readonly field: 'country' }, - ]; - readonly kind: 'mongo-index'; - }, - ]; - readonly kind: 'mongo-collection'; - readonly validator: { - readonly jsonSchema: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly _id: { readonly bsonType: 'objectId' }; - readonly city: { readonly bsonType: 'string' }; - readonly country: { readonly bsonType: 'string' }; - readonly name: { readonly bsonType: 'string' }; - readonly postalCode: { readonly bsonType: 'string' }; - readonly streetAndNumber: { readonly bsonType: 'string' }; - }; - readonly required: readonly [ - '_id', - 'city', - 'country', - 'name', - 'postalCode', - 'streetAndNumber', - ]; - }; - readonly kind: 'mongo-validator'; - readonly validationAction: 'error'; - readonly validationLevel: 'strict'; - }; - }; - readonly orders: { - readonly indexes: readonly [ - { - readonly keys: readonly [{ readonly direction: 1; readonly field: 'userId' }]; - readonly kind: 'mongo-index'; - }, - ]; - readonly kind: 'mongo-collection'; - readonly validator: { - readonly jsonSchema: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly _id: { readonly bsonType: 'objectId' }; - readonly items: { - readonly bsonType: 'array'; - readonly items: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly amount: { readonly bsonType: 'int' }; - readonly brand: { readonly bsonType: 'string' }; - readonly image: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { readonly url: { readonly bsonType: 'string' } }; - readonly required: readonly ['url']; - }; - readonly name: { readonly bsonType: 'string' }; - readonly price: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly amount: { readonly bsonType: 'double' }; - readonly currency: { readonly bsonType: 'string' }; - }; - readonly required: readonly ['amount', 'currency']; - }; - readonly productId: { readonly bsonType: 'string' }; - }; - readonly required: readonly [ - 'amount', - 'brand', - 'image', - 'name', - 'price', - 'productId', - ]; - }; - }; - readonly shippingAddress: { readonly bsonType: 'string' }; - readonly statusHistory: { - readonly bsonType: 'array'; - readonly items: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly status: { readonly bsonType: 'string' }; - readonly timestamp: { readonly bsonType: 'date' }; - }; - readonly required: readonly ['status', 'timestamp']; - }; - }; - readonly type: { - readonly bsonType: 'string'; - readonly enum: readonly ['delivery', 'pickup']; - }; - readonly userId: { readonly bsonType: 'objectId' }; - }; - readonly required: readonly [ - '_id', - 'items', - 'shippingAddress', - 'statusHistory', - 'type', - 'userId', - ]; - }; - readonly kind: 'mongo-validator'; - readonly validationAction: 'error'; - readonly validationLevel: 'strict'; - }; - }; - readonly products: { - readonly indexes: readonly [ - { - readonly keys: readonly [ - { readonly direction: 'text'; readonly field: 'name' }, - { readonly direction: 'text'; readonly field: 'description' }, - ]; - readonly kind: 'mongo-index'; - readonly weights: { readonly description: 1; readonly name: 10 }; - }, - { - readonly keys: readonly [ - { readonly direction: 1; readonly field: 'brand' }, - { readonly direction: 1; readonly field: 'subCategory' }, - ]; - readonly kind: 'mongo-index'; - }, - { - readonly keys: readonly [ - { readonly direction: 1; readonly field: 'primaryCategory' }, - { readonly direction: 1; readonly field: 'articleType' }, - ]; - readonly kind: 'mongo-index'; - }, - { - readonly keys: readonly [ - { readonly direction: 'hashed'; readonly field: 'code' }, - ]; - readonly kind: 'mongo-index'; - }, - ]; - readonly kind: 'mongo-collection'; - readonly validator: { - readonly jsonSchema: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly _id: { readonly bsonType: 'objectId' }; - readonly articleType: { readonly bsonType: 'string' }; - readonly brand: { readonly bsonType: 'string' }; - readonly code: { readonly bsonType: 'string' }; - readonly description: { readonly bsonType: 'string' }; - readonly embedding: { - readonly bsonType: 'array'; - readonly items: { readonly bsonType: 'double' }; - }; - readonly image: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { readonly url: { readonly bsonType: 'string' } }; - readonly required: readonly ['url']; - }; - readonly name: { readonly bsonType: 'string' }; - readonly price: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly amount: { readonly bsonType: 'double' }; - readonly currency: { readonly bsonType: 'string' }; - }; - readonly required: readonly ['amount', 'currency']; - }; - readonly primaryCategory: { readonly bsonType: 'string' }; - readonly status: { - readonly bsonType: 'string'; - readonly enum: readonly ['active', 'discontinued', 'out-of-stock']; - }; - readonly subCategory: { readonly bsonType: 'string' }; - }; - readonly required: readonly [ - '_id', - 'articleType', - 'brand', - 'code', - 'description', - 'image', - 'name', - 'price', - 'primaryCategory', - 'status', - 'subCategory', - ]; - }; - readonly kind: 'mongo-validator'; - readonly validationAction: 'error'; - readonly validationLevel: 'strict'; - }; - }; - readonly users: { - readonly indexes: readonly [ - { - readonly keys: readonly [{ readonly direction: 1; readonly field: 'email' }]; - readonly kind: 'mongo-index'; - readonly unique: true; - }, - ]; - readonly kind: 'mongo-collection'; - readonly validator: { - readonly jsonSchema: { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly _id: { readonly bsonType: 'objectId' }; - readonly address: { - readonly oneOf: readonly [ - { readonly bsonType: 'null' }, - { - readonly additionalProperties: false; - readonly bsonType: 'object'; - readonly properties: { - readonly city: { readonly bsonType: 'string' }; - readonly country: { readonly bsonType: 'string' }; - readonly postalCode: { readonly bsonType: 'string' }; - readonly streetAndNumber: { readonly bsonType: 'string' }; - }; - readonly required: readonly [ - 'city', - 'country', - 'postalCode', - 'streetAndNumber', - ]; - }, - ]; - }; - readonly email: { readonly bsonType: 'string' }; - readonly name: { readonly bsonType: 'string' }; - }; - readonly required: readonly ['_id', 'email', 'name']; - }; - readonly kind: 'mongo-validator'; - readonly validationAction: 'error'; - readonly validationLevel: 'strict'; - }; - }; - }; - }; - }; - }; - readonly storageHash: StorageHash; - }>, + ContractType<{ readonly namespaces: { readonly __unbound__: { readonly id: "__unbound__"; readonly kind: "mongo-database"; readonly entries: { readonly collection: { readonly carts: { readonly indexes: readonly [{ readonly keys: readonly [{ readonly direction: 1; readonly field: "userId" }]; readonly kind: "mongo-index"; readonly unique: true }]; readonly kind: "mongo-collection"; readonly validator: { readonly jsonSchema: { readonly additionalProperties: false; readonly bsonType: "object"; readonly properties: { readonly _id: { readonly bsonType: "objectId" }; readonly items: { readonly bsonType: "array"; readonly items: { readonly additionalProperties: false; readonly bsonType: "object"; readonly properties: { readonly amount: { readonly bsonType: "int" }; readonly brand: { readonly bsonType: "string" }; readonly image: { readonly additionalProperties: false; readonly bsonType: "object"; readonly properties: { readonly url: { readonly bsonType: "string" } }; readonly required: readonly ["url"] }; readonly name: { readonly bsonType: "string" }; readonly price: { readonly additionalProperties: false; readonly bsonType: "object"; readonly properties: { readonly amount: { readonly bsonType: "double" }; readonly currency: { readonly bsonType: "string" } }; readonly required: readonly ["amount", "currency"] }; readonly productId: { readonly bsonType: "string" } }; readonly required: readonly ["amount", "brand", "image", "name", "price", "productId"] } }; readonly userId: { readonly bsonType: "objectId" } }; readonly required: readonly ["_id", "items", "userId"] }; readonly kind: "mongo-validator"; readonly validationAction: "error"; readonly validationLevel: "strict" } }; readonly events: { readonly indexes: readonly [{ readonly keys: readonly [{ readonly direction: 1; readonly field: "userId" }, { readonly direction: -1; readonly field: "timestamp" }]; readonly kind: "mongo-index" }, { readonly expireAfterSeconds: 7776000; readonly keys: readonly [{ readonly direction: 1; readonly field: "timestamp" }]; readonly kind: "mongo-index" }]; readonly kind: "mongo-collection"; readonly validator: { readonly jsonSchema: { readonly bsonType: "object"; readonly oneOf: readonly [{ readonly additionalProperties: false; readonly properties: { readonly _id: { readonly bsonType: "objectId" }; readonly brand: { readonly bsonType: "string" }; readonly exitMethod: { readonly bsonType: readonly ["null", "string"] }; readonly productId: { readonly bsonType: "string" }; readonly sessionId: { readonly bsonType: "string" }; readonly subCategory: { readonly bsonType: "string" }; readonly timestamp: { readonly bsonType: "date" }; readonly type: { readonly enum: readonly ["view-product"] }; readonly userId: { readonly bsonType: "string" } }; readonly required: readonly ["brand", "productId", "subCategory", "type"] }, { readonly additionalProperties: false; readonly properties: { readonly _id: { readonly bsonType: "objectId" }; readonly query: { readonly bsonType: "string" }; readonly sessionId: { readonly bsonType: "string" }; readonly timestamp: { readonly bsonType: "date" }; readonly type: { readonly enum: readonly ["search"] }; readonly userId: { readonly bsonType: "string" } }; readonly required: readonly ["query", "type"] }, { readonly additionalProperties: false; readonly properties: { readonly _id: { readonly bsonType: "objectId" }; readonly brand: { readonly bsonType: "string" }; readonly productId: { readonly bsonType: "string" }; readonly sessionId: { readonly bsonType: "string" }; readonly timestamp: { readonly bsonType: "date" }; readonly type: { readonly enum: readonly ["add-to-cart"] }; readonly userId: { readonly bsonType: "string" } }; readonly required: readonly ["brand", "productId", "type"] }]; readonly properties: { readonly _id: { readonly bsonType: "objectId" }; readonly sessionId: { readonly bsonType: "string" }; readonly timestamp: { readonly bsonType: "date" }; readonly type: { readonly bsonType: "string" }; readonly userId: { readonly bsonType: "string" } }; readonly required: readonly ["_id", "sessionId", "timestamp", "type", "userId"] }; readonly kind: "mongo-validator"; readonly validationAction: "error"; readonly validationLevel: "strict" } }; readonly invoices: { readonly indexes: readonly [{ readonly keys: readonly [{ readonly direction: 1; readonly field: "orderId" }]; readonly kind: "mongo-index" }, { readonly keys: readonly [{ readonly direction: -1; readonly field: "issuedAt" }]; readonly kind: "mongo-index"; readonly sparse: true }]; readonly kind: "mongo-collection"; readonly validator: { readonly jsonSchema: { readonly additionalProperties: false; readonly bsonType: "object"; readonly properties: { readonly _id: { readonly bsonType: "objectId" }; readonly issuedAt: { readonly bsonType: "date" }; readonly items: { readonly bsonType: "array"; readonly items: { readonly additionalProperties: false; readonly bsonType: "object"; readonly properties: { readonly amount: { readonly bsonType: "int" }; readonly lineTotal: { readonly bsonType: "double" }; readonly name: { readonly bsonType: "string" }; readonly unitPrice: { readonly bsonType: "double" } }; readonly required: readonly ["amount", "lineTotal", "name", "unitPrice"] } }; readonly orderId: { readonly bsonType: "objectId" }; readonly subtotal: { readonly bsonType: "double" }; readonly tax: { readonly bsonType: "double" }; readonly total: { readonly bsonType: "double" } }; readonly required: readonly ["_id", "issuedAt", "items", "orderId", "subtotal", "tax", "total"] }; readonly kind: "mongo-validator"; readonly validationAction: "error"; readonly validationLevel: "strict" } }; readonly locations: { readonly indexes: readonly [{ readonly collation: { readonly locale: "en"; readonly strength: 2 }; readonly keys: readonly [{ readonly direction: 1; readonly field: "city" }, { readonly direction: 1; readonly field: "country" }]; readonly kind: "mongo-index" }]; readonly kind: "mongo-collection"; readonly validator: { readonly jsonSchema: { readonly additionalProperties: false; readonly bsonType: "object"; readonly properties: { readonly _id: { readonly bsonType: "objectId" }; readonly city: { readonly bsonType: "string" }; readonly country: { readonly bsonType: "string" }; readonly name: { readonly bsonType: "string" }; readonly postalCode: { readonly bsonType: "string" }; readonly streetAndNumber: { readonly bsonType: "string" } }; readonly required: readonly ["_id", "city", "country", "name", "postalCode", "streetAndNumber"] }; readonly kind: "mongo-validator"; readonly validationAction: "error"; readonly validationLevel: "strict" } }; readonly orders: { readonly indexes: readonly [{ readonly keys: readonly [{ readonly direction: 1; readonly field: "userId" }]; readonly kind: "mongo-index" }]; readonly kind: "mongo-collection"; readonly validator: { readonly jsonSchema: { readonly additionalProperties: false; readonly bsonType: "object"; readonly properties: { readonly _id: { readonly bsonType: "objectId" }; readonly items: { readonly bsonType: "array"; readonly items: { readonly additionalProperties: false; readonly bsonType: "object"; readonly properties: { readonly amount: { readonly bsonType: "int" }; readonly brand: { readonly bsonType: "string" }; readonly image: { readonly additionalProperties: false; readonly bsonType: "object"; readonly properties: { readonly url: { readonly bsonType: "string" } }; readonly required: readonly ["url"] }; readonly name: { readonly bsonType: "string" }; readonly price: { readonly additionalProperties: false; readonly bsonType: "object"; readonly properties: { readonly amount: { readonly bsonType: "double" }; readonly currency: { readonly bsonType: "string" } }; readonly required: readonly ["amount", "currency"] }; readonly productId: { readonly bsonType: "string" } }; readonly required: readonly ["amount", "brand", "image", "name", "price", "productId"] } }; readonly shippingAddress: { readonly bsonType: "string" }; readonly statusHistory: { readonly bsonType: "array"; readonly items: { readonly additionalProperties: false; readonly bsonType: "object"; readonly properties: { readonly status: { readonly bsonType: "string" }; readonly timestamp: { readonly bsonType: "date" } }; readonly required: readonly ["status", "timestamp"] } }; readonly type: { readonly bsonType: "string"; readonly enum: readonly ["delivery", "pickup"] }; readonly userId: { readonly bsonType: "objectId" } }; readonly required: readonly ["_id", "items", "shippingAddress", "statusHistory", "type", "userId"] }; readonly kind: "mongo-validator"; readonly validationAction: "error"; readonly validationLevel: "strict" } }; readonly products: { readonly indexes: readonly [{ readonly keys: readonly [{ readonly direction: "text"; readonly field: "name" }, { readonly direction: "text"; readonly field: "description" }]; readonly kind: "mongo-index"; readonly weights: { readonly description: 1; readonly name: 10 } }, { readonly keys: readonly [{ readonly direction: 1; readonly field: "brand" }, { readonly direction: 1; readonly field: "subCategory" }]; readonly kind: "mongo-index" }, { readonly keys: readonly [{ readonly direction: 1; readonly field: "primaryCategory" }, { readonly direction: 1; readonly field: "articleType" }]; readonly kind: "mongo-index" }, { readonly keys: readonly [{ readonly direction: "hashed"; readonly field: "code" }]; readonly kind: "mongo-index" }]; readonly kind: "mongo-collection"; readonly validator: { readonly jsonSchema: { readonly additionalProperties: false; readonly bsonType: "object"; readonly properties: { readonly _id: { readonly bsonType: "objectId" }; readonly articleType: { readonly bsonType: "string" }; readonly brand: { readonly bsonType: "string" }; readonly code: { readonly bsonType: "string" }; readonly description: { readonly bsonType: "string" }; readonly embedding: { readonly bsonType: "array"; readonly items: { readonly bsonType: "double" } }; readonly image: { readonly additionalProperties: false; readonly bsonType: "object"; readonly properties: { readonly url: { readonly bsonType: "string" } }; readonly required: readonly ["url"] }; readonly name: { readonly bsonType: "string" }; readonly price: { readonly additionalProperties: false; readonly bsonType: "object"; readonly properties: { readonly amount: { readonly bsonType: "double" }; readonly currency: { readonly bsonType: "string" } }; readonly required: readonly ["amount", "currency"] }; readonly primaryCategory: { readonly bsonType: "string" }; readonly status: { readonly bsonType: "string"; readonly enum: readonly ["active", "discontinued", "out-of-stock"] }; readonly subCategory: { readonly bsonType: "string" } }; readonly required: readonly ["_id", "articleType", "brand", "code", "description", "image", "name", "price", "primaryCategory", "status", "subCategory"] }; readonly kind: "mongo-validator"; readonly validationAction: "error"; readonly validationLevel: "strict" } }; readonly users: { readonly indexes: readonly [{ readonly keys: readonly [{ readonly direction: 1; readonly field: "email" }]; readonly kind: "mongo-index"; readonly unique: true }]; readonly kind: "mongo-collection"; readonly validator: { readonly jsonSchema: { readonly additionalProperties: false; readonly bsonType: "object"; readonly properties: { readonly _id: { readonly bsonType: "objectId" }; readonly address: { readonly oneOf: readonly [{ readonly bsonType: "null" }, { readonly additionalProperties: false; readonly bsonType: "object"; readonly properties: { readonly city: { readonly bsonType: "string" }; readonly country: { readonly bsonType: "string" }; readonly postalCode: { readonly bsonType: "string" }; readonly streetAndNumber: { readonly bsonType: "string" } }; readonly required: readonly ["city", "country", "postalCode", "streetAndNumber"] }] }; readonly email: { readonly bsonType: "string" }; readonly name: { readonly bsonType: "string" } }; readonly required: readonly ["_id", "email", "name"] }; readonly kind: "mongo-validator"; readonly validationAction: "error"; readonly validationLevel: "strict" } } }; readonly valueObject: { readonly Address: { readonly fields: { readonly city: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly country: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly postalCode: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly streetAndNumber: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } } } }; readonly CartItem: { readonly fields: { readonly amount: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/int32@1" } }; readonly brand: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly image: { readonly nullable: false; readonly type: { readonly kind: "valueObject"; readonly plane: "storage"; readonly namespaceId: "__unbound__"; readonly entityKind: "valueObject"; readonly entityName: "Image" } }; readonly name: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly price: { readonly nullable: false; readonly type: { readonly kind: "valueObject"; readonly plane: "storage"; readonly namespaceId: "__unbound__"; readonly entityKind: "valueObject"; readonly entityName: "Price" } }; readonly productId: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } } } }; readonly Image: { readonly fields: { readonly url: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } } } }; readonly InvoiceLineItem: { readonly fields: { readonly amount: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/int32@1" } }; readonly lineTotal: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/double@1" } }; readonly name: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly unitPrice: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/double@1" } } } }; readonly OrderLineItem: { readonly fields: { readonly amount: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/int32@1" } }; readonly brand: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly image: { readonly nullable: false; readonly type: { readonly kind: "valueObject"; readonly plane: "storage"; readonly namespaceId: "__unbound__"; readonly entityKind: "valueObject"; readonly entityName: "Image" } }; readonly name: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly price: { readonly nullable: false; readonly type: { readonly kind: "valueObject"; readonly plane: "storage"; readonly namespaceId: "__unbound__"; readonly entityKind: "valueObject"; readonly entityName: "Price" } }; readonly productId: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } } } }; readonly Price: { readonly fields: { readonly amount: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/double@1" } }; readonly currency: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } } } }; readonly StatusEntry: { readonly fields: { readonly status: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly timestamp: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/date@1" } } } } } } } }; readonly storageHash: StorageHash }>, 'roots' | 'domain' > & { - readonly target: 'mongo'; - readonly targetFamily: 'mongo'; - readonly roots: { - readonly carts: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'Cart' }; - readonly events: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'Event' }; - readonly invoices: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Invoice'; - }; - readonly locations: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Location'; - }; - readonly orders: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'Order' }; - readonly products: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Product'; - }; - readonly users: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'User' }; - }; + readonly target: "mongo"; + readonly targetFamily: "mongo"; + readonly roots: { readonly carts: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Cart" }; readonly events: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Event" }; readonly invoices: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Invoice" }; readonly locations: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Location" }; readonly orders: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Order" }; readonly products: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Product" }; readonly users: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "User" } }; readonly domain: { readonly namespaces: { readonly __unbound__: { - readonly models: { - readonly AddToCartEvent: { - readonly fields: { - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - readonly relations: Record; - readonly storage: { readonly collection: 'events' }; - readonly base: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Event'; - }; - }; - readonly Cart: { - readonly fields: { - readonly _id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - readonly items: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CartItem' }; - readonly many: { readonly elementNullable: false }; - }; - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - }; - readonly relations: { - readonly user: { - readonly to: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'User'; - }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['userId']; - readonly targetFields: readonly ['_id']; - }; - }; - }; - readonly storage: { readonly collection: 'carts' }; - }; - readonly Event: { - readonly fields: { - readonly _id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - readonly sessionId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly timestamp: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; - }; - readonly type: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - readonly relations: Record; - readonly storage: { readonly collection: 'events' }; - readonly discriminator: { readonly field: 'type' }; - readonly variants: { - readonly 'add-to-cart': { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'AddToCartEvent'; - }; - readonly search: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'SearchEvent'; - }; - readonly 'view-product': { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'ViewProductEvent'; - }; - }; - }; - readonly Invoice: { - readonly fields: { - readonly _id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - readonly issuedAt: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; - }; - readonly items: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'InvoiceLineItem' }; - readonly many: { readonly elementNullable: false }; - }; - readonly orderId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - readonly subtotal: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly tax: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly total: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - }; - readonly relations: { - readonly order: { - readonly to: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Order'; - }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['orderId']; - readonly targetFields: readonly ['_id']; - }; - }; - }; - readonly storage: { readonly collection: 'invoices' }; - }; - readonly Location: { - readonly fields: { - readonly _id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly postalCode: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly streetAndNumber: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - readonly relations: Record; - readonly storage: { readonly collection: 'locations' }; - }; - readonly Order: { - readonly fields: { - readonly _id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - readonly items: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'OrderLineItem' }; - readonly many: { readonly elementNullable: false }; - }; - readonly shippingAddress: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly statusHistory: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'StatusEntry' }; - readonly many: { readonly elementNullable: false }; - }; - readonly type: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - }; - readonly relations: { - readonly invoices: { - readonly to: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Invoice'; - }; - readonly cardinality: '1:N'; - readonly on: { - readonly localFields: readonly ['_id']; - readonly targetFields: readonly ['orderId']; - }; - }; - readonly user: { - readonly to: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'User'; - }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['userId']; - readonly targetFields: readonly ['_id']; - }; - }; - }; - readonly storage: { readonly collection: 'orders' }; - }; - readonly Product: { - readonly fields: { - readonly _id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - readonly articleType: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly code: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly description: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly embedding: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - readonly many: { readonly elementNullable: false }; - }; - readonly image: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly price: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; - }; - readonly primaryCategory: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly status: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly subCategory: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - readonly relations: Record; - readonly storage: { readonly collection: 'products' }; - }; - readonly SearchEvent: { - readonly fields: { - readonly query: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - readonly relations: Record; - readonly storage: { readonly collection: 'events' }; - readonly base: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Event'; - }; - }; - readonly User: { - readonly fields: { - readonly _id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - readonly address: { - readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; - }; - readonly email: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - readonly relations: { - readonly carts: { - readonly to: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Cart'; - }; - readonly cardinality: '1:N'; - readonly on: { - readonly localFields: readonly ['_id']; - readonly targetFields: readonly ['userId']; - }; - }; - readonly orders: { - readonly to: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Order'; - }; - readonly cardinality: '1:N'; - readonly on: { - readonly localFields: readonly ['_id']; - readonly targetFields: readonly ['userId']; - }; - }; - }; - readonly storage: { readonly collection: 'users' }; - }; - readonly ViewProductEvent: { - readonly fields: { - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly exitMethod: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly subCategory: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - readonly relations: Record; - readonly storage: { readonly collection: 'events' }; - readonly base: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Event'; - }; - }; - }; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly postalCode: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly streetAndNumber: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly CartItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly image: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly price: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Image: { - readonly fields: { - readonly url: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly InvoiceLineItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly lineTotal: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly unitPrice: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - }; - }; - readonly OrderLineItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly image: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly price: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Price: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly currency: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly StatusEntry: { - readonly fields: { - readonly status: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly timestamp: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; - }; - }; - }; - }; - readonly enum: { - readonly OrderType: { - readonly codecId: 'mongo/string@1'; - readonly members: readonly [ - { readonly name: 'Delivery'; readonly value: 'delivery' }, - { readonly name: 'Pickup'; readonly value: 'pickup' }, - ]; - }; - readonly ProductStatus: { - readonly codecId: 'mongo/string@1'; - readonly members: readonly [ - { readonly name: 'Active'; readonly value: 'active' }, - { readonly name: 'Discontinued'; readonly value: 'discontinued' }, - { readonly name: 'OutOfStock'; readonly value: 'out-of-stock' }, - ]; - }; - }; - }; - }; - }; - readonly capabilities: {}; - readonly extensions: {}; - readonly meta: {}; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly postalCode: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly streetAndNumber: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly CartItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly image: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly price: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Image: { - readonly fields: { - readonly url: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly InvoiceLineItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly lineTotal: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly unitPrice: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - }; - }; - readonly OrderLineItem: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly brand: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly image: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Image' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly price: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Price' }; - }; - readonly productId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Price: { - readonly fields: { - readonly amount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly currency: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly StatusEntry: { - readonly fields: { - readonly status: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly timestamp: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; - }; + readonly models: { readonly AddToCartEvent: { readonly fields: { readonly brand: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly productId: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } } }; readonly relations: Record; readonly storage: { readonly collection: "events"; readonly fields: { readonly brand: { readonly field: "brand" }; readonly productId: { readonly field: "productId" } } }; readonly base: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Event" } }; readonly Cart: { readonly fields: { readonly _id: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/objectId@1" } }; readonly items: { readonly nullable: false; readonly type: { readonly kind: "valueObject"; readonly plane: "storage"; readonly namespaceId: "__unbound__"; readonly entityKind: "valueObject"; readonly entityName: "CartItem" }; readonly many: { readonly elementNullable: false } }; readonly userId: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/objectId@1" } } }; readonly relations: { readonly user: { readonly to: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "User" }; readonly cardinality: "N:1"; readonly nullable: false; readonly on: { readonly localFields: readonly ["userId"]; readonly targetFields: readonly ["_id"] } } }; readonly storage: { readonly collection: "carts"; readonly fields: { readonly _id: { readonly field: "_id" }; readonly items: { readonly field: "items" }; readonly userId: { readonly field: "userId" } } } }; readonly Event: { readonly fields: { readonly _id: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/objectId@1" } }; readonly sessionId: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly timestamp: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/date@1" } }; readonly type: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly userId: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } } }; readonly relations: Record; readonly storage: { readonly collection: "events"; readonly fields: { readonly _id: { readonly field: "_id" }; readonly sessionId: { readonly field: "sessionId" }; readonly timestamp: { readonly field: "timestamp" }; readonly type: { readonly field: "type" }; readonly userId: { readonly field: "userId" } } }; readonly discriminator: { readonly field: "type" }; readonly variants: { readonly "add-to-cart": { readonly namespace: "__unbound__" & NamespaceId; readonly model: "AddToCartEvent" }; readonly search: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "SearchEvent" }; readonly "view-product": { readonly namespace: "__unbound__" & NamespaceId; readonly model: "ViewProductEvent" } } }; readonly Invoice: { readonly fields: { readonly _id: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/objectId@1" } }; readonly issuedAt: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/date@1" } }; readonly items: { readonly nullable: false; readonly type: { readonly kind: "valueObject"; readonly plane: "storage"; readonly namespaceId: "__unbound__"; readonly entityKind: "valueObject"; readonly entityName: "InvoiceLineItem" }; readonly many: { readonly elementNullable: false } }; readonly orderId: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/objectId@1" } }; readonly subtotal: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/double@1" } }; readonly tax: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/double@1" } }; readonly total: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/double@1" } } }; readonly relations: { readonly order: { readonly to: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Order" }; readonly cardinality: "N:1"; readonly nullable: false; readonly on: { readonly localFields: readonly ["orderId"]; readonly targetFields: readonly ["_id"] } } }; readonly storage: { readonly collection: "invoices"; readonly fields: { readonly _id: { readonly field: "_id" }; readonly issuedAt: { readonly field: "issuedAt" }; readonly items: { readonly field: "items" }; readonly orderId: { readonly field: "orderId" }; readonly subtotal: { readonly field: "subtotal" }; readonly tax: { readonly field: "tax" }; readonly total: { readonly field: "total" } } } }; readonly Location: { readonly fields: { readonly _id: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/objectId@1" } }; readonly city: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly country: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly name: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly postalCode: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly streetAndNumber: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } } }; readonly relations: Record; readonly storage: { readonly collection: "locations"; readonly fields: { readonly _id: { readonly field: "_id" }; readonly city: { readonly field: "city" }; readonly country: { readonly field: "country" }; readonly name: { readonly field: "name" }; readonly postalCode: { readonly field: "postalCode" }; readonly streetAndNumber: { readonly field: "streetAndNumber" } } } }; readonly Order: { readonly fields: { readonly _id: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/objectId@1" } }; readonly items: { readonly nullable: false; readonly type: { readonly kind: "valueObject"; readonly plane: "storage"; readonly namespaceId: "__unbound__"; readonly entityKind: "valueObject"; readonly entityName: "OrderLineItem" }; readonly many: { readonly elementNullable: false } }; readonly shippingAddress: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly statusHistory: { readonly nullable: false; readonly type: { readonly kind: "valueObject"; readonly plane: "storage"; readonly namespaceId: "__unbound__"; readonly entityKind: "valueObject"; readonly entityName: "StatusEntry" }; readonly many: { readonly elementNullable: false } }; readonly type: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly userId: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/objectId@1" } } }; readonly relations: { readonly invoices: { readonly to: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Invoice" }; readonly cardinality: "1:N"; readonly on: { readonly localFields: readonly ["_id"]; readonly targetFields: readonly ["orderId"] } }; readonly user: { readonly to: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "User" }; readonly cardinality: "N:1"; readonly nullable: false; readonly on: { readonly localFields: readonly ["userId"]; readonly targetFields: readonly ["_id"] } } }; readonly storage: { readonly collection: "orders"; readonly fields: { readonly _id: { readonly field: "_id" }; readonly items: { readonly field: "items" }; readonly shippingAddress: { readonly field: "shippingAddress" }; readonly statusHistory: { readonly field: "statusHistory" }; readonly type: { readonly field: "type" }; readonly userId: { readonly field: "userId" } } } }; readonly Product: { readonly fields: { readonly _id: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/objectId@1" } }; readonly articleType: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly brand: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly code: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly description: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly embedding: { readonly nullable: true; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/double@1" }; readonly many: { readonly elementNullable: false } }; readonly image: { readonly nullable: false; readonly type: { readonly kind: "valueObject"; readonly plane: "storage"; readonly namespaceId: "__unbound__"; readonly entityKind: "valueObject"; readonly entityName: "Image" } }; readonly name: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly price: { readonly nullable: false; readonly type: { readonly kind: "valueObject"; readonly plane: "storage"; readonly namespaceId: "__unbound__"; readonly entityKind: "valueObject"; readonly entityName: "Price" } }; readonly primaryCategory: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly status: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly subCategory: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } } }; readonly relations: Record; readonly storage: { readonly collection: "products"; readonly fields: { readonly _id: { readonly field: "_id" }; readonly articleType: { readonly field: "articleType" }; readonly brand: { readonly field: "brand" }; readonly code: { readonly field: "code" }; readonly description: { readonly field: "description" }; readonly embedding: { readonly field: "embedding" }; readonly image: { readonly field: "image" }; readonly name: { readonly field: "name" }; readonly price: { readonly field: "price" }; readonly primaryCategory: { readonly field: "primaryCategory" }; readonly status: { readonly field: "status" }; readonly subCategory: { readonly field: "subCategory" } } } }; readonly SearchEvent: { readonly fields: { readonly query: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } } }; readonly relations: Record; readonly storage: { readonly collection: "events"; readonly fields: { readonly query: { readonly field: "query" } } }; readonly base: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Event" } }; readonly User: { readonly fields: { readonly _id: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/objectId@1" } }; readonly address: { readonly nullable: true; readonly type: { readonly kind: "valueObject"; readonly plane: "storage"; readonly namespaceId: "__unbound__"; readonly entityKind: "valueObject"; readonly entityName: "Address" } }; readonly email: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly name: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } } }; readonly relations: { readonly carts: { readonly to: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Cart" }; readonly cardinality: "1:N"; readonly on: { readonly localFields: readonly ["_id"]; readonly targetFields: readonly ["userId"] } }; readonly orders: { readonly to: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Order" }; readonly cardinality: "1:N"; readonly on: { readonly localFields: readonly ["_id"]; readonly targetFields: readonly ["userId"] } } }; readonly storage: { readonly collection: "users"; readonly fields: { readonly _id: { readonly field: "_id" }; readonly address: { readonly field: "address" }; readonly email: { readonly field: "email" }; readonly name: { readonly field: "name" } } } }; readonly ViewProductEvent: { readonly fields: { readonly brand: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly exitMethod: { readonly nullable: true; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly productId: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly subCategory: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } } }; readonly relations: Record; readonly storage: { readonly collection: "events"; readonly fields: { readonly brand: { readonly field: "brand" }; readonly exitMethod: { readonly field: "exitMethod" }; readonly productId: { readonly field: "productId" }; readonly subCategory: { readonly field: "subCategory" } } }; readonly base: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Event" } } }; + readonly enum: { readonly OrderType: { readonly codecId: "mongo/string@1"; readonly members: readonly [{ readonly name: "Delivery"; readonly value: "delivery" }, { readonly name: "Pickup"; readonly value: "pickup" }] }; readonly ProductStatus: { readonly codecId: "mongo/string@1"; readonly members: readonly [{ readonly name: "Active"; readonly value: "active" }, { readonly name: "Discontinued"; readonly value: "discontinued" }, { readonly name: "OutOfStock"; readonly value: "out-of-stock" }] } }; + readonly enumMemberTypes?: { readonly OrderType: readonly [{ readonly name: "Delivery"; readonly value: CodecTypes["mongo/string@1"]["output"] }, { readonly name: "Pickup"; readonly value: CodecTypes["mongo/string@1"]["output"] }]; readonly ProductStatus: readonly [{ readonly name: "Active"; readonly value: CodecTypes["mongo/string@1"]["output"] }, { readonly name: "Discontinued"; readonly value: CodecTypes["mongo/string@1"]["output"] }, { readonly name: "OutOfStock"; readonly value: CodecTypes["mongo/string@1"]["output"] }] }; }; }; }; + readonly capabilities: { }; + readonly extensions: { }; + readonly meta: { }; readonly profileHash: ProfileHash; }; diff --git a/packages/2-mongo-family/9-family/test/fixtures/orm-contract.d.ts b/packages/2-mongo-family/9-family/test/fixtures/orm-contract.d.ts index 7657e10fc0b4..3c15fbda8e75 100644 --- a/packages/2-mongo-family/9-family/test/fixtures/orm-contract.d.ts +++ b/packages/2-mongo-family/9-family/test/fixtures/orm-contract.d.ts @@ -1,7 +1,7 @@ // ⚠️ GENERATED FILE - DO NOT EDIT // This file is automatically generated by 'prisma contract emit'. // To regenerate, run: prisma contract emit -import type { CodecTypes as MongoCodecTypes } from '@internal/target-mongo/codec-types'; +import type { CodecTypes as MongoCodecTypes } from '../../../../3-targets/mongo/1-target/src/exports/codec-types'; import type { MongoCollection, @@ -12,143 +12,79 @@ import type { import type { Contract as ContractType, ExecutionHashBase, + FieldTypesFromDomain, NamespaceId, ProfileHashBase, StorageHashBase, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; -export type StorageHash = StorageHashBase<'orm-storage'>; +export type StorageHash = StorageHashBase<"orm-storage">; export type ExecutionHash = ExecutionHashBase; -export type ProfileHash = ProfileHashBase<'orm-profile'>; +export type ProfileHash = ProfileHashBase<"orm-profile">; export type CodecTypes = MongoCodecTypes; -export type HomeAddressOutput = { - readonly city: CodecTypes['mongo/string@1']['output']; - readonly country: CodecTypes['mongo/string@1']['output']; -}; -export type HomeAddressInput = { - readonly city: CodecTypes['mongo/string@1']['input']; - readonly country: CodecTypes['mongo/string@1']['input']; -}; -export type FieldOutputTypes = { - readonly __unbound__: { - readonly Address: { - readonly street: CodecTypes['mongo/string@1']['output']; - readonly city: CodecTypes['mongo/string@1']['output']; - readonly zip: CodecTypes['mongo/string@1']['output']; - }; - readonly Bug: { readonly severity: CodecTypes['mongo/string@1']['output'] }; - readonly Comment: { - readonly _id: CodecTypes['mongo/objectId@1']['output']; - readonly text: CodecTypes['mongo/string@1']['output']; - readonly createdAt: CodecTypes['mongo/date@1']['output']; - }; - readonly Feature: { - readonly priority: CodecTypes['mongo/string@1']['output']; - readonly targetRelease: CodecTypes['mongo/string@1']['output']; - }; - readonly Task: { - readonly _id: CodecTypes['mongo/objectId@1']['output']; - readonly title: CodecTypes['mongo/string@1']['output']; - readonly type: CodecTypes['mongo/string@1']['output']; - readonly assigneeId: CodecTypes['mongo/objectId@1']['output']; - }; - readonly User: { - readonly _id: CodecTypes['mongo/objectId@1']['output']; - readonly name: CodecTypes['mongo/string@1']['output']; - readonly email: CodecTypes['mongo/string@1']['output']; - readonly loginCount: CodecTypes['mongo/int32@1']['output']; - readonly tags: ReadonlyArray; - readonly homeAddress: HomeAddressOutput | null; - }; - }; -}; -export type FieldInputTypes = { - readonly __unbound__: { - readonly Address: { - readonly street: CodecTypes['mongo/string@1']['input']; - readonly city: CodecTypes['mongo/string@1']['input']; - readonly zip: CodecTypes['mongo/string@1']['input']; - }; - readonly Bug: { readonly severity: CodecTypes['mongo/string@1']['input'] }; - readonly Comment: { - readonly _id: CodecTypes['mongo/objectId@1']['input']; - readonly text: CodecTypes['mongo/string@1']['input']; - readonly createdAt: CodecTypes['mongo/date@1']['input']; - }; - readonly Feature: { - readonly priority: CodecTypes['mongo/string@1']['input']; - readonly targetRelease: CodecTypes['mongo/string@1']['input']; - }; - readonly Task: { - readonly _id: CodecTypes['mongo/objectId@1']['input']; - readonly title: CodecTypes['mongo/string@1']['input']; - readonly type: CodecTypes['mongo/string@1']['input']; - readonly assigneeId: CodecTypes['mongo/objectId@1']['input']; - }; - readonly User: { - readonly _id: CodecTypes['mongo/objectId@1']['input']; - readonly name: CodecTypes['mongo/string@1']['input']; - readonly email: CodecTypes['mongo/string@1']['input']; - readonly loginCount: CodecTypes['mongo/int32@1']['input']; - readonly tags: ReadonlyArray; - readonly homeAddress: HomeAddressInput | null; - }; - }; -}; +type ParameterizedOutputTypeFunctions = never; +type ValueObjectOutputTypes = ValueObjectTypesFromStorage; +type ValueObjectInputTypes = ValueObjectTypesFromStorage; +export type HomeAddressOutput = ValueObjectOutputTypes["__unbound__"]["HomeAddress"]; +export type HomeAddressInput = ValueObjectInputTypes["__unbound__"]["HomeAddress"]; +type FieldTypeOverrides = { }; +export type FieldOutputTypes = FieldTypesFromDomain; +export type FieldInputTypes = FieldTypesFromDomain; export namespace Models { export type unbound_Task = { - _id: CodecTypes['mongo/objectId@1']['output']; - title: CodecTypes['mongo/string@1']['output']; - type: 'bug' | 'feature'; - assigneeId: CodecTypes['mongo/objectId@1']['output']; + _id: FieldOutputTypes["__unbound__"]["Task"]["_id"]; + title: FieldOutputTypes["__unbound__"]["Task"]["title"]; + type: "bug" | "feature"; + assigneeId: FieldOutputTypes["__unbound__"]["Task"]["assigneeId"]; comments: unbound_Comment[]; assignee: unbound_User; - readonly [RelationKeys]?: 'assignee'; + readonly [RelationKeys]?: "assignee"; }; export type unbound_Bug = { - _id: CodecTypes['mongo/objectId@1']['output']; - title: CodecTypes['mongo/string@1']['output']; - type: 'bug'; - assigneeId: CodecTypes['mongo/objectId@1']['output']; - severity: CodecTypes['mongo/string@1']['output']; + _id: FieldOutputTypes["__unbound__"]["Task"]["_id"]; + title: FieldOutputTypes["__unbound__"]["Task"]["title"]; + type: "bug"; + assigneeId: FieldOutputTypes["__unbound__"]["Task"]["assigneeId"]; + severity: FieldOutputTypes["__unbound__"]["Bug"]["severity"]; comments: unbound_Comment[]; assignee: unbound_User; - readonly [RelationKeys]?: 'assignee'; + readonly [RelationKeys]?: "assignee"; }; export type unbound_Feature = { - _id: CodecTypes['mongo/objectId@1']['output']; - title: CodecTypes['mongo/string@1']['output']; - type: 'feature'; - assigneeId: CodecTypes['mongo/objectId@1']['output']; - priority: CodecTypes['mongo/string@1']['output']; - targetRelease: CodecTypes['mongo/string@1']['output']; + _id: FieldOutputTypes["__unbound__"]["Task"]["_id"]; + title: FieldOutputTypes["__unbound__"]["Task"]["title"]; + type: "feature"; + assigneeId: FieldOutputTypes["__unbound__"]["Task"]["assigneeId"]; + priority: FieldOutputTypes["__unbound__"]["Feature"]["priority"]; + targetRelease: FieldOutputTypes["__unbound__"]["Feature"]["targetRelease"]; comments: unbound_Comment[]; assignee: unbound_User; - readonly [RelationKeys]?: 'assignee'; + readonly [RelationKeys]?: "assignee"; }; export type unbound_User = { - _id: CodecTypes['mongo/objectId@1']['output']; - name: CodecTypes['mongo/string@1']['output']; - email: CodecTypes['mongo/string@1']['output']; - loginCount: CodecTypes['mongo/int32@1']['output']; - tags: ReadonlyArray; - homeAddress: HomeAddressOutput | null; + _id: FieldOutputTypes["__unbound__"]["User"]["_id"]; + name: FieldOutputTypes["__unbound__"]["User"]["name"]; + email: FieldOutputTypes["__unbound__"]["User"]["email"]; + loginCount: FieldOutputTypes["__unbound__"]["User"]["loginCount"]; + tags: FieldOutputTypes["__unbound__"]["User"]["tags"]; + homeAddress: FieldOutputTypes["__unbound__"]["User"]["homeAddress"]; addresses: unbound_Address[]; tasks: unbound_AnyTask[]; - readonly [RelationKeys]?: 'tasks'; + readonly [RelationKeys]?: "tasks"; }; export type unbound_Address = { - street: CodecTypes['mongo/string@1']['output']; - city: CodecTypes['mongo/string@1']['output']; - zip: CodecTypes['mongo/string@1']['output']; + street: FieldOutputTypes["__unbound__"]["Address"]["street"]; + city: FieldOutputTypes["__unbound__"]["Address"]["city"]; + zip: FieldOutputTypes["__unbound__"]["Address"]["zip"]; }; export type unbound_Comment = { - _id: CodecTypes['mongo/objectId@1']['output']; - text: CodecTypes['mongo/string@1']['output']; - createdAt: CodecTypes['mongo/date@1']['output']; + _id: FieldOutputTypes["__unbound__"]["Comment"]["_id"]; + text: FieldOutputTypes["__unbound__"]["Comment"]["text"]; + createdAt: FieldOutputTypes["__unbound__"]["Comment"]["createdAt"]; }; export type unbound_AnyTask = unbound_Bug | unbound_Feature; } @@ -168,238 +104,22 @@ export declare const models: { export type TypeMaps = MongoTypeMaps; type ContractBase = Omit< - ContractType<{ - readonly namespaces: { - readonly __unbound__: { - readonly id: '__unbound__'; - readonly kind: 'mongo-namespace'; - readonly entries: { - readonly collection: { readonly tasks: MongoCollection; readonly users: MongoCollection }; - }; - }; - }; - readonly storageHash: StorageHash; - }>, + ContractType<{ readonly namespaces: { readonly __unbound__: { readonly id: "__unbound__"; readonly kind: "mongo-namespace"; readonly entries: { readonly collection: { readonly tasks: MongoCollection; readonly users: MongoCollection }; readonly valueObject: { readonly HomeAddress: { readonly fields: { readonly city: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly country: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } } } } } } } }; readonly storageHash: StorageHash }>, 'roots' | 'domain' > & { - readonly target: 'mongo'; - readonly targetFamily: 'mongo'; - readonly roots: { - readonly tasks: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'Task' }; - readonly users: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'User' }; - }; + readonly target: "mongo"; + readonly targetFamily: "mongo"; + readonly roots: { readonly tasks: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Task" }; readonly users: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "User" } }; readonly domain: { readonly namespaces: { readonly __unbound__: { - readonly models: { - readonly Address: { - readonly fields: { - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly zip: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - readonly relations: Record; - readonly storage: { readonly fields: { readonly street: { readonly field: 'street' }; readonly city: { readonly field: 'city' }; readonly zip: { readonly field: 'zip' } } }; - readonly owner: 'User'; - }; - readonly Bug: { - readonly fields: { - readonly severity: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - readonly relations: Record; - readonly storage: { readonly collection: 'tasks'; readonly fields: { readonly severity: { readonly field: 'severity' } } }; - readonly base: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Task'; - }; - }; - readonly Comment: { - readonly fields: { - readonly _id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly createdAt: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; - }; - }; - readonly relations: Record; - readonly storage: { readonly fields: { readonly _id: { readonly field: '_id' }; readonly text: { readonly field: 'text' }; readonly createdAt: { readonly field: 'createdAt' } } }; - readonly owner: 'Task'; - }; - readonly Feature: { - readonly fields: { - readonly priority: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly targetRelease: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - readonly relations: Record; - readonly storage: { readonly collection: 'tasks'; readonly fields: { readonly priority: { readonly field: 'priority' }; readonly targetRelease: { readonly field: 'targetRelease' } } }; - readonly base: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Task'; - }; - }; - readonly Task: { - readonly fields: { - readonly _id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - readonly title: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly type: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly assigneeId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - }; - readonly relations: { - readonly assignee: { - readonly to: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'User'; - }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['assigneeId']; - readonly targetFields: readonly ['_id']; - }; - }; - readonly comments: { - readonly to: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Comment'; - }; - readonly cardinality: '1:N'; - }; - }; - readonly storage: { readonly collection: 'tasks'; readonly fields: { readonly _id: { readonly field: '_id' }; readonly title: { readonly field: 'title' }; readonly type: { readonly field: 'type' }; readonly assigneeId: { readonly field: 'assigneeId' } }; readonly relations: { readonly comments: { readonly field: 'comments' } } }; - readonly discriminator: { readonly field: 'type' }; - readonly variants: { - readonly bug: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Bug'; - }; - readonly feature: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Feature'; - }; - }; - }; - readonly User: { - readonly fields: { - readonly _id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly email: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly loginCount: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - readonly tags: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - readonly many: { readonly elementNullable: false }; - }; - readonly homeAddress: { - readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'HomeAddress' }; - }; - }; - readonly relations: { - readonly addresses: { - readonly to: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Address'; - }; - readonly cardinality: '1:N'; - }; - readonly tasks: { - readonly to: { - readonly namespace: '__unbound__' & NamespaceId; - readonly model: 'Task'; - }; - readonly cardinality: '1:N'; - readonly on: { - readonly localFields: readonly ['_id']; - readonly targetFields: readonly ['assigneeId']; - }; - }; - }; - readonly storage: { readonly collection: 'users'; readonly fields: { readonly _id: { readonly field: '_id' }; readonly name: { readonly field: 'name' }; readonly email: { readonly field: 'email' }; readonly loginCount: { readonly field: 'loginCount' }; readonly tags: { readonly field: 'tags' }; readonly homeAddress: { readonly field: 'homeAddress' } }; readonly relations: { readonly addresses: { readonly field: 'addresses' } } }; - }; - }; - readonly valueObjects: { - readonly HomeAddress: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - }; - }; - }; - }; - readonly capabilities: {}; - readonly extensions: {}; - readonly meta: {}; - readonly valueObjects: { - readonly HomeAddress: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; + readonly models: { readonly Address: { readonly fields: { readonly street: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly city: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly zip: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } } }; readonly relations: Record; readonly storage: { readonly fields: { readonly street: { readonly field: "street" }; readonly city: { readonly field: "city" }; readonly zip: { readonly field: "zip" } } }; readonly owner: "User" }; readonly Bug: { readonly fields: { readonly severity: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } } }; readonly relations: Record; readonly storage: { readonly collection: "tasks"; readonly fields: { readonly severity: { readonly field: "severity" } } }; readonly base: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Task" } }; readonly Comment: { readonly fields: { readonly _id: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/objectId@1" } }; readonly text: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly createdAt: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/date@1" } } }; readonly relations: Record; readonly storage: { readonly fields: { readonly _id: { readonly field: "_id" }; readonly text: { readonly field: "text" }; readonly createdAt: { readonly field: "createdAt" } } }; readonly owner: "Task" }; readonly Feature: { readonly fields: { readonly priority: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly targetRelease: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } } }; readonly relations: Record; readonly storage: { readonly collection: "tasks"; readonly fields: { readonly priority: { readonly field: "priority" }; readonly targetRelease: { readonly field: "targetRelease" } } }; readonly base: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Task" } }; readonly Task: { readonly fields: { readonly _id: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/objectId@1" } }; readonly title: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly type: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly assigneeId: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/objectId@1" } } }; readonly relations: { readonly assignee: { readonly to: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "User" }; readonly cardinality: "N:1"; readonly nullable: false; readonly on: { readonly localFields: readonly ["assigneeId"]; readonly targetFields: readonly ["_id"] } }; readonly comments: { readonly to: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Comment" }; readonly cardinality: "1:N" } }; readonly storage: { readonly collection: "tasks"; readonly fields: { readonly _id: { readonly field: "_id" }; readonly title: { readonly field: "title" }; readonly type: { readonly field: "type" }; readonly assigneeId: { readonly field: "assigneeId" } }; readonly relations: { readonly comments: { readonly field: "comments" } } }; readonly discriminator: { readonly field: "type" }; readonly variants: { readonly bug: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Bug" }; readonly feature: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Feature" } } }; readonly User: { readonly fields: { readonly _id: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/objectId@1" } }; readonly name: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly email: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" } }; readonly loginCount: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/int32@1" } }; readonly tags: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "mongo/string@1" }; readonly many: { readonly elementNullable: false } }; readonly homeAddress: { readonly nullable: true; readonly type: { readonly kind: "valueObject"; readonly plane: "storage"; readonly namespaceId: "__unbound__"; readonly entityKind: "valueObject"; readonly entityName: "HomeAddress" } } }; readonly relations: { readonly addresses: { readonly to: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Address" }; readonly cardinality: "1:N" }; readonly tasks: { readonly to: { readonly namespace: "__unbound__" & NamespaceId; readonly model: "Task" }; readonly cardinality: "1:N"; readonly on: { readonly localFields: readonly ["_id"]; readonly targetFields: readonly ["assigneeId"] } } }; readonly storage: { readonly collection: "users"; readonly fields: { readonly _id: { readonly field: "_id" }; readonly name: { readonly field: "name" }; readonly email: { readonly field: "email" }; readonly loginCount: { readonly field: "loginCount" }; readonly tags: { readonly field: "tags" }; readonly homeAddress: { readonly field: "homeAddress" } }; readonly relations: { readonly addresses: { readonly field: "addresses" } } } } }; }; }; }; + readonly capabilities: { }; + readonly extensions: { }; + readonly meta: { }; readonly profileHash: ProfileHash; }; diff --git a/packages/2-sql-family/4-lanes/sql-builder/test/fixtures/generated/contract.d.ts b/packages/2-sql-family/4-lanes/sql-builder/test/fixtures/generated/contract.d.ts index 80b3d3eda96a..3d9c0e6b5f97 100644 --- a/packages/2-sql-family/4-lanes/sql-builder/test/fixtures/generated/contract.d.ts +++ b/packages/2-sql-family/4-lanes/sql-builder/test/fixtures/generated/contract.d.ts @@ -741,7 +741,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/packages/3-extensions/paradedb/migrations/snapshots/0c0734babd6eeb868fee1f281ca96963022475611560e9f170f465daa35f8599/contract.d.ts b/packages/3-extensions/paradedb/migrations/snapshots/0c0734babd6eeb868fee1f281ca96963022475611560e9f170f465daa35f8599/contract.d.ts index fc1798b6c3b0..7ce9da127672 100644 --- a/packages/3-extensions/paradedb/migrations/snapshots/0c0734babd6eeb868fee1f281ca96963022475611560e9f170f465daa35f8599/contract.d.ts +++ b/packages/3-extensions/paradedb/migrations/snapshots/0c0734babd6eeb868fee1f281ca96963022475611560e9f170f465daa35f8599/contract.d.ts @@ -336,7 +336,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/packages/3-extensions/paradedb/src/contract.d.ts b/packages/3-extensions/paradedb/src/contract.d.ts index fc1798b6c3b0..7ce9da127672 100644 --- a/packages/3-extensions/paradedb/src/contract.d.ts +++ b/packages/3-extensions/paradedb/src/contract.d.ts @@ -336,7 +336,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/packages/3-extensions/pgvector/migrations/snapshots/4a96b488a4ce92b434e5f7d6607b6435c0955f0d36b0018077045787764240e6/contract.d.ts b/packages/3-extensions/pgvector/migrations/snapshots/4a96b488a4ce92b434e5f7d6607b6435c0955f0d36b0018077045787764240e6/contract.d.ts index 64c2ccc4f849..d2894d94221c 100644 --- a/packages/3-extensions/pgvector/migrations/snapshots/4a96b488a4ce92b434e5f7d6607b6435c0955f0d36b0018077045787764240e6/contract.d.ts +++ b/packages/3-extensions/pgvector/migrations/snapshots/4a96b488a4ce92b434e5f7d6607b6435c0955f0d36b0018077045787764240e6/contract.d.ts @@ -344,7 +344,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/packages/3-extensions/pgvector/src/contract.d.ts b/packages/3-extensions/pgvector/src/contract.d.ts index 64c2ccc4f849..d2894d94221c 100644 --- a/packages/3-extensions/pgvector/src/contract.d.ts +++ b/packages/3-extensions/pgvector/src/contract.d.ts @@ -344,7 +344,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/packages/3-extensions/postgis/migrations/snapshots/2db551a8a5619d0d97710f29130eaea0eccbdd5238f015ee9d18408d438e0f8a/contract.d.ts b/packages/3-extensions/postgis/migrations/snapshots/2db551a8a5619d0d97710f29130eaea0eccbdd5238f015ee9d18408d438e0f8a/contract.d.ts index b56429d1fad7..dfcba852523b 100644 --- a/packages/3-extensions/postgis/migrations/snapshots/2db551a8a5619d0d97710f29130eaea0eccbdd5238f015ee9d18408d438e0f8a/contract.d.ts +++ b/packages/3-extensions/postgis/migrations/snapshots/2db551a8a5619d0d97710f29130eaea0eccbdd5238f015ee9d18408d438e0f8a/contract.d.ts @@ -344,7 +344,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/packages/3-extensions/postgis/src/contract.d.ts b/packages/3-extensions/postgis/src/contract.d.ts index b56429d1fad7..dfcba852523b 100644 --- a/packages/3-extensions/postgis/src/contract.d.ts +++ b/packages/3-extensions/postgis/src/contract.d.ts @@ -344,7 +344,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/collection-methods/generated/contract.d.ts b/packages/3-extensions/sql-orm-client/test/fixtures/collection-methods/generated/contract.d.ts index 98347ff79255..c3d606a20567 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/collection-methods/generated/contract.d.ts +++ b/packages/3-extensions/sql-orm-client/test/fixtures/collection-methods/generated/contract.d.ts @@ -28,7 +28,7 @@ import type { } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'8973aa4e3654dc5083f7ff15a7c187e3e590f977b4171bb816cd6566fa4db93b'>; + StorageHashBase<'87d224586379cdc42fda508d70ac88410eb9795e164b6031ef5a9dd9a5a1b759'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; @@ -244,7 +244,13 @@ type DefaultLiteralValue = CodecId extends keyo type ParameterizedOutputTypeFunctions = PgParameterizedOutputTypes | PgVectorParameterizedOutputTypes; -type FieldTypeOverrides = {}; +type FieldTypeOverrides = { + readonly public: { + readonly models: { + readonly Note: { readonly kind: { readonly output: 'memo'; readonly input: 'memo' } }; + }; + }; +}; export type FieldOutputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, @@ -259,7 +265,11 @@ export type FieldInputTypes = FieldTypesFromDomain< FieldTypeOverrides, 'input' >; -type ColumnTypeOverrides = {}; +type ColumnTypeOverrides = { + readonly public: { + readonly notes: { readonly kind: { readonly output: 'memo'; readonly input: 'memo' } }; + }; +}; export type StorageColumnTypes = ColumnTypesFromStorage< ContractBase['storage'], CodecTypes, @@ -451,6 +461,9 @@ type ContractBase = Omit< foreignKeys: readonly []; }; }; + readonly valueSet: { + readonly 'Note.kind': { readonly kind: 'valueSet'; readonly values: readonly ['memo'] }; + }; }; }; }; @@ -655,7 +668,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/collection-methods/generated/contract.json b/packages/3-extensions/sql-orm-client/test/fixtures/collection-methods/generated/contract.json index a13f0e70eabe..281f66221c23 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/collection-methods/generated/contract.json +++ b/packages/3-extensions/sql-orm-client/test/fixtures/collection-methods/generated/contract.json @@ -211,6 +211,13 @@ "entries": { "table": { "notes": { + "checks": [ + { + "expression": "\"kind\" IN ('memo')", + "name": "notes_kind_check_97646eca", + "prefix": "notes_kind_check" + } + ], "columns": { "body": { "codecId": "pg/text@1", @@ -225,7 +232,13 @@ "kind": { "codecId": "pg/text@1", "dataType": "pg/text", - "nullable": false + "nullable": false, + "valueSet": { + "entityKind": "valueSet", + "entityName": "Note.kind", + "namespaceId": "public", + "plane": "storage" + } }, "pinned": { "codecId": "pg/bool@1", @@ -339,13 +352,21 @@ }, "uniques": [] } + }, + "valueSet": { + "Note.kind": { + "kind": "valueSet", + "values": [ + "memo" + ] + } } }, "id": "public", "kind": "postgres-schema" } }, - "storageHash": "8973aa4e3654dc5083f7ff15a7c187e3e590f977b4171bb816cd6566fa4db93b" + "storageHash": "87d224586379cdc42fda508d70ac88410eb9795e164b6031ef5a9dd9a5a1b759" }, "capabilities": { "postgres": { diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/fragment-namespace/generated/contract.d.ts b/packages/3-extensions/sql-orm-client/test/fixtures/fragment-namespace/generated/contract.d.ts index 522738f53abe..fe71c759f155 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/fragment-namespace/generated/contract.d.ts +++ b/packages/3-extensions/sql-orm-client/test/fixtures/fragment-namespace/generated/contract.d.ts @@ -461,7 +461,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/generated/contract.d.ts b/packages/3-extensions/sql-orm-client/test/fixtures/generated/contract.d.ts index fb429f0fef68..327dea94d035 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/generated/contract.d.ts +++ b/packages/3-extensions/sql-orm-client/test/fixtures/generated/contract.d.ts @@ -38,7 +38,7 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; export type StorageHash = @@ -258,15 +258,15 @@ type DefaultLiteralValue = CodecId extends keyo : Encoded; type ParameterizedOutputTypeFunctions = PgParameterizedOutputTypes | PgVectorParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -280,14 +280,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< @@ -832,6 +834,24 @@ type ContractBase = Omit< ]; }; }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + }; + }; }; }; }; @@ -1150,7 +1170,13 @@ type ContractBase = Omit< readonly fields: { readonly address: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: 'public'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; }; readonly email: { readonly nullable: false; @@ -1316,24 +1342,6 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - }; - }; }; }; }; @@ -1427,24 +1435,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/generated/contract.json b/packages/3-extensions/sql-orm-client/test/fixtures/generated/contract.json index fe24243054be..d753a0f62643 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/generated/contract.json +++ b/packages/3-extensions/sql-orm-client/test/fixtures/generated/contract.json @@ -505,8 +505,11 @@ "address": { "nullable": true, "type": { + "entityKind": "valueObject", + "entityName": "Address", "kind": "valueObject", - "name": "Address" + "namespaceId": "public", + "plane": "storage" } }, "email": { @@ -778,33 +781,6 @@ "table": "user_tags" } } - }, - "valueObjects": { - "Address": { - "fields": { - "city": { - "nullable": false, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" - } - }, - "street": { - "nullable": false, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" - } - }, - "zip": { - "nullable": true, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" - } - } - } - } } } } @@ -1319,6 +1295,33 @@ } ] } + }, + "valueObject": { + "Address": { + "fields": { + "city": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "street": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "zip": { + "nullable": true, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + } + } + } } }, "id": "public", diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/junction-namespaces/generated/contract.d.ts b/packages/3-extensions/sql-orm-client/test/fixtures/junction-namespaces/generated/contract.d.ts index 73af750f4244..d0172b96d88d 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/junction-namespaces/generated/contract.d.ts +++ b/packages/3-extensions/sql-orm-client/test/fixtures/junction-namespaces/generated/contract.d.ts @@ -763,7 +763,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/on-update-only/generated/contract.d.ts b/packages/3-extensions/sql-orm-client/test/fixtures/on-update-only/generated/contract.d.ts index 78960b7aa687..7ef7151d6172 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/on-update-only/generated/contract.d.ts +++ b/packages/3-extensions/sql-orm-client/test/fixtures/on-update-only/generated/contract.d.ts @@ -428,7 +428,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/polymorphism/generated/contract.d.ts b/packages/3-extensions/sql-orm-client/test/fixtures/polymorphism/generated/contract.d.ts index 90af9e6edd9b..7886f911c7e7 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/polymorphism/generated/contract.d.ts +++ b/packages/3-extensions/sql-orm-client/test/fixtures/polymorphism/generated/contract.d.ts @@ -23,7 +23,7 @@ import type { } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'2ccba7c309ee13e4714b958e47eb88ca06415dac60cd475a51940499c3331f8a'>; + StorageHashBase<'00913b83286a9e6492e6621c3ba277633d43a309cdda39f9cb46f0a54752c1eb'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; @@ -237,7 +237,24 @@ type DefaultLiteralValue = CodecId extends keyo : Encoded; type ParameterizedOutputTypeFunctions = PgParameterizedOutputTypes; -type FieldTypeOverrides = {}; +type FieldTypeOverrides = { + readonly public: { + readonly models: { + readonly Task: { + readonly type: { + readonly output: 'bug' | 'epic' | 'feature'; + readonly input: 'bug' | 'epic' | 'feature'; + }; + }; + readonly User: { + readonly kind: { + readonly output: 'admin' | 'regular'; + readonly input: 'admin' | 'regular'; + }; + }; + }; + }; +}; export type FieldOutputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, @@ -252,7 +269,19 @@ export type FieldInputTypes = FieldTypesFromDomain< FieldTypeOverrides, 'input' >; -type ColumnTypeOverrides = {}; +type ColumnTypeOverrides = { + readonly public: { + readonly tasks: { + readonly type: { + readonly output: 'bug' | 'epic' | 'feature'; + readonly input: 'bug' | 'epic' | 'feature'; + }; + }; + readonly users: { + readonly kind: { readonly output: 'admin' | 'regular'; readonly input: 'admin' | 'regular' }; + }; + }; +}; export type StorageColumnTypes = ColumnTypesFromStorage< ContractBase['storage'], CodecTypes, @@ -852,6 +881,16 @@ type ContractBase = Omit< ]; }; }; + readonly valueSet: { + readonly 'Task.type': { + readonly kind: 'valueSet'; + readonly values: readonly ['bug', 'epic', 'feature']; + }; + readonly 'User.kind': { + readonly kind: 'valueSet'; + readonly values: readonly ['admin', 'regular']; + }; + }; }; }; }; @@ -1331,7 +1370,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/polymorphism/generated/contract.json b/packages/3-extensions/sql-orm-client/test/fixtures/polymorphism/generated/contract.json index c62e7950f392..fa8deeced427 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/polymorphism/generated/contract.json +++ b/packages/3-extensions/sql-orm-client/test/fixtures/polymorphism/generated/contract.json @@ -957,6 +957,13 @@ "uniques": [] }, "tasks": { + "checks": [ + { + "expression": "\"type\" IN ('bug', 'epic', 'feature')", + "name": "tasks_type_check_b3d5b43f", + "prefix": "tasks_type_check" + } + ], "columns": { "bug_assignee_person_id": { "codecId": "pg/int4@1", @@ -995,7 +1002,13 @@ "type": { "codecId": "pg/text@1", "dataType": "pg/text", - "nullable": false + "nullable": false, + "valueSet": { + "entityKind": "valueSet", + "entityName": "Task.type", + "namespaceId": "public", + "plane": "storage" + } } }, "foreignKeys": [ @@ -1124,6 +1137,13 @@ "uniques": [] }, "users": { + "checks": [ + { + "expression": "\"kind\" IN ('admin', 'regular')", + "name": "users_kind_check_dc69a8d4", + "prefix": "users_kind_check" + } + ], "columns": { "account_id": { "codecId": "pg/int4@1", @@ -1147,7 +1167,13 @@ "kind": { "codecId": "pg/text@1", "dataType": "pg/text", - "nullable": false + "nullable": false, + "valueSet": { + "entityKind": "valueSet", + "entityName": "User.kind", + "namespaceId": "public", + "plane": "storage" + } }, "name": { "codecId": "pg/text@1", @@ -1203,13 +1229,30 @@ }, "uniques": [] } + }, + "valueSet": { + "Task.type": { + "kind": "valueSet", + "values": [ + "bug", + "epic", + "feature" + ] + }, + "User.kind": { + "kind": "valueSet", + "values": [ + "admin", + "regular" + ] + } } }, "id": "public", "kind": "postgres-schema" } }, - "storageHash": "2ccba7c309ee13e4714b958e47eb88ca06415dac60cd475a51940499c3331f8a" + "storageHash": "00913b83286a9e6492e6621c3ba277633d43a309cdda39f9cb46f0a54752c1eb" }, "capabilities": { "postgres": { diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/scope-namespace/generated/contract.d.ts b/packages/3-extensions/sql-orm-client/test/fixtures/scope-namespace/generated/contract.d.ts index ffa93ebd92ef..bd82f339d292 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/scope-namespace/generated/contract.d.ts +++ b/packages/3-extensions/sql-orm-client/test/fixtures/scope-namespace/generated/contract.d.ts @@ -461,7 +461,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/soft-delete/generated/contract.d.ts b/packages/3-extensions/sql-orm-client/test/fixtures/soft-delete/generated/contract.d.ts index 64b6249f01d3..43aa0428e689 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/soft-delete/generated/contract.d.ts +++ b/packages/3-extensions/sql-orm-client/test/fixtures/soft-delete/generated/contract.d.ts @@ -20,11 +20,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'dfd3186b551a7868c3339bf4a5f80ca2a3ad4aa43a610cc64189db7c62a1e475'>; + StorageHashBase<'21a3ae2aab3f4f03b59fa65df3d452ec1d41d9ce5926b3fba6bd70cf39070dcb'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; @@ -237,15 +237,15 @@ type DefaultLiteralValue = CodecId extends keyo : CodecTypes[CodecId]['json'] : Encoded; type ParameterizedOutputTypeFunctions = PgParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -259,14 +259,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< @@ -539,6 +541,16 @@ type ContractBase = Omit< foreignKeys: readonly []; }; }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + }; + }; }; }; }; @@ -674,7 +686,13 @@ type ContractBase = Omit< readonly fields: { readonly addresses: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: 'public'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; readonly many: { readonly elementNullable: false }; }; readonly id: { @@ -740,16 +758,6 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - }; - }; }; }; }; @@ -782,16 +790,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/soft-delete/generated/contract.json b/packages/3-extensions/sql-orm-client/test/fixtures/soft-delete/generated/contract.json index d595f985f0e7..cd387f209afb 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/soft-delete/generated/contract.json +++ b/packages/3-extensions/sql-orm-client/test/fixtures/soft-delete/generated/contract.json @@ -204,8 +204,11 @@ }, "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "Address", "kind": "valueObject", - "name": "Address" + "namespaceId": "public", + "plane": "storage" } }, "id": { @@ -313,19 +316,6 @@ "table": "users" } } - }, - "valueObjects": { - "Address": { - "fields": { - "street": { - "nullable": false, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" - } - } - } - } } } } @@ -481,6 +471,11 @@ }, "tags": { "checks": [ + { + "expression": "jsonb_path_match(\"addresses\", 'strict $.type() == \"array\" && !exists($[*] ? (!((@.type() == \"object\" && exists(@.keyvalue() ? (@.key == \"street\" && (@.value.type() == \"string\"))) && !exists(@.keyvalue() ? (@.key != \"street\"))))))') IS TRUE", + "name": "tags_addresses_value_object_c752a33a", + "prefix": "tags_addresses_value_object" + }, { "expression": "array_position(\"labels\", NULL) IS NULL", "name": "tags_labels_elem_not_null_0c04ab5c", @@ -559,13 +554,26 @@ }, "uniques": [] } + }, + "valueObject": { + "Address": { + "fields": { + "street": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + } + } + } } }, "id": "public", "kind": "postgres-schema" } }, - "storageHash": "dfd3186b551a7868c3339bf4a5f80ca2a3ad4aa43a610cc64189db7c62a1e475" + "storageHash": "21a3ae2aab3f4f03b59fa65df3d452ec1d41d9ce5926b3fba6bd70cf39070dcb" }, "capabilities": { "postgres": { diff --git a/packages/3-extensions/supabase/src/contract/contract.d.ts b/packages/3-extensions/supabase/src/contract/contract.d.ts index f480e822713e..2dd25f5e125a 100644 --- a/packages/3-extensions/supabase/src/contract/contract.d.ts +++ b/packages/3-extensions/supabase/src/contract/contract.d.ts @@ -7503,7 +7503,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/packages/3-extensions/supabase/test/fixtures/example-app/contract.d.ts b/packages/3-extensions/supabase/test/fixtures/example-app/contract.d.ts index 4967bea29906..2b193f9a2ca2 100644 --- a/packages/3-extensions/supabase/test/fixtures/example-app/contract.d.ts +++ b/packages/3-extensions/supabase/test/fixtures/example-app/contract.d.ts @@ -428,7 +428,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/packages/3-extensions/supabase/test/fixtures/no-policy/contract.d.ts b/packages/3-extensions/supabase/test/fixtures/no-policy/contract.d.ts index af6ef82cb978..fdd529d08501 100644 --- a/packages/3-extensions/supabase/test/fixtures/no-policy/contract.d.ts +++ b/packages/3-extensions/supabase/test/fixtures/no-policy/contract.d.ts @@ -428,7 +428,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/packages/3-extensions/supabase/test/fixtures/renamed-policy/contract.d.ts b/packages/3-extensions/supabase/test/fixtures/renamed-policy/contract.d.ts index 09c83cd26212..f97eeb5ef997 100644 --- a/packages/3-extensions/supabase/test/fixtures/renamed-policy/contract.d.ts +++ b/packages/3-extensions/supabase/test/fixtures/renamed-policy/contract.d.ts @@ -428,7 +428,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/packages/3-targets/mysql/9-facade/test/fixtures/generated/contract.d.ts b/packages/3-targets/mysql/9-facade/test/fixtures/generated/contract.d.ts index 6dc7cedc660f..a004281d15e7 100644 --- a/packages/3-targets/mysql/9-facade/test/fixtures/generated/contract.d.ts +++ b/packages/3-targets/mysql/9-facade/test/fixtures/generated/contract.d.ts @@ -341,7 +341,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/packages/3-targets/postgres/9-facade/test/fixtures/generated/contract.d.ts b/packages/3-targets/postgres/9-facade/test/fixtures/generated/contract.d.ts index 80b3d3eda96a..3d9c0e6b5f97 100644 --- a/packages/3-targets/postgres/9-facade/test/fixtures/generated/contract.d.ts +++ b/packages/3-targets/postgres/9-facade/test/fixtures/generated/contract.d.ts @@ -741,7 +741,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/e2e/framework/test/fixtures/generated/contract.d.ts b/test/e2e/framework/test/fixtures/generated/contract.d.ts index aacd1de40319..ae27c438ef88 100644 --- a/test/e2e/framework/test/fixtures/generated/contract.d.ts +++ b/test/e2e/framework/test/fixtures/generated/contract.d.ts @@ -1325,7 +1325,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/e2e/framework/test/sqlite/fixtures/generated/contract.d.ts b/test/e2e/framework/test/sqlite/fixtures/generated/contract.d.ts index f571f8b997fb..cd1b049a0829 100644 --- a/test/e2e/framework/test/sqlite/fixtures/generated/contract.d.ts +++ b/test/e2e/framework/test/sqlite/fixtures/generated/contract.d.ts @@ -694,7 +694,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/authoring/mongo-parity/enums-and-lists/expected.contract.json b/test/integration/test/authoring/mongo-parity/enums-and-lists/expected.contract.json index 2eafb96709a7..434fc1ec29f9 100644 --- a/test/integration/test/authoring/mongo-parity/enums-and-lists/expected.contract.json +++ b/test/integration/test/authoring/mongo-parity/enums-and-lists/expected.contract.json @@ -56,8 +56,11 @@ "badge": { "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "Badge", "kind": "valueObject", - "name": "Badge" + "namespaceId": "__unbound__", + "plane": "storage" } }, "badges": { @@ -66,8 +69,11 @@ }, "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "Badge", "kind": "valueObject", - "name": "Badge" + "namespaceId": "__unbound__", + "plane": "storage" } }, "id": { @@ -164,32 +170,6 @@ } } } - }, - "valueObjects": { - "Badge": { - "fields": { - "label": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "level": { - "nullable": false, - "type": { - "codecId": "mongo/int32@1", - "kind": "scalar" - }, - "valueSet": { - "entityKind": "enum", - "entityName": "Level", - "namespaceId": "__unbound__", - "plane": "domain" - } - } - } - } } } } @@ -279,6 +259,32 @@ } } }, + "valueObject": { + "Badge": { + "fields": { + "label": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "level": { + "nullable": false, + "type": { + "codecId": "mongo/int32@1", + "kind": "scalar" + }, + "valueSet": { + "entityKind": "valueSet", + "entityName": "Level", + "namespaceId": "__unbound__", + "plane": "storage" + } + } + } + } + }, "valueSet": { "Level": { "kind": "valueSet", @@ -294,7 +300,7 @@ "kind": "mongo-database" } }, - "storageHash": "0b97ad584118f2bce663b3289d44b665cfe365baf2d164b1fc70f4d694993963" + "storageHash": "f15cc7ed8744412382801fa2f9e249273fedc015ddfa58c6ca1c0416baa0fb9c" }, "capabilities": {}, "extensions": {}, diff --git a/test/integration/test/authoring/side-by-side/mongo/contract.json b/test/integration/test/authoring/side-by-side/mongo/contract.json index cd83086f8657..f1d33d14d573 100644 --- a/test/integration/test/authoring/side-by-side/mongo/contract.json +++ b/test/integration/test/authoring/side-by-side/mongo/contract.json @@ -211,8 +211,11 @@ "address": { "nullable": true, "type": { + "entityKind": "valueObject", + "entityName": "Address", "kind": "valueObject", - "name": "Address" + "namespaceId": "__unbound__", + "plane": "storage" } }, "bio": { @@ -298,26 +301,6 @@ } } } - }, - "valueObjects": { - "Address": { - "fields": { - "street": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "zip": { - "nullable": true, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - } - } - } } } } @@ -502,6 +485,26 @@ } } }, + "valueObject": { + "Address": { + "fields": { + "street": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "zip": { + "nullable": true, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + } + } + } + }, "valueSet": { "Role": { "kind": "valueSet", @@ -516,7 +519,7 @@ "kind": "mongo-database" } }, - "storageHash": "9d0d8d271546a3ca3180095b1adaf549c22edf1cc8f8d2b63f92fe219df7d677" + "storageHash": "70cb520a3df87e0fd81be63d8801438a417be178c7bd6635a96830cbb62cc0ba" }, "capabilities": {}, "extensions": {}, diff --git a/test/integration/test/contract-parity/fixtures/mongo/generated/contract.d.ts b/test/integration/test/contract-parity/fixtures/mongo/generated/contract.d.ts index d12b3b998951..1cf270dfb29d 100644 --- a/test/integration/test/contract-parity/fixtures/mongo/generated/contract.d.ts +++ b/test/integration/test/contract-parity/fixtures/mongo/generated/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'f40b9a31525edcf6ac7faa3ae99f554ca15e128136e74743654436c4b79fb2f6'>; + StorageHashBase<'c4a473ccd6718dc606dfcda2d01643cfdcc79efc8d3ab1e1647001ea6086e133'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -78,14 +78,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -356,6 +358,51 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly kind: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly location: { + readonly nullable: true; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Point'; + }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly tags: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + readonly many: { readonly elementNullable: false }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + readonly Point: { + readonly fields: { + readonly lat: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; + }; + readonly lng: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; + }; + }; + }; + }; }; }; }; @@ -497,7 +544,13 @@ type ContractBase = Omit< }; readonly home: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; }; readonly joinedAt: { readonly nullable: false; @@ -514,7 +567,13 @@ type ContractBase = Omit< }; readonly pastHomes: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; readonly many: { readonly elementNullable: false }; }; readonly pastRoles: { @@ -580,45 +639,6 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly kind: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly location: { - readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Point' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly tags: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - readonly many: { readonly elementNullable: false }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Point: { - readonly fields: { - readonly lat: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly lng: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - }; - }; - }; readonly enum: { readonly Role: { readonly codecId: 'mongo/string@1'; @@ -640,45 +660,6 @@ type ContractBase = Omit< readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly kind: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly location: { - readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Point' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly tags: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - readonly many: { readonly elementNullable: false }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Point: { - readonly fields: { - readonly lat: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - readonly lng: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/double@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/contract-parity/fixtures/mongo/generated/contract.json b/test/integration/test/contract-parity/fixtures/mongo/generated/contract.json index 24a1a2cbba6e..5a86ccc6aef4 100644 --- a/test/integration/test/contract-parity/fixtures/mongo/generated/contract.json +++ b/test/integration/test/contract-parity/fixtures/mongo/generated/contract.json @@ -217,8 +217,11 @@ "home": { "nullable": true, "type": { + "entityKind": "valueObject", + "entityName": "Address", "kind": "valueObject", - "name": "Address" + "namespaceId": "__unbound__", + "plane": "storage" } }, "joinedAt": { @@ -251,8 +254,11 @@ }, "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "Address", "kind": "valueObject", - "name": "Address" + "namespaceId": "__unbound__", + "plane": "storage" } }, "pastRoles": { @@ -397,74 +403,6 @@ } } } - }, - "valueObjects": { - "Address": { - "fields": { - "kind": { - "nullable": true, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - }, - "valueSet": { - "entityKind": "enum", - "entityName": "Role", - "namespaceId": "__unbound__", - "plane": "domain" - } - }, - "location": { - "nullable": true, - "type": { - "kind": "valueObject", - "name": "Point" - } - }, - "street": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "tags": { - "many": { - "elementNullable": false - }, - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "zip": { - "nullable": true, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - } - } - }, - "Point": { - "fields": { - "lat": { - "nullable": false, - "type": { - "codecId": "mongo/double@1", - "kind": "scalar" - } - }, - "lng": { - "nullable": false, - "type": { - "codecId": "mongo/double@1", - "kind": "scalar" - } - } - } - } } } } @@ -822,6 +760,77 @@ } } }, + "valueObject": { + "Address": { + "fields": { + "kind": { + "nullable": true, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + }, + "valueSet": { + "entityKind": "valueSet", + "entityName": "Role", + "namespaceId": "__unbound__", + "plane": "storage" + } + }, + "location": { + "nullable": true, + "type": { + "entityKind": "valueObject", + "entityName": "Point", + "kind": "valueObject", + "namespaceId": "__unbound__", + "plane": "storage" + } + }, + "street": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "tags": { + "many": { + "elementNullable": false + }, + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "zip": { + "nullable": true, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + } + } + }, + "Point": { + "fields": { + "lat": { + "nullable": false, + "type": { + "codecId": "mongo/double@1", + "kind": "scalar" + } + }, + "lng": { + "nullable": false, + "type": { + "codecId": "mongo/double@1", + "kind": "scalar" + } + } + } + } + }, "valueSet": { "Role": { "kind": "valueSet", @@ -836,7 +845,7 @@ "kind": "mongo-database" } }, - "storageHash": "f40b9a31525edcf6ac7faa3ae99f554ca15e128136e74743654436c4b79fb2f6" + "storageHash": "c4a473ccd6718dc606dfcda2d01643cfdcc79efc8d3ab1e1647001ea6086e133" }, "capabilities": {}, "extensions": {}, diff --git a/test/integration/test/contract-parity/fixtures/postgres/generated/contract.d.ts b/test/integration/test/contract-parity/fixtures/postgres/generated/contract.d.ts index d06049f6d9e6..86af5fe7ed5c 100644 --- a/test/integration/test/contract-parity/fixtures/postgres/generated/contract.d.ts +++ b/test/integration/test/contract-parity/fixtures/postgres/generated/contract.d.ts @@ -2239,7 +2239,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/contract-parity/fixtures/sqlite/generated/contract.d.ts b/test/integration/test/contract-parity/fixtures/sqlite/generated/contract.d.ts index d55978a030b4..0c7bcb3b1ddc 100644 --- a/test/integration/test/contract-parity/fixtures/sqlite/generated/contract.d.ts +++ b/test/integration/test/contract-parity/fixtures/sqlite/generated/contract.d.ts @@ -1170,7 +1170,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/enum-members/_fixture-mongo/generated/contract.d.ts b/test/integration/test/enum-members/_fixture-mongo/generated/contract.d.ts index d1732754cb72..eedea43c6bff 100644 --- a/test/integration/test/enum-members/_fixture-mongo/generated/contract.d.ts +++ b/test/integration/test/enum-members/_fixture-mongo/generated/contract.d.ts @@ -202,7 +202,6 @@ type ContractBase = Omit< readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/enum-members/_fixture-native-enums/generated/contract.d.ts b/test/integration/test/enum-members/_fixture-native-enums/generated/contract.d.ts index 172eb296c6ab..fc717397add5 100644 --- a/test/integration/test/enum-members/_fixture-native-enums/generated/contract.d.ts +++ b/test/integration/test/enum-members/_fixture-native-enums/generated/contract.d.ts @@ -450,7 +450,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/enum-members/_fixture-postgres/generated/contract.d.ts b/test/integration/test/enum-members/_fixture-postgres/generated/contract.d.ts index b6ddb313c349..d217927b3f96 100644 --- a/test/integration/test/enum-members/_fixture-postgres/generated/contract.d.ts +++ b/test/integration/test/enum-members/_fixture-postgres/generated/contract.d.ts @@ -838,7 +838,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/enum-members/_fixture-sqlite/generated/contract.d.ts b/test/integration/test/enum-members/_fixture-sqlite/generated/contract.d.ts index 0854e38fb4d4..3ee5e7202850 100644 --- a/test/integration/test/enum-members/_fixture-sqlite/generated/contract.d.ts +++ b/test/integration/test/enum-members/_fixture-sqlite/generated/contract.d.ts @@ -387,7 +387,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/enum-order-by/_fixture/generated/contract.d.ts b/test/integration/test/enum-order-by/_fixture/generated/contract.d.ts index 3e7648083dd0..4ad24145f914 100644 --- a/test/integration/test/enum-order-by/_fixture/generated/contract.d.ts +++ b/test/integration/test/enum-order-by/_fixture/generated/contract.d.ts @@ -416,7 +416,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/fixtures/contract.d.ts b/test/integration/test/fixtures/contract.d.ts index a6ed1651533f..562f185f10f6 100644 --- a/test/integration/test/fixtures/contract.d.ts +++ b/test/integration/test/fixtures/contract.d.ts @@ -406,7 +406,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/mongo/bson-scalars/_fixture/generated/contract.d.ts b/test/integration/test/mongo/bson-scalars/_fixture/generated/contract.d.ts index 2f95321326c4..c9a299a39ad6 100644 --- a/test/integration/test/mongo/bson-scalars/_fixture/generated/contract.d.ts +++ b/test/integration/test/mongo/bson-scalars/_fixture/generated/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'e6b8f0ca84154e70d8240cabf47951883d699f4a84e45131c60f899dd900deb4'>; + StorageHashBase<'d9eb4195e93e0635171fe59d8bc76ea81da5edb4190111b37671eeb5ffd9b39c'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -65,14 +65,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -420,6 +422,41 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly zip: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int64@1' }; + }; + }; + }; + readonly Point: { + readonly fields: { + readonly tags: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int64@1' }; + readonly many: { readonly elementNullable: false }; + }; + readonly x: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; + }; + }; + }; + readonly Stamp: { + readonly fields: { + readonly note: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/json@1' }; + }; + }; + }; + }; }; }; }; @@ -475,7 +512,13 @@ type ContractBase = Omit< readonly fields: { readonly address: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; }; readonly avatar: { readonly nullable: true; @@ -573,7 +616,13 @@ type ContractBase = Omit< }; readonly stamps: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Stamp' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Stamp'; + }; readonly many: { readonly elementNullable: false }; }; }; @@ -709,7 +758,13 @@ type ContractBase = Omit< }; readonly points: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Point' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Point'; + }; readonly many: { readonly elementNullable: false }; }; readonly words: { @@ -737,41 +792,6 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly zip: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int64@1' }; - }; - }; - }; - readonly Point: { - readonly fields: { - readonly tags: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int64@1' }; - readonly many: { readonly elementNullable: false }; - }; - readonly x: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - }; - }; - readonly Stamp: { - readonly fields: { - readonly note: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/json@1' }; - }; - }; - }; - }; readonly enum: { readonly Role: { readonly codecId: 'mongo/string@1'; @@ -793,41 +813,6 @@ type ContractBase = Omit< readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly zip: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int64@1' }; - }; - }; - }; - readonly Point: { - readonly fields: { - readonly tags: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int64@1' }; - readonly many: { readonly elementNullable: false }; - }; - readonly x: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; - }; - }; - }; - readonly Stamp: { - readonly fields: { - readonly note: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/json@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/mongo/bson-scalars/_fixture/generated/contract.json b/test/integration/test/mongo/bson-scalars/_fixture/generated/contract.json index 2337d88d68eb..f74aba57aa58 100644 --- a/test/integration/test/mongo/bson-scalars/_fixture/generated/contract.json +++ b/test/integration/test/mongo/bson-scalars/_fixture/generated/contract.json @@ -92,8 +92,11 @@ "address": { "nullable": true, "type": { + "entityKind": "valueObject", + "entityName": "Address", "kind": "valueObject", - "name": "Address" + "namespaceId": "__unbound__", + "plane": "storage" } }, "avatar": { @@ -262,8 +265,11 @@ }, "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "Stamp", "kind": "valueObject", - "name": "Stamp" + "namespaceId": "__unbound__", + "plane": "storage" } } }, @@ -498,8 +504,11 @@ }, "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "Point", "kind": "valueObject", - "name": "Point" + "namespaceId": "__unbound__", + "plane": "storage" } }, "words": { @@ -553,58 +562,6 @@ } } } - }, - "valueObjects": { - "Address": { - "fields": { - "city": { - "nullable": true, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "zip": { - "nullable": false, - "type": { - "codecId": "mongo/int64@1", - "kind": "scalar" - } - } - } - }, - "Point": { - "fields": { - "tags": { - "many": { - "elementNullable": false - }, - "nullable": false, - "type": { - "codecId": "mongo/int64@1", - "kind": "scalar" - } - }, - "x": { - "nullable": false, - "type": { - "codecId": "mongo/int32@1", - "kind": "scalar" - } - } - } - }, - "Stamp": { - "fields": { - "note": { - "nullable": false, - "type": { - "codecId": "mongo/json@1", - "kind": "scalar" - } - } - } - } } } } @@ -985,6 +942,58 @@ } } }, + "valueObject": { + "Address": { + "fields": { + "city": { + "nullable": true, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "zip": { + "nullable": false, + "type": { + "codecId": "mongo/int64@1", + "kind": "scalar" + } + } + } + }, + "Point": { + "fields": { + "tags": { + "many": { + "elementNullable": false + }, + "nullable": false, + "type": { + "codecId": "mongo/int64@1", + "kind": "scalar" + } + }, + "x": { + "nullable": false, + "type": { + "codecId": "mongo/int32@1", + "kind": "scalar" + } + } + } + }, + "Stamp": { + "fields": { + "note": { + "nullable": false, + "type": { + "codecId": "mongo/json@1", + "kind": "scalar" + } + } + } + } + }, "valueSet": { "Role": { "kind": "valueSet", @@ -999,7 +1008,7 @@ "kind": "mongo-database" } }, - "storageHash": "e6b8f0ca84154e70d8240cabf47951883d699f4a84e45131c60f899dd900deb4" + "storageHash": "d9eb4195e93e0635171fe59d8bc76ea81da5edb4190111b37671eeb5ffd9b39c" }, "capabilities": {}, "extensions": {}, diff --git a/test/integration/test/mongo/fixtures/generated/contract.d.ts b/test/integration/test/mongo/fixtures/generated/contract.d.ts index eda54026dd9c..52bfe5641ab6 100644 --- a/test/integration/test/mongo/fixtures/generated/contract.d.ts +++ b/test/integration/test/mongo/fixtures/generated/contract.d.ts @@ -460,7 +460,6 @@ type ContractBase = Omit< readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/mongo/list-filters/_fixture/generated/contract.d.ts b/test/integration/test/mongo/list-filters/_fixture/generated/contract.d.ts index 151e8c5960e1..a0ad780ab2be 100644 --- a/test/integration/test/mongo/list-filters/_fixture/generated/contract.d.ts +++ b/test/integration/test/mongo/list-filters/_fixture/generated/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'5eedd9b568f168145ab3967dfddcafb7efb16c59c1d5babaed9d71cae6a75923'>; + StorageHashBase<'3959359f149bf7501cc3d923e3f0975b8c1820f99af796db88df3fc9d138ecd7'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -53,14 +53,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -154,6 +156,16 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly Entry: { + readonly fields: { + readonly value: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + }; }; }; }; @@ -191,12 +203,24 @@ type ContractBase = Omit< }; readonly optionalEntries: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Entry' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Entry'; + }; readonly many: { readonly elementNullable: false }; }; readonly optionalNullableEntries: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Entry' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Entry'; + }; readonly many: { readonly elementNullable: true }; }; readonly optionalNullableStrings: { @@ -232,32 +256,12 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly Entry: { - readonly fields: { - readonly value: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - }; }; }; }; readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly Entry: { - readonly fields: { - readonly value: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/mongo/list-filters/_fixture/generated/contract.json b/test/integration/test/mongo/list-filters/_fixture/generated/contract.json index 5f6f6bbd9df1..64ed51875721 100644 --- a/test/integration/test/mongo/list-filters/_fixture/generated/contract.json +++ b/test/integration/test/mongo/list-filters/_fixture/generated/contract.json @@ -52,8 +52,11 @@ }, "nullable": true, "type": { + "entityKind": "valueObject", + "entityName": "Entry", "kind": "valueObject", - "name": "Entry" + "namespaceId": "__unbound__", + "plane": "storage" } }, "optionalNullableEntries": { @@ -62,8 +65,11 @@ }, "nullable": true, "type": { + "entityKind": "valueObject", + "entityName": "Entry", "kind": "valueObject", - "name": "Entry" + "namespaceId": "__unbound__", + "plane": "storage" } }, "optionalNullableStrings": { @@ -131,19 +137,6 @@ } } } - }, - "valueObjects": { - "Entry": { - "fields": { - "value": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - } - } - } } } } @@ -264,13 +257,26 @@ "validationLevel": "strict" } } + }, + "valueObject": { + "Entry": { + "fields": { + "value": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + } + } + } } }, "id": "__unbound__", "kind": "mongo-database" } }, - "storageHash": "5eedd9b568f168145ab3967dfddcafb7efb16c59c1d5babaed9d71cae6a75923" + "storageHash": "3959359f149bf7501cc3d923e3f0975b8c1820f99af796db88df3fc9d138ecd7" }, "capabilities": {}, "extensions": {}, diff --git a/test/integration/test/mongo/prisma6-source/_fixture/generated/contract.d.ts b/test/integration/test/mongo/prisma6-source/_fixture/generated/contract.d.ts index eaf83df534be..7e8c665ada18 100644 --- a/test/integration/test/mongo/prisma6-source/_fixture/generated/contract.d.ts +++ b/test/integration/test/mongo/prisma6-source/_fixture/generated/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'4437f81a7adbcbf818bc57a01eeae876338e847c850fca1b59a3de9110919258'>; + StorageHashBase<'a0963d77119ea552aa28834b70bc7ce8969caaae10055d4d07deec4ad11ff5be'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -53,14 +53,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -102,6 +104,19 @@ type ContractBase = Omit< readonly Counter: MongoCollection; readonly Tally: MongoCollection; }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly zip: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'mongo/int64Number@1'; + }; + }; + }; + }; + }; }; }; }; @@ -161,11 +176,23 @@ type ContractBase = Omit< readonly fields: { readonly address: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; }; readonly addresses: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; readonly many: { readonly elementNullable: false }; }; readonly id: { @@ -195,32 +222,12 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly zip: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int64Number@1' }; - }; - }; - }; - }; }; }; }; readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly zip: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int64Number@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/mongo/prisma6-source/_fixture/generated/contract.json b/test/integration/test/mongo/prisma6-source/_fixture/generated/contract.json index 432b0adad393..2597ed49bd9c 100644 --- a/test/integration/test/mongo/prisma6-source/_fixture/generated/contract.json +++ b/test/integration/test/mongo/prisma6-source/_fixture/generated/contract.json @@ -82,8 +82,11 @@ "address": { "nullable": true, "type": { + "entityKind": "valueObject", + "entityName": "Address", "kind": "valueObject", - "name": "Address" + "namespaceId": "__unbound__", + "plane": "storage" } }, "addresses": { @@ -92,8 +95,11 @@ }, "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "Address", "kind": "valueObject", - "name": "Address" + "namespaceId": "__unbound__", + "plane": "storage" } }, "id": { @@ -143,19 +149,6 @@ } } } - }, - "valueObjects": { - "Address": { - "fields": { - "zip": { - "nullable": false, - "type": { - "codecId": "mongo/int64Number@1", - "kind": "scalar" - } - } - } - } } } } @@ -171,13 +164,26 @@ "Tally": { "kind": "mongo-collection" } + }, + "valueObject": { + "Address": { + "fields": { + "zip": { + "nullable": false, + "type": { + "codecId": "mongo/int64Number@1", + "kind": "scalar" + } + } + } + } } }, "id": "__unbound__", "kind": "mongo-database" } }, - "storageHash": "4437f81a7adbcbf818bc57a01eeae876338e847c850fca1b59a3de9110919258" + "storageHash": "a0963d77119ea552aa28834b70bc7ce8969caaae10055d4d07deec4ad11ff5be" }, "capabilities": {}, "extensions": {}, diff --git a/test/integration/test/mongo/recursive-value-objects/_fixture/generated/contract.d.ts b/test/integration/test/mongo/recursive-value-objects/_fixture/generated/contract.d.ts index 8bd0747724c1..c84a3a614b49 100644 --- a/test/integration/test/mongo/recursive-value-objects/_fixture/generated/contract.d.ts +++ b/test/integration/test/mongo/recursive-value-objects/_fixture/generated/contract.d.ts @@ -1,7 +1,10 @@ // ⚠️ GENERATED FILE - DO NOT EDIT // This file is automatically generated by 'prisma contract emit'. // To regenerate, run: prisma contract emit -import type { CodecTypes as MongoCodecTypes } from '@internal/target-mongo/codec-types'; +import type { + CodecTypes as MongoCodecTypes, + ParameterizedOutputTypesForTypeParams as MongoParameterizedOutputTypes, +} from '@internal/target-mongo/codec-types'; import type { MongoCollection, @@ -12,51 +15,61 @@ import type { import type { Contract as ContractType, ExecutionHashBase, + FieldTypesFromDomain, NamespaceId, ProfileHashBase, StorageHashBase, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'842683ee4dbc4b81d370b11a296f09c5a9ef623aea522ea76b492732f7c7a7ee'>; + StorageHashBase<'0d971a3402142eb5ee3d6dae1a8b52571ad8f06bd0bb027509f62ed409e68710'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; export type CodecTypes = MongoCodecTypes; -export type NavItemOutput = { - readonly children: ReadonlyArray; - readonly label: CodecTypes['mongo/string@1']['output']; -}; -export type NavItemInput = { - readonly children: ReadonlyArray; - readonly label: CodecTypes['mongo/string@1']['input']; -}; -export type FieldOutputTypes = { - readonly __unbound__: { - readonly Menu: { - readonly id: CodecTypes['mongo/objectId@1']['output']; - readonly items: ReadonlyArray; - readonly name: CodecTypes['mongo/string@1']['output']; - }; - }; -}; -export type FieldInputTypes = { - readonly __unbound__: { - readonly Menu: { - readonly id: CodecTypes['mongo/objectId@1']['input']; - readonly items: ReadonlyArray; - readonly name: CodecTypes['mongo/string@1']['input']; - }; - }; -}; +type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output' +>; +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input' +>; +export type NavItemOutput = ValueObjectOutputTypes['__unbound__']['NavItem']; +export type NavItemInput = ValueObjectInputTypes['__unbound__']['NavItem']; +type FieldTypeOverrides = {}; +export type FieldOutputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output', + ContractBase['storage']['namespaces'] +>; +export type FieldInputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input', + ContractBase['storage']['namespaces'] +>; export namespace Models { export type unbound_Menu = { - id: CodecTypes['mongo/objectId@1']['output']; - items: ReadonlyArray; - name: CodecTypes['mongo/string@1']['output']; + id: FieldOutputTypes['__unbound__']['Menu']['id']; + items: FieldOutputTypes['__unbound__']['Menu']['items']; + name: FieldOutputTypes['__unbound__']['Menu']['name']; readonly [RelationKeys]?: never; }; } @@ -110,6 +123,27 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly NavItem: { + readonly fields: { + readonly children: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'NavItem'; + }; + readonly many: { readonly elementNullable: false }; + }; + readonly label: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + }; }; }; }; @@ -134,7 +168,13 @@ type ContractBase = Omit< }; readonly items: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'NavItem' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'NavItem'; + }; readonly many: { readonly elementNullable: false }; }; readonly name: { @@ -153,42 +193,12 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly NavItem: { - readonly fields: { - readonly children: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'NavItem' }; - readonly many: { readonly elementNullable: false }; - }; - readonly label: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - }; }; }; }; readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly NavItem: { - readonly fields: { - readonly children: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'NavItem' }; - readonly many: { readonly elementNullable: false }; - }; - readonly label: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/mongo/recursive-value-objects/_fixture/generated/contract.json b/test/integration/test/mongo/recursive-value-objects/_fixture/generated/contract.json index 09f60ed5fc43..0e6622d28c16 100644 --- a/test/integration/test/mongo/recursive-value-objects/_fixture/generated/contract.json +++ b/test/integration/test/mongo/recursive-value-objects/_fixture/generated/contract.json @@ -28,8 +28,11 @@ }, "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "NavItem", "kind": "valueObject", - "name": "NavItem" + "namespaceId": "__unbound__", + "plane": "storage" } }, "name": { @@ -56,29 +59,6 @@ } } } - }, - "valueObjects": { - "NavItem": { - "fields": { - "children": { - "many": { - "elementNullable": false - }, - "nullable": false, - "type": { - "kind": "valueObject", - "name": "NavItem" - } - }, - "label": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - } - } - } } } } @@ -135,13 +115,39 @@ "validationLevel": "strict" } } + }, + "valueObject": { + "NavItem": { + "fields": { + "children": { + "many": { + "elementNullable": false + }, + "nullable": false, + "type": { + "entityKind": "valueObject", + "entityName": "NavItem", + "kind": "valueObject", + "namespaceId": "__unbound__", + "plane": "storage" + } + }, + "label": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + } + } + } } }, "id": "__unbound__", "kind": "mongo-database" } }, - "storageHash": "842683ee4dbc4b81d370b11a296f09c5a9ef623aea522ea76b492732f7c7a7ee" + "storageHash": "0d971a3402142eb5ee3d6dae1a8b52571ad8f06bd0bb027509f62ed409e68710" }, "capabilities": {}, "extensions": {}, diff --git a/test/integration/test/mongo/temporal-presets/_fixture-aliases/generated/contract.d.ts b/test/integration/test/mongo/temporal-presets/_fixture-aliases/generated/contract.d.ts index eb8f24c66d11..4df7ae8700a5 100644 --- a/test/integration/test/mongo/temporal-presets/_fixture-aliases/generated/contract.d.ts +++ b/test/integration/test/mongo/temporal-presets/_fixture-aliases/generated/contract.d.ts @@ -449,7 +449,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/mongo/temporal-presets/_fixture-attributes/generated/contract.d.ts b/test/integration/test/mongo/temporal-presets/_fixture-attributes/generated/contract.d.ts index eb8f24c66d11..4df7ae8700a5 100644 --- a/test/integration/test/mongo/temporal-presets/_fixture-attributes/generated/contract.d.ts +++ b/test/integration/test/mongo/temporal-presets/_fixture-attributes/generated/contract.d.ts @@ -449,7 +449,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/mongo/temporal-presets/_fixture/generated/contract.d.ts b/test/integration/test/mongo/temporal-presets/_fixture/generated/contract.d.ts index eb8f24c66d11..4df7ae8700a5 100644 --- a/test/integration/test/mongo/temporal-presets/_fixture/generated/contract.d.ts +++ b/test/integration/test/mongo/temporal-presets/_fixture/generated/contract.d.ts @@ -449,7 +449,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/namespaced-accessors/fixtures/generated/contract.d.ts b/test/integration/test/namespaced-accessors/fixtures/generated/contract.d.ts index d9eb2abfc44b..226ae8a540aa 100644 --- a/test/integration/test/namespaced-accessors/fixtures/generated/contract.d.ts +++ b/test/integration/test/namespaced-accessors/fixtures/generated/contract.d.ts @@ -628,7 +628,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/planner-golden/fixtures/postgres/generated/contract.d.ts b/test/integration/test/planner-golden/fixtures/postgres/generated/contract.d.ts index 9819eca2b0a6..86d4017618d0 100644 --- a/test/integration/test/planner-golden/fixtures/postgres/generated/contract.d.ts +++ b/test/integration/test/planner-golden/fixtures/postgres/generated/contract.d.ts @@ -1726,7 +1726,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/planner-golden/fixtures/sqlite/generated/contract.d.ts b/test/integration/test/planner-golden/fixtures/sqlite/generated/contract.d.ts index fcdb9dbeaf85..40b801a43976 100644 --- a/test/integration/test/planner-golden/fixtures/sqlite/generated/contract.d.ts +++ b/test/integration/test/planner-golden/fixtures/sqlite/generated/contract.d.ts @@ -459,7 +459,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/aggregation/avg/_fixture/decimal/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/avg/_fixture/decimal/generated/contract.d.ts index 1ff533a6d9a1..44905c1ee485 100644 --- a/test/integration/test/ports/engines/queries/aggregation/avg/_fixture/decimal/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/avg/_fixture/decimal/generated/contract.d.ts @@ -391,7 +391,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/aggregation/avg/_fixture/numeric/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/avg/_fixture/numeric/generated/contract.d.ts index b40f191cc247..59cbd9f582cf 100644 --- a/test/integration/test/ports/engines/queries/aggregation/avg/_fixture/numeric/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/avg/_fixture/numeric/generated/contract.d.ts @@ -415,7 +415,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/aggregation/count/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/count/_fixture/generated/contract.d.ts index cf24259e9beb..f77d91dcb0ca 100644 --- a/test/integration/test/ports/engines/queries/aggregation/count/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/count/_fixture/generated/contract.d.ts @@ -466,7 +466,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/aggregation/group_by/_fixture/main/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/group_by/_fixture/main/generated/contract.d.ts index 9e45bbdfb0e0..4f86240d9516 100644 --- a/test/integration/test/ports/engines/queries/aggregation/group_by/_fixture/main/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/group_by/_fixture/main/generated/contract.d.ts @@ -516,7 +516,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/aggregation/group_by/_fixture/regression-21789/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/group_by/_fixture/regression-21789/generated/contract.d.ts index cb88044c5fa5..1f8087524154 100644 --- a/test/integration/test/ports/engines/queries/aggregation/group_by/_fixture/regression-21789/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/group_by/_fixture/regression-21789/generated/contract.d.ts @@ -434,7 +434,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/aggregation/group_by_having/_fixture/common/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/group_by_having/_fixture/common/generated/contract.d.ts index ac360de4d4d3..3a83dd596131 100644 --- a/test/integration/test/ports/engines/queries/aggregation/group_by_having/_fixture/common/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/group_by_having/_fixture/common/generated/contract.d.ts @@ -427,7 +427,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/aggregation/group_by_having/_fixture/decimal/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/group_by_having/_fixture/decimal/generated/contract.d.ts index 4d36ded14a38..c533abb0220c 100644 --- a/test/integration/test/ports/engines/queries/aggregation/group_by_having/_fixture/decimal/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/group_by_having/_fixture/decimal/generated/contract.d.ts @@ -403,7 +403,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/compound/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/compound/generated/contract.d.ts index dcf67076ee4f..ff3dea513e86 100644 --- a/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/compound/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/compound/generated/contract.d.ts @@ -728,7 +728,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/generated/contract.d.ts index 6a12f4eaaacd..4cf80d5e62b3 100644 --- a/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/generated/contract.d.ts @@ -666,7 +666,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/nested/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/nested/generated/contract.d.ts index 02dc11d0e377..cfbdbb1dcffc 100644 --- a/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/nested/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/nested/generated/contract.d.ts @@ -931,7 +931,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/self/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/self/generated/contract.d.ts index 85f7eb4d6b2c..047b311fc8a6 100644 --- a/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/self/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/self/generated/contract.d.ts @@ -534,7 +534,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/aggregation/max/_fixture/common/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/max/_fixture/common/generated/contract.d.ts index b8784e99954f..a50ccf049a95 100644 --- a/test/integration/test/ports/engines/queries/aggregation/max/_fixture/common/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/max/_fixture/common/generated/contract.d.ts @@ -427,7 +427,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/aggregation/max/_fixture/decimal/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/max/_fixture/decimal/generated/contract.d.ts index 1ff533a6d9a1..44905c1ee485 100644 --- a/test/integration/test/ports/engines/queries/aggregation/max/_fixture/decimal/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/max/_fixture/decimal/generated/contract.d.ts @@ -391,7 +391,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/aggregation/min/_fixture/common/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/min/_fixture/common/generated/contract.d.ts index b8784e99954f..a50ccf049a95 100644 --- a/test/integration/test/ports/engines/queries/aggregation/min/_fixture/common/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/min/_fixture/common/generated/contract.d.ts @@ -427,7 +427,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/aggregation/min/_fixture/decimal/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/min/_fixture/decimal/generated/contract.d.ts index 1ff533a6d9a1..44905c1ee485 100644 --- a/test/integration/test/ports/engines/queries/aggregation/min/_fixture/decimal/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/min/_fixture/decimal/generated/contract.d.ts @@ -391,7 +391,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/aggregation/sum/_fixture/decimal/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/sum/_fixture/decimal/generated/contract.d.ts index 1ff533a6d9a1..44905c1ee485 100644 --- a/test/integration/test/ports/engines/queries/aggregation/sum/_fixture/decimal/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/sum/_fixture/decimal/generated/contract.d.ts @@ -391,7 +391,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/aggregation/sum/_fixture/numeric/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/sum/_fixture/numeric/generated/contract.d.ts index b40f191cc247..59cbd9f582cf 100644 --- a/test/integration/test/ports/engines/queries/aggregation/sum/_fixture/numeric/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/sum/_fixture/numeric/generated/contract.d.ts @@ -415,7 +415,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/aggregation/uniq-count-relation/_fixture/base/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/uniq-count-relation/_fixture/base/generated/contract.d.ts index 6a12f4eaaacd..4cf80d5e62b3 100644 --- a/test/integration/test/ports/engines/queries/aggregation/uniq-count-relation/_fixture/base/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/uniq-count-relation/_fixture/base/generated/contract.d.ts @@ -666,7 +666,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/aggregation/uniq-count-relation/_fixture/nested/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/uniq-count-relation/_fixture/nested/generated/contract.d.ts index 02dc11d0e377..cfbdbb1dcffc 100644 --- a/test/integration/test/ports/engines/queries/aggregation/uniq-count-relation/_fixture/nested/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/uniq-count-relation/_fixture/nested/generated/contract.d.ts @@ -931,7 +931,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/data_types/bigint/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/bigint/_fixture/generated/contract.d.ts index cf24259e9beb..f77d91dcb0ca 100644 --- a/test/integration/test/ports/engines/queries/data_types/bigint/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/bigint/_fixture/generated/contract.d.ts @@ -466,7 +466,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/data_types/bool/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/bool/_fixture/generated/contract.d.ts index cf24259e9beb..f77d91dcb0ca 100644 --- a/test/integration/test/ports/engines/queries/data_types/bool/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/bool/_fixture/generated/contract.d.ts @@ -466,7 +466,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/relations/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/relations/generated/contract.d.ts index 97890ba8fe1a..f24f885862b5 100644 --- a/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/relations/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/relations/generated/contract.d.ts @@ -484,7 +484,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/scalars/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/scalars/generated/contract.d.ts index cf24259e9beb..f77d91dcb0ca 100644 --- a/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/scalars/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/scalars/generated/contract.d.ts @@ -466,7 +466,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/data_types/datetime/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/datetime/_fixture/generated/contract.d.ts index cf24259e9beb..f77d91dcb0ca 100644 --- a/test/integration/test/ports/engines/queries/data_types/datetime/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/datetime/_fixture/generated/contract.d.ts @@ -466,7 +466,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/data_types/decimal/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/decimal/_fixture/generated/contract.d.ts index 6636199576e6..5602e10526f5 100644 --- a/test/integration/test/ports/engines/queries/data_types/decimal/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/decimal/_fixture/generated/contract.d.ts @@ -391,7 +391,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/data_types/enum_type/_fixture/postgres/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/enum_type/_fixture/postgres/generated/contract.d.ts index 6e84ee53985e..d5d7d3006e26 100644 --- a/test/integration/test/ports/engines/queries/data_types/enum_type/_fixture/postgres/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/enum_type/_fixture/postgres/generated/contract.d.ts @@ -428,7 +428,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/data_types/float/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/float/_fixture/generated/contract.d.ts index cf24259e9beb..f77d91dcb0ca 100644 --- a/test/integration/test/ports/engines/queries/data_types/float/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/float/_fixture/generated/contract.d.ts @@ -466,7 +466,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/data_types/int/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/int/_fixture/generated/contract.d.ts index cf24259e9beb..f77d91dcb0ca 100644 --- a/test/integration/test/ports/engines/queries/data_types/int/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/int/_fixture/generated/contract.d.ts @@ -466,7 +466,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/data_types/json/_fixture/scalar/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/json/_fixture/scalar/generated/contract.d.ts index e130b6241483..e7e9c09c7097 100644 --- a/test/integration/test/ports/engines/queries/data_types/json/_fixture/scalar/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/json/_fixture/scalar/generated/contract.d.ts @@ -391,7 +391,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/data_types/native/postgres/_fixture/other/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/native/postgres/_fixture/other/generated/contract.d.ts index 360807b20de6..9c1e5b36616c 100644 --- a/test/integration/test/ports/engines/queries/data_types/native/postgres/_fixture/other/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/native/postgres/_fixture/other/generated/contract.d.ts @@ -516,7 +516,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/data_types/native/postgres/_fixture/string/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/native/postgres/_fixture/string/generated/contract.d.ts index b30fa3aa4b8f..9aa87a68e5d9 100644 --- a/test/integration/test/ports/engines/queries/data_types/native/postgres/_fixture/string/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/native/postgres/_fixture/string/generated/contract.d.ts @@ -572,7 +572,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/data_types/string/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/string/_fixture/generated/contract.d.ts index cf24259e9beb..f77d91dcb0ca 100644 --- a/test/integration/test/ports/engines/queries/data_types/string/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/string/_fixture/generated/contract.d.ts @@ -466,7 +466,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/common/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/common/generated/contract.d.ts index 41b383a057c8..de5276611c3b 100644 --- a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/common/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/common/generated/contract.d.ts @@ -559,7 +559,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/decimal/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/decimal/generated/contract.d.ts index 5a23dcace66b..7eeb86efc980 100644 --- a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/decimal/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/decimal/generated/contract.d.ts @@ -484,7 +484,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/enum/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/enum/generated/contract.d.ts index 8323e337284a..8de61cd5a188 100644 --- a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/enum/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/enum/generated/contract.d.ts @@ -527,7 +527,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/json/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/json/generated/contract.d.ts index c5827a746e04..4f6ae84eaf92 100644 --- a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/json/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/json/generated/contract.d.ts @@ -484,7 +484,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/lists/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/lists/generated/contract.d.ts index 350d0a549ed6..e2b359df4bb7 100644 --- a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/lists/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/lists/generated/contract.d.ts @@ -592,7 +592,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/distinct/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/distinct/_fixture/generated/contract.d.ts index 966a66ef2215..8c96680ca347 100644 --- a/test/integration/test/ports/engines/queries/distinct/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/distinct/_fixture/generated/contract.d.ts @@ -547,7 +547,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/bigint_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/bigint_filter/_fixture/generated/contract.d.ts index cf24259e9beb..f77d91dcb0ca 100644 --- a/test/integration/test/ports/engines/queries/filters/bigint_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/bigint_filter/_fixture/generated/contract.d.ts @@ -466,7 +466,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/bytes_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/bytes_filter/_fixture/generated/contract.d.ts index cf24259e9beb..f77d91dcb0ca 100644 --- a/test/integration/test/ports/engines/queries/filters/bytes_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/bytes_filter/_fixture/generated/contract.d.ts @@ -466,7 +466,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/decimal_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/decimal_filter/_fixture/generated/contract.d.ts index 079b9b6c72ac..a6ce1cb1fe91 100644 --- a/test/integration/test/ports/engines/queries/filters/decimal_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/decimal_filter/_fixture/generated/contract.d.ts @@ -396,7 +396,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/decimal-list/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/decimal-list/generated/contract.d.ts index e47dcc59abd5..fae6ed88b43d 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/decimal-list/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/decimal-list/generated/contract.d.ts @@ -417,7 +417,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/enum/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/enum/generated/contract.d.ts index a5ebbab30e24..1c82697e7a44 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/enum/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/enum/generated/contract.d.ts @@ -436,7 +436,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/list/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/list/generated/contract.d.ts index 7c3b8d87c15b..c905279f5095 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/list/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/list/generated/contract.d.ts @@ -654,7 +654,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/mixed/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/mixed/generated/contract.d.ts index cd1448866210..0eb03ab059bf 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/mixed/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/mixed/generated/contract.d.ts @@ -560,7 +560,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/bigint_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/bigint_filter/_fixture/generated/contract.d.ts index 78c4ea4ee058..5e70f0ca67db 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/bigint_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/bigint_filter/_fixture/generated/contract.d.ts @@ -553,7 +553,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/bytes_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/bytes_filter/_fixture/generated/contract.d.ts index 78c4ea4ee058..5e70f0ca67db 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/bytes_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/bytes_filter/_fixture/generated/contract.d.ts @@ -553,7 +553,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/datetime_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/datetime_filter/_fixture/generated/contract.d.ts index 78c4ea4ee058..5e70f0ca67db 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/datetime_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/datetime_filter/_fixture/generated/contract.d.ts @@ -553,7 +553,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/decimal_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/decimal_filter/_fixture/generated/contract.d.ts index d6a5f215a045..6ac86e29a5de 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/decimal_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/decimal_filter/_fixture/generated/contract.d.ts @@ -403,7 +403,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/common/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/common/generated/contract.d.ts index 78c4ea4ee058..5e70f0ca67db 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/common/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/common/generated/contract.d.ts @@ -553,7 +553,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/default/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/default/generated/contract.d.ts index cee01631d909..1bf2cfe5136d 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/default/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/default/generated/contract.d.ts @@ -498,7 +498,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/list/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/list/generated/contract.d.ts index 8fd52a04c4a6..b75fd063fde7 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/list/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/list/generated/contract.d.ts @@ -524,7 +524,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/float_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/float_filter/_fixture/generated/contract.d.ts index 78c4ea4ee058..5e70f0ca67db 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/float_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/float_filter/_fixture/generated/contract.d.ts @@ -553,7 +553,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/having_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/having_filter/_fixture/generated/contract.d.ts index 78c4ea4ee058..5e70f0ca67db 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/having_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/having_filter/_fixture/generated/contract.d.ts @@ -553,7 +553,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/int_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/int_filter/_fixture/generated/contract.d.ts index 78c4ea4ee058..5e70f0ca67db 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/int_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/int_filter/_fixture/generated/contract.d.ts @@ -553,7 +553,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/json_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/json_filter/_fixture/generated/contract.d.ts index f68e7448e5ed..934616421f9a 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/json_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/json_filter/_fixture/generated/contract.d.ts @@ -415,7 +415,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/json_filter/_fixture/list/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/json_filter/_fixture/list/generated/contract.d.ts index ce6bdda94c3d..759d6f5cc38c 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/json_filter/_fixture/list/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/json_filter/_fixture/list/generated/contract.d.ts @@ -417,7 +417,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/complex/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/complex/generated/contract.d.ts index 5d35b0801bda..8ea750c7323e 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/complex/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/complex/generated/contract.d.ts @@ -583,7 +583,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-many/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-many/generated/contract.d.ts index 69de1382debd..41ab6230a97d 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-many/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-many/generated/contract.d.ts @@ -496,7 +496,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-one-list/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-one-list/generated/contract.d.ts index 41b4afc38621..97263724405c 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-one-list/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-one-list/generated/contract.d.ts @@ -493,7 +493,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-one/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-one/generated/contract.d.ts index b7e60375faf5..683ed2d5bf8b 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-one/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-one/generated/contract.d.ts @@ -492,7 +492,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/string_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/string_filter/_fixture/generated/contract.d.ts index 78c4ea4ee058..5e70f0ca67db 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/string_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/string_filter/_fixture/generated/contract.d.ts @@ -553,7 +553,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/compound-one-to-many/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/compound-one-to-many/generated/contract.d.ts index 4174a8373043..831fec539a93 100644 --- a/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/compound-one-to-many/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/compound-one-to-many/generated/contract.d.ts @@ -522,7 +522,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/many-to-many/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/many-to-many/generated/contract.d.ts index 5e5fa69ad8bc..7ee4099164fe 100644 --- a/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/many-to-many/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/many-to-many/generated/contract.d.ts @@ -619,7 +619,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/one-to-many/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/one-to-many/generated/contract.d.ts index 67ae535f7b43..394ec0e9190f 100644 --- a/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/one-to-many/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/one-to-many/generated/contract.d.ts @@ -498,7 +498,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/filters/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/filters/_fixture/generated/contract.d.ts index ad1c8d24c302..69b35108c589 100644 --- a/test/integration/test/ports/engines/queries/filters/filters/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/filters/_fixture/generated/contract.d.ts @@ -627,7 +627,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/json/_fixture/optional/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/json/_fixture/optional/generated/contract.d.ts index e130b6241483..e7e9c09c7097 100644 --- a/test/integration/test/ports/engines/queries/filters/json/_fixture/optional/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/json/_fixture/optional/generated/contract.d.ts @@ -391,7 +391,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/base/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/base/generated/contract.d.ts index 098ab31609a3..3e334cf42056 100644 --- a/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/base/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/base/generated/contract.d.ts @@ -473,7 +473,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/decimal/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/decimal/generated/contract.d.ts index 5685dba19d73..7af7ae2cb163 100644 --- a/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/decimal/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/decimal/generated/contract.d.ts @@ -392,7 +392,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/json/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/json/generated/contract.d.ts index d45c4f6c6374..6bd368fc3a01 100644 --- a/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/json/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/json/generated/contract.d.ts @@ -392,7 +392,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/25103/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/25103/generated/contract.d.ts index 0d74fe3722f7..1840679e9ab4 100644 --- a/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/25103/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/25103/generated/contract.d.ts @@ -691,7 +691,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/l2-to-one/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/l2-to-one/generated/contract.d.ts index aa3036bb93bf..b065503190b5 100644 --- a/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/l2-to-one/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/l2-to-one/generated/contract.d.ts @@ -577,7 +577,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/one2one_regression/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/one2one_regression/_fixture/generated/contract.d.ts index 1c8f04641d0d..485708744bbe 100644 --- a/test/integration/test/ports/engines/queries/filters/one2one_regression/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/one2one_regression/_fixture/generated/contract.d.ts @@ -437,7 +437,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/one_relation/_fixture/21356/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/one_relation/_fixture/21356/generated/contract.d.ts index cc698102ba89..f08ddb634ffd 100644 --- a/test/integration/test/ports/engines/queries/filters/one_relation/_fixture/21356/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/one_relation/_fixture/21356/generated/contract.d.ts @@ -528,7 +528,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/queries/filters/one_relation/_fixture/21366/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/one_relation/_fixture/21366/generated/contract.d.ts index 3817b98f7b38..de0de1a59aa7 100644 --- a/test/integration/test/ports/engines/queries/filters/one_relation/_fixture/21366/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/one_relation/_fixture/21366/generated/contract.d.ts @@ -482,7 +482,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/engines/writes/top_level_mutations/create_many/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/writes/top_level_mutations/create_many/_fixture/generated/contract.d.ts index f7eb12bb26c1..166ca8d3b0aa 100644 --- a/test/integration/test/ports/engines/writes/top_level_mutations/create_many/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/writes/top_level_mutations/create_many/_fixture/generated/contract.d.ts @@ -419,7 +419,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/batching-bigint/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/batching-bigint/_fixture/generated/contract.d.ts index 5442c2ece6c6..e3e5db6a652d 100644 --- a/test/integration/test/ports/prisma/functional/batching-bigint/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/batching-bigint/_fixture/generated/contract.d.ts @@ -391,7 +391,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/batching-bytes/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/batching-bytes/_fixture/generated/contract.d.ts index df41e3e37ff0..4c04b94aa005 100644 --- a/test/integration/test/ports/prisma/functional/batching-bytes/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/batching-bytes/_fixture/generated/contract.d.ts @@ -391,7 +391,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/blog-update/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/blog-update/_fixture/generated/contract.d.ts index 212e70c23d1e..292da88d31dc 100644 --- a/test/integration/test/ports/prisma/functional/blog-update/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/blog-update/_fixture/generated/contract.d.ts @@ -817,7 +817,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/bytes-upsert/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/bytes-upsert/_fixture/generated/contract.d.ts index 11a91f24ad19..d418b73166fd 100644 --- a/test/integration/test/ports/prisma/functional/bytes-upsert/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/bytes-upsert/_fixture/generated/contract.d.ts @@ -410,7 +410,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/chunking-query/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/chunking-query/_fixture/generated/contract.d.ts index 4f788737d495..175275a79498 100644 --- a/test/integration/test/ports/prisma/functional/chunking-query/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/chunking-query/_fixture/generated/contract.d.ts @@ -547,7 +547,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/composites-list-create/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/composites-list-create/_fixture/generated/contract.d.ts index 35f1682bc5bd..191591ef3fc2 100644 --- a/test/integration/test/ports/prisma/functional/composites-list-create/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/composites-list-create/_fixture/generated/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'e9069c51c3786d2cdf847963cb30bffe3fd5891d6fbfb8d1d2b123e58b10c28b'>; + StorageHashBase<'996e1dd50d20ae8f000f3e1058f779aeed3f999c307ebb2eb853b07221d895dd'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -57,14 +57,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -133,6 +135,39 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly CommentContent: { + readonly fields: { + readonly text: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly upvotes: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContentUpvotes'; + }; + readonly many: { readonly elementNullable: false }; + }; + }; + }; + readonly CommentContentUpvotes: { + readonly fields: { + readonly userId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly vote: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; + }; + }; + }; + }; }; }; }; @@ -156,7 +191,13 @@ type ContractBase = Omit< readonly fields: { readonly contents: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContent' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContent'; + }; readonly many: { readonly elementNullable: false }; }; readonly country: { @@ -179,69 +220,12 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { - readonly kind: 'valueObject'; - readonly name: 'CommentContentUpvotes'; - }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; }; }; }; readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContentUpvotes' }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/composites-list-create/_fixture/generated/contract.json b/test/integration/test/ports/prisma/functional/composites-list-create/_fixture/generated/contract.json index db9acf2be9b6..6c8a9508f653 100644 --- a/test/integration/test/ports/prisma/functional/composites-list-create/_fixture/generated/contract.json +++ b/test/integration/test/ports/prisma/functional/composites-list-create/_fixture/generated/contract.json @@ -21,8 +21,11 @@ }, "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "CommentContent", "kind": "valueObject", - "name": "CommentContent" + "namespaceId": "__unbound__", + "plane": "storage" } }, "country": { @@ -56,47 +59,6 @@ } } } - }, - "valueObjects": { - "CommentContent": { - "fields": { - "text": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "upvotes": { - "many": { - "elementNullable": false - }, - "nullable": false, - "type": { - "kind": "valueObject", - "name": "CommentContentUpvotes" - } - } - } - }, - "CommentContentUpvotes": { - "fields": { - "userId": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "vote": { - "nullable": false, - "type": { - "codecId": "mongo/bool@1", - "kind": "scalar" - } - } - } - } } } } @@ -168,13 +130,57 @@ "validationLevel": "strict" } } + }, + "valueObject": { + "CommentContent": { + "fields": { + "text": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "upvotes": { + "many": { + "elementNullable": false + }, + "nullable": false, + "type": { + "entityKind": "valueObject", + "entityName": "CommentContentUpvotes", + "kind": "valueObject", + "namespaceId": "__unbound__", + "plane": "storage" + } + } + } + }, + "CommentContentUpvotes": { + "fields": { + "userId": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "vote": { + "nullable": false, + "type": { + "codecId": "mongo/bool@1", + "kind": "scalar" + } + } + } + } } }, "id": "__unbound__", "kind": "mongo-database" } }, - "storageHash": "e9069c51c3786d2cdf847963cb30bffe3fd5891d6fbfb8d1d2b123e58b10c28b" + "storageHash": "996e1dd50d20ae8f000f3e1058f779aeed3f999c307ebb2eb853b07221d895dd" }, "capabilities": {}, "extensions": {}, diff --git a/test/integration/test/ports/prisma/functional/composites-list-createMany/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/composites-list-createMany/_fixture/generated/contract.d.ts index 35f1682bc5bd..191591ef3fc2 100644 --- a/test/integration/test/ports/prisma/functional/composites-list-createMany/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/composites-list-createMany/_fixture/generated/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'e9069c51c3786d2cdf847963cb30bffe3fd5891d6fbfb8d1d2b123e58b10c28b'>; + StorageHashBase<'996e1dd50d20ae8f000f3e1058f779aeed3f999c307ebb2eb853b07221d895dd'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -57,14 +57,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -133,6 +135,39 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly CommentContent: { + readonly fields: { + readonly text: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly upvotes: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContentUpvotes'; + }; + readonly many: { readonly elementNullable: false }; + }; + }; + }; + readonly CommentContentUpvotes: { + readonly fields: { + readonly userId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly vote: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; + }; + }; + }; + }; }; }; }; @@ -156,7 +191,13 @@ type ContractBase = Omit< readonly fields: { readonly contents: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContent' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContent'; + }; readonly many: { readonly elementNullable: false }; }; readonly country: { @@ -179,69 +220,12 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { - readonly kind: 'valueObject'; - readonly name: 'CommentContentUpvotes'; - }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; }; }; }; readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContentUpvotes' }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/composites-list-createMany/_fixture/generated/contract.json b/test/integration/test/ports/prisma/functional/composites-list-createMany/_fixture/generated/contract.json index db9acf2be9b6..6c8a9508f653 100644 --- a/test/integration/test/ports/prisma/functional/composites-list-createMany/_fixture/generated/contract.json +++ b/test/integration/test/ports/prisma/functional/composites-list-createMany/_fixture/generated/contract.json @@ -21,8 +21,11 @@ }, "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "CommentContent", "kind": "valueObject", - "name": "CommentContent" + "namespaceId": "__unbound__", + "plane": "storage" } }, "country": { @@ -56,47 +59,6 @@ } } } - }, - "valueObjects": { - "CommentContent": { - "fields": { - "text": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "upvotes": { - "many": { - "elementNullable": false - }, - "nullable": false, - "type": { - "kind": "valueObject", - "name": "CommentContentUpvotes" - } - } - } - }, - "CommentContentUpvotes": { - "fields": { - "userId": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "vote": { - "nullable": false, - "type": { - "codecId": "mongo/bool@1", - "kind": "scalar" - } - } - } - } } } } @@ -168,13 +130,57 @@ "validationLevel": "strict" } } + }, + "valueObject": { + "CommentContent": { + "fields": { + "text": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "upvotes": { + "many": { + "elementNullable": false + }, + "nullable": false, + "type": { + "entityKind": "valueObject", + "entityName": "CommentContentUpvotes", + "kind": "valueObject", + "namespaceId": "__unbound__", + "plane": "storage" + } + } + } + }, + "CommentContentUpvotes": { + "fields": { + "userId": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "vote": { + "nullable": false, + "type": { + "codecId": "mongo/bool@1", + "kind": "scalar" + } + } + } + } } }, "id": "__unbound__", "kind": "mongo-database" } }, - "storageHash": "e9069c51c3786d2cdf847963cb30bffe3fd5891d6fbfb8d1d2b123e58b10c28b" + "storageHash": "996e1dd50d20ae8f000f3e1058f779aeed3f999c307ebb2eb853b07221d895dd" }, "capabilities": {}, "extensions": {}, diff --git a/test/integration/test/ports/prisma/functional/composites-list-delete/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/composites-list-delete/_fixture/generated/contract.d.ts index 35f1682bc5bd..191591ef3fc2 100644 --- a/test/integration/test/ports/prisma/functional/composites-list-delete/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/composites-list-delete/_fixture/generated/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'e9069c51c3786d2cdf847963cb30bffe3fd5891d6fbfb8d1d2b123e58b10c28b'>; + StorageHashBase<'996e1dd50d20ae8f000f3e1058f779aeed3f999c307ebb2eb853b07221d895dd'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -57,14 +57,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -133,6 +135,39 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly CommentContent: { + readonly fields: { + readonly text: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly upvotes: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContentUpvotes'; + }; + readonly many: { readonly elementNullable: false }; + }; + }; + }; + readonly CommentContentUpvotes: { + readonly fields: { + readonly userId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly vote: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; + }; + }; + }; + }; }; }; }; @@ -156,7 +191,13 @@ type ContractBase = Omit< readonly fields: { readonly contents: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContent' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContent'; + }; readonly many: { readonly elementNullable: false }; }; readonly country: { @@ -179,69 +220,12 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { - readonly kind: 'valueObject'; - readonly name: 'CommentContentUpvotes'; - }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; }; }; }; readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContentUpvotes' }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/composites-list-delete/_fixture/generated/contract.json b/test/integration/test/ports/prisma/functional/composites-list-delete/_fixture/generated/contract.json index db9acf2be9b6..6c8a9508f653 100644 --- a/test/integration/test/ports/prisma/functional/composites-list-delete/_fixture/generated/contract.json +++ b/test/integration/test/ports/prisma/functional/composites-list-delete/_fixture/generated/contract.json @@ -21,8 +21,11 @@ }, "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "CommentContent", "kind": "valueObject", - "name": "CommentContent" + "namespaceId": "__unbound__", + "plane": "storage" } }, "country": { @@ -56,47 +59,6 @@ } } } - }, - "valueObjects": { - "CommentContent": { - "fields": { - "text": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "upvotes": { - "many": { - "elementNullable": false - }, - "nullable": false, - "type": { - "kind": "valueObject", - "name": "CommentContentUpvotes" - } - } - } - }, - "CommentContentUpvotes": { - "fields": { - "userId": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "vote": { - "nullable": false, - "type": { - "codecId": "mongo/bool@1", - "kind": "scalar" - } - } - } - } } } } @@ -168,13 +130,57 @@ "validationLevel": "strict" } } + }, + "valueObject": { + "CommentContent": { + "fields": { + "text": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "upvotes": { + "many": { + "elementNullable": false + }, + "nullable": false, + "type": { + "entityKind": "valueObject", + "entityName": "CommentContentUpvotes", + "kind": "valueObject", + "namespaceId": "__unbound__", + "plane": "storage" + } + } + } + }, + "CommentContentUpvotes": { + "fields": { + "userId": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "vote": { + "nullable": false, + "type": { + "codecId": "mongo/bool@1", + "kind": "scalar" + } + } + } + } } }, "id": "__unbound__", "kind": "mongo-database" } }, - "storageHash": "e9069c51c3786d2cdf847963cb30bffe3fd5891d6fbfb8d1d2b123e58b10c28b" + "storageHash": "996e1dd50d20ae8f000f3e1058f779aeed3f999c307ebb2eb853b07221d895dd" }, "capabilities": {}, "extensions": {}, diff --git a/test/integration/test/ports/prisma/functional/composites-list-deleteMany/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/composites-list-deleteMany/_fixture/generated/contract.d.ts index 35f1682bc5bd..191591ef3fc2 100644 --- a/test/integration/test/ports/prisma/functional/composites-list-deleteMany/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/composites-list-deleteMany/_fixture/generated/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'e9069c51c3786d2cdf847963cb30bffe3fd5891d6fbfb8d1d2b123e58b10c28b'>; + StorageHashBase<'996e1dd50d20ae8f000f3e1058f779aeed3f999c307ebb2eb853b07221d895dd'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -57,14 +57,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -133,6 +135,39 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly CommentContent: { + readonly fields: { + readonly text: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly upvotes: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContentUpvotes'; + }; + readonly many: { readonly elementNullable: false }; + }; + }; + }; + readonly CommentContentUpvotes: { + readonly fields: { + readonly userId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly vote: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; + }; + }; + }; + }; }; }; }; @@ -156,7 +191,13 @@ type ContractBase = Omit< readonly fields: { readonly contents: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContent' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContent'; + }; readonly many: { readonly elementNullable: false }; }; readonly country: { @@ -179,69 +220,12 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { - readonly kind: 'valueObject'; - readonly name: 'CommentContentUpvotes'; - }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; }; }; }; readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContentUpvotes' }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/composites-list-deleteMany/_fixture/generated/contract.json b/test/integration/test/ports/prisma/functional/composites-list-deleteMany/_fixture/generated/contract.json index db9acf2be9b6..6c8a9508f653 100644 --- a/test/integration/test/ports/prisma/functional/composites-list-deleteMany/_fixture/generated/contract.json +++ b/test/integration/test/ports/prisma/functional/composites-list-deleteMany/_fixture/generated/contract.json @@ -21,8 +21,11 @@ }, "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "CommentContent", "kind": "valueObject", - "name": "CommentContent" + "namespaceId": "__unbound__", + "plane": "storage" } }, "country": { @@ -56,47 +59,6 @@ } } } - }, - "valueObjects": { - "CommentContent": { - "fields": { - "text": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "upvotes": { - "many": { - "elementNullable": false - }, - "nullable": false, - "type": { - "kind": "valueObject", - "name": "CommentContentUpvotes" - } - } - } - }, - "CommentContentUpvotes": { - "fields": { - "userId": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "vote": { - "nullable": false, - "type": { - "codecId": "mongo/bool@1", - "kind": "scalar" - } - } - } - } } } } @@ -168,13 +130,57 @@ "validationLevel": "strict" } } + }, + "valueObject": { + "CommentContent": { + "fields": { + "text": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "upvotes": { + "many": { + "elementNullable": false + }, + "nullable": false, + "type": { + "entityKind": "valueObject", + "entityName": "CommentContentUpvotes", + "kind": "valueObject", + "namespaceId": "__unbound__", + "plane": "storage" + } + } + } + }, + "CommentContentUpvotes": { + "fields": { + "userId": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "vote": { + "nullable": false, + "type": { + "codecId": "mongo/bool@1", + "kind": "scalar" + } + } + } + } } }, "id": "__unbound__", "kind": "mongo-database" } }, - "storageHash": "e9069c51c3786d2cdf847963cb30bffe3fd5891d6fbfb8d1d2b123e58b10c28b" + "storageHash": "996e1dd50d20ae8f000f3e1058f779aeed3f999c307ebb2eb853b07221d895dd" }, "capabilities": {}, "extensions": {}, diff --git a/test/integration/test/ports/prisma/functional/composites-list-findFirst/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/composites-list-findFirst/_fixture/generated/contract.d.ts index 35f1682bc5bd..191591ef3fc2 100644 --- a/test/integration/test/ports/prisma/functional/composites-list-findFirst/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/composites-list-findFirst/_fixture/generated/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'e9069c51c3786d2cdf847963cb30bffe3fd5891d6fbfb8d1d2b123e58b10c28b'>; + StorageHashBase<'996e1dd50d20ae8f000f3e1058f779aeed3f999c307ebb2eb853b07221d895dd'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -57,14 +57,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -133,6 +135,39 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly CommentContent: { + readonly fields: { + readonly text: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly upvotes: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContentUpvotes'; + }; + readonly many: { readonly elementNullable: false }; + }; + }; + }; + readonly CommentContentUpvotes: { + readonly fields: { + readonly userId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly vote: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; + }; + }; + }; + }; }; }; }; @@ -156,7 +191,13 @@ type ContractBase = Omit< readonly fields: { readonly contents: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContent' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContent'; + }; readonly many: { readonly elementNullable: false }; }; readonly country: { @@ -179,69 +220,12 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { - readonly kind: 'valueObject'; - readonly name: 'CommentContentUpvotes'; - }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; }; }; }; readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContentUpvotes' }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/composites-list-findFirst/_fixture/generated/contract.json b/test/integration/test/ports/prisma/functional/composites-list-findFirst/_fixture/generated/contract.json index db9acf2be9b6..6c8a9508f653 100644 --- a/test/integration/test/ports/prisma/functional/composites-list-findFirst/_fixture/generated/contract.json +++ b/test/integration/test/ports/prisma/functional/composites-list-findFirst/_fixture/generated/contract.json @@ -21,8 +21,11 @@ }, "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "CommentContent", "kind": "valueObject", - "name": "CommentContent" + "namespaceId": "__unbound__", + "plane": "storage" } }, "country": { @@ -56,47 +59,6 @@ } } } - }, - "valueObjects": { - "CommentContent": { - "fields": { - "text": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "upvotes": { - "many": { - "elementNullable": false - }, - "nullable": false, - "type": { - "kind": "valueObject", - "name": "CommentContentUpvotes" - } - } - } - }, - "CommentContentUpvotes": { - "fields": { - "userId": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "vote": { - "nullable": false, - "type": { - "codecId": "mongo/bool@1", - "kind": "scalar" - } - } - } - } } } } @@ -168,13 +130,57 @@ "validationLevel": "strict" } } + }, + "valueObject": { + "CommentContent": { + "fields": { + "text": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "upvotes": { + "many": { + "elementNullable": false + }, + "nullable": false, + "type": { + "entityKind": "valueObject", + "entityName": "CommentContentUpvotes", + "kind": "valueObject", + "namespaceId": "__unbound__", + "plane": "storage" + } + } + } + }, + "CommentContentUpvotes": { + "fields": { + "userId": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "vote": { + "nullable": false, + "type": { + "codecId": "mongo/bool@1", + "kind": "scalar" + } + } + } + } } }, "id": "__unbound__", "kind": "mongo-database" } }, - "storageHash": "e9069c51c3786d2cdf847963cb30bffe3fd5891d6fbfb8d1d2b123e58b10c28b" + "storageHash": "996e1dd50d20ae8f000f3e1058f779aeed3f999c307ebb2eb853b07221d895dd" }, "capabilities": {}, "extensions": {}, diff --git a/test/integration/test/ports/prisma/functional/composites-list-findMany/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/composites-list-findMany/_fixture/generated/contract.d.ts index 35f1682bc5bd..191591ef3fc2 100644 --- a/test/integration/test/ports/prisma/functional/composites-list-findMany/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/composites-list-findMany/_fixture/generated/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'e9069c51c3786d2cdf847963cb30bffe3fd5891d6fbfb8d1d2b123e58b10c28b'>; + StorageHashBase<'996e1dd50d20ae8f000f3e1058f779aeed3f999c307ebb2eb853b07221d895dd'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -57,14 +57,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -133,6 +135,39 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly CommentContent: { + readonly fields: { + readonly text: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly upvotes: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContentUpvotes'; + }; + readonly many: { readonly elementNullable: false }; + }; + }; + }; + readonly CommentContentUpvotes: { + readonly fields: { + readonly userId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly vote: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; + }; + }; + }; + }; }; }; }; @@ -156,7 +191,13 @@ type ContractBase = Omit< readonly fields: { readonly contents: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContent' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContent'; + }; readonly many: { readonly elementNullable: false }; }; readonly country: { @@ -179,69 +220,12 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { - readonly kind: 'valueObject'; - readonly name: 'CommentContentUpvotes'; - }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; }; }; }; readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContentUpvotes' }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/composites-list-findMany/_fixture/generated/contract.json b/test/integration/test/ports/prisma/functional/composites-list-findMany/_fixture/generated/contract.json index db9acf2be9b6..6c8a9508f653 100644 --- a/test/integration/test/ports/prisma/functional/composites-list-findMany/_fixture/generated/contract.json +++ b/test/integration/test/ports/prisma/functional/composites-list-findMany/_fixture/generated/contract.json @@ -21,8 +21,11 @@ }, "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "CommentContent", "kind": "valueObject", - "name": "CommentContent" + "namespaceId": "__unbound__", + "plane": "storage" } }, "country": { @@ -56,47 +59,6 @@ } } } - }, - "valueObjects": { - "CommentContent": { - "fields": { - "text": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "upvotes": { - "many": { - "elementNullable": false - }, - "nullable": false, - "type": { - "kind": "valueObject", - "name": "CommentContentUpvotes" - } - } - } - }, - "CommentContentUpvotes": { - "fields": { - "userId": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "vote": { - "nullable": false, - "type": { - "codecId": "mongo/bool@1", - "kind": "scalar" - } - } - } - } } } } @@ -168,13 +130,57 @@ "validationLevel": "strict" } } + }, + "valueObject": { + "CommentContent": { + "fields": { + "text": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "upvotes": { + "many": { + "elementNullable": false + }, + "nullable": false, + "type": { + "entityKind": "valueObject", + "entityName": "CommentContentUpvotes", + "kind": "valueObject", + "namespaceId": "__unbound__", + "plane": "storage" + } + } + } + }, + "CommentContentUpvotes": { + "fields": { + "userId": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "vote": { + "nullable": false, + "type": { + "codecId": "mongo/bool@1", + "kind": "scalar" + } + } + } + } } }, "id": "__unbound__", "kind": "mongo-database" } }, - "storageHash": "e9069c51c3786d2cdf847963cb30bffe3fd5891d6fbfb8d1d2b123e58b10c28b" + "storageHash": "996e1dd50d20ae8f000f3e1058f779aeed3f999c307ebb2eb853b07221d895dd" }, "capabilities": {}, "extensions": {}, diff --git a/test/integration/test/ports/prisma/functional/composites-list-update/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/composites-list-update/_fixture/generated/contract.d.ts index 35f1682bc5bd..191591ef3fc2 100644 --- a/test/integration/test/ports/prisma/functional/composites-list-update/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/composites-list-update/_fixture/generated/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'e9069c51c3786d2cdf847963cb30bffe3fd5891d6fbfb8d1d2b123e58b10c28b'>; + StorageHashBase<'996e1dd50d20ae8f000f3e1058f779aeed3f999c307ebb2eb853b07221d895dd'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -57,14 +57,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -133,6 +135,39 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly CommentContent: { + readonly fields: { + readonly text: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly upvotes: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContentUpvotes'; + }; + readonly many: { readonly elementNullable: false }; + }; + }; + }; + readonly CommentContentUpvotes: { + readonly fields: { + readonly userId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly vote: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; + }; + }; + }; + }; }; }; }; @@ -156,7 +191,13 @@ type ContractBase = Omit< readonly fields: { readonly contents: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContent' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContent'; + }; readonly many: { readonly elementNullable: false }; }; readonly country: { @@ -179,69 +220,12 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { - readonly kind: 'valueObject'; - readonly name: 'CommentContentUpvotes'; - }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; }; }; }; readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContentUpvotes' }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/composites-list-update/_fixture/generated/contract.json b/test/integration/test/ports/prisma/functional/composites-list-update/_fixture/generated/contract.json index db9acf2be9b6..6c8a9508f653 100644 --- a/test/integration/test/ports/prisma/functional/composites-list-update/_fixture/generated/contract.json +++ b/test/integration/test/ports/prisma/functional/composites-list-update/_fixture/generated/contract.json @@ -21,8 +21,11 @@ }, "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "CommentContent", "kind": "valueObject", - "name": "CommentContent" + "namespaceId": "__unbound__", + "plane": "storage" } }, "country": { @@ -56,47 +59,6 @@ } } } - }, - "valueObjects": { - "CommentContent": { - "fields": { - "text": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "upvotes": { - "many": { - "elementNullable": false - }, - "nullable": false, - "type": { - "kind": "valueObject", - "name": "CommentContentUpvotes" - } - } - } - }, - "CommentContentUpvotes": { - "fields": { - "userId": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "vote": { - "nullable": false, - "type": { - "codecId": "mongo/bool@1", - "kind": "scalar" - } - } - } - } } } } @@ -168,13 +130,57 @@ "validationLevel": "strict" } } + }, + "valueObject": { + "CommentContent": { + "fields": { + "text": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "upvotes": { + "many": { + "elementNullable": false + }, + "nullable": false, + "type": { + "entityKind": "valueObject", + "entityName": "CommentContentUpvotes", + "kind": "valueObject", + "namespaceId": "__unbound__", + "plane": "storage" + } + } + } + }, + "CommentContentUpvotes": { + "fields": { + "userId": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "vote": { + "nullable": false, + "type": { + "codecId": "mongo/bool@1", + "kind": "scalar" + } + } + } + } } }, "id": "__unbound__", "kind": "mongo-database" } }, - "storageHash": "e9069c51c3786d2cdf847963cb30bffe3fd5891d6fbfb8d1d2b123e58b10c28b" + "storageHash": "996e1dd50d20ae8f000f3e1058f779aeed3f999c307ebb2eb853b07221d895dd" }, "capabilities": {}, "extensions": {}, diff --git a/test/integration/test/ports/prisma/functional/composites-list-updateMany/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/composites-list-updateMany/_fixture/generated/contract.d.ts index 35f1682bc5bd..191591ef3fc2 100644 --- a/test/integration/test/ports/prisma/functional/composites-list-updateMany/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/composites-list-updateMany/_fixture/generated/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'e9069c51c3786d2cdf847963cb30bffe3fd5891d6fbfb8d1d2b123e58b10c28b'>; + StorageHashBase<'996e1dd50d20ae8f000f3e1058f779aeed3f999c307ebb2eb853b07221d895dd'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -57,14 +57,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -133,6 +135,39 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly CommentContent: { + readonly fields: { + readonly text: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly upvotes: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContentUpvotes'; + }; + readonly many: { readonly elementNullable: false }; + }; + }; + }; + readonly CommentContentUpvotes: { + readonly fields: { + readonly userId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly vote: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; + }; + }; + }; + }; }; }; }; @@ -156,7 +191,13 @@ type ContractBase = Omit< readonly fields: { readonly contents: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContent' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContent'; + }; readonly many: { readonly elementNullable: false }; }; readonly country: { @@ -179,69 +220,12 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { - readonly kind: 'valueObject'; - readonly name: 'CommentContentUpvotes'; - }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; }; }; }; readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContentUpvotes' }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/composites-list-updateMany/_fixture/generated/contract.json b/test/integration/test/ports/prisma/functional/composites-list-updateMany/_fixture/generated/contract.json index db9acf2be9b6..6c8a9508f653 100644 --- a/test/integration/test/ports/prisma/functional/composites-list-updateMany/_fixture/generated/contract.json +++ b/test/integration/test/ports/prisma/functional/composites-list-updateMany/_fixture/generated/contract.json @@ -21,8 +21,11 @@ }, "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "CommentContent", "kind": "valueObject", - "name": "CommentContent" + "namespaceId": "__unbound__", + "plane": "storage" } }, "country": { @@ -56,47 +59,6 @@ } } } - }, - "valueObjects": { - "CommentContent": { - "fields": { - "text": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "upvotes": { - "many": { - "elementNullable": false - }, - "nullable": false, - "type": { - "kind": "valueObject", - "name": "CommentContentUpvotes" - } - } - } - }, - "CommentContentUpvotes": { - "fields": { - "userId": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "vote": { - "nullable": false, - "type": { - "codecId": "mongo/bool@1", - "kind": "scalar" - } - } - } - } } } } @@ -168,13 +130,57 @@ "validationLevel": "strict" } } + }, + "valueObject": { + "CommentContent": { + "fields": { + "text": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "upvotes": { + "many": { + "elementNullable": false + }, + "nullable": false, + "type": { + "entityKind": "valueObject", + "entityName": "CommentContentUpvotes", + "kind": "valueObject", + "namespaceId": "__unbound__", + "plane": "storage" + } + } + } + }, + "CommentContentUpvotes": { + "fields": { + "userId": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "vote": { + "nullable": false, + "type": { + "codecId": "mongo/bool@1", + "kind": "scalar" + } + } + } + } } }, "id": "__unbound__", "kind": "mongo-database" } }, - "storageHash": "e9069c51c3786d2cdf847963cb30bffe3fd5891d6fbfb8d1d2b123e58b10c28b" + "storageHash": "996e1dd50d20ae8f000f3e1058f779aeed3f999c307ebb2eb853b07221d895dd" }, "capabilities": {}, "extensions": {}, diff --git a/test/integration/test/ports/prisma/functional/composites-list-upsert-create/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/composites-list-upsert-create/_fixture/generated/contract.d.ts index 35f1682bc5bd..191591ef3fc2 100644 --- a/test/integration/test/ports/prisma/functional/composites-list-upsert-create/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/composites-list-upsert-create/_fixture/generated/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'e9069c51c3786d2cdf847963cb30bffe3fd5891d6fbfb8d1d2b123e58b10c28b'>; + StorageHashBase<'996e1dd50d20ae8f000f3e1058f779aeed3f999c307ebb2eb853b07221d895dd'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -57,14 +57,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -133,6 +135,39 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly CommentContent: { + readonly fields: { + readonly text: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly upvotes: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContentUpvotes'; + }; + readonly many: { readonly elementNullable: false }; + }; + }; + }; + readonly CommentContentUpvotes: { + readonly fields: { + readonly userId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly vote: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; + }; + }; + }; + }; }; }; }; @@ -156,7 +191,13 @@ type ContractBase = Omit< readonly fields: { readonly contents: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContent' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContent'; + }; readonly many: { readonly elementNullable: false }; }; readonly country: { @@ -179,69 +220,12 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { - readonly kind: 'valueObject'; - readonly name: 'CommentContentUpvotes'; - }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; }; }; }; readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContentUpvotes' }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/composites-list-upsert-create/_fixture/generated/contract.json b/test/integration/test/ports/prisma/functional/composites-list-upsert-create/_fixture/generated/contract.json index db9acf2be9b6..6c8a9508f653 100644 --- a/test/integration/test/ports/prisma/functional/composites-list-upsert-create/_fixture/generated/contract.json +++ b/test/integration/test/ports/prisma/functional/composites-list-upsert-create/_fixture/generated/contract.json @@ -21,8 +21,11 @@ }, "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "CommentContent", "kind": "valueObject", - "name": "CommentContent" + "namespaceId": "__unbound__", + "plane": "storage" } }, "country": { @@ -56,47 +59,6 @@ } } } - }, - "valueObjects": { - "CommentContent": { - "fields": { - "text": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "upvotes": { - "many": { - "elementNullable": false - }, - "nullable": false, - "type": { - "kind": "valueObject", - "name": "CommentContentUpvotes" - } - } - } - }, - "CommentContentUpvotes": { - "fields": { - "userId": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "vote": { - "nullable": false, - "type": { - "codecId": "mongo/bool@1", - "kind": "scalar" - } - } - } - } } } } @@ -168,13 +130,57 @@ "validationLevel": "strict" } } + }, + "valueObject": { + "CommentContent": { + "fields": { + "text": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "upvotes": { + "many": { + "elementNullable": false + }, + "nullable": false, + "type": { + "entityKind": "valueObject", + "entityName": "CommentContentUpvotes", + "kind": "valueObject", + "namespaceId": "__unbound__", + "plane": "storage" + } + } + } + }, + "CommentContentUpvotes": { + "fields": { + "userId": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "vote": { + "nullable": false, + "type": { + "codecId": "mongo/bool@1", + "kind": "scalar" + } + } + } + } } }, "id": "__unbound__", "kind": "mongo-database" } }, - "storageHash": "e9069c51c3786d2cdf847963cb30bffe3fd5891d6fbfb8d1d2b123e58b10c28b" + "storageHash": "996e1dd50d20ae8f000f3e1058f779aeed3f999c307ebb2eb853b07221d895dd" }, "capabilities": {}, "extensions": {}, diff --git a/test/integration/test/ports/prisma/functional/composites-list-upsert-update/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/composites-list-upsert-update/_fixture/generated/contract.d.ts index 35f1682bc5bd..191591ef3fc2 100644 --- a/test/integration/test/ports/prisma/functional/composites-list-upsert-update/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/composites-list-upsert-update/_fixture/generated/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'e9069c51c3786d2cdf847963cb30bffe3fd5891d6fbfb8d1d2b123e58b10c28b'>; + StorageHashBase<'996e1dd50d20ae8f000f3e1058f779aeed3f999c307ebb2eb853b07221d895dd'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -57,14 +57,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -133,6 +135,39 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly CommentContent: { + readonly fields: { + readonly text: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly upvotes: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContentUpvotes'; + }; + readonly many: { readonly elementNullable: false }; + }; + }; + }; + readonly CommentContentUpvotes: { + readonly fields: { + readonly userId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly vote: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; + }; + }; + }; + }; }; }; }; @@ -156,7 +191,13 @@ type ContractBase = Omit< readonly fields: { readonly contents: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContent' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContent'; + }; readonly many: { readonly elementNullable: false }; }; readonly country: { @@ -179,69 +220,12 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { - readonly kind: 'valueObject'; - readonly name: 'CommentContentUpvotes'; - }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; }; }; }; readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContentUpvotes' }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/composites-list-upsert-update/_fixture/generated/contract.json b/test/integration/test/ports/prisma/functional/composites-list-upsert-update/_fixture/generated/contract.json index db9acf2be9b6..6c8a9508f653 100644 --- a/test/integration/test/ports/prisma/functional/composites-list-upsert-update/_fixture/generated/contract.json +++ b/test/integration/test/ports/prisma/functional/composites-list-upsert-update/_fixture/generated/contract.json @@ -21,8 +21,11 @@ }, "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "CommentContent", "kind": "valueObject", - "name": "CommentContent" + "namespaceId": "__unbound__", + "plane": "storage" } }, "country": { @@ -56,47 +59,6 @@ } } } - }, - "valueObjects": { - "CommentContent": { - "fields": { - "text": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "upvotes": { - "many": { - "elementNullable": false - }, - "nullable": false, - "type": { - "kind": "valueObject", - "name": "CommentContentUpvotes" - } - } - } - }, - "CommentContentUpvotes": { - "fields": { - "userId": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "vote": { - "nullable": false, - "type": { - "codecId": "mongo/bool@1", - "kind": "scalar" - } - } - } - } } } } @@ -168,13 +130,57 @@ "validationLevel": "strict" } } + }, + "valueObject": { + "CommentContent": { + "fields": { + "text": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "upvotes": { + "many": { + "elementNullable": false + }, + "nullable": false, + "type": { + "entityKind": "valueObject", + "entityName": "CommentContentUpvotes", + "kind": "valueObject", + "namespaceId": "__unbound__", + "plane": "storage" + } + } + } + }, + "CommentContentUpvotes": { + "fields": { + "userId": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "vote": { + "nullable": false, + "type": { + "codecId": "mongo/bool@1", + "kind": "scalar" + } + } + } + } } }, "id": "__unbound__", "kind": "mongo-database" } }, - "storageHash": "e9069c51c3786d2cdf847963cb30bffe3fd5891d6fbfb8d1d2b123e58b10c28b" + "storageHash": "996e1dd50d20ae8f000f3e1058f779aeed3f999c307ebb2eb853b07221d895dd" }, "capabilities": {}, "extensions": {}, diff --git a/test/integration/test/ports/prisma/functional/composites-object-create/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/composites-object-create/_fixture/generated/contract.d.ts index f4d774998491..d15b086c85b8 100644 --- a/test/integration/test/ports/prisma/functional/composites-object-create/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/composites-object-create/_fixture/generated/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'95ba06155628a6148dd6c81cf187f68d1e0d09c7289de3530bbd4b58ac311fcd'>; + StorageHashBase<'20ad63fd1abaddd34b269a096dfe71a87c028cddcd13c1af679cbfbfb63a3b34'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -57,14 +57,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -179,6 +181,39 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly CommentContent: { + readonly fields: { + readonly text: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly upvotes: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContentUpvotes'; + }; + readonly many: { readonly elementNullable: false }; + }; + }; + }; + readonly CommentContentUpvotes: { + readonly fields: { + readonly userId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly vote: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; + }; + }; + }; + }; }; }; }; @@ -206,7 +241,13 @@ type ContractBase = Omit< readonly fields: { readonly content: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContent' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContent'; + }; }; readonly country: { readonly nullable: true; @@ -231,7 +272,13 @@ type ContractBase = Omit< readonly fields: { readonly content: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContent' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContent'; + }; }; readonly country: { readonly nullable: true; @@ -253,69 +300,12 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { - readonly kind: 'valueObject'; - readonly name: 'CommentContentUpvotes'; - }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; }; }; }; readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContentUpvotes' }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/composites-object-create/_fixture/generated/contract.json b/test/integration/test/ports/prisma/functional/composites-object-create/_fixture/generated/contract.json index 3999f639855d..34e5ed46fb71 100644 --- a/test/integration/test/ports/prisma/functional/composites-object-create/_fixture/generated/contract.json +++ b/test/integration/test/ports/prisma/functional/composites-object-create/_fixture/generated/contract.json @@ -22,8 +22,11 @@ "content": { "nullable": true, "type": { + "entityKind": "valueObject", + "entityName": "CommentContent", "kind": "valueObject", - "name": "CommentContent" + "namespaceId": "__unbound__", + "plane": "storage" } }, "country": { @@ -62,8 +65,11 @@ "content": { "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "CommentContent", "kind": "valueObject", - "name": "CommentContent" + "namespaceId": "__unbound__", + "plane": "storage" } }, "country": { @@ -97,47 +103,6 @@ } } } - }, - "valueObjects": { - "CommentContent": { - "fields": { - "text": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "upvotes": { - "many": { - "elementNullable": false - }, - "nullable": false, - "type": { - "kind": "valueObject", - "name": "CommentContentUpvotes" - } - } - } - }, - "CommentContentUpvotes": { - "fields": { - "userId": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "vote": { - "nullable": false, - "type": { - "codecId": "mongo/bool@1", - "kind": "scalar" - } - } - } - } } } } @@ -271,13 +236,57 @@ "validationLevel": "strict" } } + }, + "valueObject": { + "CommentContent": { + "fields": { + "text": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "upvotes": { + "many": { + "elementNullable": false + }, + "nullable": false, + "type": { + "entityKind": "valueObject", + "entityName": "CommentContentUpvotes", + "kind": "valueObject", + "namespaceId": "__unbound__", + "plane": "storage" + } + } + } + }, + "CommentContentUpvotes": { + "fields": { + "userId": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "vote": { + "nullable": false, + "type": { + "codecId": "mongo/bool@1", + "kind": "scalar" + } + } + } + } } }, "id": "__unbound__", "kind": "mongo-database" } }, - "storageHash": "95ba06155628a6148dd6c81cf187f68d1e0d09c7289de3530bbd4b58ac311fcd" + "storageHash": "20ad63fd1abaddd34b269a096dfe71a87c028cddcd13c1af679cbfbfb63a3b34" }, "capabilities": {}, "extensions": {}, diff --git a/test/integration/test/ports/prisma/functional/composites-object-createMany/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/composites-object-createMany/_fixture/generated/contract.d.ts index f4d774998491..d15b086c85b8 100644 --- a/test/integration/test/ports/prisma/functional/composites-object-createMany/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/composites-object-createMany/_fixture/generated/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'95ba06155628a6148dd6c81cf187f68d1e0d09c7289de3530bbd4b58ac311fcd'>; + StorageHashBase<'20ad63fd1abaddd34b269a096dfe71a87c028cddcd13c1af679cbfbfb63a3b34'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -57,14 +57,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -179,6 +181,39 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly CommentContent: { + readonly fields: { + readonly text: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly upvotes: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContentUpvotes'; + }; + readonly many: { readonly elementNullable: false }; + }; + }; + }; + readonly CommentContentUpvotes: { + readonly fields: { + readonly userId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly vote: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; + }; + }; + }; + }; }; }; }; @@ -206,7 +241,13 @@ type ContractBase = Omit< readonly fields: { readonly content: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContent' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContent'; + }; }; readonly country: { readonly nullable: true; @@ -231,7 +272,13 @@ type ContractBase = Omit< readonly fields: { readonly content: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContent' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContent'; + }; }; readonly country: { readonly nullable: true; @@ -253,69 +300,12 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { - readonly kind: 'valueObject'; - readonly name: 'CommentContentUpvotes'; - }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; }; }; }; readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContentUpvotes' }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/composites-object-createMany/_fixture/generated/contract.json b/test/integration/test/ports/prisma/functional/composites-object-createMany/_fixture/generated/contract.json index 3999f639855d..34e5ed46fb71 100644 --- a/test/integration/test/ports/prisma/functional/composites-object-createMany/_fixture/generated/contract.json +++ b/test/integration/test/ports/prisma/functional/composites-object-createMany/_fixture/generated/contract.json @@ -22,8 +22,11 @@ "content": { "nullable": true, "type": { + "entityKind": "valueObject", + "entityName": "CommentContent", "kind": "valueObject", - "name": "CommentContent" + "namespaceId": "__unbound__", + "plane": "storage" } }, "country": { @@ -62,8 +65,11 @@ "content": { "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "CommentContent", "kind": "valueObject", - "name": "CommentContent" + "namespaceId": "__unbound__", + "plane": "storage" } }, "country": { @@ -97,47 +103,6 @@ } } } - }, - "valueObjects": { - "CommentContent": { - "fields": { - "text": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "upvotes": { - "many": { - "elementNullable": false - }, - "nullable": false, - "type": { - "kind": "valueObject", - "name": "CommentContentUpvotes" - } - } - } - }, - "CommentContentUpvotes": { - "fields": { - "userId": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "vote": { - "nullable": false, - "type": { - "codecId": "mongo/bool@1", - "kind": "scalar" - } - } - } - } } } } @@ -271,13 +236,57 @@ "validationLevel": "strict" } } + }, + "valueObject": { + "CommentContent": { + "fields": { + "text": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "upvotes": { + "many": { + "elementNullable": false + }, + "nullable": false, + "type": { + "entityKind": "valueObject", + "entityName": "CommentContentUpvotes", + "kind": "valueObject", + "namespaceId": "__unbound__", + "plane": "storage" + } + } + } + }, + "CommentContentUpvotes": { + "fields": { + "userId": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "vote": { + "nullable": false, + "type": { + "codecId": "mongo/bool@1", + "kind": "scalar" + } + } + } + } } }, "id": "__unbound__", "kind": "mongo-database" } }, - "storageHash": "95ba06155628a6148dd6c81cf187f68d1e0d09c7289de3530bbd4b58ac311fcd" + "storageHash": "20ad63fd1abaddd34b269a096dfe71a87c028cddcd13c1af679cbfbfb63a3b34" }, "capabilities": {}, "extensions": {}, diff --git a/test/integration/test/ports/prisma/functional/composites-object-delete/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/composites-object-delete/_fixture/generated/contract.d.ts index f4d774998491..d15b086c85b8 100644 --- a/test/integration/test/ports/prisma/functional/composites-object-delete/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/composites-object-delete/_fixture/generated/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'95ba06155628a6148dd6c81cf187f68d1e0d09c7289de3530bbd4b58ac311fcd'>; + StorageHashBase<'20ad63fd1abaddd34b269a096dfe71a87c028cddcd13c1af679cbfbfb63a3b34'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -57,14 +57,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -179,6 +181,39 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly CommentContent: { + readonly fields: { + readonly text: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly upvotes: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContentUpvotes'; + }; + readonly many: { readonly elementNullable: false }; + }; + }; + }; + readonly CommentContentUpvotes: { + readonly fields: { + readonly userId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly vote: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; + }; + }; + }; + }; }; }; }; @@ -206,7 +241,13 @@ type ContractBase = Omit< readonly fields: { readonly content: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContent' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContent'; + }; }; readonly country: { readonly nullable: true; @@ -231,7 +272,13 @@ type ContractBase = Omit< readonly fields: { readonly content: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContent' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContent'; + }; }; readonly country: { readonly nullable: true; @@ -253,69 +300,12 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { - readonly kind: 'valueObject'; - readonly name: 'CommentContentUpvotes'; - }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; }; }; }; readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContentUpvotes' }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/composites-object-delete/_fixture/generated/contract.json b/test/integration/test/ports/prisma/functional/composites-object-delete/_fixture/generated/contract.json index 3999f639855d..34e5ed46fb71 100644 --- a/test/integration/test/ports/prisma/functional/composites-object-delete/_fixture/generated/contract.json +++ b/test/integration/test/ports/prisma/functional/composites-object-delete/_fixture/generated/contract.json @@ -22,8 +22,11 @@ "content": { "nullable": true, "type": { + "entityKind": "valueObject", + "entityName": "CommentContent", "kind": "valueObject", - "name": "CommentContent" + "namespaceId": "__unbound__", + "plane": "storage" } }, "country": { @@ -62,8 +65,11 @@ "content": { "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "CommentContent", "kind": "valueObject", - "name": "CommentContent" + "namespaceId": "__unbound__", + "plane": "storage" } }, "country": { @@ -97,47 +103,6 @@ } } } - }, - "valueObjects": { - "CommentContent": { - "fields": { - "text": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "upvotes": { - "many": { - "elementNullable": false - }, - "nullable": false, - "type": { - "kind": "valueObject", - "name": "CommentContentUpvotes" - } - } - } - }, - "CommentContentUpvotes": { - "fields": { - "userId": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "vote": { - "nullable": false, - "type": { - "codecId": "mongo/bool@1", - "kind": "scalar" - } - } - } - } } } } @@ -271,13 +236,57 @@ "validationLevel": "strict" } } + }, + "valueObject": { + "CommentContent": { + "fields": { + "text": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "upvotes": { + "many": { + "elementNullable": false + }, + "nullable": false, + "type": { + "entityKind": "valueObject", + "entityName": "CommentContentUpvotes", + "kind": "valueObject", + "namespaceId": "__unbound__", + "plane": "storage" + } + } + } + }, + "CommentContentUpvotes": { + "fields": { + "userId": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "vote": { + "nullable": false, + "type": { + "codecId": "mongo/bool@1", + "kind": "scalar" + } + } + } + } } }, "id": "__unbound__", "kind": "mongo-database" } }, - "storageHash": "95ba06155628a6148dd6c81cf187f68d1e0d09c7289de3530bbd4b58ac311fcd" + "storageHash": "20ad63fd1abaddd34b269a096dfe71a87c028cddcd13c1af679cbfbfb63a3b34" }, "capabilities": {}, "extensions": {}, diff --git a/test/integration/test/ports/prisma/functional/composites-object-deleteMany/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/composites-object-deleteMany/_fixture/generated/contract.d.ts index f4d774998491..d15b086c85b8 100644 --- a/test/integration/test/ports/prisma/functional/composites-object-deleteMany/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/composites-object-deleteMany/_fixture/generated/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'95ba06155628a6148dd6c81cf187f68d1e0d09c7289de3530bbd4b58ac311fcd'>; + StorageHashBase<'20ad63fd1abaddd34b269a096dfe71a87c028cddcd13c1af679cbfbfb63a3b34'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -57,14 +57,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -179,6 +181,39 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly CommentContent: { + readonly fields: { + readonly text: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly upvotes: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContentUpvotes'; + }; + readonly many: { readonly elementNullable: false }; + }; + }; + }; + readonly CommentContentUpvotes: { + readonly fields: { + readonly userId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly vote: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; + }; + }; + }; + }; }; }; }; @@ -206,7 +241,13 @@ type ContractBase = Omit< readonly fields: { readonly content: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContent' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContent'; + }; }; readonly country: { readonly nullable: true; @@ -231,7 +272,13 @@ type ContractBase = Omit< readonly fields: { readonly content: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContent' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContent'; + }; }; readonly country: { readonly nullable: true; @@ -253,69 +300,12 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { - readonly kind: 'valueObject'; - readonly name: 'CommentContentUpvotes'; - }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; }; }; }; readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContentUpvotes' }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/composites-object-deleteMany/_fixture/generated/contract.json b/test/integration/test/ports/prisma/functional/composites-object-deleteMany/_fixture/generated/contract.json index 3999f639855d..34e5ed46fb71 100644 --- a/test/integration/test/ports/prisma/functional/composites-object-deleteMany/_fixture/generated/contract.json +++ b/test/integration/test/ports/prisma/functional/composites-object-deleteMany/_fixture/generated/contract.json @@ -22,8 +22,11 @@ "content": { "nullable": true, "type": { + "entityKind": "valueObject", + "entityName": "CommentContent", "kind": "valueObject", - "name": "CommentContent" + "namespaceId": "__unbound__", + "plane": "storage" } }, "country": { @@ -62,8 +65,11 @@ "content": { "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "CommentContent", "kind": "valueObject", - "name": "CommentContent" + "namespaceId": "__unbound__", + "plane": "storage" } }, "country": { @@ -97,47 +103,6 @@ } } } - }, - "valueObjects": { - "CommentContent": { - "fields": { - "text": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "upvotes": { - "many": { - "elementNullable": false - }, - "nullable": false, - "type": { - "kind": "valueObject", - "name": "CommentContentUpvotes" - } - } - } - }, - "CommentContentUpvotes": { - "fields": { - "userId": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "vote": { - "nullable": false, - "type": { - "codecId": "mongo/bool@1", - "kind": "scalar" - } - } - } - } } } } @@ -271,13 +236,57 @@ "validationLevel": "strict" } } + }, + "valueObject": { + "CommentContent": { + "fields": { + "text": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "upvotes": { + "many": { + "elementNullable": false + }, + "nullable": false, + "type": { + "entityKind": "valueObject", + "entityName": "CommentContentUpvotes", + "kind": "valueObject", + "namespaceId": "__unbound__", + "plane": "storage" + } + } + } + }, + "CommentContentUpvotes": { + "fields": { + "userId": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "vote": { + "nullable": false, + "type": { + "codecId": "mongo/bool@1", + "kind": "scalar" + } + } + } + } } }, "id": "__unbound__", "kind": "mongo-database" } }, - "storageHash": "95ba06155628a6148dd6c81cf187f68d1e0d09c7289de3530bbd4b58ac311fcd" + "storageHash": "20ad63fd1abaddd34b269a096dfe71a87c028cddcd13c1af679cbfbfb63a3b34" }, "capabilities": {}, "extensions": {}, diff --git a/test/integration/test/ports/prisma/functional/composites-object-findFirst/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/composites-object-findFirst/_fixture/generated/contract.d.ts index f4d774998491..d15b086c85b8 100644 --- a/test/integration/test/ports/prisma/functional/composites-object-findFirst/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/composites-object-findFirst/_fixture/generated/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'95ba06155628a6148dd6c81cf187f68d1e0d09c7289de3530bbd4b58ac311fcd'>; + StorageHashBase<'20ad63fd1abaddd34b269a096dfe71a87c028cddcd13c1af679cbfbfb63a3b34'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -57,14 +57,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -179,6 +181,39 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly CommentContent: { + readonly fields: { + readonly text: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly upvotes: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContentUpvotes'; + }; + readonly many: { readonly elementNullable: false }; + }; + }; + }; + readonly CommentContentUpvotes: { + readonly fields: { + readonly userId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly vote: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; + }; + }; + }; + }; }; }; }; @@ -206,7 +241,13 @@ type ContractBase = Omit< readonly fields: { readonly content: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContent' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContent'; + }; }; readonly country: { readonly nullable: true; @@ -231,7 +272,13 @@ type ContractBase = Omit< readonly fields: { readonly content: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContent' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContent'; + }; }; readonly country: { readonly nullable: true; @@ -253,69 +300,12 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { - readonly kind: 'valueObject'; - readonly name: 'CommentContentUpvotes'; - }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; }; }; }; readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContentUpvotes' }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/composites-object-findFirst/_fixture/generated/contract.json b/test/integration/test/ports/prisma/functional/composites-object-findFirst/_fixture/generated/contract.json index 3999f639855d..34e5ed46fb71 100644 --- a/test/integration/test/ports/prisma/functional/composites-object-findFirst/_fixture/generated/contract.json +++ b/test/integration/test/ports/prisma/functional/composites-object-findFirst/_fixture/generated/contract.json @@ -22,8 +22,11 @@ "content": { "nullable": true, "type": { + "entityKind": "valueObject", + "entityName": "CommentContent", "kind": "valueObject", - "name": "CommentContent" + "namespaceId": "__unbound__", + "plane": "storage" } }, "country": { @@ -62,8 +65,11 @@ "content": { "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "CommentContent", "kind": "valueObject", - "name": "CommentContent" + "namespaceId": "__unbound__", + "plane": "storage" } }, "country": { @@ -97,47 +103,6 @@ } } } - }, - "valueObjects": { - "CommentContent": { - "fields": { - "text": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "upvotes": { - "many": { - "elementNullable": false - }, - "nullable": false, - "type": { - "kind": "valueObject", - "name": "CommentContentUpvotes" - } - } - } - }, - "CommentContentUpvotes": { - "fields": { - "userId": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "vote": { - "nullable": false, - "type": { - "codecId": "mongo/bool@1", - "kind": "scalar" - } - } - } - } } } } @@ -271,13 +236,57 @@ "validationLevel": "strict" } } + }, + "valueObject": { + "CommentContent": { + "fields": { + "text": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "upvotes": { + "many": { + "elementNullable": false + }, + "nullable": false, + "type": { + "entityKind": "valueObject", + "entityName": "CommentContentUpvotes", + "kind": "valueObject", + "namespaceId": "__unbound__", + "plane": "storage" + } + } + } + }, + "CommentContentUpvotes": { + "fields": { + "userId": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "vote": { + "nullable": false, + "type": { + "codecId": "mongo/bool@1", + "kind": "scalar" + } + } + } + } } }, "id": "__unbound__", "kind": "mongo-database" } }, - "storageHash": "95ba06155628a6148dd6c81cf187f68d1e0d09c7289de3530bbd4b58ac311fcd" + "storageHash": "20ad63fd1abaddd34b269a096dfe71a87c028cddcd13c1af679cbfbfb63a3b34" }, "capabilities": {}, "extensions": {}, diff --git a/test/integration/test/ports/prisma/functional/composites-object-findMany/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/composites-object-findMany/_fixture/generated/contract.d.ts index f4d774998491..d15b086c85b8 100644 --- a/test/integration/test/ports/prisma/functional/composites-object-findMany/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/composites-object-findMany/_fixture/generated/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'95ba06155628a6148dd6c81cf187f68d1e0d09c7289de3530bbd4b58ac311fcd'>; + StorageHashBase<'20ad63fd1abaddd34b269a096dfe71a87c028cddcd13c1af679cbfbfb63a3b34'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -57,14 +57,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -179,6 +181,39 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly CommentContent: { + readonly fields: { + readonly text: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly upvotes: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContentUpvotes'; + }; + readonly many: { readonly elementNullable: false }; + }; + }; + }; + readonly CommentContentUpvotes: { + readonly fields: { + readonly userId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly vote: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; + }; + }; + }; + }; }; }; }; @@ -206,7 +241,13 @@ type ContractBase = Omit< readonly fields: { readonly content: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContent' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContent'; + }; }; readonly country: { readonly nullable: true; @@ -231,7 +272,13 @@ type ContractBase = Omit< readonly fields: { readonly content: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContent' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContent'; + }; }; readonly country: { readonly nullable: true; @@ -253,69 +300,12 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { - readonly kind: 'valueObject'; - readonly name: 'CommentContentUpvotes'; - }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; }; }; }; readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContentUpvotes' }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/composites-object-findMany/_fixture/generated/contract.json b/test/integration/test/ports/prisma/functional/composites-object-findMany/_fixture/generated/contract.json index 3999f639855d..34e5ed46fb71 100644 --- a/test/integration/test/ports/prisma/functional/composites-object-findMany/_fixture/generated/contract.json +++ b/test/integration/test/ports/prisma/functional/composites-object-findMany/_fixture/generated/contract.json @@ -22,8 +22,11 @@ "content": { "nullable": true, "type": { + "entityKind": "valueObject", + "entityName": "CommentContent", "kind": "valueObject", - "name": "CommentContent" + "namespaceId": "__unbound__", + "plane": "storage" } }, "country": { @@ -62,8 +65,11 @@ "content": { "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "CommentContent", "kind": "valueObject", - "name": "CommentContent" + "namespaceId": "__unbound__", + "plane": "storage" } }, "country": { @@ -97,47 +103,6 @@ } } } - }, - "valueObjects": { - "CommentContent": { - "fields": { - "text": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "upvotes": { - "many": { - "elementNullable": false - }, - "nullable": false, - "type": { - "kind": "valueObject", - "name": "CommentContentUpvotes" - } - } - } - }, - "CommentContentUpvotes": { - "fields": { - "userId": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "vote": { - "nullable": false, - "type": { - "codecId": "mongo/bool@1", - "kind": "scalar" - } - } - } - } } } } @@ -271,13 +236,57 @@ "validationLevel": "strict" } } + }, + "valueObject": { + "CommentContent": { + "fields": { + "text": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "upvotes": { + "many": { + "elementNullable": false + }, + "nullable": false, + "type": { + "entityKind": "valueObject", + "entityName": "CommentContentUpvotes", + "kind": "valueObject", + "namespaceId": "__unbound__", + "plane": "storage" + } + } + } + }, + "CommentContentUpvotes": { + "fields": { + "userId": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "vote": { + "nullable": false, + "type": { + "codecId": "mongo/bool@1", + "kind": "scalar" + } + } + } + } } }, "id": "__unbound__", "kind": "mongo-database" } }, - "storageHash": "95ba06155628a6148dd6c81cf187f68d1e0d09c7289de3530bbd4b58ac311fcd" + "storageHash": "20ad63fd1abaddd34b269a096dfe71a87c028cddcd13c1af679cbfbfb63a3b34" }, "capabilities": {}, "extensions": {}, diff --git a/test/integration/test/ports/prisma/functional/composites-object-update/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/composites-object-update/_fixture/generated/contract.d.ts index f4d774998491..d15b086c85b8 100644 --- a/test/integration/test/ports/prisma/functional/composites-object-update/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/composites-object-update/_fixture/generated/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'95ba06155628a6148dd6c81cf187f68d1e0d09c7289de3530bbd4b58ac311fcd'>; + StorageHashBase<'20ad63fd1abaddd34b269a096dfe71a87c028cddcd13c1af679cbfbfb63a3b34'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -57,14 +57,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -179,6 +181,39 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly CommentContent: { + readonly fields: { + readonly text: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly upvotes: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContentUpvotes'; + }; + readonly many: { readonly elementNullable: false }; + }; + }; + }; + readonly CommentContentUpvotes: { + readonly fields: { + readonly userId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly vote: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; + }; + }; + }; + }; }; }; }; @@ -206,7 +241,13 @@ type ContractBase = Omit< readonly fields: { readonly content: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContent' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContent'; + }; }; readonly country: { readonly nullable: true; @@ -231,7 +272,13 @@ type ContractBase = Omit< readonly fields: { readonly content: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContent' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContent'; + }; }; readonly country: { readonly nullable: true; @@ -253,69 +300,12 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { - readonly kind: 'valueObject'; - readonly name: 'CommentContentUpvotes'; - }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; }; }; }; readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContentUpvotes' }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/composites-object-update/_fixture/generated/contract.json b/test/integration/test/ports/prisma/functional/composites-object-update/_fixture/generated/contract.json index 3999f639855d..34e5ed46fb71 100644 --- a/test/integration/test/ports/prisma/functional/composites-object-update/_fixture/generated/contract.json +++ b/test/integration/test/ports/prisma/functional/composites-object-update/_fixture/generated/contract.json @@ -22,8 +22,11 @@ "content": { "nullable": true, "type": { + "entityKind": "valueObject", + "entityName": "CommentContent", "kind": "valueObject", - "name": "CommentContent" + "namespaceId": "__unbound__", + "plane": "storage" } }, "country": { @@ -62,8 +65,11 @@ "content": { "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "CommentContent", "kind": "valueObject", - "name": "CommentContent" + "namespaceId": "__unbound__", + "plane": "storage" } }, "country": { @@ -97,47 +103,6 @@ } } } - }, - "valueObjects": { - "CommentContent": { - "fields": { - "text": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "upvotes": { - "many": { - "elementNullable": false - }, - "nullable": false, - "type": { - "kind": "valueObject", - "name": "CommentContentUpvotes" - } - } - } - }, - "CommentContentUpvotes": { - "fields": { - "userId": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "vote": { - "nullable": false, - "type": { - "codecId": "mongo/bool@1", - "kind": "scalar" - } - } - } - } } } } @@ -271,13 +236,57 @@ "validationLevel": "strict" } } + }, + "valueObject": { + "CommentContent": { + "fields": { + "text": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "upvotes": { + "many": { + "elementNullable": false + }, + "nullable": false, + "type": { + "entityKind": "valueObject", + "entityName": "CommentContentUpvotes", + "kind": "valueObject", + "namespaceId": "__unbound__", + "plane": "storage" + } + } + } + }, + "CommentContentUpvotes": { + "fields": { + "userId": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "vote": { + "nullable": false, + "type": { + "codecId": "mongo/bool@1", + "kind": "scalar" + } + } + } + } } }, "id": "__unbound__", "kind": "mongo-database" } }, - "storageHash": "95ba06155628a6148dd6c81cf187f68d1e0d09c7289de3530bbd4b58ac311fcd" + "storageHash": "20ad63fd1abaddd34b269a096dfe71a87c028cddcd13c1af679cbfbfb63a3b34" }, "capabilities": {}, "extensions": {}, diff --git a/test/integration/test/ports/prisma/functional/composites-object-updateMany/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/composites-object-updateMany/_fixture/generated/contract.d.ts index f4d774998491..d15b086c85b8 100644 --- a/test/integration/test/ports/prisma/functional/composites-object-updateMany/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/composites-object-updateMany/_fixture/generated/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'95ba06155628a6148dd6c81cf187f68d1e0d09c7289de3530bbd4b58ac311fcd'>; + StorageHashBase<'20ad63fd1abaddd34b269a096dfe71a87c028cddcd13c1af679cbfbfb63a3b34'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -57,14 +57,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -179,6 +181,39 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly CommentContent: { + readonly fields: { + readonly text: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly upvotes: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContentUpvotes'; + }; + readonly many: { readonly elementNullable: false }; + }; + }; + }; + readonly CommentContentUpvotes: { + readonly fields: { + readonly userId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly vote: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; + }; + }; + }; + }; }; }; }; @@ -206,7 +241,13 @@ type ContractBase = Omit< readonly fields: { readonly content: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContent' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContent'; + }; }; readonly country: { readonly nullable: true; @@ -231,7 +272,13 @@ type ContractBase = Omit< readonly fields: { readonly content: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContent' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContent'; + }; }; readonly country: { readonly nullable: true; @@ -253,69 +300,12 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { - readonly kind: 'valueObject'; - readonly name: 'CommentContentUpvotes'; - }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; }; }; }; readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContentUpvotes' }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/composites-object-updateMany/_fixture/generated/contract.json b/test/integration/test/ports/prisma/functional/composites-object-updateMany/_fixture/generated/contract.json index 3999f639855d..34e5ed46fb71 100644 --- a/test/integration/test/ports/prisma/functional/composites-object-updateMany/_fixture/generated/contract.json +++ b/test/integration/test/ports/prisma/functional/composites-object-updateMany/_fixture/generated/contract.json @@ -22,8 +22,11 @@ "content": { "nullable": true, "type": { + "entityKind": "valueObject", + "entityName": "CommentContent", "kind": "valueObject", - "name": "CommentContent" + "namespaceId": "__unbound__", + "plane": "storage" } }, "country": { @@ -62,8 +65,11 @@ "content": { "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "CommentContent", "kind": "valueObject", - "name": "CommentContent" + "namespaceId": "__unbound__", + "plane": "storage" } }, "country": { @@ -97,47 +103,6 @@ } } } - }, - "valueObjects": { - "CommentContent": { - "fields": { - "text": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "upvotes": { - "many": { - "elementNullable": false - }, - "nullable": false, - "type": { - "kind": "valueObject", - "name": "CommentContentUpvotes" - } - } - } - }, - "CommentContentUpvotes": { - "fields": { - "userId": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "vote": { - "nullable": false, - "type": { - "codecId": "mongo/bool@1", - "kind": "scalar" - } - } - } - } } } } @@ -271,13 +236,57 @@ "validationLevel": "strict" } } + }, + "valueObject": { + "CommentContent": { + "fields": { + "text": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "upvotes": { + "many": { + "elementNullable": false + }, + "nullable": false, + "type": { + "entityKind": "valueObject", + "entityName": "CommentContentUpvotes", + "kind": "valueObject", + "namespaceId": "__unbound__", + "plane": "storage" + } + } + } + }, + "CommentContentUpvotes": { + "fields": { + "userId": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "vote": { + "nullable": false, + "type": { + "codecId": "mongo/bool@1", + "kind": "scalar" + } + } + } + } } }, "id": "__unbound__", "kind": "mongo-database" } }, - "storageHash": "95ba06155628a6148dd6c81cf187f68d1e0d09c7289de3530bbd4b58ac311fcd" + "storageHash": "20ad63fd1abaddd34b269a096dfe71a87c028cddcd13c1af679cbfbfb63a3b34" }, "capabilities": {}, "extensions": {}, diff --git a/test/integration/test/ports/prisma/functional/composites-object-upsert-create/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/composites-object-upsert-create/_fixture/generated/contract.d.ts index f4d774998491..d15b086c85b8 100644 --- a/test/integration/test/ports/prisma/functional/composites-object-upsert-create/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/composites-object-upsert-create/_fixture/generated/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'95ba06155628a6148dd6c81cf187f68d1e0d09c7289de3530bbd4b58ac311fcd'>; + StorageHashBase<'20ad63fd1abaddd34b269a096dfe71a87c028cddcd13c1af679cbfbfb63a3b34'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -57,14 +57,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -179,6 +181,39 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly CommentContent: { + readonly fields: { + readonly text: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly upvotes: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContentUpvotes'; + }; + readonly many: { readonly elementNullable: false }; + }; + }; + }; + readonly CommentContentUpvotes: { + readonly fields: { + readonly userId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly vote: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; + }; + }; + }; + }; }; }; }; @@ -206,7 +241,13 @@ type ContractBase = Omit< readonly fields: { readonly content: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContent' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContent'; + }; }; readonly country: { readonly nullable: true; @@ -231,7 +272,13 @@ type ContractBase = Omit< readonly fields: { readonly content: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContent' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContent'; + }; }; readonly country: { readonly nullable: true; @@ -253,69 +300,12 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { - readonly kind: 'valueObject'; - readonly name: 'CommentContentUpvotes'; - }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; }; }; }; readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContentUpvotes' }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/composites-object-upsert-create/_fixture/generated/contract.json b/test/integration/test/ports/prisma/functional/composites-object-upsert-create/_fixture/generated/contract.json index 3999f639855d..34e5ed46fb71 100644 --- a/test/integration/test/ports/prisma/functional/composites-object-upsert-create/_fixture/generated/contract.json +++ b/test/integration/test/ports/prisma/functional/composites-object-upsert-create/_fixture/generated/contract.json @@ -22,8 +22,11 @@ "content": { "nullable": true, "type": { + "entityKind": "valueObject", + "entityName": "CommentContent", "kind": "valueObject", - "name": "CommentContent" + "namespaceId": "__unbound__", + "plane": "storage" } }, "country": { @@ -62,8 +65,11 @@ "content": { "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "CommentContent", "kind": "valueObject", - "name": "CommentContent" + "namespaceId": "__unbound__", + "plane": "storage" } }, "country": { @@ -97,47 +103,6 @@ } } } - }, - "valueObjects": { - "CommentContent": { - "fields": { - "text": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "upvotes": { - "many": { - "elementNullable": false - }, - "nullable": false, - "type": { - "kind": "valueObject", - "name": "CommentContentUpvotes" - } - } - } - }, - "CommentContentUpvotes": { - "fields": { - "userId": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "vote": { - "nullable": false, - "type": { - "codecId": "mongo/bool@1", - "kind": "scalar" - } - } - } - } } } } @@ -271,13 +236,57 @@ "validationLevel": "strict" } } + }, + "valueObject": { + "CommentContent": { + "fields": { + "text": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "upvotes": { + "many": { + "elementNullable": false + }, + "nullable": false, + "type": { + "entityKind": "valueObject", + "entityName": "CommentContentUpvotes", + "kind": "valueObject", + "namespaceId": "__unbound__", + "plane": "storage" + } + } + } + }, + "CommentContentUpvotes": { + "fields": { + "userId": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "vote": { + "nullable": false, + "type": { + "codecId": "mongo/bool@1", + "kind": "scalar" + } + } + } + } } }, "id": "__unbound__", "kind": "mongo-database" } }, - "storageHash": "95ba06155628a6148dd6c81cf187f68d1e0d09c7289de3530bbd4b58ac311fcd" + "storageHash": "20ad63fd1abaddd34b269a096dfe71a87c028cddcd13c1af679cbfbfb63a3b34" }, "capabilities": {}, "extensions": {}, diff --git a/test/integration/test/ports/prisma/functional/composites-object-upsert-update/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/composites-object-upsert-update/_fixture/generated/contract.d.ts index f4d774998491..d15b086c85b8 100644 --- a/test/integration/test/ports/prisma/functional/composites-object-upsert-update/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/composites-object-upsert-update/_fixture/generated/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'95ba06155628a6148dd6c81cf187f68d1e0d09c7289de3530bbd4b58ac311fcd'>; + StorageHashBase<'20ad63fd1abaddd34b269a096dfe71a87c028cddcd13c1af679cbfbfb63a3b34'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -57,14 +57,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -179,6 +181,39 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly CommentContent: { + readonly fields: { + readonly text: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly upvotes: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContentUpvotes'; + }; + readonly many: { readonly elementNullable: false }; + }; + }; + }; + readonly CommentContentUpvotes: { + readonly fields: { + readonly userId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly vote: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; + }; + }; + }; + }; }; }; }; @@ -206,7 +241,13 @@ type ContractBase = Omit< readonly fields: { readonly content: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContent' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContent'; + }; }; readonly country: { readonly nullable: true; @@ -231,7 +272,13 @@ type ContractBase = Omit< readonly fields: { readonly content: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContent' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'CommentContent'; + }; }; readonly country: { readonly nullable: true; @@ -253,69 +300,12 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { - readonly kind: 'valueObject'; - readonly name: 'CommentContentUpvotes'; - }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; }; }; }; readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly CommentContent: { - readonly fields: { - readonly text: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly upvotes: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'CommentContentUpvotes' }; - readonly many: { readonly elementNullable: false }; - }; - }; - }; - readonly CommentContentUpvotes: { - readonly fields: { - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly vote: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/bool@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/composites-object-upsert-update/_fixture/generated/contract.json b/test/integration/test/ports/prisma/functional/composites-object-upsert-update/_fixture/generated/contract.json index 3999f639855d..34e5ed46fb71 100644 --- a/test/integration/test/ports/prisma/functional/composites-object-upsert-update/_fixture/generated/contract.json +++ b/test/integration/test/ports/prisma/functional/composites-object-upsert-update/_fixture/generated/contract.json @@ -22,8 +22,11 @@ "content": { "nullable": true, "type": { + "entityKind": "valueObject", + "entityName": "CommentContent", "kind": "valueObject", - "name": "CommentContent" + "namespaceId": "__unbound__", + "plane": "storage" } }, "country": { @@ -62,8 +65,11 @@ "content": { "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "CommentContent", "kind": "valueObject", - "name": "CommentContent" + "namespaceId": "__unbound__", + "plane": "storage" } }, "country": { @@ -97,47 +103,6 @@ } } } - }, - "valueObjects": { - "CommentContent": { - "fields": { - "text": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "upvotes": { - "many": { - "elementNullable": false - }, - "nullable": false, - "type": { - "kind": "valueObject", - "name": "CommentContentUpvotes" - } - } - } - }, - "CommentContentUpvotes": { - "fields": { - "userId": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "vote": { - "nullable": false, - "type": { - "codecId": "mongo/bool@1", - "kind": "scalar" - } - } - } - } } } } @@ -271,13 +236,57 @@ "validationLevel": "strict" } } + }, + "valueObject": { + "CommentContent": { + "fields": { + "text": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "upvotes": { + "many": { + "elementNullable": false + }, + "nullable": false, + "type": { + "entityKind": "valueObject", + "entityName": "CommentContentUpvotes", + "kind": "valueObject", + "namespaceId": "__unbound__", + "plane": "storage" + } + } + } + }, + "CommentContentUpvotes": { + "fields": { + "userId": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "vote": { + "nullable": false, + "type": { + "codecId": "mongo/bool@1", + "kind": "scalar" + } + } + } + } } }, "id": "__unbound__", "kind": "mongo-database" } }, - "storageHash": "95ba06155628a6148dd6c81cf187f68d1e0d09c7289de3530bbd4b58ac311fcd" + "storageHash": "20ad63fd1abaddd34b269a096dfe71a87c028cddcd13c1af679cbfbfb63a3b34" }, "capabilities": {}, "extensions": {}, diff --git a/test/integration/test/ports/prisma/functional/composites-selection/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/composites-selection/_fixture/generated/contract.d.ts index 272bb53adddb..2fa82a992d61 100644 --- a/test/integration/test/ports/prisma/functional/composites-selection/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/composites-selection/_fixture/generated/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'50c7e480b0d0d6850a19a52ebe53ad54ca00fe4ffc1d9149172bc774c7b1d3b0'>; + StorageHashBase<'089550830c023543ff80ad5464449ea5f0b073a9f07a57b487a184b44e991cbb'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -57,14 +57,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -148,6 +150,67 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly Name: { + readonly fields: { + readonly firstName: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly lastName: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + readonly Profile: { + readonly fields: { + readonly alternateName: { + readonly nullable: true; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Name'; + }; + }; + readonly favoriteThings: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Thing'; + }; + readonly many: { readonly elementNullable: false }; + }; + readonly name: { + readonly nullable: false; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Name'; + }; + }; + readonly url: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + readonly Thing: { + readonly fields: { + readonly name: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + }; }; }; }; @@ -172,7 +235,13 @@ type ContractBase = Omit< }; readonly profile: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Profile' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Profile'; + }; }; }; readonly relations: Record; @@ -185,98 +254,12 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly Name: { - readonly fields: { - readonly firstName: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly lastName: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Profile: { - readonly fields: { - readonly alternateName: { - readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Name' }; - }; - readonly favoriteThings: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Thing' }; - readonly many: { readonly elementNullable: false }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Name' }; - }; - readonly url: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Thing: { - readonly fields: { - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - }; }; }; }; readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly Name: { - readonly fields: { - readonly firstName: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - readonly lastName: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Profile: { - readonly fields: { - readonly alternateName: { - readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Name' }; - }; - readonly favoriteThings: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Thing' }; - readonly many: { readonly elementNullable: false }; - }; - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Name' }; - }; - readonly url: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - readonly Thing: { - readonly fields: { - readonly name: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/composites-selection/_fixture/generated/contract.json b/test/integration/test/ports/prisma/functional/composites-selection/_fixture/generated/contract.json index 533b56b49081..1a005ec82473 100644 --- a/test/integration/test/ports/prisma/functional/composites-selection/_fixture/generated/contract.json +++ b/test/integration/test/ports/prisma/functional/composites-selection/_fixture/generated/contract.json @@ -25,8 +25,11 @@ "profile": { "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "Profile", "kind": "valueObject", - "name": "Profile" + "namespaceId": "__unbound__", + "plane": "storage" } } }, @@ -43,72 +46,6 @@ } } } - }, - "valueObjects": { - "Name": { - "fields": { - "firstName": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - }, - "lastName": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - } - } - }, - "Profile": { - "fields": { - "alternateName": { - "nullable": true, - "type": { - "kind": "valueObject", - "name": "Name" - } - }, - "favoriteThings": { - "many": { - "elementNullable": false - }, - "nullable": false, - "type": { - "kind": "valueObject", - "name": "Thing" - } - }, - "name": { - "nullable": false, - "type": { - "kind": "valueObject", - "name": "Name" - } - }, - "url": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - } - } - }, - "Thing": { - "fields": { - "name": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - } - } - } } } } @@ -207,13 +144,88 @@ "validationLevel": "strict" } } + }, + "valueObject": { + "Name": { + "fields": { + "firstName": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "lastName": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + } + } + }, + "Profile": { + "fields": { + "alternateName": { + "nullable": true, + "type": { + "entityKind": "valueObject", + "entityName": "Name", + "kind": "valueObject", + "namespaceId": "__unbound__", + "plane": "storage" + } + }, + "favoriteThings": { + "many": { + "elementNullable": false + }, + "nullable": false, + "type": { + "entityKind": "valueObject", + "entityName": "Thing", + "kind": "valueObject", + "namespaceId": "__unbound__", + "plane": "storage" + } + }, + "name": { + "nullable": false, + "type": { + "entityKind": "valueObject", + "entityName": "Name", + "kind": "valueObject", + "namespaceId": "__unbound__", + "plane": "storage" + } + }, + "url": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + } + } + }, + "Thing": { + "fields": { + "name": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + } + } + } } }, "id": "__unbound__", "kind": "mongo-database" } }, - "storageHash": "50c7e480b0d0d6850a19a52ebe53ad54ca00fe4ffc1d9149172bc774c7b1d3b0" + "storageHash": "089550830c023543ff80ad5464449ea5f0b073a9f07a57b487a184b44e991cbb" }, "capabilities": {}, "extensions": {}, diff --git a/test/integration/test/ports/prisma/functional/create-default-date/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/create-default-date/_fixture/generated/contract.d.ts index 7503f6f24167..7ab478b6ffad 100644 --- a/test/integration/test/ports/prisma/functional/create-default-date/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/create-default-date/_fixture/generated/contract.d.ts @@ -399,7 +399,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/decimal-list/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/decimal-list/_fixture/generated/contract.d.ts index 05c908043cd3..bbe028b2ed4b 100644 --- a/test/integration/test/ports/prisma/functional/decimal-list/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/decimal-list/_fixture/generated/contract.d.ts @@ -413,7 +413,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/decimal-precision/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/decimal-precision/_fixture/generated/contract.d.ts index 072b88e9d9ff..a2b39484e931 100644 --- a/test/integration/test/ports/prisma/functional/decimal-precision/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/decimal-precision/_fixture/generated/contract.d.ts @@ -446,7 +446,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/decimal-scalar/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/decimal-scalar/_fixture/generated/contract.d.ts index a2efef065de8..145e3561e7ac 100644 --- a/test/integration/test/ports/prisma/functional/decimal-scalar/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/decimal-scalar/_fixture/generated/contract.d.ts @@ -407,7 +407,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/default-selection/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/default-selection/_fixture/generated/contract.d.ts index ff585733f252..1798a6678c98 100644 --- a/test/integration/test/ports/prisma/functional/default-selection/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/default-selection/_fixture/generated/contract.d.ts @@ -550,7 +550,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/default-selection/_fixture/mongo/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/default-selection/_fixture/mongo/generated/contract.d.ts index 8d8f74c8baa7..504b0ee99b90 100644 --- a/test/integration/test/ports/prisma/functional/default-selection/_fixture/mongo/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/default-selection/_fixture/mongo/generated/contract.d.ts @@ -19,11 +19,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'7fe8a5ef11abf3a2db023b5af601b57e7c00e94563ef45301135e34b64182696'>; + StorageHashBase<'b3aa4824e50739d258a1bae8ba11729a450c18c6276f6008bdf5276d6f5ed8f5'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; @@ -31,15 +31,15 @@ export type ProfileHash = export type CodecTypes = MongoCodecTypes; type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -62,14 +62,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { @@ -177,6 +179,16 @@ type ContractBase = Omit< }; }; }; + readonly valueObject: { + readonly Composite: { + readonly fields: { + readonly value: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + }; }; }; }; @@ -198,7 +210,13 @@ type ContractBase = Omit< readonly fields: { readonly composite: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Composite' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Composite'; + }; }; readonly enum: { readonly nullable: false; @@ -281,16 +299,6 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly Composite: { - readonly fields: { - readonly value: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - }; readonly enum: { readonly Enum: { readonly codecId: 'mongo/string@1'; @@ -312,16 +320,6 @@ type ContractBase = Omit< readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly Composite: { - readonly fields: { - readonly value: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/default-selection/_fixture/mongo/generated/contract.json b/test/integration/test/ports/prisma/functional/default-selection/_fixture/mongo/generated/contract.json index 57391f610072..e25bee088ae3 100644 --- a/test/integration/test/ports/prisma/functional/default-selection/_fixture/mongo/generated/contract.json +++ b/test/integration/test/ports/prisma/functional/default-selection/_fixture/mongo/generated/contract.json @@ -37,8 +37,11 @@ "composite": { "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "Composite", "kind": "valueObject", - "name": "Composite" + "namespaceId": "__unbound__", + "plane": "storage" } }, "enum": { @@ -184,19 +187,6 @@ } } } - }, - "valueObjects": { - "Composite": { - "fields": { - "value": { - "nullable": false, - "type": { - "codecId": "mongo/string@1", - "kind": "scalar" - } - } - } - } } } } @@ -306,6 +296,19 @@ } } }, + "valueObject": { + "Composite": { + "fields": { + "value": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + } + } + } + }, "valueSet": { "Enum": { "kind": "valueSet", @@ -320,7 +323,7 @@ "kind": "mongo-database" } }, - "storageHash": "7fe8a5ef11abf3a2db023b5af601b57e7c00e94563ef45301135e34b64182696" + "storageHash": "b3aa4824e50739d258a1bae8ba11729a450c18c6276f6008bdf5276d6f5ed8f5" }, "capabilities": {}, "extensions": {}, diff --git a/test/integration/test/ports/prisma/functional/distinct/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/distinct/_fixture/generated/contract.d.ts index 35a444c683c8..bc27aba0eb80 100644 --- a/test/integration/test/ports/prisma/functional/distinct/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/distinct/_fixture/generated/contract.d.ts @@ -403,7 +403,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/driver-adapters-error-forwarding/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/driver-adapters-error-forwarding/_fixture/generated/contract.d.ts index 434588009d75..a9ee4717b539 100644 --- a/test/integration/test/ports/prisma/functional/driver-adapters-error-forwarding/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/driver-adapters-error-forwarding/_fixture/generated/contract.d.ts @@ -463,7 +463,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/driver-adapters-team-orm-687-bytes/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/driver-adapters-team-orm-687-bytes/_fixture/generated/contract.d.ts index cb4957706b68..f57cb340bc49 100644 --- a/test/integration/test/ports/prisma/functional/driver-adapters-team-orm-687-bytes/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/driver-adapters-team-orm-687-bytes/_fixture/generated/contract.d.ts @@ -391,7 +391,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/driver-adapters-validate-active-provider/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/driver-adapters-validate-active-provider/_fixture/generated/contract.d.ts index 8246d093e8e6..1fcf434ce159 100644 --- a/test/integration/test/ports/prisma/functional/driver-adapters-validate-active-provider/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/driver-adapters-validate-active-provider/_fixture/generated/contract.d.ts @@ -377,7 +377,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/driver-adapters-validate-active-provider/_fixture/sqlite/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/driver-adapters-validate-active-provider/_fixture/sqlite/generated/contract.d.ts index d7c088adf5c2..029b1270e896 100644 --- a/test/integration/test/ports/prisma/functional/driver-adapters-validate-active-provider/_fixture/sqlite/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/driver-adapters-validate-active-provider/_fixture/sqlite/generated/contract.d.ts @@ -255,7 +255,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/enum-array/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/enum-array/_fixture/generated/contract.d.ts index 558b18e08eea..aab52b77c6ac 100644 --- a/test/integration/test/ports/prisma/functional/enum-array/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/enum-array/_fixture/generated/contract.d.ts @@ -451,7 +451,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/enums/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/enums/_fixture/generated/contract.d.ts index a5211479783c..9cfb6d68e177 100644 --- a/test/integration/test/ports/prisma/functional/enums/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/enums/_fixture/generated/contract.d.ts @@ -163,7 +163,6 @@ type ContractBase = Omit< readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/extended-where/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/extended-where/_fixture/generated/contract.d.ts index 0f252678bacf..fd274226ec28 100644 --- a/test/integration/test/ports/prisma/functional/extended-where/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/extended-where/_fixture/generated/contract.d.ts @@ -745,7 +745,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/filter-count-relations/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/filter-count-relations/_fixture/generated/contract.d.ts index 7220221939cd..bbd67af87f72 100644 --- a/test/integration/test/ports/prisma/functional/filter-count-relations/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/filter-count-relations/_fixture/generated/contract.d.ts @@ -726,7 +726,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/find-unique-or-throw-batching/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/find-unique-or-throw-batching/_fixture/generated/contract.d.ts index ad374b9e2c05..b3f18a5dd0f9 100644 --- a/test/integration/test/ports/prisma/functional/find-unique-or-throw-batching/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/find-unique-or-throw-batching/_fixture/generated/contract.d.ts @@ -377,7 +377,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/handle-int-overflow/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/handle-int-overflow/_fixture/generated/contract.d.ts index 441e47645949..c7586b85aa6f 100644 --- a/test/integration/test/ports/prisma/functional/handle-int-overflow/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/handle-int-overflow/_fixture/generated/contract.d.ts @@ -407,7 +407,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/interactive-transactions/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/interactive-transactions/_fixture/generated/contract.d.ts index ea2602b6aaf1..ff2c0ac41489 100644 --- a/test/integration/test/ports/prisma/functional/interactive-transactions/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/interactive-transactions/_fixture/generated/contract.d.ts @@ -588,7 +588,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-11974/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-11974/_fixture/generated/contract.d.ts index 67625d8cd789..dcc1529a3a33 100644 --- a/test/integration/test/ports/prisma/functional/issues-11974/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-11974/_fixture/generated/contract.d.ts @@ -703,7 +703,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-12378/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-12378/_fixture/generated/contract.d.ts index 27fdf0e9c7c9..8765b704c29c 100644 --- a/test/integration/test/ports/prisma/functional/issues-12378/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-12378/_fixture/generated/contract.d.ts @@ -614,7 +614,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-12557/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-12557/_fixture/generated/contract.d.ts index 404aed9799da..001b87538b9d 100644 --- a/test/integration/test/ports/prisma/functional/issues-12557/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-12557/_fixture/generated/contract.d.ts @@ -595,7 +595,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-12572/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-12572/_fixture/generated/contract.d.ts index 828696196906..a8fa43467ac6 100644 --- a/test/integration/test/ports/prisma/functional/issues-12572/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-12572/_fixture/generated/contract.d.ts @@ -435,7 +435,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-13089-dollar-in-search/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-13089-dollar-in-search/_fixture/generated/contract.d.ts index 92a562952693..6c8cf008e42f 100644 --- a/test/integration/test/ports/prisma/functional/issues-13089-dollar-in-search/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-13089-dollar-in-search/_fixture/generated/contract.d.ts @@ -139,7 +139,6 @@ type ContractBase = Omit< readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-14271/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-14271/_fixture/generated/contract.d.ts index 30f3e55f8532..e04b55233d11 100644 --- a/test/integration/test/ports/prisma/functional/issues-14271/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-14271/_fixture/generated/contract.d.ts @@ -506,7 +506,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-14954-date-batch/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-14954-date-batch/_fixture/generated/contract.d.ts index 5ed008a091e6..6c3084600208 100644 --- a/test/integration/test/ports/prisma/functional/issues-14954-date-batch/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-14954-date-batch/_fixture/generated/contract.d.ts @@ -410,7 +410,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-15044/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-15044/_fixture/generated/contract.d.ts index 367761783b63..35c7d451eab1 100644 --- a/test/integration/test/ports/prisma/functional/issues-15044/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-15044/_fixture/generated/contract.d.ts @@ -634,7 +634,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-16535-select-enum/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-16535-select-enum/_fixture/generated/contract.d.ts index c7fa2039b90d..8a8f18afa07d 100644 --- a/test/integration/test/ports/prisma/functional/issues-16535-select-enum/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-16535-select-enum/_fixture/generated/contract.d.ts @@ -442,7 +442,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-17005-args-type-conflict/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-17005-args-type-conflict/_fixture/generated/contract.d.ts index 82b29c6c5c8b..5c8852b3dcb3 100644 --- a/test/integration/test/ports/prisma/functional/issues-17005-args-type-conflict/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-17005-args-type-conflict/_fixture/generated/contract.d.ts @@ -591,7 +591,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-17030-args-type-conflict/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-17030-args-type-conflict/_fixture/generated/contract.d.ts index 9efcd4e85870..6bcf371bb3f7 100644 --- a/test/integration/test/ports/prisma/functional/issues-17030-args-type-conflict/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-17030-args-type-conflict/_fixture/generated/contract.d.ts @@ -487,7 +487,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-18970-invalid-date/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-18970-invalid-date/_fixture/generated/contract.d.ts index 0d78e286070e..105a9b167af7 100644 --- a/test/integration/test/ports/prisma/functional/issues-18970-invalid-date/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-18970-invalid-date/_fixture/generated/contract.d.ts @@ -410,7 +410,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-20261-group-by-shortcut/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-20261-group-by-shortcut/_fixture/generated/contract.d.ts index 1039b8a3e3c5..7b35922c7b09 100644 --- a/test/integration/test/ports/prisma/functional/issues-20261-group-by-shortcut/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-20261-group-by-shortcut/_fixture/generated/contract.d.ts @@ -419,7 +419,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-21352-id-does-not-exist/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-21352-id-does-not-exist/_fixture/generated/contract.d.ts index 93fd9e1aea26..227514e32e8f 100644 --- a/test/integration/test/ports/prisma/functional/issues-21352-id-does-not-exist/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-21352-id-does-not-exist/_fixture/generated/contract.d.ts @@ -601,7 +601,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-21454-type-in-json/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-21454-type-in-json/_fixture/generated/contract.d.ts index a75262d5b7a2..eccb76fb46f6 100644 --- a/test/integration/test/ports/prisma/functional/issues-21454-type-in-json/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-21454-type-in-json/_fixture/generated/contract.d.ts @@ -407,7 +407,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-21631-batching-in-transaction/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-21631-batching-in-transaction/_fixture/generated/contract.d.ts index 2ce42f9c0bba..6062c1904dd9 100644 --- a/test/integration/test/ports/prisma/functional/issues-21631-batching-in-transaction/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-21631-batching-in-transaction/_fixture/generated/contract.d.ts @@ -422,7 +422,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-22098-column-does-not-exist/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-22098-column-does-not-exist/_fixture/generated/contract.d.ts index 2dc93b76bc2a..41bbf30787ef 100644 --- a/test/integration/test/ports/prisma/functional/issues-22098-column-does-not-exist/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-22098-column-does-not-exist/_fixture/generated/contract.d.ts @@ -407,7 +407,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-22610-parallel-batch/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-22610-parallel-batch/_fixture/generated/contract.d.ts index 1ae9b1d22e8a..cf782371555f 100644 --- a/test/integration/test/ports/prisma/functional/issues-22610-parallel-batch/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-22610-parallel-batch/_fixture/generated/contract.d.ts @@ -556,7 +556,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-23201-non-ascii-comments/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-23201-non-ascii-comments/_fixture/generated/contract.d.ts index 54b45c9f1231..8f6ad060a732 100644 --- a/test/integration/test/ports/prisma/functional/issues-23201-non-ascii-comments/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-23201-non-ascii-comments/_fixture/generated/contract.d.ts @@ -407,7 +407,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-23902/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-23902/_fixture/generated/contract.d.ts index a2db97613ab5..9af0e31b8ba0 100644 --- a/test/integration/test/ports/prisma/functional/issues-23902/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-23902/_fixture/generated/contract.d.ts @@ -612,7 +612,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-25404/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-25404/_fixture/generated/contract.d.ts index 8b48e0f8fb34..402e11aa3aec 100644 --- a/test/integration/test/ports/prisma/functional/issues-25404/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-25404/_fixture/generated/contract.d.ts @@ -391,7 +391,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-27455-bytes-id/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-27455-bytes-id/_fixture/generated/contract.d.ts index 4ee1796fcbc1..57ece0bd3095 100644 --- a/test/integration/test/ports/prisma/functional/issues-27455-bytes-id/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-27455-bytes-id/_fixture/generated/contract.d.ts @@ -492,7 +492,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-27511-include-enum-array/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-27511-include-enum-array/_fixture/generated/contract.d.ts index bef4771ee869..9f0f14ad3a7b 100644 --- a/test/integration/test/ports/prisma/functional/issues-27511-include-enum-array/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-27511-include-enum-array/_fixture/generated/contract.d.ts @@ -637,7 +637,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-28151-broken-nested-set/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-28151-broken-nested-set/_fixture/generated/contract.d.ts index 8461b89b101d..dc23a7173cd4 100644 --- a/test/integration/test/ports/prisma/functional/issues-28151-broken-nested-set/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-28151-broken-nested-set/_fixture/generated/contract.d.ts @@ -490,7 +490,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-28192-pg-historical-dates/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-28192-pg-historical-dates/_fixture/generated/contract.d.ts index e03e2576bda1..dd7ee8ef6de1 100644 --- a/test/integration/test/ports/prisma/functional/issues-28192-pg-historical-dates/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-28192-pg-historical-dates/_fixture/generated/contract.d.ts @@ -437,7 +437,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-28591-mapped-enums/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-28591-mapped-enums/_fixture/generated/contract.d.ts index 6bb3ef95362c..4f43555f0910 100644 --- a/test/integration/test/ports/prisma/functional/issues-28591-mapped-enums/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-28591-mapped-enums/_fixture/generated/contract.d.ts @@ -452,7 +452,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-29010-bigint-precision-relation-joins/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-29010-bigint-precision-relation-joins/_fixture/generated/contract.d.ts index 9b8b0da55d2a..b3c8b444a55f 100644 --- a/test/integration/test/ports/prisma/functional/issues-29010-bigint-precision-relation-joins/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-29010-bigint-precision-relation-joins/_fixture/generated/contract.d.ts @@ -492,7 +492,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-29174-jsonb-parameter-regression/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-29174-jsonb-parameter-regression/_fixture/generated/contract.d.ts index 604b7d5230d5..e9f2d4c74226 100644 --- a/test/integration/test/ports/prisma/functional/issues-29174-jsonb-parameter-regression/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-29174-jsonb-parameter-regression/_fixture/generated/contract.d.ts @@ -407,7 +407,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-29176-cursor-parameter-regression/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-29176-cursor-parameter-regression/_fixture/generated/contract.d.ts index deaf3380d778..38748e6258ec 100644 --- a/test/integration/test/ports/prisma/functional/issues-29176-cursor-parameter-regression/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-29176-cursor-parameter-regression/_fixture/generated/contract.d.ts @@ -436,7 +436,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-29254-query-plan-cache-mutation/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-29254-query-plan-cache-mutation/_fixture/generated/contract.d.ts index 6f5957de50a0..43d71f226b61 100644 --- a/test/integration/test/ports/prisma/functional/issues-29254-query-plan-cache-mutation/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-29254-query-plan-cache-mutation/_fixture/generated/contract.d.ts @@ -407,7 +407,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-29267-uint8array-in-json/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-29267-uint8array-in-json/_fixture/generated/contract.d.ts index 6fbfd0302bfd..9a5f96708a91 100644 --- a/test/integration/test/ports/prisma/functional/issues-29267-uint8array-in-json/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-29267-uint8array-in-json/_fixture/generated/contract.d.ts @@ -410,7 +410,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-29309-datetime-cursor/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-29309-datetime-cursor/_fixture/generated/contract.d.ts index 6930fcdd7f47..0e1667682e03 100644 --- a/test/integration/test/ports/prisma/functional/issues-29309-datetime-cursor/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-29309-datetime-cursor/_fixture/generated/contract.d.ts @@ -403,7 +403,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-29331-query-plan-cache-bloat/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-29331-query-plan-cache-bloat/_fixture/generated/contract.d.ts index 2366985dc1c5..2d331d94ea77 100644 --- a/test/integration/test/ports/prisma/functional/issues-29331-query-plan-cache-bloat/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-29331-query-plan-cache-bloat/_fixture/generated/contract.d.ts @@ -724,7 +724,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-4004/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-4004/_fixture/generated/contract.d.ts index 44d3afa049b7..7be21d19a757 100644 --- a/test/integration/test/ports/prisma/functional/issues-4004/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-4004/_fixture/generated/contract.d.ts @@ -609,7 +609,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-5952-decimal-batch/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-5952-decimal-batch/_fixture/generated/contract.d.ts index 4809252859be..33d1ed5d6c48 100644 --- a/test/integration/test/ports/prisma/functional/issues-5952-decimal-batch/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-5952-decimal-batch/_fixture/generated/contract.d.ts @@ -407,7 +407,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-TML-1664-invalid-enum-value-error/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-TML-1664-invalid-enum-value-error/_fixture/generated/contract.d.ts index bd59d3e2460a..d0447f808054 100644 --- a/test/integration/test/ports/prisma/functional/issues-TML-1664-invalid-enum-value-error/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-TML-1664-invalid-enum-value-error/_fixture/generated/contract.d.ts @@ -422,7 +422,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-TML-1664-unknown-enum-value-read-error/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-TML-1664-unknown-enum-value-read-error/_fixture/generated/contract.d.ts index 2997b4fb9517..d38f6a23afb0 100644 --- a/test/integration/test/ports/prisma/functional/issues-TML-1664-unknown-enum-value-read-error/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-TML-1664-unknown-enum-value-read-error/_fixture/generated/contract.d.ts @@ -422,7 +422,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/issues-unmapped-driver-error-user-facing/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-unmapped-driver-error-user-facing/_fixture/generated/contract.d.ts index ec2866dccc62..9eb3c7861ba9 100644 --- a/test/integration/test/ports/prisma/functional/issues-unmapped-driver-error-user-facing/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-unmapped-driver-error-user-facing/_fixture/generated/contract.d.ts @@ -403,7 +403,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/json-fields/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/json-fields/_fixture/generated/contract.d.ts index e3ec8dcf5995..97ab9c52a7cf 100644 --- a/test/integration/test/ports/prisma/functional/json-fields/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/json-fields/_fixture/generated/contract.d.ts @@ -391,7 +391,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/large-floats/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/large-floats/_fixture/generated/contract.d.ts index 56f66ee21946..ccef22939106 100644 --- a/test/integration/test/ports/prisma/functional/large-floats/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/large-floats/_fixture/generated/contract.d.ts @@ -391,7 +391,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/legacy-aggregate-raw/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/legacy-aggregate-raw/_fixture/generated/contract.d.ts index 7a02f1e4bb8e..7f722c6e37f2 100644 --- a/test/integration/test/ports/prisma/functional/legacy-aggregate-raw/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/legacy-aggregate-raw/_fixture/generated/contract.d.ts @@ -265,7 +265,6 @@ type ContractBase = Omit< readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/legacy-aggregations/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/legacy-aggregations/_fixture/generated/contract.d.ts index a39a62ca4873..e35314f8860c 100644 --- a/test/integration/test/ports/prisma/functional/legacy-aggregations/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/legacy-aggregations/_fixture/generated/contract.d.ts @@ -604,7 +604,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/legacy-json/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/legacy-json/_fixture/generated/contract.d.ts index cfebb938dd67..a43946074832 100644 --- a/test/integration/test/ports/prisma/functional/legacy-json/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/legacy-json/_fixture/generated/contract.d.ts @@ -403,7 +403,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/legacy-malformed-id/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/legacy-malformed-id/_fixture/generated/contract.d.ts index 22cbdc3672f2..8382a0d2da93 100644 --- a/test/integration/test/ports/prisma/functional/legacy-malformed-id/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/legacy-malformed-id/_fixture/generated/contract.d.ts @@ -143,7 +143,6 @@ type ContractBase = Omit< readonly capabilities: {}; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/legacy-optional-relation-filters/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/legacy-optional-relation-filters/_fixture/generated/contract.d.ts index e151fbd40ecf..223f7b89742e 100644 --- a/test/integration/test/ports/prisma/functional/legacy-optional-relation-filters/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/legacy-optional-relation-filters/_fixture/generated/contract.d.ts @@ -502,7 +502,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/methods-count/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/methods-count/_fixture/generated/contract.d.ts index ce9f177f0828..0a1084b338d5 100644 --- a/test/integration/test/ports/prisma/functional/methods-count/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/methods-count/_fixture/generated/contract.d.ts @@ -604,7 +604,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/methods-createMany/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/methods-createMany/_fixture/generated/contract.d.ts index 7957de406432..bd0ddb643078 100644 --- a/test/integration/test/ports/prisma/functional/methods-createMany/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/methods-createMany/_fixture/generated/contract.d.ts @@ -528,7 +528,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/methods-createManyAndReturn/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/methods-createManyAndReturn/_fixture/generated/contract.d.ts index 7957de406432..bd0ddb643078 100644 --- a/test/integration/test/ports/prisma/functional/methods-createManyAndReturn/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/methods-createManyAndReturn/_fixture/generated/contract.d.ts @@ -528,7 +528,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/methods-findFirstOrThrow/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/methods-findFirstOrThrow/_fixture/generated/contract.d.ts index e63c1fa50725..78067003e93c 100644 --- a/test/integration/test/ports/prisma/functional/methods-findFirstOrThrow/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/methods-findFirstOrThrow/_fixture/generated/contract.d.ts @@ -516,7 +516,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/methods-findUniqueOrThrow/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/methods-findUniqueOrThrow/_fixture/generated/contract.d.ts index eb999fd63451..505d7d5579bc 100644 --- a/test/integration/test/ports/prisma/functional/methods-findUniqueOrThrow/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/methods-findUniqueOrThrow/_fixture/generated/contract.d.ts @@ -516,7 +516,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/methods-updateManyAndReturn/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/methods-updateManyAndReturn/_fixture/generated/contract.d.ts index 7957de406432..bd0ddb643078 100644 --- a/test/integration/test/ports/prisma/functional/methods-updateManyAndReturn/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/methods-updateManyAndReturn/_fixture/generated/contract.d.ts @@ -528,7 +528,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/methods-upsert-native-atomic/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/methods-upsert-native-atomic/_fixture/generated/contract.d.ts index 728eb727f31b..12fb87f82fa5 100644 --- a/test/integration/test/ports/prisma/functional/methods-upsert-native-atomic/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/methods-upsert-native-atomic/_fixture/generated/contract.d.ts @@ -592,7 +592,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/methods-upsert-simple/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/methods-upsert-simple/_fixture/generated/contract.d.ts index 10df8d913b94..4da4d802ebb6 100644 --- a/test/integration/test/ports/prisma/functional/methods-upsert-simple/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/methods-upsert-simple/_fixture/generated/contract.d.ts @@ -407,7 +407,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/mixed-string-uuid-datetime-list-inputs/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/mixed-string-uuid-datetime-list-inputs/_fixture/generated/contract.d.ts index a34339912d4e..b05498f0539a 100644 --- a/test/integration/test/ports/prisma/functional/mixed-string-uuid-datetime-list-inputs/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/mixed-string-uuid-datetime-list-inputs/_fixture/generated/contract.d.ts @@ -408,7 +408,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/multi-schema/_fixture/different-names/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/multi-schema/_fixture/different-names/generated/contract.d.ts index a2ba82ad58c5..142bd5f2aa52 100644 --- a/test/integration/test/ports/prisma/functional/multi-schema/_fixture/different-names/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/multi-schema/_fixture/different-names/generated/contract.d.ts @@ -541,7 +541,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/multi-schema/_fixture/identical-names/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/multi-schema/_fixture/identical-names/generated/contract.d.ts index 73c3316d7dbe..9f8d0af69556 100644 --- a/test/integration/test/ports/prisma/functional/multi-schema/_fixture/identical-names/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/multi-schema/_fixture/identical-names/generated/contract.d.ts @@ -544,7 +544,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/multi-schema/_fixture/no-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/multi-schema/_fixture/no-map/generated/contract.d.ts index c2051d019797..4601924dcc17 100644 --- a/test/integration/test/ports/prisma/functional/multi-schema/_fixture/no-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/multi-schema/_fixture/no-map/generated/contract.d.ts @@ -538,7 +538,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/multiple-types/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/multiple-types/_fixture/generated/contract.d.ts index 6607bbc7e2ec..854edbbc111e 100644 --- a/test/integration/test/ports/prisma/functional/multiple-types/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/multiple-types/_fixture/generated/contract.d.ts @@ -494,7 +494,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/optimistic-concurrency-control/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/optimistic-concurrency-control/_fixture/generated/contract.d.ts index c2ac9b36d543..147bbfbd3351 100644 --- a/test/integration/test/ports/prisma/functional/optimistic-concurrency-control/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/optimistic-concurrency-control/_fixture/generated/contract.d.ts @@ -508,7 +508,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/referential-actions-set-default-1to1/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/referential-actions-set-default-1to1/_fixture/generated/contract.d.ts index b8d42779829b..8fceb0b45af5 100644 --- a/test/integration/test/ports/prisma/functional/referential-actions-set-default-1to1/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/referential-actions-set-default-1to1/_fixture/generated/contract.d.ts @@ -615,7 +615,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/referential-actions-set-default-1ton/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/referential-actions-set-default-1ton/_fixture/generated/contract.d.ts index b8d42779829b..8fceb0b45af5 100644 --- a/test/integration/test/ports/prisma/functional/referential-actions-set-default-1ton/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/referential-actions-set-default-1ton/_fixture/generated/contract.d.ts @@ -615,7 +615,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-17255-mixed-actions/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-17255-mixed-actions/_fixture/generated/contract.d.ts index 738396e1fd8c..88f651fe0feb 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-17255-mixed-actions/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-17255-mixed-actions/_fixture/generated/contract.d.ts @@ -551,7 +551,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-17255-same-actions/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-17255-same-actions/_fixture/generated/contract.d.ts index 0858344c6c1a..d3a8b591f38a 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-17255-same-actions/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-17255-same-actions/_fixture/generated/contract.d.ts @@ -551,7 +551,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/cascade-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/cascade-map/generated/contract.d.ts index 138f4c7f85a6..3ebbaa0498c3 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/cascade-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/cascade-map/generated/contract.d.ts @@ -601,7 +601,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/cascade-plain/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/cascade-plain/generated/contract.d.ts index df73f0165817..6dc84b5936f3 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/cascade-plain/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/cascade-plain/generated/contract.d.ts @@ -601,7 +601,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/noaction-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/noaction-map/generated/contract.d.ts index 75e7514706de..3a8dc8e0f668 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/noaction-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/noaction-map/generated/contract.d.ts @@ -601,7 +601,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/noaction-plain/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/noaction-plain/generated/contract.d.ts index cbb89d73881b..c9677eb364e9 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/noaction-plain/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/noaction-plain/generated/contract.d.ts @@ -601,7 +601,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/restrict-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/restrict-map/generated/contract.d.ts index ed8629506426..15a3a3a2cfde 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/restrict-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/restrict-map/generated/contract.d.ts @@ -601,7 +601,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/restrict-plain/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/restrict-plain/generated/contract.d.ts index 5c964cdeb42d..1773aa8e2986 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/restrict-plain/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/restrict-plain/generated/contract.d.ts @@ -601,7 +601,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/setnull-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/setnull-map/generated/contract.d.ts index 83c9487f03db..7de72a1b12dc 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/setnull-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/setnull-map/generated/contract.d.ts @@ -601,7 +601,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/setnull-plain/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/setnull-plain/generated/contract.d.ts index 6d6ceb4b0560..b1f55c0f4426 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/setnull-plain/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/setnull-plain/generated/contract.d.ts @@ -601,7 +601,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/cascade-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/cascade-map/generated/contract.d.ts index 61bcead75c74..63a012a00d46 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/cascade-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/cascade-map/generated/contract.d.ts @@ -589,7 +589,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/cascade-plain/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/cascade-plain/generated/contract.d.ts index 559c884fa183..f8cf70bbd1b4 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/cascade-plain/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/cascade-plain/generated/contract.d.ts @@ -589,7 +589,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/noaction-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/noaction-map/generated/contract.d.ts index c76fe9a7e6f7..af32f6dc1014 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/noaction-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/noaction-map/generated/contract.d.ts @@ -589,7 +589,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/noaction-plain/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/noaction-plain/generated/contract.d.ts index 522ec12a381a..e2bd4c5c43c3 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/noaction-plain/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/noaction-plain/generated/contract.d.ts @@ -589,7 +589,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/restrict-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/restrict-map/generated/contract.d.ts index 51cf3ca209c9..73da8129afda 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/restrict-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/restrict-map/generated/contract.d.ts @@ -589,7 +589,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/restrict-plain/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/restrict-plain/generated/contract.d.ts index ffd708b3abd3..786650b5c760 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/restrict-plain/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/restrict-plain/generated/contract.d.ts @@ -589,7 +589,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/setnull-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/setnull-map/generated/contract.d.ts index f38479f32b90..513eb074a05d 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/setnull-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/setnull-map/generated/contract.d.ts @@ -589,7 +589,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/setnull-plain/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/setnull-plain/generated/contract.d.ts index 9ee3083bc309..7b0c1dc9d506 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/setnull-plain/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/setnull-plain/generated/contract.d.ts @@ -589,7 +589,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/cascade-nomap/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/cascade-nomap/generated/contract.d.ts index 08f1413bc117..d2ee879a2d3a 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/cascade-nomap/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/cascade-nomap/generated/contract.d.ts @@ -587,7 +587,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/default-nomap/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/default-nomap/generated/contract.d.ts index 0fb7ff132e86..6fc93161c546 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/default-nomap/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/default-nomap/generated/contract.d.ts @@ -587,7 +587,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/noaction-nomap/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/noaction-nomap/generated/contract.d.ts index 0fb7ff132e86..6fc93161c546 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/noaction-nomap/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/noaction-nomap/generated/contract.d.ts @@ -587,7 +587,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/restrict-nomap/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/restrict-nomap/generated/contract.d.ts index c94b4d2e9421..93dd06fef5de 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/restrict-nomap/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/restrict-nomap/generated/contract.d.ts @@ -587,7 +587,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/sql-builder/fixtures/generated-no-pgvector/contract.d.ts b/test/integration/test/sql-builder/fixtures/generated-no-pgvector/contract.d.ts index 80b3d3eda96a..3d9c0e6b5f97 100644 --- a/test/integration/test/sql-builder/fixtures/generated-no-pgvector/contract.d.ts +++ b/test/integration/test/sql-builder/fixtures/generated-no-pgvector/contract.d.ts @@ -741,7 +741,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/sql-builder/fixtures/generated/contract.d.ts b/test/integration/test/sql-builder/fixtures/generated/contract.d.ts index 937ed7662d5c..fd92c5aef1f4 100644 --- a/test/integration/test/sql-builder/fixtures/generated/contract.d.ts +++ b/test/integration/test/sql-builder/fixtures/generated/contract.d.ts @@ -980,7 +980,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/sql-orm-client/fixtures/collection-methods/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/collection-methods/generated/contract.d.ts index 98347ff79255..c3d606a20567 100644 --- a/test/integration/test/sql-orm-client/fixtures/collection-methods/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/collection-methods/generated/contract.d.ts @@ -28,7 +28,7 @@ import type { } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'8973aa4e3654dc5083f7ff15a7c187e3e590f977b4171bb816cd6566fa4db93b'>; + StorageHashBase<'87d224586379cdc42fda508d70ac88410eb9795e164b6031ef5a9dd9a5a1b759'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; @@ -244,7 +244,13 @@ type DefaultLiteralValue = CodecId extends keyo type ParameterizedOutputTypeFunctions = PgParameterizedOutputTypes | PgVectorParameterizedOutputTypes; -type FieldTypeOverrides = {}; +type FieldTypeOverrides = { + readonly public: { + readonly models: { + readonly Note: { readonly kind: { readonly output: 'memo'; readonly input: 'memo' } }; + }; + }; +}; export type FieldOutputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, @@ -259,7 +265,11 @@ export type FieldInputTypes = FieldTypesFromDomain< FieldTypeOverrides, 'input' >; -type ColumnTypeOverrides = {}; +type ColumnTypeOverrides = { + readonly public: { + readonly notes: { readonly kind: { readonly output: 'memo'; readonly input: 'memo' } }; + }; +}; export type StorageColumnTypes = ColumnTypesFromStorage< ContractBase['storage'], CodecTypes, @@ -451,6 +461,9 @@ type ContractBase = Omit< foreignKeys: readonly []; }; }; + readonly valueSet: { + readonly 'Note.kind': { readonly kind: 'valueSet'; readonly values: readonly ['memo'] }; + }; }; }; }; @@ -655,7 +668,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/sql-orm-client/fixtures/collection-methods/generated/contract.json b/test/integration/test/sql-orm-client/fixtures/collection-methods/generated/contract.json index a13f0e70eabe..281f66221c23 100644 --- a/test/integration/test/sql-orm-client/fixtures/collection-methods/generated/contract.json +++ b/test/integration/test/sql-orm-client/fixtures/collection-methods/generated/contract.json @@ -211,6 +211,13 @@ "entries": { "table": { "notes": { + "checks": [ + { + "expression": "\"kind\" IN ('memo')", + "name": "notes_kind_check_97646eca", + "prefix": "notes_kind_check" + } + ], "columns": { "body": { "codecId": "pg/text@1", @@ -225,7 +232,13 @@ "kind": { "codecId": "pg/text@1", "dataType": "pg/text", - "nullable": false + "nullable": false, + "valueSet": { + "entityKind": "valueSet", + "entityName": "Note.kind", + "namespaceId": "public", + "plane": "storage" + } }, "pinned": { "codecId": "pg/bool@1", @@ -339,13 +352,21 @@ }, "uniques": [] } + }, + "valueSet": { + "Note.kind": { + "kind": "valueSet", + "values": [ + "memo" + ] + } } }, "id": "public", "kind": "postgres-schema" } }, - "storageHash": "8973aa4e3654dc5083f7ff15a7c187e3e590f977b4171bb816cd6566fa4db93b" + "storageHash": "87d224586379cdc42fda508d70ac88410eb9795e164b6031ef5a9dd9a5a1b759" }, "capabilities": { "postgres": { diff --git a/test/integration/test/sql-orm-client/fixtures/execution-defaulted-tags/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/execution-defaulted-tags/generated/contract.d.ts index 97e1d6846948..681404fceefe 100644 --- a/test/integration/test/sql-orm-client/fixtures/execution-defaulted-tags/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/execution-defaulted-tags/generated/contract.d.ts @@ -567,7 +567,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/sql-orm-client/fixtures/fragment-namespace/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/fragment-namespace/generated/contract.d.ts index 522738f53abe..fe71c759f155 100644 --- a/test/integration/test/sql-orm-client/fixtures/fragment-namespace/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/fragment-namespace/generated/contract.d.ts @@ -461,7 +461,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/sql-orm-client/fixtures/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/generated/contract.d.ts index e4e7b0d65f35..21a93183df43 100644 --- a/test/integration/test/sql-orm-client/fixtures/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/generated/contract.d.ts @@ -25,7 +25,7 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; export type StorageHash = @@ -245,15 +245,15 @@ type DefaultLiteralValue = CodecId extends keyo : Encoded; type ParameterizedOutputTypeFunctions = PgParameterizedOutputTypes | PgVectorParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -267,14 +267,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< @@ -819,6 +821,24 @@ type ContractBase = Omit< ]; }; }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + }; + }; }; }; }; @@ -1137,7 +1157,13 @@ type ContractBase = Omit< readonly fields: { readonly address: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: 'public'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; }; readonly email: { readonly nullable: false; @@ -1303,24 +1329,6 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - }; - }; }; }; }; @@ -1414,24 +1422,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/sql-orm-client/fixtures/generated/contract.json b/test/integration/test/sql-orm-client/fixtures/generated/contract.json index fe24243054be..d753a0f62643 100644 --- a/test/integration/test/sql-orm-client/fixtures/generated/contract.json +++ b/test/integration/test/sql-orm-client/fixtures/generated/contract.json @@ -505,8 +505,11 @@ "address": { "nullable": true, "type": { + "entityKind": "valueObject", + "entityName": "Address", "kind": "valueObject", - "name": "Address" + "namespaceId": "public", + "plane": "storage" } }, "email": { @@ -778,33 +781,6 @@ "table": "user_tags" } } - }, - "valueObjects": { - "Address": { - "fields": { - "city": { - "nullable": false, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" - } - }, - "street": { - "nullable": false, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" - } - }, - "zip": { - "nullable": true, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" - } - } - } - } } } } @@ -1319,6 +1295,33 @@ } ] } + }, + "valueObject": { + "Address": { + "fields": { + "city": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "street": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "zip": { + "nullable": true, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + } + } + } } }, "id": "public", diff --git a/test/integration/test/sql-orm-client/fixtures/integer-representation-sqlite/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/integer-representation-sqlite/generated/contract.d.ts index 7fd5d0eca066..cd76cf0f6c18 100644 --- a/test/integration/test/sql-orm-client/fixtures/integer-representation-sqlite/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/integer-representation-sqlite/generated/contract.d.ts @@ -388,7 +388,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/sql-orm-client/fixtures/integer-representation/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/integer-representation/generated/contract.d.ts index 9002874effb4..db24c8e9444b 100644 --- a/test/integration/test/sql-orm-client/fixtures/integer-representation/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/integer-representation/generated/contract.d.ts @@ -516,7 +516,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/sql-orm-client/fixtures/junction-namespaces/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/junction-namespaces/generated/contract.d.ts index 73af750f4244..d0172b96d88d 100644 --- a/test/integration/test/sql-orm-client/fixtures/junction-namespaces/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/junction-namespaces/generated/contract.d.ts @@ -763,7 +763,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/sql-orm-client/fixtures/mn-psl/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/mn-psl/generated/contract.d.ts index 93efa01d2b11..76c509024a79 100644 --- a/test/integration/test/sql-orm-client/fixtures/mn-psl/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/mn-psl/generated/contract.d.ts @@ -592,7 +592,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/sql-orm-client/fixtures/non-identifier-names/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/non-identifier-names/generated/contract.d.ts index 7918c3eda57a..6b16523f893b 100644 --- a/test/integration/test/sql-orm-client/fixtures/non-identifier-names/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/non-identifier-names/generated/contract.d.ts @@ -391,7 +391,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/sql-orm-client/fixtures/on-update-only/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/on-update-only/generated/contract.d.ts index 78960b7aa687..7ef7151d6172 100644 --- a/test/integration/test/sql-orm-client/fixtures/on-update-only/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/on-update-only/generated/contract.d.ts @@ -428,7 +428,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/sql-orm-client/fixtures/polymorphism-namespaces/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/polymorphism-namespaces/generated/contract.d.ts index d77236c61c04..35c01bf3f769 100644 --- a/test/integration/test/sql-orm-client/fixtures/polymorphism-namespaces/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/polymorphism-namespaces/generated/contract.d.ts @@ -23,7 +23,7 @@ import type { } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'20f073da80d13b3d30479280c2ff02efa9594c42efebfbb14f233b93e9df3d2f'>; + StorageHashBase<'b4c46998d16621f5bfb704da34f41ffc3a24a4029fb2c0ef7d2c10f9878597cc'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; @@ -237,7 +237,28 @@ type DefaultLiteralValue = CodecId extends keyo : Encoded; type ParameterizedOutputTypeFunctions = PgParameterizedOutputTypes; -type FieldTypeOverrides = {}; +type FieldTypeOverrides = { + readonly a: { + readonly models: { + readonly Task: { + readonly type: { + readonly output: 'b-epic' | 'bug' | 'c-epic' | 'chore' | 'feature'; + readonly input: 'b-epic' | 'bug' | 'c-epic' | 'chore' | 'feature'; + }; + }; + }; + }; + readonly b: { + readonly models: { + readonly Doc: { readonly kind: { readonly output: 'memo'; readonly input: 'memo' } }; + }; + }; + readonly c: { + readonly models: { + readonly Doc: { readonly kind: { readonly output: 'note'; readonly input: 'note' } }; + }; + }; +}; export type FieldOutputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, @@ -252,7 +273,22 @@ export type FieldInputTypes = FieldTypesFromDomain< FieldTypeOverrides, 'input' >; -type ColumnTypeOverrides = {}; +type ColumnTypeOverrides = { + readonly a: { + readonly tasks: { + readonly type: { + readonly output: 'b-epic' | 'bug' | 'c-epic' | 'chore' | 'feature'; + readonly input: 'b-epic' | 'bug' | 'c-epic' | 'chore' | 'feature'; + }; + }; + }; + readonly b: { + readonly docs: { readonly kind: { readonly output: 'memo'; readonly input: 'memo' } }; + }; + readonly c: { + readonly docs: { readonly kind: { readonly output: 'note'; readonly input: 'note' } }; + }; +}; export type StorageColumnTypes = ColumnTypesFromStorage< ContractBase['storage'], CodecTypes, @@ -516,6 +552,12 @@ type ContractBase = Omit< ]; }; }; + readonly valueSet: { + readonly 'Task.type': { + readonly kind: 'valueSet'; + readonly values: readonly ['b-epic', 'bug', 'c-epic', 'chore', 'feature']; + }; + }; }; }; readonly b: { @@ -661,6 +703,9 @@ type ContractBase = Omit< ]; }; }; + readonly valueSet: { + readonly 'Doc.kind': { readonly kind: 'valueSet'; readonly values: readonly ['memo'] }; + }; }; }; readonly c: { @@ -728,6 +773,9 @@ type ContractBase = Omit< ]; }; }; + readonly valueSet: { + readonly 'Doc.kind': { readonly kind: 'valueSet'; readonly values: readonly ['note'] }; + }; }; }; readonly public: { @@ -1069,7 +1117,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/sql-orm-client/fixtures/polymorphism-namespaces/generated/contract.json b/test/integration/test/sql-orm-client/fixtures/polymorphism-namespaces/generated/contract.json index fda2e0e38e84..a3aeeaaa2844 100644 --- a/test/integration/test/sql-orm-client/fixtures/polymorphism-namespaces/generated/contract.json +++ b/test/integration/test/sql-orm-client/fixtures/polymorphism-namespaces/generated/contract.json @@ -565,6 +565,13 @@ "uniques": [] }, "tasks": { + "checks": [ + { + "expression": "\"type\" IN ('b-epic', 'bug', 'c-epic', 'chore', 'feature')", + "name": "tasks_type_check_fb4446ef", + "prefix": "tasks_type_check" + } + ], "columns": { "id": { "codecId": "pg/int4@1", @@ -589,7 +596,13 @@ "type": { "codecId": "pg/text@1", "dataType": "pg/text", - "nullable": false + "nullable": false, + "valueSet": { + "entityKind": "valueSet", + "entityName": "Task.type", + "namespaceId": "a", + "plane": "storage" + } } }, "foreignKeys": [ @@ -630,6 +643,18 @@ }, "uniques": [] } + }, + "valueSet": { + "Task.type": { + "kind": "valueSet", + "values": [ + "b-epic", + "bug", + "c-epic", + "chore", + "feature" + ] + } } }, "id": "a", @@ -639,6 +664,13 @@ "entries": { "table": { "docs": { + "checks": [ + { + "expression": "\"kind\" IN ('memo')", + "name": "docs_kind_check_97646eca", + "prefix": "docs_kind_check" + } + ], "columns": { "body": { "codecId": "pg/text@1", @@ -653,7 +685,13 @@ "kind": { "codecId": "pg/text@1", "dataType": "pg/text", - "nullable": false + "nullable": false, + "valueSet": { + "entityKind": "valueSet", + "entityName": "Doc.kind", + "namespaceId": "b", + "plane": "storage" + } } }, "foreignKeys": [], @@ -806,6 +844,14 @@ }, "uniques": [] } + }, + "valueSet": { + "Doc.kind": { + "kind": "valueSet", + "values": [ + "memo" + ] + } } }, "id": "b", @@ -815,6 +861,13 @@ "entries": { "table": { "docs": { + "checks": [ + { + "expression": "\"kind\" IN ('note')", + "name": "docs_kind_check_144bbad4", + "prefix": "docs_kind_check" + } + ], "columns": { "body": { "codecId": "pg/text@1", @@ -829,7 +882,13 @@ "kind": { "codecId": "pg/text@1", "dataType": "pg/text", - "nullable": false + "nullable": false, + "valueSet": { + "entityKind": "valueSet", + "entityName": "Doc.kind", + "namespaceId": "c", + "plane": "storage" + } } }, "foreignKeys": [], @@ -884,6 +943,14 @@ }, "uniques": [] } + }, + "valueSet": { + "Doc.kind": { + "kind": "valueSet", + "values": [ + "note" + ] + } } }, "id": "c", @@ -897,7 +964,7 @@ "kind": "postgres-schema" } }, - "storageHash": "20f073da80d13b3d30479280c2ff02efa9594c42efebfbb14f233b93e9df3d2f" + "storageHash": "b4c46998d16621f5bfb704da34f41ffc3a24a4029fb2c0ef7d2c10f9878597cc" }, "capabilities": { "postgres": { diff --git a/test/integration/test/sql-orm-client/fixtures/polymorphism-self-relation/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/polymorphism-self-relation/generated/contract.d.ts index 22a16e54d56a..bd9e494204b8 100644 --- a/test/integration/test/sql-orm-client/fixtures/polymorphism-self-relation/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/polymorphism-self-relation/generated/contract.d.ts @@ -23,7 +23,7 @@ import type { } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'8938bb7c885ba3bade87681ec83e5856d1a2107f4db096afde6a52bd48bea45d'>; + StorageHashBase<'819d42189875a8fbc83dedd6c2b2616a82ec5c3628dea2b618935d3597c29f95'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; @@ -237,7 +237,15 @@ type DefaultLiteralValue = CodecId extends keyo : Encoded; type ParameterizedOutputTypeFunctions = PgParameterizedOutputTypes; -type FieldTypeOverrides = {}; +type FieldTypeOverrides = { + readonly public: { + readonly models: { + readonly Task: { + readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; + }; + }; + }; +}; export type FieldOutputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, @@ -252,7 +260,13 @@ export type FieldInputTypes = FieldTypesFromDomain< FieldTypeOverrides, 'input' >; -type ColumnTypeOverrides = {}; +type ColumnTypeOverrides = { + readonly public: { + readonly tasks: { + readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; + }; + }; +}; export type StorageColumnTypes = ColumnTypesFromStorage< ContractBase['storage'], CodecTypes, @@ -447,6 +461,12 @@ type ContractBase = Omit< ]; }; }; + readonly valueSet: { + readonly 'Task.type': { + readonly kind: 'valueSet'; + readonly values: readonly ['bug', 'feature']; + }; + }; }; }; }; @@ -600,7 +620,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/sql-orm-client/fixtures/polymorphism-self-relation/generated/contract.json b/test/integration/test/sql-orm-client/fixtures/polymorphism-self-relation/generated/contract.json index 7830a05c5b25..d986883f9d63 100644 --- a/test/integration/test/sql-orm-client/fixtures/polymorphism-self-relation/generated/contract.json +++ b/test/integration/test/sql-orm-client/fixtures/polymorphism-self-relation/generated/contract.json @@ -272,6 +272,13 @@ "uniques": [] }, "tasks": { + "checks": [ + { + "expression": "\"type\" IN ('bug', 'feature')", + "name": "tasks_type_check_cda3f5e7", + "prefix": "tasks_type_check" + } + ], "columns": { "id": { "codecId": "pg/int4@1", @@ -296,7 +303,13 @@ "type": { "codecId": "pg/text@1", "dataType": "pg/text", - "nullable": false + "nullable": false, + "valueSet": { + "entityKind": "valueSet", + "entityName": "Task.type", + "namespaceId": "public", + "plane": "storage" + } } }, "foreignKeys": [ @@ -337,13 +350,22 @@ }, "uniques": [] } + }, + "valueSet": { + "Task.type": { + "kind": "valueSet", + "values": [ + "bug", + "feature" + ] + } } }, "id": "public", "kind": "postgres-schema" } }, - "storageHash": "8938bb7c885ba3bade87681ec83e5856d1a2107f4db096afde6a52bd48bea45d" + "storageHash": "819d42189875a8fbc83dedd6c2b2616a82ec5c3628dea2b618935d3597c29f95" }, "capabilities": { "postgres": { diff --git a/test/integration/test/sql-orm-client/fixtures/polymorphism/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/polymorphism/generated/contract.d.ts index 90af9e6edd9b..7886f911c7e7 100644 --- a/test/integration/test/sql-orm-client/fixtures/polymorphism/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/polymorphism/generated/contract.d.ts @@ -23,7 +23,7 @@ import type { } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'2ccba7c309ee13e4714b958e47eb88ca06415dac60cd475a51940499c3331f8a'>; + StorageHashBase<'00913b83286a9e6492e6621c3ba277633d43a309cdda39f9cb46f0a54752c1eb'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; @@ -237,7 +237,24 @@ type DefaultLiteralValue = CodecId extends keyo : Encoded; type ParameterizedOutputTypeFunctions = PgParameterizedOutputTypes; -type FieldTypeOverrides = {}; +type FieldTypeOverrides = { + readonly public: { + readonly models: { + readonly Task: { + readonly type: { + readonly output: 'bug' | 'epic' | 'feature'; + readonly input: 'bug' | 'epic' | 'feature'; + }; + }; + readonly User: { + readonly kind: { + readonly output: 'admin' | 'regular'; + readonly input: 'admin' | 'regular'; + }; + }; + }; + }; +}; export type FieldOutputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, @@ -252,7 +269,19 @@ export type FieldInputTypes = FieldTypesFromDomain< FieldTypeOverrides, 'input' >; -type ColumnTypeOverrides = {}; +type ColumnTypeOverrides = { + readonly public: { + readonly tasks: { + readonly type: { + readonly output: 'bug' | 'epic' | 'feature'; + readonly input: 'bug' | 'epic' | 'feature'; + }; + }; + readonly users: { + readonly kind: { readonly output: 'admin' | 'regular'; readonly input: 'admin' | 'regular' }; + }; + }; +}; export type StorageColumnTypes = ColumnTypesFromStorage< ContractBase['storage'], CodecTypes, @@ -852,6 +881,16 @@ type ContractBase = Omit< ]; }; }; + readonly valueSet: { + readonly 'Task.type': { + readonly kind: 'valueSet'; + readonly values: readonly ['bug', 'epic', 'feature']; + }; + readonly 'User.kind': { + readonly kind: 'valueSet'; + readonly values: readonly ['admin', 'regular']; + }; + }; }; }; }; @@ -1331,7 +1370,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/sql-orm-client/fixtures/polymorphism/generated/contract.json b/test/integration/test/sql-orm-client/fixtures/polymorphism/generated/contract.json index c62e7950f392..fa8deeced427 100644 --- a/test/integration/test/sql-orm-client/fixtures/polymorphism/generated/contract.json +++ b/test/integration/test/sql-orm-client/fixtures/polymorphism/generated/contract.json @@ -957,6 +957,13 @@ "uniques": [] }, "tasks": { + "checks": [ + { + "expression": "\"type\" IN ('bug', 'epic', 'feature')", + "name": "tasks_type_check_b3d5b43f", + "prefix": "tasks_type_check" + } + ], "columns": { "bug_assignee_person_id": { "codecId": "pg/int4@1", @@ -995,7 +1002,13 @@ "type": { "codecId": "pg/text@1", "dataType": "pg/text", - "nullable": false + "nullable": false, + "valueSet": { + "entityKind": "valueSet", + "entityName": "Task.type", + "namespaceId": "public", + "plane": "storage" + } } }, "foreignKeys": [ @@ -1124,6 +1137,13 @@ "uniques": [] }, "users": { + "checks": [ + { + "expression": "\"kind\" IN ('admin', 'regular')", + "name": "users_kind_check_dc69a8d4", + "prefix": "users_kind_check" + } + ], "columns": { "account_id": { "codecId": "pg/int4@1", @@ -1147,7 +1167,13 @@ "kind": { "codecId": "pg/text@1", "dataType": "pg/text", - "nullable": false + "nullable": false, + "valueSet": { + "entityKind": "valueSet", + "entityName": "User.kind", + "namespaceId": "public", + "plane": "storage" + } }, "name": { "codecId": "pg/text@1", @@ -1203,13 +1229,30 @@ }, "uniques": [] } + }, + "valueSet": { + "Task.type": { + "kind": "valueSet", + "values": [ + "bug", + "epic", + "feature" + ] + }, + "User.kind": { + "kind": "valueSet", + "values": [ + "admin", + "regular" + ] + } } }, "id": "public", "kind": "postgres-schema" } }, - "storageHash": "2ccba7c309ee13e4714b958e47eb88ca06415dac60cd475a51940499c3331f8a" + "storageHash": "00913b83286a9e6492e6621c3ba277633d43a309cdda39f9cb46f0a54752c1eb" }, "capabilities": { "postgres": { diff --git a/test/integration/test/sql-orm-client/fixtures/scalar-lists/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/scalar-lists/generated/contract.d.ts index 1d4e5a338ded..5f0fb36f3788 100644 --- a/test/integration/test/sql-orm-client/fixtures/scalar-lists/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/scalar-lists/generated/contract.d.ts @@ -405,7 +405,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/sql-orm-client/fixtures/scope-namespace/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/scope-namespace/generated/contract.d.ts index ffa93ebd92ef..bd82f339d292 100644 --- a/test/integration/test/sql-orm-client/fixtures/scope-namespace/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/scope-namespace/generated/contract.d.ts @@ -461,7 +461,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/sql-orm-client/fixtures/self-relations/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/self-relations/generated/contract.d.ts index 1611d36bb3ba..2d3be2c8cc99 100644 --- a/test/integration/test/sql-orm-client/fixtures/self-relations/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/self-relations/generated/contract.d.ts @@ -824,7 +824,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/sql-orm-client/fixtures/soft-delete/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/soft-delete/generated/contract.d.ts index 64b6249f01d3..43aa0428e689 100644 --- a/test/integration/test/sql-orm-client/fixtures/soft-delete/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/soft-delete/generated/contract.d.ts @@ -20,11 +20,11 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromDomain, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'dfd3186b551a7868c3339bf4a5f80ca2a3ad4aa43a610cc64189db7c62a1e475'>; + StorageHashBase<'21a3ae2aab3f4f03b59fa65df3d452ec1d41d9ce5926b3fba6bd70cf39070dcb'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; @@ -237,15 +237,15 @@ type DefaultLiteralValue = CodecId extends keyo : CodecTypes[CodecId]['json'] : Encoded; type ParameterizedOutputTypeFunctions = PgParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, 'output' >; -type ValueObjectInputTypes = ValueObjectTypesFromDomain< - ContractBase['domain']['namespaces'], +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, @@ -259,14 +259,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< @@ -539,6 +541,16 @@ type ContractBase = Omit< foreignKeys: readonly []; }; }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + }; + }; }; }; }; @@ -674,7 +686,13 @@ type ContractBase = Omit< readonly fields: { readonly addresses: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: 'public'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; readonly many: { readonly elementNullable: false }; }; readonly id: { @@ -740,16 +758,6 @@ type ContractBase = Omit< }; }; }; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - }; - }; }; }; }; @@ -782,16 +790,6 @@ type ContractBase = Omit< }; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/sql-orm-client/fixtures/soft-delete/generated/contract.json b/test/integration/test/sql-orm-client/fixtures/soft-delete/generated/contract.json index d595f985f0e7..cd387f209afb 100644 --- a/test/integration/test/sql-orm-client/fixtures/soft-delete/generated/contract.json +++ b/test/integration/test/sql-orm-client/fixtures/soft-delete/generated/contract.json @@ -204,8 +204,11 @@ }, "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "Address", "kind": "valueObject", - "name": "Address" + "namespaceId": "public", + "plane": "storage" } }, "id": { @@ -313,19 +316,6 @@ "table": "users" } } - }, - "valueObjects": { - "Address": { - "fields": { - "street": { - "nullable": false, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" - } - } - } - } } } } @@ -481,6 +471,11 @@ }, "tags": { "checks": [ + { + "expression": "jsonb_path_match(\"addresses\", 'strict $.type() == \"array\" && !exists($[*] ? (!((@.type() == \"object\" && exists(@.keyvalue() ? (@.key == \"street\" && (@.value.type() == \"string\"))) && !exists(@.keyvalue() ? (@.key != \"street\"))))))') IS TRUE", + "name": "tags_addresses_value_object_c752a33a", + "prefix": "tags_addresses_value_object" + }, { "expression": "array_position(\"labels\", NULL) IS NULL", "name": "tags_labels_elem_not_null_0c04ab5c", @@ -559,13 +554,26 @@ }, "uniques": [] } + }, + "valueObject": { + "Address": { + "fields": { + "street": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + } + } + } } }, "id": "public", "kind": "postgres-schema" } }, - "storageHash": "dfd3186b551a7868c3339bf4a5f80ca2a3ad4aa43a610cc64189db7c62a1e475" + "storageHash": "21a3ae2aab3f4f03b59fa65df3d452ec1d41d9ce5926b3fba6bd70cf39070dcb" }, "capabilities": { "postgres": { diff --git a/test/integration/test/temporal-defaults/_fixture-aliases/generated/contract.d.ts b/test/integration/test/temporal-defaults/_fixture-aliases/generated/contract.d.ts index c9d5de5e4971..7bd96be4b68b 100644 --- a/test/integration/test/temporal-defaults/_fixture-aliases/generated/contract.d.ts +++ b/test/integration/test/temporal-defaults/_fixture-aliases/generated/contract.d.ts @@ -493,7 +493,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.d.ts b/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.d.ts index e476373c4fac..a329f87114d0 100644 --- a/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.d.ts +++ b/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.d.ts @@ -1137,7 +1137,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/temporal-defaults/_fixture-now/generated/contract.d.ts b/test/integration/test/temporal-defaults/_fixture-now/generated/contract.d.ts index e476373c4fac..a329f87114d0 100644 --- a/test/integration/test/temporal-defaults/_fixture-now/generated/contract.d.ts +++ b/test/integration/test/temporal-defaults/_fixture-now/generated/contract.d.ts @@ -1137,7 +1137,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/temporal-defaults/_fixture-timestamp/generated/contract.d.ts b/test/integration/test/temporal-defaults/_fixture-timestamp/generated/contract.d.ts index b1278162199d..d7d578c0d136 100644 --- a/test/integration/test/temporal-defaults/_fixture-timestamp/generated/contract.d.ts +++ b/test/integration/test/temporal-defaults/_fixture-timestamp/generated/contract.d.ts @@ -451,7 +451,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/temporal-defaults/_fixture/generated/contract.d.ts b/test/integration/test/temporal-defaults/_fixture/generated/contract.d.ts index c9d5de5e4971..7bd96be4b68b 100644 --- a/test/integration/test/temporal-defaults/_fixture/generated/contract.d.ts +++ b/test/integration/test/temporal-defaults/_fixture/generated/contract.d.ts @@ -493,7 +493,6 @@ type ContractBase = Omit< }; }; readonly meta: {}; - readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/value-objects/fixtures/generated/sql-contract.d.ts b/test/integration/test/value-objects/fixtures/generated/sql-contract.d.ts index b7875c82f73d..2ad59a1267a1 100644 --- a/test/integration/test/value-objects/fixtures/generated/sql-contract.d.ts +++ b/test/integration/test/value-objects/fixtures/generated/sql-contract.d.ts @@ -1,33 +1,30 @@ // ⚠️ GENERATED FILE - DO NOT EDIT // This file is automatically generated by 'prisma contract emit'. // To regenerate, run: prisma contract emit -import type { QueryOperationTypes as PgTargetQueryOps } from '@internal/target-postgres/operation-types'; import type { - Bit, - Char, CodecTypes as PgTypes, - Interval, - JsonValue, - Numeric, - Time, - Timestamp, - Timestamptz, - Timetz, - VarBit, - Varchar, + ParameterizedOutputTypesForTypeParams as PgParameterizedOutputTypes, } from '@internal/target-postgres/codec-types'; +import type { QueryOperationTypes as PgTargetQueryOps } from '@internal/target-postgres/operation-types'; -import type { ContractWithTypeMaps, TypeMaps as TypeMapsType } from '@internal/sql-contract/types'; +import type { + ColumnTypesFromStorage, + ContractWithTypeMaps, + RelationKeys, + TypeMaps as TypeMapsType, +} from '@internal/sql-contract/types'; import type { Contract as ContractType, ExecutionHashBase, + FieldTypesFromDomain, NamespaceId, ProfileHashBase, StorageHashBase, + ValueObjectTypesFromStorage, } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'8334dda9271ee13d092ca25003aed6d6507dbad3e83de89f994a48661d16bbf4'>; + StorageHashBase<'4216574839577232bdfaee2c8202fc9044a7873412428368c9ee430266747fab'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; @@ -35,66 +32,284 @@ export type ProfileHash = export type CodecTypes = PgTypes; export type LaneCodecTypes = CodecTypes; export type QueryOperationTypes = PgTargetQueryOps; -type DefaultLiteralValue = CodecId extends keyof CodecTypes - ? CodecTypes[CodecId]['output'] - : _Encoded; -export type AddressOutput = { - readonly street: CodecTypes['pg/text@1']['output']; - readonly city: CodecTypes['pg/text@1']['output']; - readonly zip: CodecTypes['pg/text@1']['output']; -}; -export type AddressInput = { - readonly street: CodecTypes['pg/text@1']['input']; - readonly city: CodecTypes['pg/text@1']['input']; - readonly zip: CodecTypes['pg/text@1']['input']; -}; -export type FieldOutputTypes = { - readonly public: { - readonly Shop: { - readonly id: CodecTypes['pg/int4@1']['output']; - readonly name: CodecTypes['pg/text@1']['output']; - readonly location: AddressOutput; - readonly notes: AddressOutput | null; +export type AggregateTypes = { + readonly avg: { + readonly byCodec: { + readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; }; }; -}; -export type FieldInputTypes = { - readonly public: { - readonly Shop: { - readonly id: CodecTypes['pg/int4@1']['input']; - readonly name: CodecTypes['pg/text@1']['input']; - readonly location: AddressInput; - readonly notes: AddressInput | null; + readonly avgDecimal: { + readonly byCodec: { + readonly 'pg/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; }; }; -}; -export type StorageColumnTypes = { - readonly public: { - readonly shop: { - readonly id: CodecTypes['pg/int4@1']['output']; - readonly location: CodecTypes['pg/jsonb@1']['output']; - readonly name: CodecTypes['pg/text@1']['output']; - readonly notes: CodecTypes['pg/jsonb@1']['output'] | null; + readonly count: { + readonly byCodec: {}; + readonly withoutInput: { readonly output: 'pg/int8number@1'; readonly nullable: false }; + readonly anyInput: { readonly output: 'pg/int8number@1'; readonly nullable: false }; + }; + readonly countBigInt: { + readonly byCodec: {}; + readonly withoutInput: { readonly output: 'pg/int8@1'; readonly nullable: false }; + readonly anyInput: { readonly output: 'pg/int8@1'; readonly nullable: false }; + }; + readonly max: { + readonly byCodec: { + readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true }; + readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true }; + readonly 'pg/date-temporal@1': { + readonly output: 'pg/date-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true }; + readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; + readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { + readonly output: 'pg/time-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-string@1': { + readonly output: 'pg/timestamp-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-temporal@1': { + readonly output: 'pg/timestamp-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-date@1': { + readonly output: 'pg/timestamptz-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-string@1': { + readonly output: 'pg/timestamptz-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-temporal@1': { + readonly output: 'pg/timestamptz-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true }; + readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true }; + readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; }; }; -}; -export type StorageColumnInputTypes = { - readonly public: { - readonly shop: { - readonly id: CodecTypes['pg/int4@1']['input']; - readonly location: CodecTypes['pg/jsonb@1']['input']; - readonly name: CodecTypes['pg/text@1']['input']; - readonly notes: CodecTypes['pg/jsonb@1']['input'] | null; + readonly min: { + readonly byCodec: { + readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true }; + readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true }; + readonly 'pg/date-temporal@1': { + readonly output: 'pg/date-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true }; + readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; + readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { + readonly output: 'pg/time-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-string@1': { + readonly output: 'pg/timestamp-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-temporal@1': { + readonly output: 'pg/timestamp-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-date@1': { + readonly output: 'pg/timestamptz-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-string@1': { + readonly output: 'pg/timestamptz-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-temporal@1': { + readonly output: 'pg/timestamptz-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true }; + readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true }; + readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; }; }; + readonly sum: { + readonly byCodec: { + readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + }; + }; + readonly sumBigInt: { + readonly byCodec: { + readonly 'pg/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'sql/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + }; + }; +}; +type DefaultLiteralValue = CodecId extends keyof CodecTypes + ? Encoded extends CodecTypes[CodecId]['json'] + ? Encoded + : CodecTypes[CodecId]['json'] + : Encoded; +type ParameterizedOutputTypeFunctions = PgParameterizedOutputTypes; +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output' +>; +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input' +>; +export type AddressOutput = ValueObjectOutputTypes['public']['Address']; +export type AddressInput = ValueObjectInputTypes['public']['Address']; +type FieldTypeOverrides = {}; +export type FieldOutputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output', + ContractBase['storage']['namespaces'] +>; +export type FieldInputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input', + ContractBase['storage']['namespaces'] +>; +type ColumnTypeOverrides = {}; +export type StorageColumnTypes = ColumnTypesFromStorage< + ContractBase['storage'], + CodecTypes, + ParameterizedOutputTypeFunctions, + ColumnTypeOverrides, + 'output' +>; +export type StorageColumnInputTypes = ColumnTypesFromStorage< + ContractBase['storage'], + CodecTypes, + ParameterizedOutputTypeFunctions, + ColumnTypeOverrides, + 'input' +>; + +export namespace Models { + export type public_Shop = { + id: FieldOutputTypes['public']['Shop']['id']; + location: FieldOutputTypes['public']['Shop']['location']; + name: FieldOutputTypes['public']['Shop']['name']; + notes: FieldOutputTypes['public']['Shop']['notes']; + readonly [RelationKeys]?: never; + }; +} + +export declare const models: { + public: { + Shop: Models.public_Shop; + }; }; + export type TypeMaps = TypeMapsType< CodecTypes, QueryOperationTypes, FieldOutputTypes, FieldInputTypes, StorageColumnTypes, - StorageColumnInputTypes + StorageColumnInputTypes, + AggregateTypes >; type ContractBase = Omit< @@ -108,28 +323,28 @@ type ContractBase = Omit< readonly shop: { columns: { readonly id: { - readonly many: false; readonly dataType: 'pg/int4'; readonly codecId: 'pg/int4@1'; readonly nullable: false; - }; - readonly name: { readonly many: false; - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; }; readonly location: { - readonly many: false; readonly dataType: 'pg/jsonb'; readonly codecId: 'pg/jsonb@1'; readonly nullable: false; + readonly many: false; }; - readonly notes: { + readonly name: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; readonly many: false; + }; + readonly notes: { readonly dataType: 'pg/jsonb'; readonly codecId: 'pg/jsonb@1'; readonly nullable: true; + readonly many: false; }; }; primaryKey: { readonly columns: readonly ['id'] }; @@ -138,6 +353,24 @@ type ContractBase = Omit< foreignKeys: readonly []; }; }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly zip: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + }; + }; }; }; }; @@ -160,17 +393,29 @@ type ContractBase = Omit< readonly nullable: false; readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int4@1' }; }; - readonly name: { + readonly location: { readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: 'public'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; }; - readonly location: { + readonly name: { readonly nullable: false; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; }; readonly notes: { readonly nullable: true; - readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: 'public'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; }; }; readonly relations: Record; @@ -179,37 +424,21 @@ type ContractBase = Omit< readonly namespaceId: 'public'; readonly fields: { readonly id: { readonly column: 'id' }; - readonly name: { readonly column: 'name' }; readonly location: { readonly column: 'location' }; + readonly name: { readonly column: 'name' }; readonly notes: { readonly column: 'notes' }; }; }; }; }; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly zip: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - }; - }; }; }; }; readonly capabilities: { readonly postgres: { readonly distinctOn: true; + readonly forKeyShare: true; + readonly forNoKeyUpdate: true; readonly jsonAgg: true; readonly lateral: true; readonly limit: true; @@ -217,33 +446,23 @@ type ContractBase = Omit< readonly returning: true; }; readonly sql: { + readonly checkConstraint: true; readonly defaultInInsert: true; readonly enums: true; + readonly forShare: true; + readonly forUpdate: true; + readonly insertOnConflictSkip: true; + readonly insertOnConflictWithoutTarget: true; readonly lateral: true; + readonly lockNowait: true; + readonly lockOf: true; + readonly lockSkipLocked: true; readonly returning: true; readonly scalarList: true; }; }; readonly extensions: {}; readonly meta: {}; - readonly valueObjects: { - readonly Address: { - readonly fields: { - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly zip: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - }; - }; readonly profileHash: ProfileHash; }; diff --git a/test/integration/test/value-objects/fixtures/generated/sql-contract.json b/test/integration/test/value-objects/fixtures/generated/sql-contract.json index 92a473b18cad..2ce5d0610f9b 100644 --- a/test/integration/test/value-objects/fixtures/generated/sql-contract.json +++ b/test/integration/test/value-objects/fixtures/generated/sql-contract.json @@ -16,7 +16,6 @@ "Shop": { "fields": { "id": { - "many": false, "nullable": false, "type": { "codecId": "pg/int4@1", @@ -24,15 +23,16 @@ } }, "location": { - "many": false, "nullable": false, "type": { + "entityKind": "valueObject", + "entityName": "Address", "kind": "valueObject", - "name": "Address" + "namespaceId": "public", + "plane": "storage" } }, "name": { - "many": false, "nullable": false, "type": { "codecId": "pg/text@1", @@ -40,11 +40,13 @@ } }, "notes": { - "many": false, "nullable": true, "type": { + "entityKind": "valueObject", + "entityName": "Address", "kind": "valueObject", - "name": "Address" + "namespaceId": "public", + "plane": "storage" } } }, @@ -68,36 +70,6 @@ "table": "shop" } } - }, - "valueObjects": { - "Address": { - "fields": { - "city": { - "many": false, - "nullable": false, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" - } - }, - "street": { - "many": false, - "nullable": false, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" - } - }, - "zip": { - "many": false, - "nullable": false, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" - } - } - } - } } } } @@ -108,6 +80,18 @@ "entries": { "table": { "shop": { + "checks": [ + { + "expression": "jsonb_path_match(\"location\", 'strict $.type() == \"object\" && exists($.keyvalue() ? (@.key == \"city\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"street\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"zip\" && (@.value.type() == \"string\"))) && !exists($.keyvalue() ? (@.key != \"city\" && @.key != \"street\" && @.key != \"zip\"))') IS TRUE", + "name": "shop_location_value_object_9799c974", + "prefix": "shop_location_value_object" + }, + { + "expression": "\"notes\" IS NULL OR jsonb_path_match(\"notes\", 'strict $.type() == \"null\" || ($.type() == \"object\" && exists($.keyvalue() ? (@.key == \"city\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"street\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"zip\" && (@.value.type() == \"string\"))) && !exists($.keyvalue() ? (@.key != \"city\" && @.key != \"street\" && @.key != \"zip\")))') IS TRUE", + "name": "shop_notes_value_object_5554921a", + "prefix": "shop_notes_value_object" + } + ], "columns": { "id": { "codecId": "pg/int4@1", @@ -139,17 +123,46 @@ }, "uniques": [] } + }, + "valueObject": { + "Address": { + "fields": { + "city": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "street": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "zip": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + } + } + } } }, "id": "public", "kind": "postgres-schema" } }, - "storageHash": "8334dda9271ee13d092ca25003aed6d6507dbad3e83de89f994a48661d16bbf4" + "storageHash": "4216574839577232bdfaee2c8202fc9044a7873412428368c9ee430266747fab" }, "capabilities": { "postgres": { "distinctOn": true, + "forKeyShare": true, + "forNoKeyUpdate": true, "jsonAgg": true, "lateral": true, "limit": true, @@ -157,9 +170,17 @@ "returning": true }, "sql": { + "checkConstraint": true, "defaultInInsert": true, "enums": true, + "forShare": true, + "forUpdate": true, + "insertOnConflictSkip": true, + "insertOnConflictWithoutTarget": true, "lateral": true, + "lockNowait": true, + "lockOf": true, + "lockSkipLocked": true, "returning": true, "scalarList": true } From f8f564e939aa8470b05e1064b1a1e3ebfe9b0c03 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 15:46:11 +0200 Subject: [PATCH 050/104] TML-3537: the prisma-8-demo initial migration creates the value object check on user.address and the discriminator check on task.type db migrate from an empty PGlite database and db verify --strict now pass on the whole chain. Signed-off-by: willbot Signed-off-by: Will Madden --- .../app/20260422T0720_initial/migration.json | 4 ++-- .../app/20260422T0720_initial/migration.ts | 13 ++++++++++--- .../migrations/app/20260422T0720_initial/ops.json | 4 ++-- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/examples/prisma-8-demo/migrations/app/20260422T0720_initial/migration.json b/examples/prisma-8-demo/migrations/app/20260422T0720_initial/migration.json index 21aa5ae9040c..f39fba49e8db 100644 --- a/examples/prisma-8-demo/migrations/app/20260422T0720_initial/migration.json +++ b/examples/prisma-8-demo/migrations/app/20260422T0720_initial/migration.json @@ -1,7 +1,7 @@ { "from": null, - "to": "58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b", + "to": "11d8695d1b8ea9c748834093ac4111ce68145827709363b7c748c2892be84146", "providedInvariants": [], "createdAt": "2026-06-19T15:33:14.992Z", - "migrationHash": "bde42f97a6cbf4ca06e4507692274c8e3ece1e1b0672f050bb78a6a2eaa0ff0d" + "migrationHash": "649ede9e1ba6844054d945bf044bd63eea000c88617340e1206717a7cfe3e90a" } diff --git a/examples/prisma-8-demo/migrations/app/20260422T0720_initial/migration.ts b/examples/prisma-8-demo/migrations/app/20260422T0720_initial/migration.ts index 5f113150356b..c76279819fad 100644 --- a/examples/prisma-8-demo/migrations/app/20260422T0720_initial/migration.ts +++ b/examples/prisma-8-demo/migrations/app/20260422T0720_initial/migration.ts @@ -10,8 +10,8 @@ import { rawSql, unique, } from '@prisma/orm-postgres/target/migration'; -import type { Contract as End } from '../../snapshots/58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b/contract'; -import endContract from '../../snapshots/58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b/contract.json' with { +import type { Contract as End } from '../../snapshots/11d8695d1b8ea9c748834093ac4111ce68145827709363b7c748c2892be84146/contract'; +import endContract from '../../snapshots/11d8695d1b8ea9c748834093ac4111ce68145827709363b7c748c2892be84146/contract.json' with { type: 'json', }; @@ -86,6 +86,10 @@ export default class M extends Migration { ], constraints: [ primaryKey(['id']), + checkExpression( + 'user_address_value_object_69432dc1', + '"address" IS NULL OR jsonb_path_match("address", \'strict $.type() == "null" || ($.type() == "object" && exists($.keyvalue() ? (@.key == "city" && (@.value.type() == "string"))) && exists($.keyvalue() ? (@.key == "country" && (@.value.type() == "string"))) && exists($.keyvalue() ? (@.key == "street" && (@.value.type() == "string"))) && !exists($.keyvalue() ? (@.key == "zip" && !(@.value.type() == "null" || (@.value.type() == "string")))) && !exists($.keyvalue() ? (@.key != "city" && @.key != "country" && @.key != "street" && @.key != "zip")))\') IS TRUE', + ), checkExpression('user_kind_check_836d43ef', "\"kind\" IN ('admin', 'user')"), ], }), @@ -117,7 +121,10 @@ export default class M extends Migration { col('type', 'text', { notNull: true }), col('userId', 'uuid', { notNull: true }), ], - constraints: [primaryKey(['id'])], + constraints: [ + primaryKey(['id']), + checkExpression('task_type_check_cda3f5e7', "\"type\" IN ('bug', 'feature')"), + ], }), this.createTable({ schema: 'public', diff --git a/examples/prisma-8-demo/migrations/app/20260422T0720_initial/ops.json b/examples/prisma-8-demo/migrations/app/20260422T0720_initial/ops.json index 3843b04f54cb..1e915303a888 100644 --- a/examples/prisma-8-demo/migrations/app/20260422T0720_initial/ops.json +++ b/examples/prisma-8-demo/migrations/app/20260422T0720_initial/ops.json @@ -190,7 +190,7 @@ "execute": [ { "description": "create table \"user\"", - "sql": "CREATE TABLE \"public\".\"user\" (\n \"address\" jsonb,\n \"createdAt\" timestamptz DEFAULT (now()) NOT NULL,\n \"displayName\" text NOT NULL,\n \"email\" text NOT NULL,\n \"id\" uuid NOT NULL,\n \"kind\" text NOT NULL,\n PRIMARY KEY (\"id\"),\n CONSTRAINT \"user_kind_check_836d43ef\" CHECK (\"kind\" IN ('admin', 'user'))\n)", + "sql": "CREATE TABLE \"public\".\"user\" (\n \"address\" jsonb,\n \"createdAt\" timestamptz DEFAULT (now()) NOT NULL,\n \"displayName\" text NOT NULL,\n \"email\" text NOT NULL,\n \"id\" uuid NOT NULL,\n \"kind\" text NOT NULL,\n PRIMARY KEY (\"id\"),\n CONSTRAINT \"user_address_value_object_69432dc1\" CHECK (\"address\" IS NULL OR jsonb_path_match(\"address\", 'strict $.type() == \"null\" || ($.type() == \"object\" && exists($.keyvalue() ? (@.key == \"city\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"country\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"street\" && (@.value.type() == \"string\"))) && !exists($.keyvalue() ? (@.key == \"zip\" && !(@.value.type() == \"null\" || (@.value.type() == \"string\")))) && !exists($.keyvalue() ? (@.key != \"city\" && @.key != \"country\" && @.key != \"street\" && @.key != \"zip\")))') IS TRUE),\n CONSTRAINT \"user_kind_check_836d43ef\" CHECK (\"kind\" IN ('admin', 'user'))\n)", "params": [] } ], @@ -276,7 +276,7 @@ "execute": [ { "description": "create table \"task\"", - "sql": "CREATE TABLE \"public\".\"task\" (\n \"createdAt\" timestamptz DEFAULT (now()) NOT NULL,\n \"description\" text,\n \"id\" uuid NOT NULL,\n \"status\" text DEFAULT 'open' NOT NULL,\n \"title\" text NOT NULL,\n \"type\" text NOT NULL,\n \"userId\" uuid NOT NULL,\n PRIMARY KEY (\"id\")\n)", + "sql": "CREATE TABLE \"public\".\"task\" (\n \"createdAt\" timestamptz DEFAULT (now()) NOT NULL,\n \"description\" text,\n \"id\" uuid NOT NULL,\n \"status\" text DEFAULT 'open' NOT NULL,\n \"title\" text NOT NULL,\n \"type\" text NOT NULL,\n \"userId\" uuid NOT NULL,\n PRIMARY KEY (\"id\"),\n CONSTRAINT \"task_type_check_cda3f5e7\" CHECK (\"type\" IN ('bug', 'feature'))\n)", "params": [] } ], From 9d25b3a9084ce786b353df3cc04ca0ab87767b51 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 15:58:40 +0200 Subject: [PATCH 051/104] TML-3537: a SQL storage value object read from JSON fills many as false, so it equals the one its source builds Signed-off-by: willbot Signed-off-by: Will Madden --- .../contract/src/ir/storage-value-object.ts | 11 +++++++++- .../test/storage-value-object.test.ts | 22 ++++++++++++++----- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/packages/2-sql-family/1-core/contract/src/ir/storage-value-object.ts b/packages/2-sql-family/1-core/contract/src/ir/storage-value-object.ts index 4fbd23fd699e..05bb73a16f64 100644 --- a/packages/2-sql-family/1-core/contract/src/ir/storage-value-object.ts +++ b/packages/2-sql-family/1-core/contract/src/ir/storage-value-object.ts @@ -10,6 +10,8 @@ export interface StorageValueObjectInput { /** * SQL Contract IR node for a value object in a namespace's `valueObject` map (`SqlNamespace.entries.valueObject`): the keys every row holds in a column that stores it (ADR 278). A domain field refers to it by its storage coordinate. * + * A field that leaves out `many` holds `many: false`, as the authoring builders write it, so a contract read from JSON equals the one built from its source. + * * The entry's name is not on the class: value objects are keyed by name in the parent namespace's `valueObject` map. */ export class StorageValueObject extends SqlNode { @@ -17,7 +19,14 @@ export class StorageValueObject extends SqlNode { constructor(input: StorageValueObjectInput) { super(); - this.fields = Object.freeze({ ...input.fields }); + this.fields = Object.freeze( + Object.fromEntries( + Object.entries(input.fields).map(([name, field]) => [ + name, + Object.freeze({ ...field, many: field.many ?? false }), + ]), + ), + ); freezeNode(this); } } diff --git a/packages/2-sql-family/1-core/contract/test/storage-value-object.test.ts b/packages/2-sql-family/1-core/contract/test/storage-value-object.test.ts index ed0b253767bf..b8bbd269ae0b 100644 --- a/packages/2-sql-family/1-core/contract/test/storage-value-object.test.ts +++ b/packages/2-sql-family/1-core/contract/test/storage-value-object.test.ts @@ -64,17 +64,27 @@ function contractWith( }); } +const addressWithMany = { + fields: { street: { ...text, many: false }, city: { ...text, many: false } }, +}; + describe('StorageValueObject', () => { - it('holds the fields it is given, frozen', () => { + it('holds the fields it is given, with many false where a field leaves it out, frozen', () => { const valueObject = new StorageValueObject(address); - expect({ fields: valueObject.fields, frozen: Object.isFrozen(valueObject) }).toEqual({ - fields: address.fields, - frozen: true, - }); + expect({ + fields: valueObject.fields, + frozen: Object.isFrozen(valueObject), + fieldFrozen: Object.isFrozen(valueObject.fields['street']), + }).toEqual({ fields: addressWithMany.fields, frozen: true, fieldFrozen: true }); + }); + + it('keeps the many a field gives', () => { + const tags = { ...text, many: { elementNullable: true } }; + expect(new StorageValueObject({ fields: { tags } }).fields).toEqual({ tags }); }); it('serializes to its fields only', () => { - expect(JSON.parse(JSON.stringify(new StorageValueObject(address)))).toEqual(address); + expect(JSON.parse(JSON.stringify(new StorageValueObject(address)))).toEqual(addressWithMany); }); it('is hydrated from the valueObject slot', () => { From 58657b03aaa5baeee3c97fddf7db11351c0538fd Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 15:58:48 +0200 Subject: [PATCH 052/104] TML-3537: a prepared polymorphic select types the discriminator as its variant values Signed-off-by: willbot Signed-off-by: Will Madden --- test/integration/test/prepared-polymorphism.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/test/prepared-polymorphism.test.ts b/test/integration/test/prepared-polymorphism.test.ts index 82aa95bfaac5..6a317e549e6a 100644 --- a/test/integration/test/prepared-polymorphism.test.ts +++ b/test/integration/test/prepared-polymorphism.test.ts @@ -32,7 +32,7 @@ it( .prepared.all(), ); expectTypeOf(all.query(runtime, { first: 1, second: 2 })).toEqualTypeOf< - AsyncIterableResult<{ id: number; title: string; type: string }> + AsyncIterableResult<{ id: number; title: string; type: 'bug' | 'epic' | 'feature' }> >(); expect(await all.query(runtime, { first: 1, second: 2 })).toEqual([ { id: 1, title: 'Bug', type: 'bug' }, From ec32042dcc832a70a3f1b8a0043d25079dc102da Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 16:13:38 +0200 Subject: [PATCH 053/104] TML-3537: an upgrade script moves the value objects of a project's contracts and snapshots into storage It rewrites every SQL and MongoDB contract, renames each snapshot directory to its new storage hash, and rewrites the migrations, refs, migration.ts and contract.d.ts files that name an old hash. On SQL it writes each discriminator's value set. It adds no derived check, since no migration created one; the next migration plan adds them. Signed-off-by: willbot Signed-off-by: Will Madden --- .../20260409T1030_migration/migration.json | 7 + .../app/20260409T1030_migration/migration.ts | 16 + .../app/20260409T1030_migration/ops.json | 101 ++ .../migration.json | 7 + .../migration.ts | 65 + .../20260626T1605_add_user_role_enum/ops.json | 171 +++ .../migration.json | 7 + .../migration.ts | 32 + .../20260626T1916_add_posts_indexes/ops.json | 210 +++ .../contract.d.ts | 441 ++++++ .../contract.json | 1 + .../contract.d.ts | 490 ++++++ .../contract.json | 1 + .../contract.d.ts | 479 ++++++ .../contract.json | 1 + .../fixtures/after/mongo/src/contract.d.ts | 490 ++++++ .../fixtures/after/mongo/src/contract.json | 583 +++++++ .../app/20260422T0720_initial/migration.json | 7 + .../app/20260422T0720_initial/migration.ts | 215 +++ .../app/20260422T0720_initial/ops.json | 810 ++++++++++ .../contract.d.ts | 1323 ++++++++++++++++ .../contract.json | 1 + .../20260409T1030_migration/migration.json | 7 + .../app/20260409T1030_migration/migration.ts | 16 + .../app/20260409T1030_migration/ops.json | 101 ++ .../migration.json | 7 + .../migration.ts | 65 + .../20260626T1605_add_user_role_enum/ops.json | 171 +++ .../migration.json | 7 + .../migration.ts | 32 + .../20260626T1916_add_posts_indexes/ops.json | 210 +++ .../contract.d.ts | 493 ++++++ .../contract.json | 1 + .../contract.d.ts | 504 +++++++ .../contract.json | 1 + .../contract.d.ts | 455 ++++++ .../contract.json | 1 + .../fixtures/before/mongo/src/contract.d.ts | 504 +++++++ .../fixtures/before/mongo/src/contract.json | 580 +++++++ .../app/20260422T0720_initial/migration.json | 7 + .../app/20260422T0720_initial/migration.ts | 215 +++ .../app/20260422T0720_initial/ops.json | 810 ++++++++++ .../contract.d.ts | 1337 +++++++++++++++++ .../contract.json | 1 + .../value-objects-in-storage/test-helpers.ts | 73 + .../value-objects-in-storage.test.ts | 248 +++ .../value-objects-in-storage.ts | 1123 ++++++++++++++ 47 files changed, 12427 insertions(+) create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260409T1030_migration/migration.json create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260409T1030_migration/migration.ts create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260409T1030_migration/ops.json create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260626T1605_add_user_role_enum/migration.json create mode 100755 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260626T1605_add_user_role_enum/migration.ts create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260626T1605_add_user_role_enum/ops.json create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260626T1916_add_posts_indexes/migration.json create mode 100755 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260626T1916_add_posts_indexes/migration.ts create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260626T1916_add_posts_indexes/ops.json create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/snapshots/396942f6c9ee5f946d7f67b397929bab327fe84dd8f7f80e87b8097f416fb901/contract.d.ts create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/snapshots/396942f6c9ee5f946d7f67b397929bab327fe84dd8f7f80e87b8097f416fb901/contract.json create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/snapshots/406b672b68a6b01f04c0d6f910f5fefa4ac16d48b26e6c91b43856cd42c8cc5d/contract.d.ts create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/snapshots/406b672b68a6b01f04c0d6f910f5fefa4ac16d48b26e6c91b43856cd42c8cc5d/contract.json create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/snapshots/95332b0bd6fbbfab4462d882c037a574c92e51897249c333ba681b12c4d603c9/contract.d.ts create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/snapshots/95332b0bd6fbbfab4462d882c037a574c92e51897249c333ba681b12c4d603c9/contract.json create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/src/contract.d.ts create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/src/contract.json create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/sql/migrations/app/20260422T0720_initial/migration.json create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/sql/migrations/app/20260422T0720_initial/migration.ts create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/sql/migrations/app/20260422T0720_initial/ops.json create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/sql/migrations/snapshots/1cb334a7b32d249d4fea0934dbbaa48c473b5946cd77ee1ce7442272479d6cf3/contract.d.ts create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/sql/migrations/snapshots/1cb334a7b32d249d4fea0934dbbaa48c473b5946cd77ee1ce7442272479d6cf3/contract.json create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260409T1030_migration/migration.json create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260409T1030_migration/migration.ts create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260409T1030_migration/ops.json create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260626T1605_add_user_role_enum/migration.json create mode 100755 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260626T1605_add_user_role_enum/migration.ts create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260626T1605_add_user_role_enum/ops.json create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260626T1916_add_posts_indexes/migration.json create mode 100755 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260626T1916_add_posts_indexes/migration.ts create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260626T1916_add_posts_indexes/ops.json create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/snapshots/62671fbf016c515d808ac613743fddbf3db80384c1997251eac9d6f5fc063590/contract.d.ts create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/snapshots/62671fbf016c515d808ac613743fddbf3db80384c1997251eac9d6f5fc063590/contract.json create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/snapshots/d264cabd6a02546054a776ed3c04438b9a22989f406c49f5590f7f2101156eb0/contract.d.ts create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/snapshots/d264cabd6a02546054a776ed3c04438b9a22989f406c49f5590f7f2101156eb0/contract.json create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/snapshots/da1339e341177f79b37f765f08200844d7cb4d59d26fe27fe4d95b0112b0c2cd/contract.d.ts create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/snapshots/da1339e341177f79b37f765f08200844d7cb4d59d26fe27fe4d95b0112b0c2cd/contract.json create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/src/contract.d.ts create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/src/contract.json create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/sql/migrations/app/20260422T0720_initial/migration.json create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/sql/migrations/app/20260422T0720_initial/migration.ts create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/sql/migrations/app/20260422T0720_initial/ops.json create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/sql/migrations/snapshots/58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b/contract.d.ts create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/sql/migrations/snapshots/58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b/contract.json create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/test-helpers.ts create mode 100644 test/integration/test/upgrade-instructions/value-objects-in-storage/value-objects-in-storage.test.ts create mode 100644 upgrade-instructions/pending/value-objects-in-storage/app/scripts/value-objects-in-storage/value-objects-in-storage.ts diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260409T1030_migration/migration.json b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260409T1030_migration/migration.json new file mode 100644 index 000000000000..eb148543a554 --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260409T1030_migration/migration.json @@ -0,0 +1,7 @@ +{ + "from": null, + "to": "396942f6c9ee5f946d7f67b397929bab327fe84dd8f7f80e87b8097f416fb901", + "providedInvariants": [], + "createdAt": "2026-04-09T10:30:13.948Z", + "migrationHash": "a4dbf4e008b176a2fed392de87662f68cd4c2f537b60cf760e234048e6f55abe" +} diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260409T1030_migration/migration.ts b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260409T1030_migration/migration.ts new file mode 100644 index 000000000000..24f8a81660b0 --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260409T1030_migration/migration.ts @@ -0,0 +1,16 @@ +import { createIndex, Migration, MigrationCLI } from '@prisma/orm-mongo/target/migration'; +import type { Contract as End } from '../../snapshots/396942f6c9ee5f946d7f67b397929bab327fe84dd8f7f80e87b8097f416fb901/contract'; +import endContract from '../../snapshots/396942f6c9ee5f946d7f67b397929bab327fe84dd8f7f80e87b8097f416fb901/contract.json' with { + type: 'json', +}; + +class InitialMigration extends Migration { + override readonly endContractJson = endContract; + + override get operations() { + return [createIndex('users', [{ field: 'email', direction: 1 }], { unique: true })]; + } +} + +export default InitialMigration; +MigrationCLI.run(import.meta.url, InitialMigration); diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260409T1030_migration/ops.json b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260409T1030_migration/ops.json new file mode 100644 index 000000000000..533b620b26a8 --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260409T1030_migration/ops.json @@ -0,0 +1,101 @@ +[ + { + "id": "index.users.create(email:1)", + "label": "Create index on users (email:1)", + "operationClass": "additive", + "precheck": [ + { + "description": "index does not already exist on users", + "source": { + "kind": "listIndexes", + "collection": "users" + }, + "filter": { + "kind": "field", + "field": "key", + "op": "$eq", + "value": { + "email": 1 + } + }, + "expect": "notExists", + "object": "an index on { email: 1 } in \"users\"" + } + ], + "execute": [ + { + "description": "create index on users", + "command": { + "kind": "createIndex", + "collection": "users", + "keys": [ + { + "field": "email", + "direction": 1 + } + ], + "unique": true, + "name": "email_1" + } + } + ], + "postcheck": [ + { + "description": "index exists on users", + "source": { + "kind": "listIndexes", + "collection": "users" + }, + "filter": { + "kind": "and", + "exprs": [ + { + "kind": "field", + "field": "key", + "op": "$eq", + "value": { + "email": 1 + } + }, + { + "kind": "field", + "field": "unique", + "op": "$eq", + "value": true + }, + { + "kind": "not", + "expr": { + "kind": "field", + "field": "sparse", + "op": "$eq", + "value": true + } + }, + { + "kind": "exists", + "field": "expireAfterSeconds", + "exists": false + }, + { + "kind": "exists", + "field": "partialFilterExpression", + "exists": false + }, + { + "kind": "exists", + "field": "wildcardProjection", + "exists": false + }, + { + "kind": "exists", + "field": "collation", + "exists": false + } + ] + }, + "expect": "exists" + } + ] + } +] diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260626T1605_add_user_role_enum/migration.json b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260626T1605_add_user_role_enum/migration.json new file mode 100644 index 000000000000..8ee13dc3e337 --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260626T1605_add_user_role_enum/migration.json @@ -0,0 +1,7 @@ +{ + "from": "396942f6c9ee5f946d7f67b397929bab327fe84dd8f7f80e87b8097f416fb901", + "to": "95332b0bd6fbbfab4462d882c037a574c92e51897249c333ba681b12c4d603c9", + "providedInvariants": [], + "createdAt": "2026-06-26T16:05:45.955Z", + "migrationHash": "08ac0867c606d262ac4dc80b3ad7f1f0afc8418bd72fe957c13c9c549bfc4651" +} diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260626T1605_add_user_role_enum/migration.ts b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260626T1605_add_user_role_enum/migration.ts new file mode 100755 index 000000000000..9284d486566d --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260626T1605_add_user_role_enum/migration.ts @@ -0,0 +1,65 @@ +#!/usr/bin/env -S node +import { collMod, Migration, MigrationCLI } from '@prisma/orm-mongo/target/migration'; +import type { Contract as End } from '../../snapshots/95332b0bd6fbbfab4462d882c037a574c92e51897249c333ba681b12c4d603c9/contract'; +import endContract from '../../snapshots/95332b0bd6fbbfab4462d882c037a574c92e51897249c333ba681b12c4d603c9/contract.json' with { + type: 'json', +}; +import type { Contract as Start } from '../../snapshots/396942f6c9ee5f946d7f67b397929bab327fe84dd8f7f80e87b8097f416fb901/contract'; +import startContract from '../../snapshots/396942f6c9ee5f946d7f67b397929bab327fe84dd8f7f80e87b8097f416fb901/contract.json' with { + type: 'json', +}; + +class M extends Migration { + override readonly startContractJson = startContract; + override readonly endContractJson = endContract; + + override get operations() { + return [ + collMod( + 'users', + { + validator: { + $jsonSchema: { + additionalProperties: false, + bsonType: 'object', + properties: { + _id: { bsonType: 'objectId' }, + address: { + oneOf: [ + { bsonType: 'null' }, + { + additionalProperties: false, + bsonType: 'object', + properties: { + city: { bsonType: 'string' }, + country: { bsonType: 'string' }, + street: { bsonType: 'string' }, + zip: { bsonType: ['null', 'string'] }, + }, + required: ['city', 'country', 'street'], + }, + ], + }, + bio: { bsonType: ['null', 'string'] }, + email: { bsonType: 'string' }, + name: { bsonType: 'string' }, + role: { bsonType: 'string', enum: ['admin', 'author', 'reader'] }, + }, + required: ['_id', 'email', 'name', 'role'], + }, + }, + validationLevel: 'strict', + validationAction: 'error', + }, + { + id: 'validator.users.update', + label: 'Update validator on users', + operationClass: 'destructive', + }, + ), + ]; + } +} + +export default M; +MigrationCLI.run(import.meta.url, M); diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260626T1605_add_user_role_enum/ops.json b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260626T1605_add_user_role_enum/ops.json new file mode 100644 index 000000000000..11605cf4708e --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260626T1605_add_user_role_enum/ops.json @@ -0,0 +1,171 @@ +[ + { + "id": "validator.users.update", + "label": "Update validator on users", + "operationClass": "destructive", + "precheck": [ + { + "description": "collection users exists", + "source": { + "kind": "listCollections" + }, + "filter": { + "kind": "field", + "field": "name", + "op": "$eq", + "value": "users" + }, + "expect": "exists", + "object": "collection \"users\"" + } + ], + "execute": [ + { + "description": "modify users", + "command": { + "kind": "collMod", + "collection": "users", + "validator": { + "$jsonSchema": { + "additionalProperties": false, + "bsonType": "object", + "properties": { + "_id": { + "bsonType": "objectId" + }, + "address": { + "oneOf": [ + { + "bsonType": "null" + }, + { + "additionalProperties": false, + "bsonType": "object", + "properties": { + "city": { + "bsonType": "string" + }, + "country": { + "bsonType": "string" + }, + "street": { + "bsonType": "string" + }, + "zip": { + "bsonType": ["null", "string"] + } + }, + "required": ["city", "country", "street"] + } + ] + }, + "bio": { + "bsonType": ["null", "string"] + }, + "email": { + "bsonType": "string" + }, + "name": { + "bsonType": "string" + }, + "role": { + "bsonType": "string", + "enum": ["admin", "author", "reader"] + } + }, + "required": ["_id", "email", "name", "role"] + } + }, + "validationLevel": "strict", + "validationAction": "error" + } + } + ], + "postcheck": [ + { + "description": "validator applied on users", + "source": { + "kind": "listCollections" + }, + "filter": { + "kind": "and", + "exprs": [ + { + "kind": "field", + "field": "name", + "op": "$eq", + "value": "users" + }, + { + "kind": "field", + "field": "options.validationLevel", + "op": "$eq", + "value": "strict" + }, + { + "kind": "field", + "field": "options.validationAction", + "op": "$eq", + "value": "error" + }, + { + "kind": "field", + "field": "options.validator.$jsonSchema", + "op": "$eq", + "value": { + "additionalProperties": false, + "bsonType": "object", + "properties": { + "_id": { + "bsonType": "objectId" + }, + "address": { + "oneOf": [ + { + "bsonType": "null" + }, + { + "additionalProperties": false, + "bsonType": "object", + "properties": { + "city": { + "bsonType": "string" + }, + "country": { + "bsonType": "string" + }, + "street": { + "bsonType": "string" + }, + "zip": { + "bsonType": ["null", "string"] + } + }, + "required": ["city", "country", "street"] + } + ] + }, + "bio": { + "bsonType": ["null", "string"] + }, + "email": { + "bsonType": "string" + }, + "name": { + "bsonType": "string" + }, + "role": { + "bsonType": "string", + "enum": ["admin", "author", "reader"] + } + }, + "required": ["_id", "email", "name", "role"] + } + } + ] + }, + "expect": "exists" + } + ] + } +] diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260626T1916_add_posts_indexes/migration.json b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260626T1916_add_posts_indexes/migration.json new file mode 100644 index 000000000000..76235940857c --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260626T1916_add_posts_indexes/migration.json @@ -0,0 +1,7 @@ +{ + "from": "95332b0bd6fbbfab4462d882c037a574c92e51897249c333ba681b12c4d603c9", + "to": "406b672b68a6b01f04c0d6f910f5fefa4ac16d48b26e6c91b43856cd42c8cc5d", + "providedInvariants": [], + "createdAt": "2026-06-26T19:16:47.657Z", + "migrationHash": "a77e60df92e1d3ee6a5a000904e3aa1869a382d602279a64bce9038c17c0475d" +} diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260626T1916_add_posts_indexes/migration.ts b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260626T1916_add_posts_indexes/migration.ts new file mode 100755 index 000000000000..a5488460b751 --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260626T1916_add_posts_indexes/migration.ts @@ -0,0 +1,32 @@ +#!/usr/bin/env -S node +import { createIndex, Migration, MigrationCLI } from '@prisma/orm-mongo/target/migration'; +import type { Contract as Start } from '../../snapshots/95332b0bd6fbbfab4462d882c037a574c92e51897249c333ba681b12c4d603c9/contract'; +import startContract from '../../snapshots/95332b0bd6fbbfab4462d882c037a574c92e51897249c333ba681b12c4d603c9/contract.json' with { + type: 'json', +}; +import type { Contract as End } from '../../snapshots/406b672b68a6b01f04c0d6f910f5fefa4ac16d48b26e6c91b43856cd42c8cc5d/contract'; +import endContract from '../../snapshots/406b672b68a6b01f04c0d6f910f5fefa4ac16d48b26e6c91b43856cd42c8cc5d/contract.json' with { + type: 'json', +}; + +class M extends Migration { + override readonly startContractJson = startContract; + override readonly endContractJson = endContract; + + override get operations() { + return [ + createIndex('posts', [{ direction: 1, field: 'authorId' }], {}), + createIndex( + 'posts', + [ + { direction: -1, field: 'createdAt' }, + { direction: 1, field: 'authorId' }, + ], + {}, + ), + ]; + } +} + +export default M; +MigrationCLI.run(import.meta.url, M); diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260626T1916_add_posts_indexes/ops.json b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260626T1916_add_posts_indexes/ops.json new file mode 100644 index 000000000000..d9b60da89298 --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/app/20260626T1916_add_posts_indexes/ops.json @@ -0,0 +1,210 @@ +[ + { + "id": "index.posts.create(authorId:1)", + "label": "Create index on posts (authorId:1)", + "operationClass": "additive", + "precheck": [ + { + "description": "index does not already exist on posts", + "source": { + "kind": "listIndexes", + "collection": "posts" + }, + "filter": { + "kind": "field", + "field": "key", + "op": "$eq", + "value": { + "authorId": 1 + } + }, + "expect": "notExists", + "object": "an index on { authorId: 1 } in \"posts\"" + } + ], + "execute": [ + { + "description": "create index on posts", + "command": { + "kind": "createIndex", + "collection": "posts", + "keys": [ + { + "direction": 1, + "field": "authorId" + } + ], + "name": "authorId_1" + } + } + ], + "postcheck": [ + { + "description": "index exists on posts", + "source": { + "kind": "listIndexes", + "collection": "posts" + }, + "filter": { + "kind": "and", + "exprs": [ + { + "kind": "field", + "field": "key", + "op": "$eq", + "value": { + "authorId": 1 + } + }, + { + "kind": "not", + "expr": { + "kind": "field", + "field": "unique", + "op": "$eq", + "value": true + } + }, + { + "kind": "not", + "expr": { + "kind": "field", + "field": "sparse", + "op": "$eq", + "value": true + } + }, + { + "kind": "exists", + "field": "expireAfterSeconds", + "exists": false + }, + { + "kind": "exists", + "field": "partialFilterExpression", + "exists": false + }, + { + "kind": "exists", + "field": "wildcardProjection", + "exists": false + }, + { + "kind": "exists", + "field": "collation", + "exists": false + } + ] + }, + "expect": "exists" + } + ] + }, + { + "id": "index.posts.create(createdAt:-1,authorId:1)", + "label": "Create index on posts (createdAt:-1, authorId:1)", + "operationClass": "additive", + "precheck": [ + { + "description": "index does not already exist on posts", + "source": { + "kind": "listIndexes", + "collection": "posts" + }, + "filter": { + "kind": "field", + "field": "key", + "op": "$eq", + "value": { + "createdAt": -1, + "authorId": 1 + } + }, + "expect": "notExists", + "object": "an index on { createdAt: -1, authorId: 1 } in \"posts\"" + } + ], + "execute": [ + { + "description": "create index on posts", + "command": { + "kind": "createIndex", + "collection": "posts", + "keys": [ + { + "direction": -1, + "field": "createdAt" + }, + { + "direction": 1, + "field": "authorId" + } + ], + "name": "createdAt_-1_authorId_1" + } + } + ], + "postcheck": [ + { + "description": "index exists on posts", + "source": { + "kind": "listIndexes", + "collection": "posts" + }, + "filter": { + "kind": "and", + "exprs": [ + { + "kind": "field", + "field": "key", + "op": "$eq", + "value": { + "createdAt": -1, + "authorId": 1 + } + }, + { + "kind": "not", + "expr": { + "kind": "field", + "field": "unique", + "op": "$eq", + "value": true + } + }, + { + "kind": "not", + "expr": { + "kind": "field", + "field": "sparse", + "op": "$eq", + "value": true + } + }, + { + "kind": "exists", + "field": "expireAfterSeconds", + "exists": false + }, + { + "kind": "exists", + "field": "partialFilterExpression", + "exists": false + }, + { + "kind": "exists", + "field": "wildcardProjection", + "exists": false + }, + { + "kind": "exists", + "field": "collation", + "exists": false + } + ] + }, + "expect": "exists" + } + ] + } +] diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/snapshots/396942f6c9ee5f946d7f67b397929bab327fe84dd8f7f80e87b8097f416fb901/contract.d.ts b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/snapshots/396942f6c9ee5f946d7f67b397929bab327fe84dd8f7f80e87b8097f416fb901/contract.d.ts new file mode 100644 index 000000000000..450aa81593d9 --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/snapshots/396942f6c9ee5f946d7f67b397929bab327fe84dd8f7f80e87b8097f416fb901/contract.d.ts @@ -0,0 +1,441 @@ +// ⚠️ GENERATED FILE - DO NOT EDIT +// This file is automatically generated by 'prisma contract emit'. +// To regenerate, run: prisma contract emit +import type { + CodecTypes as MongoCodecTypes, + ParameterizedOutputTypesForTypeParams as MongoParameterizedOutputTypes, +} from '@prisma/orm-mongo/target/codec-types'; + +import type { + MongoCollection, + MongoContractWithTypeMaps, + MongoTypeMaps, + RelationKeys, +} from '@prisma/orm-mongo/family-contract'; +import type { + Contract as ContractType, + ExecutionHashBase, + FieldTypesFromDomain, + NamespaceId, + ProfileHashBase, + StorageHashBase, + ValueObjectTypesFromStorage, +} from '@prisma/orm-mongo/contract/types'; + +export type StorageHash = + StorageHashBase<'396942f6c9ee5f946d7f67b397929bab327fe84dd8f7f80e87b8097f416fb901'>; +export type ExecutionHash = ExecutionHashBase; +export type ProfileHash = + ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; + +export type CodecTypes = MongoCodecTypes; + +type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output' +>; +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input' +>; +export type AddressOutput = ValueObjectOutputTypes['__unbound__']['Address']; +export type AddressInput = ValueObjectInputTypes['__unbound__']['Address']; +type FieldTypeOverrides = {}; +export type FieldOutputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output', + ContractBase['storage']['namespaces'] +>; +export type FieldInputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input', + ContractBase['storage']['namespaces'] +>; + +export namespace Models { + export type unbound_Article = { + authorId: FieldOutputTypes['__unbound__']['Post']['authorId']; + content: FieldOutputTypes['__unbound__']['Post']['content']; + createdAt: FieldOutputTypes['__unbound__']['Post']['createdAt']; + id: FieldOutputTypes['__unbound__']['Post']['id']; + kind: 'article'; + title: FieldOutputTypes['__unbound__']['Post']['title']; + summary: FieldOutputTypes['__unbound__']['Article']['summary']; + author: unbound_User; + readonly [RelationKeys]?: 'author'; + }; + export type unbound_Post = { + authorId: FieldOutputTypes['__unbound__']['Post']['authorId']; + content: FieldOutputTypes['__unbound__']['Post']['content']; + createdAt: FieldOutputTypes['__unbound__']['Post']['createdAt']; + id: FieldOutputTypes['__unbound__']['Post']['id']; + kind: 'article' | 'tutorial'; + title: FieldOutputTypes['__unbound__']['Post']['title']; + author: unbound_User; + readonly [RelationKeys]?: 'author'; + }; + export type unbound_Tutorial = { + authorId: FieldOutputTypes['__unbound__']['Post']['authorId']; + content: FieldOutputTypes['__unbound__']['Post']['content']; + createdAt: FieldOutputTypes['__unbound__']['Post']['createdAt']; + id: FieldOutputTypes['__unbound__']['Post']['id']; + kind: 'tutorial'; + title: FieldOutputTypes['__unbound__']['Post']['title']; + difficulty: FieldOutputTypes['__unbound__']['Tutorial']['difficulty']; + duration: FieldOutputTypes['__unbound__']['Tutorial']['duration']; + author: unbound_User; + readonly [RelationKeys]?: 'author'; + }; + export type unbound_User = { + address: FieldOutputTypes['__unbound__']['User']['address']; + bio: FieldOutputTypes['__unbound__']['User']['bio']; + email: FieldOutputTypes['__unbound__']['User']['email']; + id: FieldOutputTypes['__unbound__']['User']['id']; + name: FieldOutputTypes['__unbound__']['User']['name']; + posts: unbound_AnyPost[]; + readonly [RelationKeys]?: 'posts'; + }; + export type unbound_AnyPost = unbound_Article | unbound_Tutorial; +} + +export declare const models: { + __unbound__: { + Article: Models.unbound_Article; + Post: Models.unbound_Post; + Tutorial: Models.unbound_Tutorial; + User: Models.unbound_User; + AnyPost: Models.unbound_AnyPost; + }; +}; + +export type TypeMaps = MongoTypeMaps; + +type ContractBase = Omit< + ContractType<{ + readonly namespaces: { + readonly __unbound__: { + readonly id: '__unbound__'; + readonly kind: 'mongo-database'; + readonly entries: { + readonly collection: { + readonly posts: { + readonly indexes: readonly [ + { + readonly keys: readonly [{ readonly direction: 1; readonly field: 'summary' }]; + readonly kind: 'mongo-index'; + readonly partialFilterExpression: { readonly kind: 'article' }; + readonly unique: true; + }, + ]; + readonly kind: 'mongo-collection'; + readonly validator: { + readonly jsonSchema: { + readonly bsonType: 'object'; + readonly oneOf: readonly [ + { + readonly additionalProperties: false; + readonly properties: { + readonly _id: { readonly bsonType: 'objectId' }; + readonly authorId: { readonly bsonType: 'objectId' }; + readonly content: { readonly bsonType: 'string' }; + readonly createdAt: { readonly bsonType: 'date' }; + readonly kind: { readonly enum: readonly ['article'] }; + readonly summary: { readonly bsonType: 'string' }; + readonly title: { readonly bsonType: 'string' }; + }; + readonly required: readonly ['kind', 'summary']; + }, + { + readonly additionalProperties: false; + readonly properties: { + readonly _id: { readonly bsonType: 'objectId' }; + readonly authorId: { readonly bsonType: 'objectId' }; + readonly content: { readonly bsonType: 'string' }; + readonly createdAt: { readonly bsonType: 'date' }; + readonly difficulty: { readonly bsonType: 'string' }; + readonly duration: { readonly bsonType: 'int' }; + readonly kind: { readonly enum: readonly ['tutorial'] }; + readonly title: { readonly bsonType: 'string' }; + }; + readonly required: readonly ['difficulty', 'duration', 'kind']; + }, + ]; + readonly properties: { + readonly _id: { readonly bsonType: 'objectId' }; + readonly authorId: { readonly bsonType: 'objectId' }; + readonly content: { readonly bsonType: 'string' }; + readonly createdAt: { readonly bsonType: 'date' }; + readonly kind: { readonly bsonType: 'string' }; + readonly title: { readonly bsonType: 'string' }; + }; + readonly required: readonly [ + '_id', + 'authorId', + 'content', + 'createdAt', + 'kind', + 'title', + ]; + }; + readonly kind: 'mongo-validator'; + readonly validationAction: 'error'; + readonly validationLevel: 'strict'; + }; + }; + readonly users: { + readonly kind: 'mongo-collection'; + readonly validator: { + readonly jsonSchema: { + readonly additionalProperties: false; + readonly bsonType: 'object'; + readonly properties: { + readonly _id: { readonly bsonType: 'objectId' }; + readonly address: { + readonly oneOf: readonly [ + { readonly bsonType: 'null' }, + { + readonly additionalProperties: false; + readonly bsonType: 'object'; + readonly properties: { + readonly city: { readonly bsonType: 'string' }; + readonly country: { readonly bsonType: 'string' }; + readonly street: { readonly bsonType: 'string' }; + readonly zip: { readonly bsonType: readonly ['null', 'string'] }; + }; + readonly required: readonly ['city', 'country', 'street']; + }, + ]; + }; + readonly bio: { readonly bsonType: readonly ['null', 'string'] }; + readonly email: { readonly bsonType: 'string' }; + readonly name: { readonly bsonType: 'string' }; + }; + readonly required: readonly ['_id', 'email', 'name']; + }; + readonly kind: 'mongo-validator'; + readonly validationAction: 'error'; + readonly validationLevel: 'strict'; + }; + }; + }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + }; + }; + }; + }; + readonly storageHash: StorageHash; + }>, + 'roots' | 'domain' +> & { + readonly target: 'mongo'; + readonly targetFamily: 'mongo'; + readonly roots: { + readonly posts: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'Post' }; + readonly users: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'User' }; + }; + readonly domain: { + readonly namespaces: { + readonly __unbound__: { + readonly models: { + readonly Article: { + readonly fields: { + readonly summary: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly collection: 'posts'; + readonly fields: { readonly summary: { readonly field: 'summary' } }; + }; + readonly base: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Post'; + }; + }; + readonly Post: { + readonly fields: { + readonly authorId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; + }; + readonly content: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly createdAt: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; + }; + readonly kind: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly title: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + readonly relations: { + readonly author: { + readonly to: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'User'; + }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['authorId']; + readonly targetFields: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly collection: 'posts'; + readonly fields: { + readonly authorId: { readonly field: 'authorId' }; + readonly content: { readonly field: 'content' }; + readonly createdAt: { readonly field: 'createdAt' }; + readonly id: { readonly field: '_id' }; + readonly kind: { readonly field: 'kind' }; + readonly title: { readonly field: 'title' }; + }; + }; + readonly discriminator: { readonly field: 'kind' }; + readonly variants: { + readonly article: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Article'; + }; + readonly tutorial: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Tutorial'; + }; + }; + }; + readonly Tutorial: { + readonly fields: { + readonly difficulty: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly duration: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly collection: 'posts'; + readonly fields: { + readonly difficulty: { readonly field: 'difficulty' }; + readonly duration: { readonly field: 'duration' }; + }; + }; + readonly base: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Post'; + }; + }; + readonly User: { + readonly fields: { + readonly address: { + readonly nullable: true; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; + }; + readonly bio: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; + }; + readonly name: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + readonly relations: { + readonly posts: { + readonly to: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Post'; + }; + readonly cardinality: '1:N'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['authorId']; + }; + }; + }; + readonly storage: { + readonly collection: 'users'; + readonly fields: { + readonly address: { readonly field: 'address' }; + readonly bio: { readonly field: 'bio' }; + readonly email: { readonly field: 'email' }; + readonly id: { readonly field: '_id' }; + readonly name: { readonly field: 'name' }; + }; + }; + }; + }; + }; + }; + }; + readonly capabilities: {}; + readonly extensions: {}; + readonly meta: {}; + readonly profileHash: ProfileHash; +}; + +export type Contract = MongoContractWithTypeMaps; diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/snapshots/396942f6c9ee5f946d7f67b397929bab327fe84dd8f7f80e87b8097f416fb901/contract.json b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/snapshots/396942f6c9ee5f946d7f67b397929bab327fe84dd8f7f80e87b8097f416fb901/contract.json new file mode 100644 index 000000000000..5699666ec9cd --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/snapshots/396942f6c9ee5f946d7f67b397929bab327fe84dd8f7f80e87b8097f416fb901/contract.json @@ -0,0 +1 @@ +{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{},"domain":{"namespaces":{"__unbound__":{"models":{"Article":{"base":{"model":"Post","namespace":"__unbound__"},"fields":{"summary":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"posts","fields":{"summary":{"field":"summary"}}}},"Post":{"discriminator":{"field":"kind"},"fields":{"authorId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"content":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"createdAt":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{"author":{"cardinality":"N:1","nullable":false,"on":{"localFields":["authorId"],"targetFields":["id"]},"to":{"model":"User","namespace":"__unbound__"}}},"storage":{"collection":"posts","fields":{"authorId":{"field":"authorId"},"content":{"field":"content"},"createdAt":{"field":"createdAt"},"id":{"field":"_id"},"kind":{"field":"kind"},"title":{"field":"title"}}},"variants":{"article":{"model":"Article","namespace":"__unbound__"},"tutorial":{"model":"Tutorial","namespace":"__unbound__"}}},"Tutorial":{"base":{"model":"Post","namespace":"__unbound__"},"fields":{"difficulty":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"duration":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"posts","fields":{"difficulty":{"field":"difficulty"},"duration":{"field":"duration"}}}},"User":{"fields":{"address":{"nullable":true,"type":{"entityKind":"valueObject","entityName":"Address","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"bio":{"nullable":true,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["authorId"]},"to":{"model":"Post","namespace":"__unbound__"}}},"storage":{"collection":"users","fields":{"address":{"field":"address"},"bio":{"field":"bio"},"email":{"field":"email"},"id":{"field":"_id"},"name":{"field":"name"}}}}}}}},"extensions":{},"meta":{},"profileHash":"251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3","roots":{"posts":{"model":"Post","namespace":"__unbound__"},"users":{"model":"User","namespace":"__unbound__"}},"schemaVersion":"1","storage":{"namespaces":{"__unbound__":{"entries":{"collection":{"posts":{"indexes":[{"keys":[{"direction":1,"field":"summary"}],"kind":"mongo-index","partialFilterExpression":{"kind":"article"},"unique":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"bsonType":"object","oneOf":[{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"kind":{"enum":["article"]},"summary":{"bsonType":"string"},"title":{"bsonType":"string"}},"required":["kind","summary"]},{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"difficulty":{"bsonType":"string"},"duration":{"bsonType":"int"},"kind":{"enum":["tutorial"]},"title":{"bsonType":"string"}},"required":["difficulty","duration","kind"]}],"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"kind":{"bsonType":"string"},"title":{"bsonType":"string"}},"required":["_id","authorId","content","createdAt","kind","title"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"users":{"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"address":{"oneOf":[{"bsonType":"null"},{"additionalProperties":false,"bsonType":"object","properties":{"city":{"bsonType":"string"},"country":{"bsonType":"string"},"street":{"bsonType":"string"},"zip":{"bsonType":["null","string"]}},"required":["city","country","street"]}]},"bio":{"bsonType":["null","string"]},"email":{"bsonType":"string"},"name":{"bsonType":"string"}},"required":["_id","email","name"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}}},"valueObject":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}}}},"id":"__unbound__","kind":"mongo-database"}},"storageHash":"396942f6c9ee5f946d7f67b397929bab327fe84dd8f7f80e87b8097f416fb901"},"target":"mongo","targetFamily":"mongo"} diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/snapshots/406b672b68a6b01f04c0d6f910f5fefa4ac16d48b26e6c91b43856cd42c8cc5d/contract.d.ts b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/snapshots/406b672b68a6b01f04c0d6f910f5fefa4ac16d48b26e6c91b43856cd42c8cc5d/contract.d.ts new file mode 100644 index 000000000000..80c6ac7a1cf7 --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/snapshots/406b672b68a6b01f04c0d6f910f5fefa4ac16d48b26e6c91b43856cd42c8cc5d/contract.d.ts @@ -0,0 +1,490 @@ +// ⚠️ GENERATED FILE - DO NOT EDIT +// This file is automatically generated by 'prisma contract emit'. +// To regenerate, run: prisma contract emit +import type { + CodecTypes as MongoCodecTypes, + ParameterizedOutputTypesForTypeParams as MongoParameterizedOutputTypes, +} from '@prisma/orm-mongo/target/codec-types'; + +import type { + MongoCollection, + MongoContractWithTypeMaps, + MongoTypeMaps, + RelationKeys, +} from '@prisma/orm-mongo/family-contract'; +import type { + Contract as ContractType, + ExecutionHashBase, + FieldTypesFromDomain, + NamespaceId, + ProfileHashBase, + StorageHashBase, + ValueObjectTypesFromStorage, +} from '@prisma/orm-mongo/contract/types'; + +export type StorageHash = + StorageHashBase<'406b672b68a6b01f04c0d6f910f5fefa4ac16d48b26e6c91b43856cd42c8cc5d'>; +export type ExecutionHash = ExecutionHashBase; +export type ProfileHash = + ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; + +export type CodecTypes = MongoCodecTypes; + +type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output' +>; +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input' +>; +export type AddressOutput = ValueObjectOutputTypes['__unbound__']['Address']; +export type AddressInput = ValueObjectInputTypes['__unbound__']['Address']; +type FieldTypeOverrides = { + readonly __unbound__: { + readonly models: { + readonly User: { + readonly role: { + readonly output: 'admin' | 'author' | 'reader'; + readonly input: 'admin' | 'author' | 'reader'; + }; + }; + }; + }; +}; +export type FieldOutputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output', + ContractBase['storage']['namespaces'] +>; +export type FieldInputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input', + ContractBase['storage']['namespaces'] +>; + +export namespace Models { + export type unbound_Article = { + authorId: FieldOutputTypes['__unbound__']['Post']['authorId']; + content: FieldOutputTypes['__unbound__']['Post']['content']; + createdAt: FieldOutputTypes['__unbound__']['Post']['createdAt']; + id: FieldOutputTypes['__unbound__']['Post']['id']; + kind: 'article'; + title: FieldOutputTypes['__unbound__']['Post']['title']; + summary: FieldOutputTypes['__unbound__']['Article']['summary']; + author: unbound_User; + readonly [RelationKeys]?: 'author'; + }; + export type unbound_Post = { + authorId: FieldOutputTypes['__unbound__']['Post']['authorId']; + content: FieldOutputTypes['__unbound__']['Post']['content']; + createdAt: FieldOutputTypes['__unbound__']['Post']['createdAt']; + id: FieldOutputTypes['__unbound__']['Post']['id']; + kind: 'article' | 'tutorial'; + title: FieldOutputTypes['__unbound__']['Post']['title']; + author: unbound_User; + readonly [RelationKeys]?: 'author'; + }; + export type unbound_Tutorial = { + authorId: FieldOutputTypes['__unbound__']['Post']['authorId']; + content: FieldOutputTypes['__unbound__']['Post']['content']; + createdAt: FieldOutputTypes['__unbound__']['Post']['createdAt']; + id: FieldOutputTypes['__unbound__']['Post']['id']; + kind: 'tutorial'; + title: FieldOutputTypes['__unbound__']['Post']['title']; + difficulty: FieldOutputTypes['__unbound__']['Tutorial']['difficulty']; + duration: FieldOutputTypes['__unbound__']['Tutorial']['duration']; + author: unbound_User; + readonly [RelationKeys]?: 'author'; + }; + export type unbound_User = { + address: FieldOutputTypes['__unbound__']['User']['address']; + bio: FieldOutputTypes['__unbound__']['User']['bio']; + email: FieldOutputTypes['__unbound__']['User']['email']; + id: FieldOutputTypes['__unbound__']['User']['id']; + name: FieldOutputTypes['__unbound__']['User']['name']; + role: FieldOutputTypes['__unbound__']['User']['role']; + posts: unbound_AnyPost[]; + readonly [RelationKeys]?: 'posts'; + }; + export type unbound_AnyPost = unbound_Article | unbound_Tutorial; +} + +export declare const models: { + __unbound__: { + Article: Models.unbound_Article; + Post: Models.unbound_Post; + Tutorial: Models.unbound_Tutorial; + User: Models.unbound_User; + AnyPost: Models.unbound_AnyPost; + }; +}; + +export type TypeMaps = MongoTypeMaps; + +type ContractBase = Omit< + ContractType<{ + readonly namespaces: { + readonly __unbound__: { + readonly id: '__unbound__'; + readonly kind: 'mongo-database'; + readonly entries: { + readonly collection: { + readonly posts: { + readonly indexes: readonly [ + { + readonly keys: readonly [{ readonly direction: 1; readonly field: 'authorId' }]; + readonly kind: 'mongo-index'; + }, + { + readonly keys: readonly [ + { readonly direction: -1; readonly field: 'createdAt' }, + { readonly direction: 1; readonly field: 'authorId' }, + ]; + readonly kind: 'mongo-index'; + }, + { + readonly keys: readonly [{ readonly direction: 1; readonly field: 'summary' }]; + readonly kind: 'mongo-index'; + readonly partialFilterExpression: { readonly kind: 'article' }; + readonly unique: true; + }, + ]; + readonly kind: 'mongo-collection'; + readonly validator: { + readonly jsonSchema: { + readonly bsonType: 'object'; + readonly oneOf: readonly [ + { + readonly additionalProperties: false; + readonly properties: { + readonly _id: { readonly bsonType: 'objectId' }; + readonly authorId: { readonly bsonType: 'objectId' }; + readonly content: { readonly bsonType: 'string' }; + readonly createdAt: { readonly bsonType: 'date' }; + readonly kind: { readonly enum: readonly ['article'] }; + readonly summary: { readonly bsonType: 'string' }; + readonly title: { readonly bsonType: 'string' }; + }; + readonly required: readonly ['kind', 'summary']; + }, + { + readonly additionalProperties: false; + readonly properties: { + readonly _id: { readonly bsonType: 'objectId' }; + readonly authorId: { readonly bsonType: 'objectId' }; + readonly content: { readonly bsonType: 'string' }; + readonly createdAt: { readonly bsonType: 'date' }; + readonly difficulty: { readonly bsonType: 'string' }; + readonly duration: { readonly bsonType: 'int' }; + readonly kind: { readonly enum: readonly ['tutorial'] }; + readonly title: { readonly bsonType: 'string' }; + }; + readonly required: readonly ['difficulty', 'duration', 'kind']; + }, + ]; + readonly properties: { + readonly _id: { readonly bsonType: 'objectId' }; + readonly authorId: { readonly bsonType: 'objectId' }; + readonly content: { readonly bsonType: 'string' }; + readonly createdAt: { readonly bsonType: 'date' }; + readonly kind: { readonly bsonType: 'string' }; + readonly title: { readonly bsonType: 'string' }; + }; + readonly required: readonly [ + '_id', + 'authorId', + 'content', + 'createdAt', + 'kind', + 'title', + ]; + }; + readonly kind: 'mongo-validator'; + readonly validationAction: 'error'; + readonly validationLevel: 'strict'; + }; + }; + readonly users: { + readonly kind: 'mongo-collection'; + readonly validator: { + readonly jsonSchema: { + readonly additionalProperties: false; + readonly bsonType: 'object'; + readonly properties: { + readonly _id: { readonly bsonType: 'objectId' }; + readonly address: { + readonly oneOf: readonly [ + { readonly bsonType: 'null' }, + { + readonly additionalProperties: false; + readonly bsonType: 'object'; + readonly properties: { + readonly city: { readonly bsonType: 'string' }; + readonly country: { readonly bsonType: 'string' }; + readonly street: { readonly bsonType: 'string' }; + readonly zip: { readonly bsonType: readonly ['null', 'string'] }; + }; + readonly required: readonly ['city', 'country', 'street']; + }, + ]; + }; + readonly bio: { readonly bsonType: readonly ['null', 'string'] }; + readonly email: { readonly bsonType: 'string' }; + readonly name: { readonly bsonType: 'string' }; + readonly role: { + readonly bsonType: 'string'; + readonly enum: readonly ['admin', 'author', 'reader']; + }; + }; + readonly required: readonly ['_id', 'email', 'name', 'role']; + }; + readonly kind: 'mongo-validator'; + readonly validationAction: 'error'; + readonly validationLevel: 'strict'; + }; + }; + }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + }; + }; + }; + }; + readonly storageHash: StorageHash; + }>, + 'roots' | 'domain' +> & { + readonly target: 'mongo'; + readonly targetFamily: 'mongo'; + readonly roots: { + readonly posts: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'Post' }; + readonly users: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'User' }; + }; + readonly domain: { + readonly namespaces: { + readonly __unbound__: { + readonly models: { + readonly Article: { + readonly fields: { + readonly summary: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly collection: 'posts'; + readonly fields: { readonly summary: { readonly field: 'summary' } }; + }; + readonly base: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Post'; + }; + }; + readonly Post: { + readonly fields: { + readonly authorId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; + }; + readonly content: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly createdAt: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; + }; + readonly kind: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly title: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + readonly relations: { + readonly author: { + readonly to: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'User'; + }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['authorId']; + readonly targetFields: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly collection: 'posts'; + readonly fields: { + readonly authorId: { readonly field: 'authorId' }; + readonly content: { readonly field: 'content' }; + readonly createdAt: { readonly field: 'createdAt' }; + readonly id: { readonly field: '_id' }; + readonly kind: { readonly field: 'kind' }; + readonly title: { readonly field: 'title' }; + }; + }; + readonly discriminator: { readonly field: 'kind' }; + readonly variants: { + readonly article: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Article'; + }; + readonly tutorial: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Tutorial'; + }; + }; + }; + readonly Tutorial: { + readonly fields: { + readonly difficulty: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly duration: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly collection: 'posts'; + readonly fields: { + readonly difficulty: { readonly field: 'difficulty' }; + readonly duration: { readonly field: 'duration' }; + }; + }; + readonly base: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Post'; + }; + }; + readonly User: { + readonly fields: { + readonly address: { + readonly nullable: true; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; + }; + readonly bio: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; + }; + readonly name: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly role: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + readonly relations: { + readonly posts: { + readonly to: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Post'; + }; + readonly cardinality: '1:N'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['authorId']; + }; + }; + }; + readonly storage: { + readonly collection: 'users'; + readonly fields: { + readonly address: { readonly field: 'address' }; + readonly bio: { readonly field: 'bio' }; + readonly email: { readonly field: 'email' }; + readonly id: { readonly field: '_id' }; + readonly name: { readonly field: 'name' }; + readonly role: { readonly field: 'role' }; + }; + }; + }; + }; + readonly enum: { + readonly UserRole: { + readonly codecId: 'mongo/string@1'; + readonly members: readonly [ + { readonly name: 'Admin'; readonly value: 'admin' }, + { readonly name: 'Author'; readonly value: 'author' }, + { readonly name: 'Reader'; readonly value: 'reader' }, + ]; + }; + }; + readonly enumMemberTypes?: { + readonly UserRole: readonly [ + { readonly name: 'Admin'; readonly value: 'admin' }, + { readonly name: 'Author'; readonly value: 'author' }, + { readonly name: 'Reader'; readonly value: 'reader' }, + ]; + }; + }; + }; + }; + readonly capabilities: {}; + readonly extensions: {}; + readonly meta: {}; + readonly profileHash: ProfileHash; +}; + +export type Contract = MongoContractWithTypeMaps; diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/snapshots/406b672b68a6b01f04c0d6f910f5fefa4ac16d48b26e6c91b43856cd42c8cc5d/contract.json b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/snapshots/406b672b68a6b01f04c0d6f910f5fefa4ac16d48b26e6c91b43856cd42c8cc5d/contract.json new file mode 100644 index 000000000000..83ba3e77dc37 --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/snapshots/406b672b68a6b01f04c0d6f910f5fefa4ac16d48b26e6c91b43856cd42c8cc5d/contract.json @@ -0,0 +1 @@ +{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{},"domain":{"namespaces":{"__unbound__":{"enum":{"UserRole":{"codecId":"mongo/string@1","members":[{"name":"Admin","value":"admin"},{"name":"Author","value":"author"},{"name":"Reader","value":"reader"}]}},"models":{"Article":{"base":{"model":"Post","namespace":"__unbound__"},"fields":{"summary":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"posts","fields":{"summary":{"field":"summary"}}}},"Post":{"discriminator":{"field":"kind"},"fields":{"authorId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"content":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"createdAt":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{"author":{"cardinality":"N:1","nullable":false,"on":{"localFields":["authorId"],"targetFields":["id"]},"to":{"model":"User","namespace":"__unbound__"}}},"storage":{"collection":"posts","fields":{"authorId":{"field":"authorId"},"content":{"field":"content"},"createdAt":{"field":"createdAt"},"id":{"field":"_id"},"kind":{"field":"kind"},"title":{"field":"title"}}},"variants":{"article":{"model":"Article","namespace":"__unbound__"},"tutorial":{"model":"Tutorial","namespace":"__unbound__"}}},"Tutorial":{"base":{"model":"Post","namespace":"__unbound__"},"fields":{"difficulty":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"duration":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"posts","fields":{"difficulty":{"field":"difficulty"},"duration":{"field":"duration"}}}},"User":{"fields":{"address":{"nullable":true,"type":{"entityKind":"valueObject","entityName":"Address","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"bio":{"nullable":true,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"role":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"UserRole","namespaceId":"__unbound__","plane":"domain"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["authorId"]},"to":{"model":"Post","namespace":"__unbound__"}}},"storage":{"collection":"users","fields":{"address":{"field":"address"},"bio":{"field":"bio"},"email":{"field":"email"},"id":{"field":"_id"},"name":{"field":"name"},"role":{"field":"role"}}}}}}}},"extensions":{},"meta":{},"profileHash":"251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3","roots":{"posts":{"model":"Post","namespace":"__unbound__"},"users":{"model":"User","namespace":"__unbound__"}},"schemaVersion":"1","storage":{"namespaces":{"__unbound__":{"entries":{"collection":{"posts":{"indexes":[{"keys":[{"direction":1,"field":"authorId"}],"kind":"mongo-index"},{"keys":[{"direction":-1,"field":"createdAt"},{"direction":1,"field":"authorId"}],"kind":"mongo-index"},{"keys":[{"direction":1,"field":"summary"}],"kind":"mongo-index","partialFilterExpression":{"kind":"article"},"unique":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"bsonType":"object","oneOf":[{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"kind":{"enum":["article"]},"summary":{"bsonType":"string"},"title":{"bsonType":"string"}},"required":["kind","summary"]},{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"difficulty":{"bsonType":"string"},"duration":{"bsonType":"int"},"kind":{"enum":["tutorial"]},"title":{"bsonType":"string"}},"required":["difficulty","duration","kind"]}],"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"kind":{"bsonType":"string"},"title":{"bsonType":"string"}},"required":["_id","authorId","content","createdAt","kind","title"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"users":{"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"address":{"oneOf":[{"bsonType":"null"},{"additionalProperties":false,"bsonType":"object","properties":{"city":{"bsonType":"string"},"country":{"bsonType":"string"},"street":{"bsonType":"string"},"zip":{"bsonType":["null","string"]}},"required":["city","country","street"]}]},"bio":{"bsonType":["null","string"]},"email":{"bsonType":"string"},"name":{"bsonType":"string"},"role":{"bsonType":"string","enum":["admin","author","reader"]}},"required":["_id","email","name","role"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}}},"valueObject":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}}},"valueSet":{"UserRole":{"kind":"valueSet","values":["admin","author","reader"]}}},"id":"__unbound__","kind":"mongo-database"}},"storageHash":"406b672b68a6b01f04c0d6f910f5fefa4ac16d48b26e6c91b43856cd42c8cc5d"},"target":"mongo","targetFamily":"mongo"} diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/snapshots/95332b0bd6fbbfab4462d882c037a574c92e51897249c333ba681b12c4d603c9/contract.d.ts b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/snapshots/95332b0bd6fbbfab4462d882c037a574c92e51897249c333ba681b12c4d603c9/contract.d.ts new file mode 100644 index 000000000000..c61fa21a4db6 --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/snapshots/95332b0bd6fbbfab4462d882c037a574c92e51897249c333ba681b12c4d603c9/contract.d.ts @@ -0,0 +1,479 @@ +// ⚠️ GENERATED FILE - DO NOT EDIT +// This file is automatically generated by 'prisma contract emit'. +// To regenerate, run: prisma contract emit +import type { + CodecTypes as MongoCodecTypes, + ParameterizedOutputTypesForTypeParams as MongoParameterizedOutputTypes, +} from '@prisma/orm-mongo/target/codec-types'; + +import type { + MongoCollection, + MongoContractWithTypeMaps, + MongoTypeMaps, + RelationKeys, +} from '@prisma/orm-mongo/family-contract'; +import type { + Contract as ContractType, + ExecutionHashBase, + FieldTypesFromDomain, + NamespaceId, + ProfileHashBase, + StorageHashBase, + ValueObjectTypesFromStorage, +} from '@prisma/orm-mongo/contract/types'; + +export type StorageHash = + StorageHashBase<'95332b0bd6fbbfab4462d882c037a574c92e51897249c333ba681b12c4d603c9'>; +export type ExecutionHash = ExecutionHashBase; +export type ProfileHash = + ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; + +export type CodecTypes = MongoCodecTypes; + +type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output' +>; +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input' +>; +export type AddressOutput = ValueObjectOutputTypes['__unbound__']['Address']; +export type AddressInput = ValueObjectInputTypes['__unbound__']['Address']; +type FieldTypeOverrides = { + readonly __unbound__: { + readonly models: { + readonly User: { + readonly role: { + readonly output: 'admin' | 'author' | 'reader'; + readonly input: 'admin' | 'author' | 'reader'; + }; + }; + }; + }; +}; +export type FieldOutputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output', + ContractBase['storage']['namespaces'] +>; +export type FieldInputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input', + ContractBase['storage']['namespaces'] +>; + +export namespace Models { + export type unbound_Article = { + authorId: FieldOutputTypes['__unbound__']['Post']['authorId']; + content: FieldOutputTypes['__unbound__']['Post']['content']; + createdAt: FieldOutputTypes['__unbound__']['Post']['createdAt']; + id: FieldOutputTypes['__unbound__']['Post']['id']; + kind: 'article'; + title: FieldOutputTypes['__unbound__']['Post']['title']; + summary: FieldOutputTypes['__unbound__']['Article']['summary']; + author: unbound_User; + readonly [RelationKeys]?: 'author'; + }; + export type unbound_Post = { + authorId: FieldOutputTypes['__unbound__']['Post']['authorId']; + content: FieldOutputTypes['__unbound__']['Post']['content']; + createdAt: FieldOutputTypes['__unbound__']['Post']['createdAt']; + id: FieldOutputTypes['__unbound__']['Post']['id']; + kind: 'article' | 'tutorial'; + title: FieldOutputTypes['__unbound__']['Post']['title']; + author: unbound_User; + readonly [RelationKeys]?: 'author'; + }; + export type unbound_Tutorial = { + authorId: FieldOutputTypes['__unbound__']['Post']['authorId']; + content: FieldOutputTypes['__unbound__']['Post']['content']; + createdAt: FieldOutputTypes['__unbound__']['Post']['createdAt']; + id: FieldOutputTypes['__unbound__']['Post']['id']; + kind: 'tutorial'; + title: FieldOutputTypes['__unbound__']['Post']['title']; + difficulty: FieldOutputTypes['__unbound__']['Tutorial']['difficulty']; + duration: FieldOutputTypes['__unbound__']['Tutorial']['duration']; + author: unbound_User; + readonly [RelationKeys]?: 'author'; + }; + export type unbound_User = { + address: FieldOutputTypes['__unbound__']['User']['address']; + bio: FieldOutputTypes['__unbound__']['User']['bio']; + email: FieldOutputTypes['__unbound__']['User']['email']; + id: FieldOutputTypes['__unbound__']['User']['id']; + name: FieldOutputTypes['__unbound__']['User']['name']; + role: FieldOutputTypes['__unbound__']['User']['role']; + posts: unbound_AnyPost[]; + readonly [RelationKeys]?: 'posts'; + }; + export type unbound_AnyPost = unbound_Article | unbound_Tutorial; +} + +export declare const models: { + __unbound__: { + Article: Models.unbound_Article; + Post: Models.unbound_Post; + Tutorial: Models.unbound_Tutorial; + User: Models.unbound_User; + AnyPost: Models.unbound_AnyPost; + }; +}; + +export type TypeMaps = MongoTypeMaps; + +type ContractBase = Omit< + ContractType<{ + readonly namespaces: { + readonly __unbound__: { + readonly id: '__unbound__'; + readonly kind: 'mongo-database'; + readonly entries: { + readonly collection: { + readonly posts: { + readonly indexes: readonly [ + { + readonly keys: readonly [{ readonly direction: 1; readonly field: 'summary' }]; + readonly kind: 'mongo-index'; + readonly partialFilterExpression: { readonly kind: 'article' }; + readonly unique: true; + }, + ]; + readonly kind: 'mongo-collection'; + readonly validator: { + readonly jsonSchema: { + readonly bsonType: 'object'; + readonly oneOf: readonly [ + { + readonly additionalProperties: false; + readonly properties: { + readonly _id: { readonly bsonType: 'objectId' }; + readonly authorId: { readonly bsonType: 'objectId' }; + readonly content: { readonly bsonType: 'string' }; + readonly createdAt: { readonly bsonType: 'date' }; + readonly kind: { readonly enum: readonly ['article'] }; + readonly summary: { readonly bsonType: 'string' }; + readonly title: { readonly bsonType: 'string' }; + }; + readonly required: readonly ['kind', 'summary']; + }, + { + readonly additionalProperties: false; + readonly properties: { + readonly _id: { readonly bsonType: 'objectId' }; + readonly authorId: { readonly bsonType: 'objectId' }; + readonly content: { readonly bsonType: 'string' }; + readonly createdAt: { readonly bsonType: 'date' }; + readonly difficulty: { readonly bsonType: 'string' }; + readonly duration: { readonly bsonType: 'int' }; + readonly kind: { readonly enum: readonly ['tutorial'] }; + readonly title: { readonly bsonType: 'string' }; + }; + readonly required: readonly ['difficulty', 'duration', 'kind']; + }, + ]; + readonly properties: { + readonly _id: { readonly bsonType: 'objectId' }; + readonly authorId: { readonly bsonType: 'objectId' }; + readonly content: { readonly bsonType: 'string' }; + readonly createdAt: { readonly bsonType: 'date' }; + readonly kind: { readonly bsonType: 'string' }; + readonly title: { readonly bsonType: 'string' }; + }; + readonly required: readonly [ + '_id', + 'authorId', + 'content', + 'createdAt', + 'kind', + 'title', + ]; + }; + readonly kind: 'mongo-validator'; + readonly validationAction: 'error'; + readonly validationLevel: 'strict'; + }; + }; + readonly users: { + readonly kind: 'mongo-collection'; + readonly validator: { + readonly jsonSchema: { + readonly additionalProperties: false; + readonly bsonType: 'object'; + readonly properties: { + readonly _id: { readonly bsonType: 'objectId' }; + readonly address: { + readonly oneOf: readonly [ + { readonly bsonType: 'null' }, + { + readonly additionalProperties: false; + readonly bsonType: 'object'; + readonly properties: { + readonly city: { readonly bsonType: 'string' }; + readonly country: { readonly bsonType: 'string' }; + readonly street: { readonly bsonType: 'string' }; + readonly zip: { readonly bsonType: readonly ['null', 'string'] }; + }; + readonly required: readonly ['city', 'country', 'street']; + }, + ]; + }; + readonly bio: { readonly bsonType: readonly ['null', 'string'] }; + readonly email: { readonly bsonType: 'string' }; + readonly name: { readonly bsonType: 'string' }; + readonly role: { + readonly bsonType: 'string'; + readonly enum: readonly ['admin', 'author', 'reader']; + }; + }; + readonly required: readonly ['_id', 'email', 'name', 'role']; + }; + readonly kind: 'mongo-validator'; + readonly validationAction: 'error'; + readonly validationLevel: 'strict'; + }; + }; + }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + }; + }; + }; + }; + readonly storageHash: StorageHash; + }>, + 'roots' | 'domain' +> & { + readonly target: 'mongo'; + readonly targetFamily: 'mongo'; + readonly roots: { + readonly posts: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'Post' }; + readonly users: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'User' }; + }; + readonly domain: { + readonly namespaces: { + readonly __unbound__: { + readonly models: { + readonly Article: { + readonly fields: { + readonly summary: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly collection: 'posts'; + readonly fields: { readonly summary: { readonly field: 'summary' } }; + }; + readonly base: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Post'; + }; + }; + readonly Post: { + readonly fields: { + readonly authorId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; + }; + readonly content: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly createdAt: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; + }; + readonly kind: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly title: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + readonly relations: { + readonly author: { + readonly to: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'User'; + }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['authorId']; + readonly targetFields: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly collection: 'posts'; + readonly fields: { + readonly authorId: { readonly field: 'authorId' }; + readonly content: { readonly field: 'content' }; + readonly createdAt: { readonly field: 'createdAt' }; + readonly id: { readonly field: '_id' }; + readonly kind: { readonly field: 'kind' }; + readonly title: { readonly field: 'title' }; + }; + }; + readonly discriminator: { readonly field: 'kind' }; + readonly variants: { + readonly article: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Article'; + }; + readonly tutorial: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Tutorial'; + }; + }; + }; + readonly Tutorial: { + readonly fields: { + readonly difficulty: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly duration: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly collection: 'posts'; + readonly fields: { + readonly difficulty: { readonly field: 'difficulty' }; + readonly duration: { readonly field: 'duration' }; + }; + }; + readonly base: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Post'; + }; + }; + readonly User: { + readonly fields: { + readonly address: { + readonly nullable: true; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; + }; + readonly bio: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; + }; + readonly name: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly role: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + readonly relations: { + readonly posts: { + readonly to: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Post'; + }; + readonly cardinality: '1:N'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['authorId']; + }; + }; + }; + readonly storage: { + readonly collection: 'users'; + readonly fields: { + readonly address: { readonly field: 'address' }; + readonly bio: { readonly field: 'bio' }; + readonly email: { readonly field: 'email' }; + readonly id: { readonly field: '_id' }; + readonly name: { readonly field: 'name' }; + readonly role: { readonly field: 'role' }; + }; + }; + }; + }; + readonly enum: { + readonly UserRole: { + readonly codecId: 'mongo/string@1'; + readonly members: readonly [ + { readonly name: 'Admin'; readonly value: 'admin' }, + { readonly name: 'Author'; readonly value: 'author' }, + { readonly name: 'Reader'; readonly value: 'reader' }, + ]; + }; + }; + readonly enumMemberTypes?: { + readonly UserRole: readonly [ + { readonly name: 'Admin'; readonly value: 'admin' }, + { readonly name: 'Author'; readonly value: 'author' }, + { readonly name: 'Reader'; readonly value: 'reader' }, + ]; + }; + }; + }; + }; + readonly capabilities: {}; + readonly extensions: {}; + readonly meta: {}; + readonly profileHash: ProfileHash; +}; + +export type Contract = MongoContractWithTypeMaps; diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/snapshots/95332b0bd6fbbfab4462d882c037a574c92e51897249c333ba681b12c4d603c9/contract.json b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/snapshots/95332b0bd6fbbfab4462d882c037a574c92e51897249c333ba681b12c4d603c9/contract.json new file mode 100644 index 000000000000..feed3a57d193 --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/migrations/snapshots/95332b0bd6fbbfab4462d882c037a574c92e51897249c333ba681b12c4d603c9/contract.json @@ -0,0 +1 @@ +{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{},"domain":{"namespaces":{"__unbound__":{"enum":{"UserRole":{"codecId":"mongo/string@1","members":[{"name":"Admin","value":"admin"},{"name":"Author","value":"author"},{"name":"Reader","value":"reader"}]}},"models":{"Article":{"base":{"model":"Post","namespace":"__unbound__"},"fields":{"summary":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"posts","fields":{"summary":{"field":"summary"}}}},"Post":{"discriminator":{"field":"kind"},"fields":{"authorId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"content":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"createdAt":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{"author":{"cardinality":"N:1","nullable":false,"on":{"localFields":["authorId"],"targetFields":["id"]},"to":{"model":"User","namespace":"__unbound__"}}},"storage":{"collection":"posts","fields":{"authorId":{"field":"authorId"},"content":{"field":"content"},"createdAt":{"field":"createdAt"},"id":{"field":"_id"},"kind":{"field":"kind"},"title":{"field":"title"}}},"variants":{"article":{"model":"Article","namespace":"__unbound__"},"tutorial":{"model":"Tutorial","namespace":"__unbound__"}}},"Tutorial":{"base":{"model":"Post","namespace":"__unbound__"},"fields":{"difficulty":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"duration":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"posts","fields":{"difficulty":{"field":"difficulty"},"duration":{"field":"duration"}}}},"User":{"fields":{"address":{"nullable":true,"type":{"entityKind":"valueObject","entityName":"Address","kind":"valueObject","namespaceId":"__unbound__","plane":"storage"}},"bio":{"nullable":true,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"role":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"UserRole","namespaceId":"__unbound__","plane":"domain"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["authorId"]},"to":{"model":"Post","namespace":"__unbound__"}}},"storage":{"collection":"users","fields":{"address":{"field":"address"},"bio":{"field":"bio"},"email":{"field":"email"},"id":{"field":"_id"},"name":{"field":"name"},"role":{"field":"role"}}}}}}}},"extensions":{},"meta":{},"profileHash":"251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3","roots":{"posts":{"model":"Post","namespace":"__unbound__"},"users":{"model":"User","namespace":"__unbound__"}},"schemaVersion":"1","storage":{"namespaces":{"__unbound__":{"entries":{"collection":{"posts":{"indexes":[{"keys":[{"direction":1,"field":"summary"}],"kind":"mongo-index","partialFilterExpression":{"kind":"article"},"unique":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"bsonType":"object","oneOf":[{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"kind":{"enum":["article"]},"summary":{"bsonType":"string"},"title":{"bsonType":"string"}},"required":["kind","summary"]},{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"difficulty":{"bsonType":"string"},"duration":{"bsonType":"int"},"kind":{"enum":["tutorial"]},"title":{"bsonType":"string"}},"required":["difficulty","duration","kind"]}],"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"kind":{"bsonType":"string"},"title":{"bsonType":"string"}},"required":["_id","authorId","content","createdAt","kind","title"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"users":{"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"address":{"oneOf":[{"bsonType":"null"},{"additionalProperties":false,"bsonType":"object","properties":{"city":{"bsonType":"string"},"country":{"bsonType":"string"},"street":{"bsonType":"string"},"zip":{"bsonType":["null","string"]}},"required":["city","country","street"]}]},"bio":{"bsonType":["null","string"]},"email":{"bsonType":"string"},"name":{"bsonType":"string"},"role":{"bsonType":"string","enum":["admin","author","reader"]}},"required":["_id","email","name","role"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}}},"valueObject":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}}},"valueSet":{"UserRole":{"kind":"valueSet","values":["admin","author","reader"]}}},"id":"__unbound__","kind":"mongo-database"}},"storageHash":"95332b0bd6fbbfab4462d882c037a574c92e51897249c333ba681b12c4d603c9"},"target":"mongo","targetFamily":"mongo"} diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/src/contract.d.ts b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/src/contract.d.ts new file mode 100644 index 000000000000..80c6ac7a1cf7 --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/src/contract.d.ts @@ -0,0 +1,490 @@ +// ⚠️ GENERATED FILE - DO NOT EDIT +// This file is automatically generated by 'prisma contract emit'. +// To regenerate, run: prisma contract emit +import type { + CodecTypes as MongoCodecTypes, + ParameterizedOutputTypesForTypeParams as MongoParameterizedOutputTypes, +} from '@prisma/orm-mongo/target/codec-types'; + +import type { + MongoCollection, + MongoContractWithTypeMaps, + MongoTypeMaps, + RelationKeys, +} from '@prisma/orm-mongo/family-contract'; +import type { + Contract as ContractType, + ExecutionHashBase, + FieldTypesFromDomain, + NamespaceId, + ProfileHashBase, + StorageHashBase, + ValueObjectTypesFromStorage, +} from '@prisma/orm-mongo/contract/types'; + +export type StorageHash = + StorageHashBase<'406b672b68a6b01f04c0d6f910f5fefa4ac16d48b26e6c91b43856cd42c8cc5d'>; +export type ExecutionHash = ExecutionHashBase; +export type ProfileHash = + ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; + +export type CodecTypes = MongoCodecTypes; + +type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output' +>; +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input' +>; +export type AddressOutput = ValueObjectOutputTypes['__unbound__']['Address']; +export type AddressInput = ValueObjectInputTypes['__unbound__']['Address']; +type FieldTypeOverrides = { + readonly __unbound__: { + readonly models: { + readonly User: { + readonly role: { + readonly output: 'admin' | 'author' | 'reader'; + readonly input: 'admin' | 'author' | 'reader'; + }; + }; + }; + }; +}; +export type FieldOutputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output', + ContractBase['storage']['namespaces'] +>; +export type FieldInputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input', + ContractBase['storage']['namespaces'] +>; + +export namespace Models { + export type unbound_Article = { + authorId: FieldOutputTypes['__unbound__']['Post']['authorId']; + content: FieldOutputTypes['__unbound__']['Post']['content']; + createdAt: FieldOutputTypes['__unbound__']['Post']['createdAt']; + id: FieldOutputTypes['__unbound__']['Post']['id']; + kind: 'article'; + title: FieldOutputTypes['__unbound__']['Post']['title']; + summary: FieldOutputTypes['__unbound__']['Article']['summary']; + author: unbound_User; + readonly [RelationKeys]?: 'author'; + }; + export type unbound_Post = { + authorId: FieldOutputTypes['__unbound__']['Post']['authorId']; + content: FieldOutputTypes['__unbound__']['Post']['content']; + createdAt: FieldOutputTypes['__unbound__']['Post']['createdAt']; + id: FieldOutputTypes['__unbound__']['Post']['id']; + kind: 'article' | 'tutorial'; + title: FieldOutputTypes['__unbound__']['Post']['title']; + author: unbound_User; + readonly [RelationKeys]?: 'author'; + }; + export type unbound_Tutorial = { + authorId: FieldOutputTypes['__unbound__']['Post']['authorId']; + content: FieldOutputTypes['__unbound__']['Post']['content']; + createdAt: FieldOutputTypes['__unbound__']['Post']['createdAt']; + id: FieldOutputTypes['__unbound__']['Post']['id']; + kind: 'tutorial'; + title: FieldOutputTypes['__unbound__']['Post']['title']; + difficulty: FieldOutputTypes['__unbound__']['Tutorial']['difficulty']; + duration: FieldOutputTypes['__unbound__']['Tutorial']['duration']; + author: unbound_User; + readonly [RelationKeys]?: 'author'; + }; + export type unbound_User = { + address: FieldOutputTypes['__unbound__']['User']['address']; + bio: FieldOutputTypes['__unbound__']['User']['bio']; + email: FieldOutputTypes['__unbound__']['User']['email']; + id: FieldOutputTypes['__unbound__']['User']['id']; + name: FieldOutputTypes['__unbound__']['User']['name']; + role: FieldOutputTypes['__unbound__']['User']['role']; + posts: unbound_AnyPost[]; + readonly [RelationKeys]?: 'posts'; + }; + export type unbound_AnyPost = unbound_Article | unbound_Tutorial; +} + +export declare const models: { + __unbound__: { + Article: Models.unbound_Article; + Post: Models.unbound_Post; + Tutorial: Models.unbound_Tutorial; + User: Models.unbound_User; + AnyPost: Models.unbound_AnyPost; + }; +}; + +export type TypeMaps = MongoTypeMaps; + +type ContractBase = Omit< + ContractType<{ + readonly namespaces: { + readonly __unbound__: { + readonly id: '__unbound__'; + readonly kind: 'mongo-database'; + readonly entries: { + readonly collection: { + readonly posts: { + readonly indexes: readonly [ + { + readonly keys: readonly [{ readonly direction: 1; readonly field: 'authorId' }]; + readonly kind: 'mongo-index'; + }, + { + readonly keys: readonly [ + { readonly direction: -1; readonly field: 'createdAt' }, + { readonly direction: 1; readonly field: 'authorId' }, + ]; + readonly kind: 'mongo-index'; + }, + { + readonly keys: readonly [{ readonly direction: 1; readonly field: 'summary' }]; + readonly kind: 'mongo-index'; + readonly partialFilterExpression: { readonly kind: 'article' }; + readonly unique: true; + }, + ]; + readonly kind: 'mongo-collection'; + readonly validator: { + readonly jsonSchema: { + readonly bsonType: 'object'; + readonly oneOf: readonly [ + { + readonly additionalProperties: false; + readonly properties: { + readonly _id: { readonly bsonType: 'objectId' }; + readonly authorId: { readonly bsonType: 'objectId' }; + readonly content: { readonly bsonType: 'string' }; + readonly createdAt: { readonly bsonType: 'date' }; + readonly kind: { readonly enum: readonly ['article'] }; + readonly summary: { readonly bsonType: 'string' }; + readonly title: { readonly bsonType: 'string' }; + }; + readonly required: readonly ['kind', 'summary']; + }, + { + readonly additionalProperties: false; + readonly properties: { + readonly _id: { readonly bsonType: 'objectId' }; + readonly authorId: { readonly bsonType: 'objectId' }; + readonly content: { readonly bsonType: 'string' }; + readonly createdAt: { readonly bsonType: 'date' }; + readonly difficulty: { readonly bsonType: 'string' }; + readonly duration: { readonly bsonType: 'int' }; + readonly kind: { readonly enum: readonly ['tutorial'] }; + readonly title: { readonly bsonType: 'string' }; + }; + readonly required: readonly ['difficulty', 'duration', 'kind']; + }, + ]; + readonly properties: { + readonly _id: { readonly bsonType: 'objectId' }; + readonly authorId: { readonly bsonType: 'objectId' }; + readonly content: { readonly bsonType: 'string' }; + readonly createdAt: { readonly bsonType: 'date' }; + readonly kind: { readonly bsonType: 'string' }; + readonly title: { readonly bsonType: 'string' }; + }; + readonly required: readonly [ + '_id', + 'authorId', + 'content', + 'createdAt', + 'kind', + 'title', + ]; + }; + readonly kind: 'mongo-validator'; + readonly validationAction: 'error'; + readonly validationLevel: 'strict'; + }; + }; + readonly users: { + readonly kind: 'mongo-collection'; + readonly validator: { + readonly jsonSchema: { + readonly additionalProperties: false; + readonly bsonType: 'object'; + readonly properties: { + readonly _id: { readonly bsonType: 'objectId' }; + readonly address: { + readonly oneOf: readonly [ + { readonly bsonType: 'null' }, + { + readonly additionalProperties: false; + readonly bsonType: 'object'; + readonly properties: { + readonly city: { readonly bsonType: 'string' }; + readonly country: { readonly bsonType: 'string' }; + readonly street: { readonly bsonType: 'string' }; + readonly zip: { readonly bsonType: readonly ['null', 'string'] }; + }; + readonly required: readonly ['city', 'country', 'street']; + }, + ]; + }; + readonly bio: { readonly bsonType: readonly ['null', 'string'] }; + readonly email: { readonly bsonType: 'string' }; + readonly name: { readonly bsonType: 'string' }; + readonly role: { + readonly bsonType: 'string'; + readonly enum: readonly ['admin', 'author', 'reader']; + }; + }; + readonly required: readonly ['_id', 'email', 'name', 'role']; + }; + readonly kind: 'mongo-validator'; + readonly validationAction: 'error'; + readonly validationLevel: 'strict'; + }; + }; + }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + }; + }; + }; + }; + readonly storageHash: StorageHash; + }>, + 'roots' | 'domain' +> & { + readonly target: 'mongo'; + readonly targetFamily: 'mongo'; + readonly roots: { + readonly posts: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'Post' }; + readonly users: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'User' }; + }; + readonly domain: { + readonly namespaces: { + readonly __unbound__: { + readonly models: { + readonly Article: { + readonly fields: { + readonly summary: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly collection: 'posts'; + readonly fields: { readonly summary: { readonly field: 'summary' } }; + }; + readonly base: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Post'; + }; + }; + readonly Post: { + readonly fields: { + readonly authorId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; + }; + readonly content: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly createdAt: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; + }; + readonly kind: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly title: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + readonly relations: { + readonly author: { + readonly to: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'User'; + }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['authorId']; + readonly targetFields: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly collection: 'posts'; + readonly fields: { + readonly authorId: { readonly field: 'authorId' }; + readonly content: { readonly field: 'content' }; + readonly createdAt: { readonly field: 'createdAt' }; + readonly id: { readonly field: '_id' }; + readonly kind: { readonly field: 'kind' }; + readonly title: { readonly field: 'title' }; + }; + }; + readonly discriminator: { readonly field: 'kind' }; + readonly variants: { + readonly article: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Article'; + }; + readonly tutorial: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Tutorial'; + }; + }; + }; + readonly Tutorial: { + readonly fields: { + readonly difficulty: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly duration: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly collection: 'posts'; + readonly fields: { + readonly difficulty: { readonly field: 'difficulty' }; + readonly duration: { readonly field: 'duration' }; + }; + }; + readonly base: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Post'; + }; + }; + readonly User: { + readonly fields: { + readonly address: { + readonly nullable: true; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: '__unbound__'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; + }; + readonly bio: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; + }; + readonly name: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly role: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + readonly relations: { + readonly posts: { + readonly to: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Post'; + }; + readonly cardinality: '1:N'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['authorId']; + }; + }; + }; + readonly storage: { + readonly collection: 'users'; + readonly fields: { + readonly address: { readonly field: 'address' }; + readonly bio: { readonly field: 'bio' }; + readonly email: { readonly field: 'email' }; + readonly id: { readonly field: '_id' }; + readonly name: { readonly field: 'name' }; + readonly role: { readonly field: 'role' }; + }; + }; + }; + }; + readonly enum: { + readonly UserRole: { + readonly codecId: 'mongo/string@1'; + readonly members: readonly [ + { readonly name: 'Admin'; readonly value: 'admin' }, + { readonly name: 'Author'; readonly value: 'author' }, + { readonly name: 'Reader'; readonly value: 'reader' }, + ]; + }; + }; + readonly enumMemberTypes?: { + readonly UserRole: readonly [ + { readonly name: 'Admin'; readonly value: 'admin' }, + { readonly name: 'Author'; readonly value: 'author' }, + { readonly name: 'Reader'; readonly value: 'reader' }, + ]; + }; + }; + }; + }; + readonly capabilities: {}; + readonly extensions: {}; + readonly meta: {}; + readonly profileHash: ProfileHash; +}; + +export type Contract = MongoContractWithTypeMaps; diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/src/contract.json b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/src/contract.json new file mode 100644 index 000000000000..2809c2eb8410 --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/mongo/src/contract.json @@ -0,0 +1,583 @@ +{ + "schemaVersion": "1", + "targetFamily": "mongo", + "target": "mongo", + "profileHash": "251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3", + "roots": { + "posts": { + "model": "Post", + "namespace": "__unbound__" + }, + "users": { + "model": "User", + "namespace": "__unbound__" + } + }, + "domain": { + "namespaces": { + "__unbound__": { + "enum": { + "UserRole": { + "codecId": "mongo/string@1", + "members": [ + { + "name": "Admin", + "value": "admin" + }, + { + "name": "Author", + "value": "author" + }, + { + "name": "Reader", + "value": "reader" + } + ] + } + }, + "models": { + "Article": { + "base": { + "model": "Post", + "namespace": "__unbound__" + }, + "fields": { + "summary": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + } + }, + "relations": {}, + "storage": { + "collection": "posts", + "fields": { + "summary": { + "field": "summary" + } + } + } + }, + "Post": { + "discriminator": { + "field": "kind" + }, + "fields": { + "authorId": { + "nullable": false, + "type": { + "codecId": "mongo/objectId@1", + "kind": "scalar" + } + }, + "content": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "createdAt": { + "nullable": false, + "type": { + "codecId": "mongo/date@1", + "kind": "scalar" + } + }, + "id": { + "nullable": false, + "type": { + "codecId": "mongo/objectId@1", + "kind": "scalar" + } + }, + "kind": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "title": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + } + }, + "relations": { + "author": { + "cardinality": "N:1", + "nullable": false, + "on": { + "localFields": [ + "authorId" + ], + "targetFields": [ + "id" + ] + }, + "to": { + "model": "User", + "namespace": "__unbound__" + } + } + }, + "storage": { + "collection": "posts", + "fields": { + "authorId": { + "field": "authorId" + }, + "content": { + "field": "content" + }, + "createdAt": { + "field": "createdAt" + }, + "id": { + "field": "_id" + }, + "kind": { + "field": "kind" + }, + "title": { + "field": "title" + } + } + }, + "variants": { + "article": { + "model": "Article", + "namespace": "__unbound__" + }, + "tutorial": { + "model": "Tutorial", + "namespace": "__unbound__" + } + } + }, + "Tutorial": { + "base": { + "model": "Post", + "namespace": "__unbound__" + }, + "fields": { + "difficulty": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "duration": { + "nullable": false, + "type": { + "codecId": "mongo/int32@1", + "kind": "scalar" + } + } + }, + "relations": {}, + "storage": { + "collection": "posts", + "fields": { + "difficulty": { + "field": "difficulty" + }, + "duration": { + "field": "duration" + } + } + } + }, + "User": { + "fields": { + "address": { + "nullable": true, + "type": { + "kind": "valueObject", + "plane": "storage", + "namespaceId": "__unbound__", + "entityKind": "valueObject", + "entityName": "Address" + } + }, + "bio": { + "nullable": true, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "email": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "id": { + "nullable": false, + "type": { + "codecId": "mongo/objectId@1", + "kind": "scalar" + } + }, + "name": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "role": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + }, + "valueSet": { + "entityKind": "enum", + "entityName": "UserRole", + "namespaceId": "__unbound__", + "plane": "domain" + } + } + }, + "relations": { + "posts": { + "cardinality": "1:N", + "on": { + "localFields": [ + "id" + ], + "targetFields": [ + "authorId" + ] + }, + "to": { + "model": "Post", + "namespace": "__unbound__" + } + } + }, + "storage": { + "collection": "users", + "fields": { + "address": { + "field": "address" + }, + "bio": { + "field": "bio" + }, + "email": { + "field": "email" + }, + "id": { + "field": "_id" + }, + "name": { + "field": "name" + }, + "role": { + "field": "role" + } + } + } + } + } + } + } + }, + "storage": { + "namespaces": { + "__unbound__": { + "entries": { + "collection": { + "posts": { + "indexes": [ + { + "keys": [ + { + "direction": 1, + "field": "authorId" + } + ], + "kind": "mongo-index" + }, + { + "keys": [ + { + "direction": -1, + "field": "createdAt" + }, + { + "direction": 1, + "field": "authorId" + } + ], + "kind": "mongo-index" + }, + { + "keys": [ + { + "direction": 1, + "field": "summary" + } + ], + "kind": "mongo-index", + "partialFilterExpression": { + "kind": "article" + }, + "unique": true + } + ], + "kind": "mongo-collection", + "validator": { + "jsonSchema": { + "bsonType": "object", + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "_id": { + "bsonType": "objectId" + }, + "authorId": { + "bsonType": "objectId" + }, + "content": { + "bsonType": "string" + }, + "createdAt": { + "bsonType": "date" + }, + "kind": { + "enum": [ + "article" + ] + }, + "summary": { + "bsonType": "string" + }, + "title": { + "bsonType": "string" + } + }, + "required": [ + "kind", + "summary" + ] + }, + { + "additionalProperties": false, + "properties": { + "_id": { + "bsonType": "objectId" + }, + "authorId": { + "bsonType": "objectId" + }, + "content": { + "bsonType": "string" + }, + "createdAt": { + "bsonType": "date" + }, + "difficulty": { + "bsonType": "string" + }, + "duration": { + "bsonType": "int" + }, + "kind": { + "enum": [ + "tutorial" + ] + }, + "title": { + "bsonType": "string" + } + }, + "required": [ + "difficulty", + "duration", + "kind" + ] + } + ], + "properties": { + "_id": { + "bsonType": "objectId" + }, + "authorId": { + "bsonType": "objectId" + }, + "content": { + "bsonType": "string" + }, + "createdAt": { + "bsonType": "date" + }, + "kind": { + "bsonType": "string" + }, + "title": { + "bsonType": "string" + } + }, + "required": [ + "_id", + "authorId", + "content", + "createdAt", + "kind", + "title" + ] + }, + "kind": "mongo-validator", + "validationAction": "error", + "validationLevel": "strict" + } + }, + "users": { + "kind": "mongo-collection", + "validator": { + "jsonSchema": { + "additionalProperties": false, + "bsonType": "object", + "properties": { + "_id": { + "bsonType": "objectId" + }, + "address": { + "oneOf": [ + { + "bsonType": "null" + }, + { + "additionalProperties": false, + "bsonType": "object", + "properties": { + "city": { + "bsonType": "string" + }, + "country": { + "bsonType": "string" + }, + "street": { + "bsonType": "string" + }, + "zip": { + "bsonType": [ + "null", + "string" + ] + } + }, + "required": [ + "city", + "country", + "street" + ] + } + ] + }, + "bio": { + "bsonType": [ + "null", + "string" + ] + }, + "email": { + "bsonType": "string" + }, + "name": { + "bsonType": "string" + }, + "role": { + "bsonType": "string", + "enum": [ + "admin", + "author", + "reader" + ] + } + }, + "required": [ + "_id", + "email", + "name", + "role" + ] + }, + "kind": "mongo-validator", + "validationAction": "error", + "validationLevel": "strict" + } + } + }, + "valueSet": { + "UserRole": { + "kind": "valueSet", + "values": [ + "admin", + "author", + "reader" + ] + } + }, + "valueObject": { + "Address": { + "fields": { + "city": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "country": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "street": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "zip": { + "nullable": true, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + } + } + } + } + }, + "id": "__unbound__", + "kind": "mongo-database" + } + }, + "storageHash": "406b672b68a6b01f04c0d6f910f5fefa4ac16d48b26e6c91b43856cd42c8cc5d" + }, + "capabilities": {}, + "extensions": {}, + "meta": {}, + "_generated": { + "warning": "⚠️ GENERATED FILE - DO NOT EDIT", + "message": "This file is automatically generated by \"prisma contract emit\".", + "regenerate": "To regenerate, run: prisma contract emit" + } +} \ No newline at end of file diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/sql/migrations/app/20260422T0720_initial/migration.json b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/sql/migrations/app/20260422T0720_initial/migration.json new file mode 100644 index 000000000000..d6a35f5bf1fc --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/sql/migrations/app/20260422T0720_initial/migration.json @@ -0,0 +1,7 @@ +{ + "from": null, + "to": "1cb334a7b32d249d4fea0934dbbaa48c473b5946cd77ee1ce7442272479d6cf3", + "providedInvariants": [], + "createdAt": "2026-06-19T15:33:14.992Z", + "migrationHash": "51cd63b5815f236613c14c46542184aab1ea662907704b3c4733f379ffd02e6f" +} diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/sql/migrations/app/20260422T0720_initial/migration.ts b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/sql/migrations/app/20260422T0720_initial/migration.ts new file mode 100644 index 000000000000..cbb223b1ba03 --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/sql/migrations/app/20260422T0720_initial/migration.ts @@ -0,0 +1,215 @@ +#!/usr/bin/env -S node +import { + checkExpression, + col, + fn, + lit, + Migration, + MigrationCLI, + primaryKey, + rawSql, + unique, +} from '@prisma/orm-postgres/target/migration'; +import type { Contract as End } from '../../snapshots/1cb334a7b32d249d4fea0934dbbaa48c473b5946cd77ee1ce7442272479d6cf3/contract'; +import endContract from '../../snapshots/1cb334a7b32d249d4fea0934dbbaa48c473b5946cd77ee1ce7442272479d6cf3/contract.json' with { + type: 'json', +}; + +export default class M extends Migration { + override readonly endContractJson = endContract; + + override get operations() { + return [ + rawSql({ + id: 'extension.vector', + label: 'Enable extension "vector"', + summary: 'Ensures the vector extension is available for pgvector operations', + operationClass: 'additive', + target: { id: 'postgres' }, + precheck: [ + { + description: 'verify extension "vector" is not already enabled', + sql: "SELECT NOT EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'vector')", + expectation: { expects: 'absent', object: 'extension "vector"' }, + }, + ], + execute: [ + { + description: 'create extension "vector"', + sql: 'CREATE EXTENSION IF NOT EXISTS vector', + }, + ], + postcheck: [ + { + description: 'confirm extension "vector" is enabled', + sql: "SELECT EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'vector')", + expectation: { expects: 'present', object: 'extension "vector"' }, + }, + ], + }), + this.createTable({ + schema: 'public', + table: 'bug', + columns: [ + col('id', 'uuid', { notNull: true }), + col('severity', 'text', { notNull: true }), + col('stepsToRepro', 'text'), + ], + constraints: [primaryKey(['id'])], + }), + this.createTable({ + schema: 'public', + table: 'feature', + columns: [ + col('id', 'uuid', { notNull: true }), + col('priority', 'text', { notNull: true }), + col('targetRelease', 'text'), + ], + constraints: [primaryKey(['id'])], + }), + this.createTable({ + schema: 'public', + table: 'tag', + columns: [col('id', 'uuid', { notNull: true }), col('label', 'text', { notNull: true })], + constraints: [primaryKey(['id']), unique(['label'], { name: 'tag_label_key' })], + }), + this.createTable({ + schema: 'public', + table: 'user', + columns: [ + col('address', 'jsonb'), + col('createdAt', 'timestamptz', { notNull: true, default: fn('now()') }), + col('displayName', 'text', { notNull: true }), + col('email', 'text', { notNull: true }), + col('id', 'uuid', { notNull: true }), + col('kind', 'text', { notNull: true }), + ], + constraints: [ + primaryKey(['id']), + checkExpression('user_kind_check_836d43ef', "\"kind\" IN ('admin', 'user')"), + ], + }), + this.createTable({ + schema: 'public', + table: 'post', + columns: [ + col('createdAt', 'timestamptz', { notNull: true, default: fn('now()') }), + col('embedding', 'vector(1536)'), + col('id', 'uuid', { notNull: true }), + col('priority', 'int4', { notNull: true, default: lit(0) }), + col('title', 'text', { notNull: true }), + col('userId', 'uuid', { notNull: true }), + ], + constraints: [ + primaryKey(['id']), + checkExpression('post_priority_check_b236ace9', '"priority" IN (0, 1, 2)'), + ], + }), + this.createTable({ + schema: 'public', + table: 'task', + columns: [ + col('createdAt', 'timestamptz', { notNull: true, default: fn('now()') }), + col('description', 'text'), + col('id', 'uuid', { notNull: true }), + col('status', 'text', { notNull: true, default: lit('open') }), + col('title', 'text', { notNull: true }), + col('type', 'text', { notNull: true }), + col('userId', 'uuid', { notNull: true }), + ], + constraints: [primaryKey(['id'])], + }), + this.createTable({ + schema: 'public', + table: 'post_tag', + columns: [ + col('postId', 'uuid', { notNull: true }), + col('tagId', 'uuid', { notNull: true }), + ], + constraints: [primaryKey(['postId', 'tagId'])], + }), + this.addForeignKey({ + schema: 'public', + table: 'post', + foreignKey: { + name: 'post_userId_fkey', + columns: ['userId'], + references: { schema: 'public', table: 'user', columns: ['id'] }, + }, + }), + this.createIndex({ + schema: 'public', + table: 'post', + index: 'post_userId_idx_a489d58a', + columns: ['userId'], + }), + this.addForeignKey({ + schema: 'public', + table: 'task', + foreignKey: { + name: 'task_userId_fkey', + columns: ['userId'], + references: { schema: 'public', table: 'user', columns: ['id'] }, + }, + }), + this.createIndex({ + schema: 'public', + table: 'task', + index: 'task_userId_idx_a489d58a', + columns: ['userId'], + }), + this.addForeignKey({ + schema: 'public', + table: 'bug', + foreignKey: { + name: 'bug_id_fkey', + columns: ['id'], + references: { schema: 'public', table: 'task', columns: ['id'] }, + onDelete: 'cascade', + }, + }), + this.addForeignKey({ + schema: 'public', + table: 'feature', + foreignKey: { + name: 'feature_id_fkey', + columns: ['id'], + references: { schema: 'public', table: 'task', columns: ['id'] }, + onDelete: 'cascade', + }, + }), + this.createIndex({ + schema: 'public', + table: 'post_tag', + index: 'post_tag_postId_idx_a7a72715', + columns: ['postId'], + }), + this.createIndex({ + schema: 'public', + table: 'post_tag', + index: 'post_tag_tagId_idx_86854244', + columns: ['tagId'], + }), + this.addForeignKey({ + schema: 'public', + table: 'post_tag', + foreignKey: { + name: 'post_tag_postId_fkey', + columns: ['postId'], + references: { schema: 'public', table: 'post', columns: ['id'] }, + }, + }), + this.addForeignKey({ + schema: 'public', + table: 'post_tag', + foreignKey: { + name: 'post_tag_tagId_fkey', + columns: ['tagId'], + references: { schema: 'public', table: 'tag', columns: ['id'] }, + }, + }), + ]; + } +} + +MigrationCLI.run(import.meta.url, M); diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/sql/migrations/app/20260422T0720_initial/ops.json b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/sql/migrations/app/20260422T0720_initial/ops.json new file mode 100644 index 000000000000..3843b04f54cb --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/sql/migrations/app/20260422T0720_initial/ops.json @@ -0,0 +1,810 @@ +[ + { + "id": "extension.vector", + "label": "Enable extension \"vector\"", + "summary": "Ensures the vector extension is available for pgvector operations", + "operationClass": "additive", + "target": { + "id": "postgres" + }, + "precheck": [ + { + "description": "verify extension \"vector\" is not already enabled", + "sql": "SELECT NOT EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'vector')", + "expectation": { + "expects": "absent", + "object": "extension \"vector\"" + } + } + ], + "execute": [ + { + "description": "create extension \"vector\"", + "sql": "CREATE EXTENSION IF NOT EXISTS vector" + } + ], + "postcheck": [ + { + "description": "confirm extension \"vector\" is enabled", + "sql": "SELECT EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'vector')", + "expectation": { + "expects": "present", + "object": "extension \"vector\"" + } + } + ] + }, + { + "id": "table.bug", + "label": "Create table \"bug\"", + "summary": "Creates table \"bug\"", + "operationClass": "additive", + "target": { + "id": "postgres", + "details": { + "schema": "public", + "objectType": "table", + "name": "bug" + } + }, + "precheck": [ + { + "description": "ensure table \"bug\" does not exist", + "sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"", + "params": ["\"public\".\"bug\""], + "expectation": { + "expects": "absent", + "object": "table \"bug\"" + } + } + ], + "execute": [ + { + "description": "create table \"bug\"", + "sql": "CREATE TABLE \"public\".\"bug\" (\n \"id\" uuid NOT NULL,\n \"severity\" text NOT NULL,\n \"stepsToRepro\" text,\n PRIMARY KEY (\"id\")\n)", + "params": [] + } + ], + "postcheck": [ + { + "description": "verify table \"bug\" exists", + "sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_class\" AS \"c\" WHERE (\"c\".\"oid\" = to_regclass($1) AND (\"c\".\"relkind\" = $2 OR \"c\".\"relkind\" = $3))) AS \"result\"", + "params": ["\"public\".\"bug\"", "r", "p"], + "expectation": { + "expects": "present", + "object": "table \"bug\"" + } + } + ] + }, + { + "id": "table.feature", + "label": "Create table \"feature\"", + "summary": "Creates table \"feature\"", + "operationClass": "additive", + "target": { + "id": "postgres", + "details": { + "schema": "public", + "objectType": "table", + "name": "feature" + } + }, + "precheck": [ + { + "description": "ensure table \"feature\" does not exist", + "sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"", + "params": ["\"public\".\"feature\""], + "expectation": { + "expects": "absent", + "object": "table \"feature\"" + } + } + ], + "execute": [ + { + "description": "create table \"feature\"", + "sql": "CREATE TABLE \"public\".\"feature\" (\n \"id\" uuid NOT NULL,\n \"priority\" text NOT NULL,\n \"targetRelease\" text,\n PRIMARY KEY (\"id\")\n)", + "params": [] + } + ], + "postcheck": [ + { + "description": "verify table \"feature\" exists", + "sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_class\" AS \"c\" WHERE (\"c\".\"oid\" = to_regclass($1) AND (\"c\".\"relkind\" = $2 OR \"c\".\"relkind\" = $3))) AS \"result\"", + "params": ["\"public\".\"feature\"", "r", "p"], + "expectation": { + "expects": "present", + "object": "table \"feature\"" + } + } + ] + }, + { + "id": "table.tag", + "label": "Create table \"tag\"", + "summary": "Creates table \"tag\"", + "operationClass": "additive", + "target": { + "id": "postgres", + "details": { + "schema": "public", + "objectType": "table", + "name": "tag" + } + }, + "precheck": [ + { + "description": "ensure table \"tag\" does not exist", + "sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"", + "params": ["\"public\".\"tag\""], + "expectation": { + "expects": "absent", + "object": "table \"tag\"" + } + } + ], + "execute": [ + { + "description": "create table \"tag\"", + "sql": "CREATE TABLE \"public\".\"tag\" (\n \"id\" uuid NOT NULL,\n \"label\" text NOT NULL,\n PRIMARY KEY (\"id\"),\n CONSTRAINT \"tag_label_key\" UNIQUE (\"label\")\n)", + "params": [] + } + ], + "postcheck": [ + { + "description": "verify table \"tag\" exists", + "sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_class\" AS \"c\" WHERE (\"c\".\"oid\" = to_regclass($1) AND (\"c\".\"relkind\" = $2 OR \"c\".\"relkind\" = $3))) AS \"result\"", + "params": ["\"public\".\"tag\"", "r", "p"], + "expectation": { + "expects": "present", + "object": "table \"tag\"" + } + } + ] + }, + { + "id": "table.user", + "label": "Create table \"user\"", + "summary": "Creates table \"user\"", + "operationClass": "additive", + "target": { + "id": "postgres", + "details": { + "schema": "public", + "objectType": "table", + "name": "user" + } + }, + "precheck": [ + { + "description": "ensure table \"user\" does not exist", + "sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"", + "params": ["\"public\".\"user\""], + "expectation": { + "expects": "absent", + "object": "table \"user\"" + } + } + ], + "execute": [ + { + "description": "create table \"user\"", + "sql": "CREATE TABLE \"public\".\"user\" (\n \"address\" jsonb,\n \"createdAt\" timestamptz DEFAULT (now()) NOT NULL,\n \"displayName\" text NOT NULL,\n \"email\" text NOT NULL,\n \"id\" uuid NOT NULL,\n \"kind\" text NOT NULL,\n PRIMARY KEY (\"id\"),\n CONSTRAINT \"user_kind_check_836d43ef\" CHECK (\"kind\" IN ('admin', 'user'))\n)", + "params": [] + } + ], + "postcheck": [ + { + "description": "verify table \"user\" exists", + "sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_class\" AS \"c\" WHERE (\"c\".\"oid\" = to_regclass($1) AND (\"c\".\"relkind\" = $2 OR \"c\".\"relkind\" = $3))) AS \"result\"", + "params": ["\"public\".\"user\"", "r", "p"], + "expectation": { + "expects": "present", + "object": "table \"user\"" + } + } + ] + }, + { + "id": "table.post", + "label": "Create table \"post\"", + "summary": "Creates table \"post\"", + "operationClass": "additive", + "target": { + "id": "postgres", + "details": { + "schema": "public", + "objectType": "table", + "name": "post" + } + }, + "precheck": [ + { + "description": "ensure table \"post\" does not exist", + "sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"", + "params": ["\"public\".\"post\""], + "expectation": { + "expects": "absent", + "object": "table \"post\"" + } + } + ], + "execute": [ + { + "description": "create table \"post\"", + "sql": "CREATE TABLE \"public\".\"post\" (\n \"createdAt\" timestamptz DEFAULT (now()) NOT NULL,\n \"embedding\" vector(1536),\n \"id\" uuid NOT NULL,\n \"priority\" int4 DEFAULT 0 NOT NULL,\n \"title\" text NOT NULL,\n \"userId\" uuid NOT NULL,\n PRIMARY KEY (\"id\"),\n CONSTRAINT \"post_priority_check_b236ace9\" CHECK (\"priority\" IN (0, 1, 2))\n)", + "params": [] + } + ], + "postcheck": [ + { + "description": "verify table \"post\" exists", + "sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_class\" AS \"c\" WHERE (\"c\".\"oid\" = to_regclass($1) AND (\"c\".\"relkind\" = $2 OR \"c\".\"relkind\" = $3))) AS \"result\"", + "params": ["\"public\".\"post\"", "r", "p"], + "expectation": { + "expects": "present", + "object": "table \"post\"" + } + } + ] + }, + { + "id": "table.task", + "label": "Create table \"task\"", + "summary": "Creates table \"task\"", + "operationClass": "additive", + "target": { + "id": "postgres", + "details": { + "schema": "public", + "objectType": "table", + "name": "task" + } + }, + "precheck": [ + { + "description": "ensure table \"task\" does not exist", + "sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"", + "params": ["\"public\".\"task\""], + "expectation": { + "expects": "absent", + "object": "table \"task\"" + } + } + ], + "execute": [ + { + "description": "create table \"task\"", + "sql": "CREATE TABLE \"public\".\"task\" (\n \"createdAt\" timestamptz DEFAULT (now()) NOT NULL,\n \"description\" text,\n \"id\" uuid NOT NULL,\n \"status\" text DEFAULT 'open' NOT NULL,\n \"title\" text NOT NULL,\n \"type\" text NOT NULL,\n \"userId\" uuid NOT NULL,\n PRIMARY KEY (\"id\")\n)", + "params": [] + } + ], + "postcheck": [ + { + "description": "verify table \"task\" exists", + "sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_class\" AS \"c\" WHERE (\"c\".\"oid\" = to_regclass($1) AND (\"c\".\"relkind\" = $2 OR \"c\".\"relkind\" = $3))) AS \"result\"", + "params": ["\"public\".\"task\"", "r", "p"], + "expectation": { + "expects": "present", + "object": "table \"task\"" + } + } + ] + }, + { + "id": "table.post_tag", + "label": "Create table \"post_tag\"", + "summary": "Creates table \"post_tag\"", + "operationClass": "additive", + "target": { + "id": "postgres", + "details": { + "schema": "public", + "objectType": "table", + "name": "post_tag" + } + }, + "precheck": [ + { + "description": "ensure table \"post_tag\" does not exist", + "sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"", + "params": ["\"public\".\"post_tag\""], + "expectation": { + "expects": "absent", + "object": "table \"post_tag\"" + } + } + ], + "execute": [ + { + "description": "create table \"post_tag\"", + "sql": "CREATE TABLE \"public\".\"post_tag\" (\n \"postId\" uuid NOT NULL,\n \"tagId\" uuid NOT NULL,\n PRIMARY KEY (\"postId\", \"tagId\")\n)", + "params": [] + } + ], + "postcheck": [ + { + "description": "verify table \"post_tag\" exists", + "sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_class\" AS \"c\" WHERE (\"c\".\"oid\" = to_regclass($1) AND (\"c\".\"relkind\" = $2 OR \"c\".\"relkind\" = $3))) AS \"result\"", + "params": ["\"public\".\"post_tag\"", "r", "p"], + "expectation": { + "expects": "present", + "object": "table \"post_tag\"" + } + } + ] + }, + { + "id": "foreignKey.post.post_userId_fkey", + "label": "Add foreign key \"post_userId_fkey\" on \"post\"", + "operationClass": "additive", + "target": { + "id": "postgres", + "details": { + "schema": "public", + "objectType": "foreignKey", + "name": "post_userId_fkey", + "table": "post" + } + }, + "precheck": [ + { + "description": "ensure FK \"post_userId_fkey\" does not exist", + "sql": "SELECT NOT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"", + "params": ["post_userId_fkey", "public", "\"public\".\"post\""], + "expectation": { + "expects": "absent", + "object": "FK \"post_userId_fkey\" on \"post\"" + } + } + ], + "execute": [ + { + "description": "add FK \"post_userId_fkey\"", + "sql": "ALTER TABLE \"public\".\"post\"\nADD CONSTRAINT \"post_userId_fkey\"\nFOREIGN KEY (\"userId\")\nREFERENCES \"public\".\"user\" (\"id\")" + } + ], + "postcheck": [ + { + "description": "verify FK \"post_userId_fkey\" exists with the planned columns, referenced table and actions", + "sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3) AND \"c\".\"contype\" = $4 AND (SELECT coalesce(jsonb_agg(key_attribute.attname ORDER BY key_column.position), '[]'::jsonb) FROM unnest(\"c\".\"conkey\"::int2[]) WITH ORDINALITY AS key_column(attnum, position) JOIN pg_attribute key_attribute ON key_attribute.attrelid = \"c\".\"conrelid\" AND key_attribute.attnum = key_column.attnum) = ($5)::jsonb AND \"c\".\"confrelid\" = to_regclass($6) AND (SELECT coalesce(jsonb_agg(key_attribute.attname ORDER BY key_column.position), '[]'::jsonb) FROM unnest(\"c\".\"confkey\"::int2[]) WITH ORDINALITY AS key_column(attnum, position) JOIN pg_attribute key_attribute ON key_attribute.attrelid = \"c\".\"confrelid\" AND key_attribute.attnum = key_column.attnum) = ($7)::jsonb AND \"c\".\"confdeltype\" = $8 AND \"c\".\"confupdtype\" = $9)) AS \"result\"", + "params": [ + "post_userId_fkey", + "public", + "\"public\".\"post\"", + "f", + "[\"userId\"]", + "\"public\".\"user\"", + "[\"id\"]", + "a", + "a" + ] + } + ] + }, + { + "id": "index.post.post_userId_idx_a489d58a", + "label": "Create index \"post_userId_idx_a489d58a\" on \"post\"", + "operationClass": "additive", + "target": { + "id": "postgres", + "details": { + "schema": "public", + "objectType": "index", + "name": "post_userId_idx_a489d58a", + "table": "post" + } + }, + "precheck": [ + { + "description": "ensure index \"post_userId_idx_a489d58a\" does not exist", + "sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"", + "params": ["\"public\".\"post_userId_idx_a489d58a\""], + "expectation": { + "expects": "absent", + "object": "index \"post_userId_idx_a489d58a\"" + } + } + ], + "execute": [ + { + "description": "create index \"post_userId_idx_a489d58a\"", + "sql": "CREATE INDEX \"post_userId_idx_a489d58a\" ON \"public\".\"post\" (\"userId\")", + "params": [] + } + ], + "postcheck": [ + { + "description": "verify index \"post_userId_idx_a489d58a\" exists on \"post\" with the planned definition", + "sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_class\" AS \"c\" INNER JOIN \"pg_index\" AS \"i\" ON \"i\".\"indexrelid\" = \"c\".\"oid\" INNER JOIN \"pg_am\" AS \"m\" ON \"m\".\"oid\" = \"c\".\"relam\" WHERE (\"c\".\"oid\" = to_regclass($1) AND (\"c\".\"relkind\" = $2 OR \"c\".\"relkind\" = $3) AND \"i\".\"indrelid\" = to_regclass($4) AND NOT (\"i\".\"indisunique\") AND \"m\".\"amname\" = $5 AND (\"i\".\"indexprs\" IS NULL AND (SELECT coalesce(jsonb_agg(key_attribute.attname ORDER BY key_column.position), '[]'::jsonb) FROM unnest(\"i\".\"indkey\"::int2[]) WITH ORDINALITY AS key_column(attnum, position) JOIN pg_attribute key_attribute ON key_attribute.attrelid = \"i\".\"indrelid\" AND key_attribute.attnum = key_column.attnum) = ($6)::jsonb) AND \"i\".\"indpred\" IS NULL)) AS \"result\"", + "params": [ + "\"public\".\"post_userId_idx_a489d58a\"", + "i", + "I", + "\"public\".\"post\"", + "btree", + "[\"userId\"]" + ] + } + ] + }, + { + "id": "foreignKey.task.task_userId_fkey", + "label": "Add foreign key \"task_userId_fkey\" on \"task\"", + "operationClass": "additive", + "target": { + "id": "postgres", + "details": { + "schema": "public", + "objectType": "foreignKey", + "name": "task_userId_fkey", + "table": "task" + } + }, + "precheck": [ + { + "description": "ensure FK \"task_userId_fkey\" does not exist", + "sql": "SELECT NOT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"", + "params": ["task_userId_fkey", "public", "\"public\".\"task\""], + "expectation": { + "expects": "absent", + "object": "FK \"task_userId_fkey\" on \"task\"" + } + } + ], + "execute": [ + { + "description": "add FK \"task_userId_fkey\"", + "sql": "ALTER TABLE \"public\".\"task\"\nADD CONSTRAINT \"task_userId_fkey\"\nFOREIGN KEY (\"userId\")\nREFERENCES \"public\".\"user\" (\"id\")" + } + ], + "postcheck": [ + { + "description": "verify FK \"task_userId_fkey\" exists with the planned columns, referenced table and actions", + "sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3) AND \"c\".\"contype\" = $4 AND (SELECT coalesce(jsonb_agg(key_attribute.attname ORDER BY key_column.position), '[]'::jsonb) FROM unnest(\"c\".\"conkey\"::int2[]) WITH ORDINALITY AS key_column(attnum, position) JOIN pg_attribute key_attribute ON key_attribute.attrelid = \"c\".\"conrelid\" AND key_attribute.attnum = key_column.attnum) = ($5)::jsonb AND \"c\".\"confrelid\" = to_regclass($6) AND (SELECT coalesce(jsonb_agg(key_attribute.attname ORDER BY key_column.position), '[]'::jsonb) FROM unnest(\"c\".\"confkey\"::int2[]) WITH ORDINALITY AS key_column(attnum, position) JOIN pg_attribute key_attribute ON key_attribute.attrelid = \"c\".\"confrelid\" AND key_attribute.attnum = key_column.attnum) = ($7)::jsonb AND \"c\".\"confdeltype\" = $8 AND \"c\".\"confupdtype\" = $9)) AS \"result\"", + "params": [ + "task_userId_fkey", + "public", + "\"public\".\"task\"", + "f", + "[\"userId\"]", + "\"public\".\"user\"", + "[\"id\"]", + "a", + "a" + ] + } + ] + }, + { + "id": "index.task.task_userId_idx_a489d58a", + "label": "Create index \"task_userId_idx_a489d58a\" on \"task\"", + "operationClass": "additive", + "target": { + "id": "postgres", + "details": { + "schema": "public", + "objectType": "index", + "name": "task_userId_idx_a489d58a", + "table": "task" + } + }, + "precheck": [ + { + "description": "ensure index \"task_userId_idx_a489d58a\" does not exist", + "sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"", + "params": ["\"public\".\"task_userId_idx_a489d58a\""], + "expectation": { + "expects": "absent", + "object": "index \"task_userId_idx_a489d58a\"" + } + } + ], + "execute": [ + { + "description": "create index \"task_userId_idx_a489d58a\"", + "sql": "CREATE INDEX \"task_userId_idx_a489d58a\" ON \"public\".\"task\" (\"userId\")", + "params": [] + } + ], + "postcheck": [ + { + "description": "verify index \"task_userId_idx_a489d58a\" exists on \"task\" with the planned definition", + "sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_class\" AS \"c\" INNER JOIN \"pg_index\" AS \"i\" ON \"i\".\"indexrelid\" = \"c\".\"oid\" INNER JOIN \"pg_am\" AS \"m\" ON \"m\".\"oid\" = \"c\".\"relam\" WHERE (\"c\".\"oid\" = to_regclass($1) AND (\"c\".\"relkind\" = $2 OR \"c\".\"relkind\" = $3) AND \"i\".\"indrelid\" = to_regclass($4) AND NOT (\"i\".\"indisunique\") AND \"m\".\"amname\" = $5 AND (\"i\".\"indexprs\" IS NULL AND (SELECT coalesce(jsonb_agg(key_attribute.attname ORDER BY key_column.position), '[]'::jsonb) FROM unnest(\"i\".\"indkey\"::int2[]) WITH ORDINALITY AS key_column(attnum, position) JOIN pg_attribute key_attribute ON key_attribute.attrelid = \"i\".\"indrelid\" AND key_attribute.attnum = key_column.attnum) = ($6)::jsonb) AND \"i\".\"indpred\" IS NULL)) AS \"result\"", + "params": [ + "\"public\".\"task_userId_idx_a489d58a\"", + "i", + "I", + "\"public\".\"task\"", + "btree", + "[\"userId\"]" + ] + } + ] + }, + { + "id": "foreignKey.bug.bug_id_fkey", + "label": "Add foreign key \"bug_id_fkey\" on \"bug\"", + "operationClass": "additive", + "target": { + "id": "postgres", + "details": { + "schema": "public", + "objectType": "foreignKey", + "name": "bug_id_fkey", + "table": "bug" + } + }, + "precheck": [ + { + "description": "ensure FK \"bug_id_fkey\" does not exist", + "sql": "SELECT NOT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"", + "params": ["bug_id_fkey", "public", "\"public\".\"bug\""], + "expectation": { + "expects": "absent", + "object": "FK \"bug_id_fkey\" on \"bug\"" + } + } + ], + "execute": [ + { + "description": "add FK \"bug_id_fkey\"", + "sql": "ALTER TABLE \"public\".\"bug\"\nADD CONSTRAINT \"bug_id_fkey\"\nFOREIGN KEY (\"id\")\nREFERENCES \"public\".\"task\" (\"id\")\nON DELETE CASCADE" + } + ], + "postcheck": [ + { + "description": "verify FK \"bug_id_fkey\" exists with the planned columns, referenced table and actions", + "sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3) AND \"c\".\"contype\" = $4 AND (SELECT coalesce(jsonb_agg(key_attribute.attname ORDER BY key_column.position), '[]'::jsonb) FROM unnest(\"c\".\"conkey\"::int2[]) WITH ORDINALITY AS key_column(attnum, position) JOIN pg_attribute key_attribute ON key_attribute.attrelid = \"c\".\"conrelid\" AND key_attribute.attnum = key_column.attnum) = ($5)::jsonb AND \"c\".\"confrelid\" = to_regclass($6) AND (SELECT coalesce(jsonb_agg(key_attribute.attname ORDER BY key_column.position), '[]'::jsonb) FROM unnest(\"c\".\"confkey\"::int2[]) WITH ORDINALITY AS key_column(attnum, position) JOIN pg_attribute key_attribute ON key_attribute.attrelid = \"c\".\"confrelid\" AND key_attribute.attnum = key_column.attnum) = ($7)::jsonb AND \"c\".\"confdeltype\" = $8 AND \"c\".\"confupdtype\" = $9)) AS \"result\"", + "params": [ + "bug_id_fkey", + "public", + "\"public\".\"bug\"", + "f", + "[\"id\"]", + "\"public\".\"task\"", + "[\"id\"]", + "c", + "a" + ] + } + ] + }, + { + "id": "foreignKey.feature.feature_id_fkey", + "label": "Add foreign key \"feature_id_fkey\" on \"feature\"", + "operationClass": "additive", + "target": { + "id": "postgres", + "details": { + "schema": "public", + "objectType": "foreignKey", + "name": "feature_id_fkey", + "table": "feature" + } + }, + "precheck": [ + { + "description": "ensure FK \"feature_id_fkey\" does not exist", + "sql": "SELECT NOT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"", + "params": ["feature_id_fkey", "public", "\"public\".\"feature\""], + "expectation": { + "expects": "absent", + "object": "FK \"feature_id_fkey\" on \"feature\"" + } + } + ], + "execute": [ + { + "description": "add FK \"feature_id_fkey\"", + "sql": "ALTER TABLE \"public\".\"feature\"\nADD CONSTRAINT \"feature_id_fkey\"\nFOREIGN KEY (\"id\")\nREFERENCES \"public\".\"task\" (\"id\")\nON DELETE CASCADE" + } + ], + "postcheck": [ + { + "description": "verify FK \"feature_id_fkey\" exists with the planned columns, referenced table and actions", + "sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3) AND \"c\".\"contype\" = $4 AND (SELECT coalesce(jsonb_agg(key_attribute.attname ORDER BY key_column.position), '[]'::jsonb) FROM unnest(\"c\".\"conkey\"::int2[]) WITH ORDINALITY AS key_column(attnum, position) JOIN pg_attribute key_attribute ON key_attribute.attrelid = \"c\".\"conrelid\" AND key_attribute.attnum = key_column.attnum) = ($5)::jsonb AND \"c\".\"confrelid\" = to_regclass($6) AND (SELECT coalesce(jsonb_agg(key_attribute.attname ORDER BY key_column.position), '[]'::jsonb) FROM unnest(\"c\".\"confkey\"::int2[]) WITH ORDINALITY AS key_column(attnum, position) JOIN pg_attribute key_attribute ON key_attribute.attrelid = \"c\".\"confrelid\" AND key_attribute.attnum = key_column.attnum) = ($7)::jsonb AND \"c\".\"confdeltype\" = $8 AND \"c\".\"confupdtype\" = $9)) AS \"result\"", + "params": [ + "feature_id_fkey", + "public", + "\"public\".\"feature\"", + "f", + "[\"id\"]", + "\"public\".\"task\"", + "[\"id\"]", + "c", + "a" + ] + } + ] + }, + { + "id": "index.post_tag.post_tag_postId_idx_a7a72715", + "label": "Create index \"post_tag_postId_idx_a7a72715\" on \"post_tag\"", + "operationClass": "additive", + "target": { + "id": "postgres", + "details": { + "schema": "public", + "objectType": "index", + "name": "post_tag_postId_idx_a7a72715", + "table": "post_tag" + } + }, + "precheck": [ + { + "description": "ensure index \"post_tag_postId_idx_a7a72715\" does not exist", + "sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"", + "params": ["\"public\".\"post_tag_postId_idx_a7a72715\""], + "expectation": { + "expects": "absent", + "object": "index \"post_tag_postId_idx_a7a72715\"" + } + } + ], + "execute": [ + { + "description": "create index \"post_tag_postId_idx_a7a72715\"", + "sql": "CREATE INDEX \"post_tag_postId_idx_a7a72715\" ON \"public\".\"post_tag\" (\"postId\")", + "params": [] + } + ], + "postcheck": [ + { + "description": "verify index \"post_tag_postId_idx_a7a72715\" exists on \"post_tag\" with the planned definition", + "sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_class\" AS \"c\" INNER JOIN \"pg_index\" AS \"i\" ON \"i\".\"indexrelid\" = \"c\".\"oid\" INNER JOIN \"pg_am\" AS \"m\" ON \"m\".\"oid\" = \"c\".\"relam\" WHERE (\"c\".\"oid\" = to_regclass($1) AND (\"c\".\"relkind\" = $2 OR \"c\".\"relkind\" = $3) AND \"i\".\"indrelid\" = to_regclass($4) AND NOT (\"i\".\"indisunique\") AND \"m\".\"amname\" = $5 AND (\"i\".\"indexprs\" IS NULL AND (SELECT coalesce(jsonb_agg(key_attribute.attname ORDER BY key_column.position), '[]'::jsonb) FROM unnest(\"i\".\"indkey\"::int2[]) WITH ORDINALITY AS key_column(attnum, position) JOIN pg_attribute key_attribute ON key_attribute.attrelid = \"i\".\"indrelid\" AND key_attribute.attnum = key_column.attnum) = ($6)::jsonb) AND \"i\".\"indpred\" IS NULL)) AS \"result\"", + "params": [ + "\"public\".\"post_tag_postId_idx_a7a72715\"", + "i", + "I", + "\"public\".\"post_tag\"", + "btree", + "[\"postId\"]" + ] + } + ] + }, + { + "id": "index.post_tag.post_tag_tagId_idx_86854244", + "label": "Create index \"post_tag_tagId_idx_86854244\" on \"post_tag\"", + "operationClass": "additive", + "target": { + "id": "postgres", + "details": { + "schema": "public", + "objectType": "index", + "name": "post_tag_tagId_idx_86854244", + "table": "post_tag" + } + }, + "precheck": [ + { + "description": "ensure index \"post_tag_tagId_idx_86854244\" does not exist", + "sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"", + "params": ["\"public\".\"post_tag_tagId_idx_86854244\""], + "expectation": { + "expects": "absent", + "object": "index \"post_tag_tagId_idx_86854244\"" + } + } + ], + "execute": [ + { + "description": "create index \"post_tag_tagId_idx_86854244\"", + "sql": "CREATE INDEX \"post_tag_tagId_idx_86854244\" ON \"public\".\"post_tag\" (\"tagId\")", + "params": [] + } + ], + "postcheck": [ + { + "description": "verify index \"post_tag_tagId_idx_86854244\" exists on \"post_tag\" with the planned definition", + "sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_class\" AS \"c\" INNER JOIN \"pg_index\" AS \"i\" ON \"i\".\"indexrelid\" = \"c\".\"oid\" INNER JOIN \"pg_am\" AS \"m\" ON \"m\".\"oid\" = \"c\".\"relam\" WHERE (\"c\".\"oid\" = to_regclass($1) AND (\"c\".\"relkind\" = $2 OR \"c\".\"relkind\" = $3) AND \"i\".\"indrelid\" = to_regclass($4) AND NOT (\"i\".\"indisunique\") AND \"m\".\"amname\" = $5 AND (\"i\".\"indexprs\" IS NULL AND (SELECT coalesce(jsonb_agg(key_attribute.attname ORDER BY key_column.position), '[]'::jsonb) FROM unnest(\"i\".\"indkey\"::int2[]) WITH ORDINALITY AS key_column(attnum, position) JOIN pg_attribute key_attribute ON key_attribute.attrelid = \"i\".\"indrelid\" AND key_attribute.attnum = key_column.attnum) = ($6)::jsonb) AND \"i\".\"indpred\" IS NULL)) AS \"result\"", + "params": [ + "\"public\".\"post_tag_tagId_idx_86854244\"", + "i", + "I", + "\"public\".\"post_tag\"", + "btree", + "[\"tagId\"]" + ] + } + ] + }, + { + "id": "foreignKey.post_tag.post_tag_postId_fkey", + "label": "Add foreign key \"post_tag_postId_fkey\" on \"post_tag\"", + "operationClass": "additive", + "target": { + "id": "postgres", + "details": { + "schema": "public", + "objectType": "foreignKey", + "name": "post_tag_postId_fkey", + "table": "post_tag" + } + }, + "precheck": [ + { + "description": "ensure FK \"post_tag_postId_fkey\" does not exist", + "sql": "SELECT NOT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"", + "params": ["post_tag_postId_fkey", "public", "\"public\".\"post_tag\""], + "expectation": { + "expects": "absent", + "object": "FK \"post_tag_postId_fkey\" on \"post_tag\"" + } + } + ], + "execute": [ + { + "description": "add FK \"post_tag_postId_fkey\"", + "sql": "ALTER TABLE \"public\".\"post_tag\"\nADD CONSTRAINT \"post_tag_postId_fkey\"\nFOREIGN KEY (\"postId\")\nREFERENCES \"public\".\"post\" (\"id\")" + } + ], + "postcheck": [ + { + "description": "verify FK \"post_tag_postId_fkey\" exists with the planned columns, referenced table and actions", + "sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3) AND \"c\".\"contype\" = $4 AND (SELECT coalesce(jsonb_agg(key_attribute.attname ORDER BY key_column.position), '[]'::jsonb) FROM unnest(\"c\".\"conkey\"::int2[]) WITH ORDINALITY AS key_column(attnum, position) JOIN pg_attribute key_attribute ON key_attribute.attrelid = \"c\".\"conrelid\" AND key_attribute.attnum = key_column.attnum) = ($5)::jsonb AND \"c\".\"confrelid\" = to_regclass($6) AND (SELECT coalesce(jsonb_agg(key_attribute.attname ORDER BY key_column.position), '[]'::jsonb) FROM unnest(\"c\".\"confkey\"::int2[]) WITH ORDINALITY AS key_column(attnum, position) JOIN pg_attribute key_attribute ON key_attribute.attrelid = \"c\".\"confrelid\" AND key_attribute.attnum = key_column.attnum) = ($7)::jsonb AND \"c\".\"confdeltype\" = $8 AND \"c\".\"confupdtype\" = $9)) AS \"result\"", + "params": [ + "post_tag_postId_fkey", + "public", + "\"public\".\"post_tag\"", + "f", + "[\"postId\"]", + "\"public\".\"post\"", + "[\"id\"]", + "a", + "a" + ] + } + ] + }, + { + "id": "foreignKey.post_tag.post_tag_tagId_fkey", + "label": "Add foreign key \"post_tag_tagId_fkey\" on \"post_tag\"", + "operationClass": "additive", + "target": { + "id": "postgres", + "details": { + "schema": "public", + "objectType": "foreignKey", + "name": "post_tag_tagId_fkey", + "table": "post_tag" + } + }, + "precheck": [ + { + "description": "ensure FK \"post_tag_tagId_fkey\" does not exist", + "sql": "SELECT NOT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"", + "params": ["post_tag_tagId_fkey", "public", "\"public\".\"post_tag\""], + "expectation": { + "expects": "absent", + "object": "FK \"post_tag_tagId_fkey\" on \"post_tag\"" + } + } + ], + "execute": [ + { + "description": "add FK \"post_tag_tagId_fkey\"", + "sql": "ALTER TABLE \"public\".\"post_tag\"\nADD CONSTRAINT \"post_tag_tagId_fkey\"\nFOREIGN KEY (\"tagId\")\nREFERENCES \"public\".\"tag\" (\"id\")" + } + ], + "postcheck": [ + { + "description": "verify FK \"post_tag_tagId_fkey\" exists with the planned columns, referenced table and actions", + "sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3) AND \"c\".\"contype\" = $4 AND (SELECT coalesce(jsonb_agg(key_attribute.attname ORDER BY key_column.position), '[]'::jsonb) FROM unnest(\"c\".\"conkey\"::int2[]) WITH ORDINALITY AS key_column(attnum, position) JOIN pg_attribute key_attribute ON key_attribute.attrelid = \"c\".\"conrelid\" AND key_attribute.attnum = key_column.attnum) = ($5)::jsonb AND \"c\".\"confrelid\" = to_regclass($6) AND (SELECT coalesce(jsonb_agg(key_attribute.attname ORDER BY key_column.position), '[]'::jsonb) FROM unnest(\"c\".\"confkey\"::int2[]) WITH ORDINALITY AS key_column(attnum, position) JOIN pg_attribute key_attribute ON key_attribute.attrelid = \"c\".\"confrelid\" AND key_attribute.attnum = key_column.attnum) = ($7)::jsonb AND \"c\".\"confdeltype\" = $8 AND \"c\".\"confupdtype\" = $9)) AS \"result\"", + "params": [ + "post_tag_tagId_fkey", + "public", + "\"public\".\"post_tag\"", + "f", + "[\"tagId\"]", + "\"public\".\"tag\"", + "[\"id\"]", + "a", + "a" + ] + } + ] + } +] diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/sql/migrations/snapshots/1cb334a7b32d249d4fea0934dbbaa48c473b5946cd77ee1ce7442272479d6cf3/contract.d.ts b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/sql/migrations/snapshots/1cb334a7b32d249d4fea0934dbbaa48c473b5946cd77ee1ce7442272479d6cf3/contract.d.ts new file mode 100644 index 000000000000..2c5c317b884a --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/sql/migrations/snapshots/1cb334a7b32d249d4fea0934dbbaa48c473b5946cd77ee1ce7442272479d6cf3/contract.d.ts @@ -0,0 +1,1323 @@ +// ⚠️ GENERATED FILE - DO NOT EDIT +// This file is automatically generated by 'prisma contract emit'. +// To regenerate, run: prisma contract emit +import type { + CodecTypes as PgVectorTypes, + ParameterizedOutputTypesForTypeParams as PgVectorParameterizedOutputTypes, +} from '@prisma/orm-extension-pgvector/codec-types'; +import type { QueryOperationTypes as PgVectorQueryOperationTypes } from '@prisma/orm-extension-pgvector/operation-types'; +import type { + CodecTypes as PgTypes, + ParameterizedOutputTypesForTypeParams as PgParameterizedOutputTypes, +} from '@prisma/orm-postgres/target/codec-types'; +import type { QueryOperationTypes as PgTargetQueryOps } from '@prisma/orm-postgres/target/operation-types'; + +import type { + ColumnTypesFromStorage, + ContractWithTypeMaps, + RelationKeys, + TypeMaps as TypeMapsType, +} from '@prisma/orm-postgres/family-contract/types'; +import type { + Contract as ContractType, + ExecutionHashBase, + FieldTypesFromDomain, + NamespaceId, + ProfileHashBase, + StorageHashBase, + ValueObjectTypesFromStorage, +} from '@prisma/orm-postgres/contract/types'; + +export type StorageHash = + StorageHashBase<'1cb334a7b32d249d4fea0934dbbaa48c473b5946cd77ee1ce7442272479d6cf3'>; +export type ExecutionHash = + ExecutionHashBase<'eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655'>; +export type ProfileHash = + ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; + +export type CodecTypes = PgTypes & PgVectorTypes; +export type LaneCodecTypes = CodecTypes; +export type QueryOperationTypes = PgTargetQueryOps & + PgVectorQueryOperationTypes; +export type AggregateTypes = { + readonly avg: { + readonly byCodec: { + readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + }; + }; + readonly avgDecimal: { + readonly byCodec: { + readonly 'pg/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + }; + }; + readonly count: { + readonly byCodec: {}; + readonly withoutInput: { readonly output: 'pg/int8number@1'; readonly nullable: false }; + readonly anyInput: { readonly output: 'pg/int8number@1'; readonly nullable: false }; + }; + readonly countBigInt: { + readonly byCodec: {}; + readonly withoutInput: { readonly output: 'pg/int8@1'; readonly nullable: false }; + readonly anyInput: { readonly output: 'pg/int8@1'; readonly nullable: false }; + }; + readonly max: { + readonly byCodec: { + readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true }; + readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true }; + readonly 'pg/date-temporal@1': { + readonly output: 'pg/date-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true }; + readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; + readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { + readonly output: 'pg/time-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-string@1': { + readonly output: 'pg/timestamp-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-temporal@1': { + readonly output: 'pg/timestamp-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-date@1': { + readonly output: 'pg/timestamptz-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-string@1': { + readonly output: 'pg/timestamptz-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-temporal@1': { + readonly output: 'pg/timestamptz-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true }; + readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true }; + readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + }; + }; + readonly min: { + readonly byCodec: { + readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true }; + readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true }; + readonly 'pg/date-temporal@1': { + readonly output: 'pg/date-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true }; + readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; + readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { + readonly output: 'pg/time-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-string@1': { + readonly output: 'pg/timestamp-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-temporal@1': { + readonly output: 'pg/timestamp-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-date@1': { + readonly output: 'pg/timestamptz-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-string@1': { + readonly output: 'pg/timestamptz-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-temporal@1': { + readonly output: 'pg/timestamptz-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true }; + readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true }; + readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + }; + }; + readonly stddev: { + readonly byCodec: { + readonly 'pg/float@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + }; + }; + readonly sum: { + readonly byCodec: { + readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + }; + }; + readonly sumBigInt: { + readonly byCodec: { + readonly 'pg/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'sql/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + }; + }; +}; +type DefaultLiteralValue = CodecId extends keyof CodecTypes + ? Encoded extends CodecTypes[CodecId]['json'] + ? Encoded + : CodecTypes[CodecId]['json'] + : Encoded; +type ParameterizedOutputTypeFunctions = + PgParameterizedOutputTypes | PgVectorParameterizedOutputTypes; +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output' +>; +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input' +>; +export type AddressOutput = ValueObjectOutputTypes['public']['Address']; +export type AddressInput = ValueObjectInputTypes['public']['Address']; +type FieldTypeOverrides = { + readonly public: { + readonly models: { + readonly Post: { + readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 }; + }; + readonly User: { + readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; + }; + }; + }; +}; +export type FieldOutputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output', + ContractBase['storage']['namespaces'] +>; +export type FieldInputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input', + ContractBase['storage']['namespaces'] +>; +type ColumnTypeOverrides = { + readonly public: { + readonly post: { readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 } }; + readonly user: { + readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; + }; + }; +}; +export type StorageColumnTypes = ColumnTypesFromStorage< + ContractBase['storage'], + CodecTypes, + ParameterizedOutputTypeFunctions, + ColumnTypeOverrides, + 'output' +>; +export type StorageColumnInputTypes = ColumnTypesFromStorage< + ContractBase['storage'], + CodecTypes, + ParameterizedOutputTypeFunctions, + ColumnTypeOverrides, + 'input' +>; + +export namespace Models { + export type public_Bug = { + createdAt: FieldOutputTypes['public']['Task']['createdAt']; + description: FieldOutputTypes['public']['Task']['description']; + id: FieldOutputTypes['public']['Task']['id']; + status: FieldOutputTypes['public']['Task']['status']; + title: FieldOutputTypes['public']['Task']['title']; + type: 'bug'; + userId: FieldOutputTypes['public']['Task']['userId']; + severity: FieldOutputTypes['public']['Bug']['severity']; + stepsToRepro: FieldOutputTypes['public']['Bug']['stepsToRepro']; + user: public_User; + readonly [RelationKeys]?: 'user'; + }; + export type public_Feature = { + createdAt: FieldOutputTypes['public']['Task']['createdAt']; + description: FieldOutputTypes['public']['Task']['description']; + id: FieldOutputTypes['public']['Task']['id']; + status: FieldOutputTypes['public']['Task']['status']; + title: FieldOutputTypes['public']['Task']['title']; + type: 'feature'; + userId: FieldOutputTypes['public']['Task']['userId']; + priority: FieldOutputTypes['public']['Feature']['priority']; + targetRelease: FieldOutputTypes['public']['Feature']['targetRelease']; + user: public_User; + readonly [RelationKeys]?: 'user'; + }; + export type public_Post = { + createdAt: FieldOutputTypes['public']['Post']['createdAt']; + embedding: FieldOutputTypes['public']['Post']['embedding']; + id: FieldOutputTypes['public']['Post']['id']; + priority: FieldOutputTypes['public']['Post']['priority']; + title: FieldOutputTypes['public']['Post']['title']; + userId: FieldOutputTypes['public']['Post']['userId']; + tags: public_Tag[]; + user: public_User; + readonly [RelationKeys]?: 'tags' | 'user'; + }; + export type public_PostTag = { + postId: FieldOutputTypes['public']['PostTag']['postId']; + tagId: FieldOutputTypes['public']['PostTag']['tagId']; + post: public_Post; + tag: public_Tag; + readonly [RelationKeys]?: 'post' | 'tag'; + }; + export type public_Tag = { + id: FieldOutputTypes['public']['Tag']['id']; + label: FieldOutputTypes['public']['Tag']['label']; + posts: public_Post[]; + readonly [RelationKeys]?: 'posts'; + }; + export type public_Task = { + createdAt: FieldOutputTypes['public']['Task']['createdAt']; + description: FieldOutputTypes['public']['Task']['description']; + id: FieldOutputTypes['public']['Task']['id']; + status: FieldOutputTypes['public']['Task']['status']; + title: FieldOutputTypes['public']['Task']['title']; + type: 'bug' | 'feature'; + userId: FieldOutputTypes['public']['Task']['userId']; + user: public_User; + readonly [RelationKeys]?: 'user'; + }; + export type public_User = { + address: FieldOutputTypes['public']['User']['address']; + createdAt: FieldOutputTypes['public']['User']['createdAt']; + displayName: FieldOutputTypes['public']['User']['displayName']; + email: FieldOutputTypes['public']['User']['email']; + id: FieldOutputTypes['public']['User']['id']; + kind: FieldOutputTypes['public']['User']['kind']; + posts: public_Post[]; + tasks: public_AnyTask[]; + readonly [RelationKeys]?: 'posts' | 'tasks'; + }; + export type public_AnyTask = public_Bug | public_Feature; +} + +export declare const models: { + public: { + Bug: Models.public_Bug; + Feature: Models.public_Feature; + Post: Models.public_Post; + PostTag: Models.public_PostTag; + Tag: Models.public_Tag; + Task: Models.public_Task; + User: Models.public_User; + AnyTask: Models.public_AnyTask; + }; +}; + +export type TypeMaps = TypeMapsType< + CodecTypes, + QueryOperationTypes, + FieldOutputTypes, + FieldInputTypes, + StorageColumnTypes, + StorageColumnInputTypes, + AggregateTypes +>; + +type ContractBase = Omit< + ContractType<{ + readonly namespaces: { + readonly public: { + readonly id: 'public'; + readonly kind: 'postgres-schema'; + readonly entries: { + readonly table: { + readonly bug: { + columns: { + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly severity: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly stepsToRepro: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: true; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'bug'; + readonly columns: readonly ['id']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'task'; + readonly columns: readonly ['id']; + }; + }, + ]; + }; + readonly feature: { + columns: { + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly priority: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly targetRelease: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: true; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'feature'; + readonly columns: readonly ['id']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'task'; + readonly columns: readonly ['id']; + }; + }, + ]; + }; + readonly post: { + columns: { + readonly createdAt: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-temporal@1'; + readonly nullable: false; + readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; + readonly many: false; + }; + readonly embedding: { + readonly dataType: 'pgvector/vector'; + readonly codecId: 'pg/vector@1'; + readonly nullable: true; + readonly typeParams: { readonly length: 1536 }; + readonly many: false; + }; + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly priority: { + readonly dataType: 'pg/int4'; + readonly codecId: 'pg/int4@1'; + readonly nullable: false; + readonly default: { + readonly kind: 'literal'; + readonly value: DefaultLiteralValue<'pg/int4@1', 0>; + }; + readonly many: false; + }; + readonly title: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly userId: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly [ + { + readonly name: 'post_userId_idx_a489d58a'; + readonly prefix: 'post_userId_idx'; + readonly columns: readonly ['userId']; + readonly unique: false; + }, + ]; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'post'; + readonly columns: readonly ['userId']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'user'; + readonly columns: readonly ['id']; + }; + }, + ]; + }; + readonly post_tag: { + columns: { + readonly postId: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly tagId: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['postId', 'tagId'] }; + uniques: readonly []; + indexes: readonly [ + { + readonly name: 'post_tag_postId_idx_a7a72715'; + readonly prefix: 'post_tag_postId_idx'; + readonly columns: readonly ['postId']; + readonly unique: false; + }, + { + readonly name: 'post_tag_tagId_idx_86854244'; + readonly prefix: 'post_tag_tagId_idx'; + readonly columns: readonly ['tagId']; + readonly unique: false; + }, + ]; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'post_tag'; + readonly columns: readonly ['postId']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'post'; + readonly columns: readonly ['id']; + }; + }, + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'post_tag'; + readonly columns: readonly ['tagId']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'tag'; + readonly columns: readonly ['id']; + }; + }, + ]; + }; + readonly tag: { + columns: { + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly label: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly [{ readonly columns: readonly ['label'] }]; + indexes: readonly []; + foreignKeys: readonly []; + }; + readonly task: { + columns: { + readonly createdAt: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-temporal@1'; + readonly nullable: false; + readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; + readonly many: false; + }; + readonly description: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: true; + readonly many: false; + }; + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly status: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly default: { + readonly kind: 'literal'; + readonly value: DefaultLiteralValue<'pg/text@1', 'open'>; + }; + readonly many: false; + }; + readonly title: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly type: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly userId: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly [ + { + readonly name: 'task_userId_idx_a489d58a'; + readonly prefix: 'task_userId_idx'; + readonly columns: readonly ['userId']; + readonly unique: false; + }, + ]; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'task'; + readonly columns: readonly ['userId']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'user'; + readonly columns: readonly ['id']; + }; + }, + ]; + }; + readonly user: { + columns: { + readonly address: { + readonly dataType: 'pg/jsonb'; + readonly codecId: 'pg/jsonb@1'; + readonly nullable: true; + readonly many: false; + }; + readonly createdAt: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-temporal@1'; + readonly nullable: false; + readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; + readonly many: false; + }; + readonly displayName: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly email: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly kind: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly []; + }; + }; + readonly valueSet: { + readonly Priority: { readonly kind: 'valueSet'; readonly values: readonly [0, 1, 2] }; + readonly user_type: { + readonly kind: 'valueSet'; + readonly values: readonly ['admin', 'user']; + }; + }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + }; + }; + }; + }; + }; + readonly storageHash: StorageHash; + }>, + 'roots' | 'domain' +> & { + readonly target: 'postgres'; + readonly targetFamily: 'sql'; + readonly roots: { + readonly post: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; + readonly post_tag: { readonly namespace: 'public' & NamespaceId; readonly model: 'PostTag' }; + readonly tag: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; + readonly task: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; + readonly user: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; + }; + readonly domain: { + readonly namespaces: { + readonly public: { + readonly models: { + readonly Bug: { + readonly fields: { + readonly severity: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly stepsToRepro: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'bug'; + readonly namespaceId: 'public'; + readonly fields: { + readonly severity: { readonly column: 'severity' }; + readonly stepsToRepro: { readonly column: 'stepsToRepro' }; + }; + }; + readonly base: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; + }; + readonly Feature: { + readonly fields: { + readonly priority: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly targetRelease: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'feature'; + readonly namespaceId: 'public'; + readonly fields: { + readonly priority: { readonly column: 'priority' }; + readonly targetRelease: { readonly column: 'targetRelease' }; + }; + }; + readonly base: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; + }; + readonly Post: { + readonly fields: { + readonly createdAt: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-temporal@1'; + }; + }; + readonly embedding: { + readonly nullable: true; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/vector@1'; + readonly typeParams: { readonly length: 1536 }; + }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly priority: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int4@1' }; + }; + readonly title: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly userId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + }; + readonly relations: { + readonly tags: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; + readonly cardinality: 'N:M'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['postId']; + }; + readonly through: { + readonly table: 'post_tag'; + readonly namespaceId: 'public'; + readonly parentColumns: readonly ['postId']; + readonly childColumns: readonly ['tagId']; + readonly targetColumns: readonly ['id']; + }; + }; + readonly user: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['userId']; + readonly targetFields: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly table: 'post'; + readonly namespaceId: 'public'; + readonly fields: { + readonly createdAt: { readonly column: 'createdAt' }; + readonly embedding: { readonly column: 'embedding' }; + readonly id: { readonly column: 'id' }; + readonly priority: { readonly column: 'priority' }; + readonly title: { readonly column: 'title' }; + readonly userId: { readonly column: 'userId' }; + }; + }; + }; + readonly PostTag: { + readonly fields: { + readonly postId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly tagId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + }; + readonly relations: { + readonly post: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['postId']; + readonly targetFields: readonly ['id']; + }; + }; + readonly tag: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['tagId']; + readonly targetFields: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly table: 'post_tag'; + readonly namespaceId: 'public'; + readonly fields: { + readonly postId: { readonly column: 'postId' }; + readonly tagId: { readonly column: 'tagId' }; + }; + }; + }; + readonly Tag: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly label: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: { + readonly posts: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; + readonly cardinality: 'N:M'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['tagId']; + }; + readonly through: { + readonly table: 'post_tag'; + readonly namespaceId: 'public'; + readonly parentColumns: readonly ['tagId']; + readonly childColumns: readonly ['postId']; + readonly targetColumns: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly table: 'tag'; + readonly namespaceId: 'public'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly label: { readonly column: 'label' }; + }; + }; + }; + readonly Task: { + readonly fields: { + readonly createdAt: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-temporal@1'; + }; + }; + readonly description: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly status: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly title: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly type: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly userId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + }; + readonly relations: { + readonly user: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['userId']; + readonly targetFields: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly table: 'task'; + readonly namespaceId: 'public'; + readonly fields: { + readonly createdAt: { readonly column: 'createdAt' }; + readonly description: { readonly column: 'description' }; + readonly id: { readonly column: 'id' }; + readonly status: { readonly column: 'status' }; + readonly title: { readonly column: 'title' }; + readonly type: { readonly column: 'type' }; + readonly userId: { readonly column: 'userId' }; + }; + }; + readonly discriminator: { readonly field: 'type' }; + readonly variants: { + readonly bug: { readonly namespace: 'public' & NamespaceId; readonly model: 'Bug' }; + readonly feature: { + readonly namespace: 'public' & NamespaceId; + readonly model: 'Feature'; + }; + }; + }; + readonly User: { + readonly fields: { + readonly address: { + readonly nullable: true; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: 'public'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; + }; + readonly createdAt: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-temporal@1'; + }; + }; + readonly displayName: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly kind: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: { + readonly posts: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; + readonly cardinality: '1:N'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['userId']; + }; + }; + readonly tasks: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; + readonly cardinality: '1:N'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['userId']; + }; + }; + }; + readonly storage: { + readonly table: 'user'; + readonly namespaceId: 'public'; + readonly fields: { + readonly address: { readonly column: 'address' }; + readonly createdAt: { readonly column: 'createdAt' }; + readonly displayName: { readonly column: 'displayName' }; + readonly email: { readonly column: 'email' }; + readonly id: { readonly column: 'id' }; + readonly kind: { readonly column: 'kind' }; + }; + }; + }; + }; + readonly enum: { + readonly Priority: { + readonly codecId: 'pg/int4@1'; + readonly members: readonly [ + { readonly name: 'Low'; readonly value: 0 }, + { readonly name: 'High'; readonly value: 1 }, + { readonly name: 'Urgent'; readonly value: 2 }, + ]; + }; + readonly user_type: { + readonly codecId: 'pg/text@1'; + readonly members: readonly [ + { readonly name: 'admin'; readonly value: 'admin' }, + { readonly name: 'user'; readonly value: 'user' }, + ]; + }; + }; + readonly enumMemberTypes?: { + readonly Priority: readonly [ + { readonly name: 'Low'; readonly value: 0 }, + { readonly name: 'High'; readonly value: 1 }, + { readonly name: 'Urgent'; readonly value: 2 }, + ]; + readonly user_type: readonly [ + { readonly name: 'admin'; readonly value: 'admin' }, + { readonly name: 'user'; readonly value: 'user' }, + ]; + }; + }; + }; + }; + readonly capabilities: { + readonly postgres: { + readonly distinctOn: true; + readonly forKeyShare: true; + readonly forNoKeyUpdate: true; + readonly jsonAgg: true; + readonly lateral: true; + readonly limit: true; + readonly orderBy: true; + readonly 'pgvector.cosine': true; + readonly returning: true; + }; + readonly sql: { + readonly checkConstraint: true; + readonly defaultInInsert: true; + readonly enums: true; + readonly forShare: true; + readonly forUpdate: true; + readonly insertOnConflictSkip: true; + readonly insertOnConflictWithoutTarget: true; + readonly lateral: true; + readonly lockNowait: true; + readonly lockOf: true; + readonly lockSkipLocked: true; + readonly returning: true; + readonly scalarList: true; + }; + }; + readonly extensions: { + readonly 'demo/engagement-stats': { + readonly familyId: 'sql'; + readonly id: 'demo/engagement-stats'; + readonly kind: 'extension'; + readonly targetId: 'postgres'; + readonly types: { + readonly aggregateDescriptors: readonly [ + { + readonly input: { readonly kind: 'trait'; readonly trait: 'numeric' }; + readonly lower: unknown; + readonly nullable: true; + readonly operation: 'stddev'; + readonly output: { readonly codecId: 'pg/numeric@1'; readonly kind: 'codec' }; + }, + ]; + }; + readonly version: '0.0.1'; + }; + readonly pgvector: { + readonly capabilities: { readonly postgres: { readonly 'pgvector.cosine': true } }; + readonly familyId: 'sql'; + readonly id: 'pgvector'; + readonly kind: 'extension'; + readonly targetId: 'postgres'; + readonly types: { + readonly codecTypes: { + readonly import: { + readonly alias: 'PgVectorTypes'; + readonly named: 'CodecTypes'; + readonly package: '@internal/extension-pgvector/codec-types'; + }; + readonly parameterizedOutputTypes: { + readonly alias: 'PgVectorParameterizedOutputTypes'; + readonly named: 'ParameterizedOutputTypesForTypeParams'; + readonly package: '@internal/extension-pgvector/codec-types'; + }; + }; + readonly operationTypes: { + readonly import: { + readonly alias: 'PgVectorOperationTypes'; + readonly named: 'OperationTypes'; + readonly package: '@internal/extension-pgvector/operation-types'; + }; + }; + readonly queryOperationTypes: { + readonly import: { + readonly alias: 'PgVectorQueryOperationTypes'; + readonly named: 'QueryOperationTypes'; + readonly package: '@internal/extension-pgvector/operation-types'; + }; + }; + }; + readonly version: '0.0.1'; + }; + }; + readonly execution: { + readonly executionHash: ExecutionHash; + readonly mutations: { + readonly defaults: readonly [ + { + readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; + readonly ref: { + readonly entry: 'post'; + readonly field: 'id'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; + readonly ref: { + readonly entry: 'tag'; + readonly field: 'id'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; + readonly ref: { + readonly entry: 'task'; + readonly field: 'id'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; + readonly ref: { + readonly entry: 'user'; + readonly field: 'id'; + readonly namespace: 'public'; + }; + }, + ]; + }; + }; + readonly meta: {}; + readonly profileHash: ProfileHash; +}; + +export type Contract = ContractWithTypeMaps; + +export type Namespaces = Contract['storage']['namespaces']; diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/sql/migrations/snapshots/1cb334a7b32d249d4fea0934dbbaa48c473b5946cd77ee1ce7442272479d6cf3/contract.json b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/sql/migrations/snapshots/1cb334a7b32d249d4fea0934dbbaa48c473b5946cd77ee1ce7442272479d6cf3/contract.json new file mode 100644 index 000000000000..b6a876b59a88 --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/after/sql/migrations/snapshots/1cb334a7b32d249d4fea0934dbbaa48c473b5946cd77ee1ce7442272479d6cf3/contract.json @@ -0,0 +1 @@ +{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{"postgres":{"distinctOn":true,"forKeyShare":true,"forNoKeyUpdate":true,"jsonAgg":true,"lateral":true,"limit":true,"orderBy":true,"pgvector.cosine":true,"returning":true},"sql":{"checkConstraint":true,"defaultInInsert":true,"enums":true,"forShare":true,"forUpdate":true,"insertOnConflictSkip":true,"insertOnConflictWithoutTarget":true,"lateral":true,"lockNowait":true,"lockOf":true,"lockSkipLocked":true,"returning":true,"scalarList":true}},"domain":{"namespaces":{"public":{"enum":{"Priority":{"codecId":"pg/int4@1","members":[{"name":"Low","value":0},{"name":"High","value":1},{"name":"Urgent","value":2}]},"user_type":{"codecId":"pg/text@1","members":[{"name":"admin","value":"admin"},{"name":"user","value":"user"}]}},"models":{"Bug":{"base":{"model":"Task","namespace":"public"},"fields":{"severity":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"stepsToRepro":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"severity":{"column":"severity"},"stepsToRepro":{"column":"stepsToRepro"}},"namespaceId":"public","table":"bug"}},"Feature":{"base":{"model":"Task","namespace":"public"},"fields":{"priority":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"targetRelease":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"priority":{"column":"priority"},"targetRelease":{"column":"targetRelease"}},"namespaceId":"public","table":"feature"}},"Post":{"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"embedding":{"nullable":true,"type":{"codecId":"pg/vector@1","kind":"scalar","typeParams":{"length":1536}}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"priority":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"Priority","namespaceId":"public","plane":"domain"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"tags":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["postId"]},"through":{"childColumns":["tagId"],"namespaceId":"public","parentColumns":["postId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Tag","namespace":"public"}},"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"embedding":{"column":"embedding"},"id":{"column":"id"},"priority":{"column":"priority"},"title":{"column":"title"},"userId":{"column":"userId"}},"namespaceId":"public","table":"post"}},"PostTag":{"fields":{"postId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"tagId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"post":{"cardinality":"N:1","nullable":false,"on":{"localFields":["postId"],"targetFields":["id"]},"to":{"model":"Post","namespace":"public"}},"tag":{"cardinality":"N:1","nullable":false,"on":{"localFields":["tagId"],"targetFields":["id"]},"to":{"model":"Tag","namespace":"public"}}},"storage":{"fields":{"postId":{"column":"postId"},"tagId":{"column":"tagId"}},"namespaceId":"public","table":"post_tag"}},"Tag":{"fields":{"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"label":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{"posts":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["tagId"]},"through":{"childColumns":["postId"],"namespaceId":"public","parentColumns":["tagId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Post","namespace":"public"}}},"storage":{"fields":{"id":{"column":"id"},"label":{"column":"label"}},"namespaceId":"public","table":"tag"}},"Task":{"discriminator":{"field":"type"},"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"description":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"status":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"type":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"description":{"column":"description"},"id":{"column":"id"},"status":{"column":"status"},"title":{"column":"title"},"type":{"column":"type"},"userId":{"column":"userId"}},"namespaceId":"public","table":"task"},"variants":{"bug":{"model":"Bug","namespace":"public"},"feature":{"model":"Feature","namespace":"public"}}},"User":{"fields":{"address":{"nullable":true,"type":{"entityKind":"valueObject","entityName":"Address","kind":"valueObject","namespaceId":"public","plane":"storage"}},"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"displayName":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"user_type","namespaceId":"public","plane":"domain"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Post","namespace":"public"}},"tasks":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Task","namespace":"public"}}},"storage":{"fields":{"address":{"column":"address"},"createdAt":{"column":"createdAt"},"displayName":{"column":"displayName"},"email":{"column":"email"},"id":{"column":"id"},"kind":{"column":"kind"}},"namespaceId":"public","table":"user"}}}}}},"execution":{"executionHash":"eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655","mutations":{"defaults":[{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"post","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"tag","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"task","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"user","field":"id","namespace":"public"}}]}},"extensions":{"demo/engagement-stats":{"familyId":"sql","id":"demo/engagement-stats","kind":"extension","targetId":"postgres","types":{"aggregateDescriptors":[{"input":{"kind":"trait","trait":"numeric"},"nullable":true,"operation":"stddev","output":{"codecId":"pg/numeric@1","kind":"codec"}}]},"version":"0.0.1"},"pgvector":{"capabilities":{"postgres":{"pgvector.cosine":true}},"familyId":"sql","id":"pgvector","kind":"extension","targetId":"postgres","types":{"codecTypes":{"import":{"alias":"PgVectorTypes","named":"CodecTypes","package":"@internal/extension-pgvector/codec-types"},"parameterizedOutputTypes":{"alias":"PgVectorParameterizedOutputTypes","named":"ParameterizedOutputTypesForTypeParams","package":"@internal/extension-pgvector/codec-types"}},"operationTypes":{"import":{"alias":"PgVectorOperationTypes","named":"OperationTypes","package":"@internal/extension-pgvector/operation-types"}},"queryOperationTypes":{"import":{"alias":"PgVectorQueryOperationTypes","named":"QueryOperationTypes","package":"@internal/extension-pgvector/operation-types"}}},"version":"0.0.1"}},"meta":{},"profileHash":"3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2","roots":{"post":{"model":"Post","namespace":"public"},"post_tag":{"model":"PostTag","namespace":"public"},"tag":{"model":"Tag","namespace":"public"},"task":{"model":"Task","namespace":"public"},"user":{"model":"User","namespace":"public"}},"schemaVersion":"1","storage":{"namespaces":{"public":{"entries":{"table":{"bug":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"severity":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"stepsToRepro":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"bug"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"feature":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"priority":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"targetRelease":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"feature"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"post":{"checks":[{"expression":"\"priority\" IN (0, 1, 2)","name":"post_priority_check_b236ace9","prefix":"post_priority_check"}],"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"embedding":{"codecId":"pg/vector@1","dataType":"pgvector/vector","nullable":true,"typeParams":{"length":1536}},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"priority":{"codecId":"pg/int4@1","dataType":"pg/int4","default":{"kind":"literal","value":0},"nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Priority","namespaceId":"public","plane":"storage"}},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"post_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"post"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"post_userId_idx_a489d58a","prefix":"post_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"post_tag":{"columns":{"postId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"tagId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"post_tag_postId_idx_a7a72715"},"source":{"columns":["postId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"post"}},{"index":{"name":"post_tag_tagId_idx_86854244"},"source":{"columns":["tagId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"tag"}}],"indexes":[{"columns":["postId"],"name":"post_tag_postId_idx_a7a72715","prefix":"post_tag_postId_idx","unique":false},{"columns":["tagId"],"name":"post_tag_tagId_idx_86854244","prefix":"post_tag_tagId_idx","unique":false}],"primaryKey":{"columns":["postId","tagId"]},"uniques":[]},"tag":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"label":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[{"columns":["label"]}]},"task":{"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"description":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"status":{"codecId":"pg/text@1","dataType":"pg/text","default":{"kind":"literal","value":"open"},"nullable":false},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"type":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Task.type","namespaceId":"public","plane":"storage"}},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"task_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"task"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"task_userId_idx_a489d58a","prefix":"task_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"user":{"checks":[{"expression":"\"kind\" IN ('admin', 'user')","name":"user_kind_check_836d43ef","prefix":"user_kind_check"}],"columns":{"address":{"codecId":"pg/jsonb@1","dataType":"pg/jsonb","nullable":true},"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"displayName":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"email":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"kind":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"user_type","namespaceId":"public","plane":"storage"}}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]}},"valueObject":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}}}},"valueSet":{"Priority":{"kind":"valueSet","values":[0,1,2]},"Task.type":{"kind":"valueSet","values":["bug","feature"]},"user_type":{"kind":"valueSet","values":["admin","user"]}}},"id":"public","kind":"postgres-schema"}},"storageHash":"1cb334a7b32d249d4fea0934dbbaa48c473b5946cd77ee1ce7442272479d6cf3"},"target":"postgres","targetFamily":"sql"} diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260409T1030_migration/migration.json b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260409T1030_migration/migration.json new file mode 100644 index 000000000000..3cb8739196ad --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260409T1030_migration/migration.json @@ -0,0 +1,7 @@ +{ + "from": null, + "to": "da1339e341177f79b37f765f08200844d7cb4d59d26fe27fe4d95b0112b0c2cd", + "providedInvariants": [], + "createdAt": "2026-04-09T10:30:13.948Z", + "migrationHash": "3a28bd528b39d9b9c9a52082485a3274a8de15257cf9296776c290cc7b4e4fb5" +} diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260409T1030_migration/migration.ts b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260409T1030_migration/migration.ts new file mode 100644 index 000000000000..ee3167d3c896 --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260409T1030_migration/migration.ts @@ -0,0 +1,16 @@ +import { createIndex, Migration, MigrationCLI } from '@prisma/orm-mongo/target/migration'; +import type { Contract as End } from '../../snapshots/da1339e341177f79b37f765f08200844d7cb4d59d26fe27fe4d95b0112b0c2cd/contract'; +import endContract from '../../snapshots/da1339e341177f79b37f765f08200844d7cb4d59d26fe27fe4d95b0112b0c2cd/contract.json' with { + type: 'json', +}; + +class InitialMigration extends Migration { + override readonly endContractJson = endContract; + + override get operations() { + return [createIndex('users', [{ field: 'email', direction: 1 }], { unique: true })]; + } +} + +export default InitialMigration; +MigrationCLI.run(import.meta.url, InitialMigration); diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260409T1030_migration/ops.json b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260409T1030_migration/ops.json new file mode 100644 index 000000000000..533b620b26a8 --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260409T1030_migration/ops.json @@ -0,0 +1,101 @@ +[ + { + "id": "index.users.create(email:1)", + "label": "Create index on users (email:1)", + "operationClass": "additive", + "precheck": [ + { + "description": "index does not already exist on users", + "source": { + "kind": "listIndexes", + "collection": "users" + }, + "filter": { + "kind": "field", + "field": "key", + "op": "$eq", + "value": { + "email": 1 + } + }, + "expect": "notExists", + "object": "an index on { email: 1 } in \"users\"" + } + ], + "execute": [ + { + "description": "create index on users", + "command": { + "kind": "createIndex", + "collection": "users", + "keys": [ + { + "field": "email", + "direction": 1 + } + ], + "unique": true, + "name": "email_1" + } + } + ], + "postcheck": [ + { + "description": "index exists on users", + "source": { + "kind": "listIndexes", + "collection": "users" + }, + "filter": { + "kind": "and", + "exprs": [ + { + "kind": "field", + "field": "key", + "op": "$eq", + "value": { + "email": 1 + } + }, + { + "kind": "field", + "field": "unique", + "op": "$eq", + "value": true + }, + { + "kind": "not", + "expr": { + "kind": "field", + "field": "sparse", + "op": "$eq", + "value": true + } + }, + { + "kind": "exists", + "field": "expireAfterSeconds", + "exists": false + }, + { + "kind": "exists", + "field": "partialFilterExpression", + "exists": false + }, + { + "kind": "exists", + "field": "wildcardProjection", + "exists": false + }, + { + "kind": "exists", + "field": "collation", + "exists": false + } + ] + }, + "expect": "exists" + } + ] + } +] diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260626T1605_add_user_role_enum/migration.json b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260626T1605_add_user_role_enum/migration.json new file mode 100644 index 000000000000..dfb26991f67f --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260626T1605_add_user_role_enum/migration.json @@ -0,0 +1,7 @@ +{ + "from": "da1339e341177f79b37f765f08200844d7cb4d59d26fe27fe4d95b0112b0c2cd", + "to": "62671fbf016c515d808ac613743fddbf3db80384c1997251eac9d6f5fc063590", + "providedInvariants": [], + "createdAt": "2026-06-26T16:05:45.955Z", + "migrationHash": "671d9b676330b6af88db75c5566f1fe1a7d7d4d0af4ca282828f093b8dd295c0" +} diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260626T1605_add_user_role_enum/migration.ts b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260626T1605_add_user_role_enum/migration.ts new file mode 100755 index 000000000000..9471c658b0b7 --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260626T1605_add_user_role_enum/migration.ts @@ -0,0 +1,65 @@ +#!/usr/bin/env -S node +import { collMod, Migration, MigrationCLI } from '@prisma/orm-mongo/target/migration'; +import type { Contract as End } from '../../snapshots/62671fbf016c515d808ac613743fddbf3db80384c1997251eac9d6f5fc063590/contract'; +import endContract from '../../snapshots/62671fbf016c515d808ac613743fddbf3db80384c1997251eac9d6f5fc063590/contract.json' with { + type: 'json', +}; +import type { Contract as Start } from '../../snapshots/da1339e341177f79b37f765f08200844d7cb4d59d26fe27fe4d95b0112b0c2cd/contract'; +import startContract from '../../snapshots/da1339e341177f79b37f765f08200844d7cb4d59d26fe27fe4d95b0112b0c2cd/contract.json' with { + type: 'json', +}; + +class M extends Migration { + override readonly startContractJson = startContract; + override readonly endContractJson = endContract; + + override get operations() { + return [ + collMod( + 'users', + { + validator: { + $jsonSchema: { + additionalProperties: false, + bsonType: 'object', + properties: { + _id: { bsonType: 'objectId' }, + address: { + oneOf: [ + { bsonType: 'null' }, + { + additionalProperties: false, + bsonType: 'object', + properties: { + city: { bsonType: 'string' }, + country: { bsonType: 'string' }, + street: { bsonType: 'string' }, + zip: { bsonType: ['null', 'string'] }, + }, + required: ['city', 'country', 'street'], + }, + ], + }, + bio: { bsonType: ['null', 'string'] }, + email: { bsonType: 'string' }, + name: { bsonType: 'string' }, + role: { bsonType: 'string', enum: ['admin', 'author', 'reader'] }, + }, + required: ['_id', 'email', 'name', 'role'], + }, + }, + validationLevel: 'strict', + validationAction: 'error', + }, + { + id: 'validator.users.update', + label: 'Update validator on users', + operationClass: 'destructive', + }, + ), + ]; + } +} + +export default M; +MigrationCLI.run(import.meta.url, M); diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260626T1605_add_user_role_enum/ops.json b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260626T1605_add_user_role_enum/ops.json new file mode 100644 index 000000000000..11605cf4708e --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260626T1605_add_user_role_enum/ops.json @@ -0,0 +1,171 @@ +[ + { + "id": "validator.users.update", + "label": "Update validator on users", + "operationClass": "destructive", + "precheck": [ + { + "description": "collection users exists", + "source": { + "kind": "listCollections" + }, + "filter": { + "kind": "field", + "field": "name", + "op": "$eq", + "value": "users" + }, + "expect": "exists", + "object": "collection \"users\"" + } + ], + "execute": [ + { + "description": "modify users", + "command": { + "kind": "collMod", + "collection": "users", + "validator": { + "$jsonSchema": { + "additionalProperties": false, + "bsonType": "object", + "properties": { + "_id": { + "bsonType": "objectId" + }, + "address": { + "oneOf": [ + { + "bsonType": "null" + }, + { + "additionalProperties": false, + "bsonType": "object", + "properties": { + "city": { + "bsonType": "string" + }, + "country": { + "bsonType": "string" + }, + "street": { + "bsonType": "string" + }, + "zip": { + "bsonType": ["null", "string"] + } + }, + "required": ["city", "country", "street"] + } + ] + }, + "bio": { + "bsonType": ["null", "string"] + }, + "email": { + "bsonType": "string" + }, + "name": { + "bsonType": "string" + }, + "role": { + "bsonType": "string", + "enum": ["admin", "author", "reader"] + } + }, + "required": ["_id", "email", "name", "role"] + } + }, + "validationLevel": "strict", + "validationAction": "error" + } + } + ], + "postcheck": [ + { + "description": "validator applied on users", + "source": { + "kind": "listCollections" + }, + "filter": { + "kind": "and", + "exprs": [ + { + "kind": "field", + "field": "name", + "op": "$eq", + "value": "users" + }, + { + "kind": "field", + "field": "options.validationLevel", + "op": "$eq", + "value": "strict" + }, + { + "kind": "field", + "field": "options.validationAction", + "op": "$eq", + "value": "error" + }, + { + "kind": "field", + "field": "options.validator.$jsonSchema", + "op": "$eq", + "value": { + "additionalProperties": false, + "bsonType": "object", + "properties": { + "_id": { + "bsonType": "objectId" + }, + "address": { + "oneOf": [ + { + "bsonType": "null" + }, + { + "additionalProperties": false, + "bsonType": "object", + "properties": { + "city": { + "bsonType": "string" + }, + "country": { + "bsonType": "string" + }, + "street": { + "bsonType": "string" + }, + "zip": { + "bsonType": ["null", "string"] + } + }, + "required": ["city", "country", "street"] + } + ] + }, + "bio": { + "bsonType": ["null", "string"] + }, + "email": { + "bsonType": "string" + }, + "name": { + "bsonType": "string" + }, + "role": { + "bsonType": "string", + "enum": ["admin", "author", "reader"] + } + }, + "required": ["_id", "email", "name", "role"] + } + } + ] + }, + "expect": "exists" + } + ] + } +] diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260626T1916_add_posts_indexes/migration.json b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260626T1916_add_posts_indexes/migration.json new file mode 100644 index 000000000000..6d6ea9a9ebad --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260626T1916_add_posts_indexes/migration.json @@ -0,0 +1,7 @@ +{ + "from": "62671fbf016c515d808ac613743fddbf3db80384c1997251eac9d6f5fc063590", + "to": "d264cabd6a02546054a776ed3c04438b9a22989f406c49f5590f7f2101156eb0", + "providedInvariants": [], + "createdAt": "2026-06-26T19:16:47.657Z", + "migrationHash": "a435f5206064150010733fe0d93690358b92cb892cfdfbd80c1b4aade84c16ef" +} diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260626T1916_add_posts_indexes/migration.ts b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260626T1916_add_posts_indexes/migration.ts new file mode 100755 index 000000000000..146f9deaca22 --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260626T1916_add_posts_indexes/migration.ts @@ -0,0 +1,32 @@ +#!/usr/bin/env -S node +import { createIndex, Migration, MigrationCLI } from '@prisma/orm-mongo/target/migration'; +import type { Contract as Start } from '../../snapshots/62671fbf016c515d808ac613743fddbf3db80384c1997251eac9d6f5fc063590/contract'; +import startContract from '../../snapshots/62671fbf016c515d808ac613743fddbf3db80384c1997251eac9d6f5fc063590/contract.json' with { + type: 'json', +}; +import type { Contract as End } from '../../snapshots/d264cabd6a02546054a776ed3c04438b9a22989f406c49f5590f7f2101156eb0/contract'; +import endContract from '../../snapshots/d264cabd6a02546054a776ed3c04438b9a22989f406c49f5590f7f2101156eb0/contract.json' with { + type: 'json', +}; + +class M extends Migration { + override readonly startContractJson = startContract; + override readonly endContractJson = endContract; + + override get operations() { + return [ + createIndex('posts', [{ direction: 1, field: 'authorId' }], {}), + createIndex( + 'posts', + [ + { direction: -1, field: 'createdAt' }, + { direction: 1, field: 'authorId' }, + ], + {}, + ), + ]; + } +} + +export default M; +MigrationCLI.run(import.meta.url, M); diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260626T1916_add_posts_indexes/ops.json b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260626T1916_add_posts_indexes/ops.json new file mode 100644 index 000000000000..d9b60da89298 --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/app/20260626T1916_add_posts_indexes/ops.json @@ -0,0 +1,210 @@ +[ + { + "id": "index.posts.create(authorId:1)", + "label": "Create index on posts (authorId:1)", + "operationClass": "additive", + "precheck": [ + { + "description": "index does not already exist on posts", + "source": { + "kind": "listIndexes", + "collection": "posts" + }, + "filter": { + "kind": "field", + "field": "key", + "op": "$eq", + "value": { + "authorId": 1 + } + }, + "expect": "notExists", + "object": "an index on { authorId: 1 } in \"posts\"" + } + ], + "execute": [ + { + "description": "create index on posts", + "command": { + "kind": "createIndex", + "collection": "posts", + "keys": [ + { + "direction": 1, + "field": "authorId" + } + ], + "name": "authorId_1" + } + } + ], + "postcheck": [ + { + "description": "index exists on posts", + "source": { + "kind": "listIndexes", + "collection": "posts" + }, + "filter": { + "kind": "and", + "exprs": [ + { + "kind": "field", + "field": "key", + "op": "$eq", + "value": { + "authorId": 1 + } + }, + { + "kind": "not", + "expr": { + "kind": "field", + "field": "unique", + "op": "$eq", + "value": true + } + }, + { + "kind": "not", + "expr": { + "kind": "field", + "field": "sparse", + "op": "$eq", + "value": true + } + }, + { + "kind": "exists", + "field": "expireAfterSeconds", + "exists": false + }, + { + "kind": "exists", + "field": "partialFilterExpression", + "exists": false + }, + { + "kind": "exists", + "field": "wildcardProjection", + "exists": false + }, + { + "kind": "exists", + "field": "collation", + "exists": false + } + ] + }, + "expect": "exists" + } + ] + }, + { + "id": "index.posts.create(createdAt:-1,authorId:1)", + "label": "Create index on posts (createdAt:-1, authorId:1)", + "operationClass": "additive", + "precheck": [ + { + "description": "index does not already exist on posts", + "source": { + "kind": "listIndexes", + "collection": "posts" + }, + "filter": { + "kind": "field", + "field": "key", + "op": "$eq", + "value": { + "createdAt": -1, + "authorId": 1 + } + }, + "expect": "notExists", + "object": "an index on { createdAt: -1, authorId: 1 } in \"posts\"" + } + ], + "execute": [ + { + "description": "create index on posts", + "command": { + "kind": "createIndex", + "collection": "posts", + "keys": [ + { + "direction": -1, + "field": "createdAt" + }, + { + "direction": 1, + "field": "authorId" + } + ], + "name": "createdAt_-1_authorId_1" + } + } + ], + "postcheck": [ + { + "description": "index exists on posts", + "source": { + "kind": "listIndexes", + "collection": "posts" + }, + "filter": { + "kind": "and", + "exprs": [ + { + "kind": "field", + "field": "key", + "op": "$eq", + "value": { + "createdAt": -1, + "authorId": 1 + } + }, + { + "kind": "not", + "expr": { + "kind": "field", + "field": "unique", + "op": "$eq", + "value": true + } + }, + { + "kind": "not", + "expr": { + "kind": "field", + "field": "sparse", + "op": "$eq", + "value": true + } + }, + { + "kind": "exists", + "field": "expireAfterSeconds", + "exists": false + }, + { + "kind": "exists", + "field": "partialFilterExpression", + "exists": false + }, + { + "kind": "exists", + "field": "wildcardProjection", + "exists": false + }, + { + "kind": "exists", + "field": "collation", + "exists": false + } + ] + }, + "expect": "exists" + } + ] + } +] diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/snapshots/62671fbf016c515d808ac613743fddbf3db80384c1997251eac9d6f5fc063590/contract.d.ts b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/snapshots/62671fbf016c515d808ac613743fddbf3db80384c1997251eac9d6f5fc063590/contract.d.ts new file mode 100644 index 000000000000..6512ab80b5a1 --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/snapshots/62671fbf016c515d808ac613743fddbf3db80384c1997251eac9d6f5fc063590/contract.d.ts @@ -0,0 +1,493 @@ +// ⚠️ GENERATED FILE - DO NOT EDIT +// This file is automatically generated by 'prisma contract emit'. +// To regenerate, run: prisma contract emit +import type { + CodecTypes as MongoCodecTypes, + ParameterizedOutputTypesForTypeParams as MongoParameterizedOutputTypes, +} from '@prisma/orm-mongo/target/codec-types'; + +import type { + MongoCollection, + MongoContractWithTypeMaps, + MongoTypeMaps, + RelationKeys, +} from '@prisma/orm-mongo/family-contract'; +import type { + Contract as ContractType, + ExecutionHashBase, + FieldTypesFromDomain, + NamespaceId, + ProfileHashBase, + StorageHashBase, + ValueObjectTypesFromDomain, +} from '@prisma/orm-mongo/contract/types'; + +export type StorageHash = + StorageHashBase<'62671fbf016c515d808ac613743fddbf3db80384c1997251eac9d6f5fc063590'>; +export type ExecutionHash = ExecutionHashBase; +export type ProfileHash = + ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; + +export type CodecTypes = MongoCodecTypes; + +type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; +type ValueObjectOutputTypes = ValueObjectTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output' +>; +type ValueObjectInputTypes = ValueObjectTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input' +>; +export type AddressOutput = ValueObjectOutputTypes['__unbound__']['Address']; +export type AddressInput = ValueObjectInputTypes['__unbound__']['Address']; +type FieldTypeOverrides = { + readonly __unbound__: { + readonly models: { + readonly User: { + readonly role: { + readonly output: 'admin' | 'author' | 'reader'; + readonly input: 'admin' | 'author' | 'reader'; + }; + }; + }; + }; +}; +export type FieldOutputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output' +>; +export type FieldInputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input' +>; + +export namespace Models { + export type unbound_Article = { + authorId: FieldOutputTypes['__unbound__']['Post']['authorId']; + content: FieldOutputTypes['__unbound__']['Post']['content']; + createdAt: FieldOutputTypes['__unbound__']['Post']['createdAt']; + id: FieldOutputTypes['__unbound__']['Post']['id']; + kind: 'article'; + title: FieldOutputTypes['__unbound__']['Post']['title']; + summary: FieldOutputTypes['__unbound__']['Article']['summary']; + author: unbound_User; + readonly [RelationKeys]?: 'author'; + }; + export type unbound_Post = { + authorId: FieldOutputTypes['__unbound__']['Post']['authorId']; + content: FieldOutputTypes['__unbound__']['Post']['content']; + createdAt: FieldOutputTypes['__unbound__']['Post']['createdAt']; + id: FieldOutputTypes['__unbound__']['Post']['id']; + kind: 'article' | 'tutorial'; + title: FieldOutputTypes['__unbound__']['Post']['title']; + author: unbound_User; + readonly [RelationKeys]?: 'author'; + }; + export type unbound_Tutorial = { + authorId: FieldOutputTypes['__unbound__']['Post']['authorId']; + content: FieldOutputTypes['__unbound__']['Post']['content']; + createdAt: FieldOutputTypes['__unbound__']['Post']['createdAt']; + id: FieldOutputTypes['__unbound__']['Post']['id']; + kind: 'tutorial'; + title: FieldOutputTypes['__unbound__']['Post']['title']; + difficulty: FieldOutputTypes['__unbound__']['Tutorial']['difficulty']; + duration: FieldOutputTypes['__unbound__']['Tutorial']['duration']; + author: unbound_User; + readonly [RelationKeys]?: 'author'; + }; + export type unbound_User = { + address: FieldOutputTypes['__unbound__']['User']['address']; + bio: FieldOutputTypes['__unbound__']['User']['bio']; + email: FieldOutputTypes['__unbound__']['User']['email']; + id: FieldOutputTypes['__unbound__']['User']['id']; + name: FieldOutputTypes['__unbound__']['User']['name']; + role: FieldOutputTypes['__unbound__']['User']['role']; + posts: unbound_AnyPost[]; + readonly [RelationKeys]?: 'posts'; + }; + export type unbound_AnyPost = unbound_Article | unbound_Tutorial; +} + +export declare const models: { + __unbound__: { + Article: Models.unbound_Article; + Post: Models.unbound_Post; + Tutorial: Models.unbound_Tutorial; + User: Models.unbound_User; + AnyPost: Models.unbound_AnyPost; + }; +}; + +export type TypeMaps = MongoTypeMaps; + +type ContractBase = Omit< + ContractType<{ + readonly namespaces: { + readonly __unbound__: { + readonly id: '__unbound__'; + readonly kind: 'mongo-database'; + readonly entries: { + readonly collection: { + readonly posts: { + readonly indexes: readonly [ + { + readonly keys: readonly [{ readonly direction: 1; readonly field: 'summary' }]; + readonly kind: 'mongo-index'; + readonly partialFilterExpression: { readonly kind: 'article' }; + readonly unique: true; + }, + ]; + readonly kind: 'mongo-collection'; + readonly validator: { + readonly jsonSchema: { + readonly bsonType: 'object'; + readonly oneOf: readonly [ + { + readonly additionalProperties: false; + readonly properties: { + readonly _id: { readonly bsonType: 'objectId' }; + readonly authorId: { readonly bsonType: 'objectId' }; + readonly content: { readonly bsonType: 'string' }; + readonly createdAt: { readonly bsonType: 'date' }; + readonly kind: { readonly enum: readonly ['article'] }; + readonly summary: { readonly bsonType: 'string' }; + readonly title: { readonly bsonType: 'string' }; + }; + readonly required: readonly ['kind', 'summary']; + }, + { + readonly additionalProperties: false; + readonly properties: { + readonly _id: { readonly bsonType: 'objectId' }; + readonly authorId: { readonly bsonType: 'objectId' }; + readonly content: { readonly bsonType: 'string' }; + readonly createdAt: { readonly bsonType: 'date' }; + readonly difficulty: { readonly bsonType: 'string' }; + readonly duration: { readonly bsonType: 'int' }; + readonly kind: { readonly enum: readonly ['tutorial'] }; + readonly title: { readonly bsonType: 'string' }; + }; + readonly required: readonly ['difficulty', 'duration', 'kind']; + }, + ]; + readonly properties: { + readonly _id: { readonly bsonType: 'objectId' }; + readonly authorId: { readonly bsonType: 'objectId' }; + readonly content: { readonly bsonType: 'string' }; + readonly createdAt: { readonly bsonType: 'date' }; + readonly kind: { readonly bsonType: 'string' }; + readonly title: { readonly bsonType: 'string' }; + }; + readonly required: readonly [ + '_id', + 'authorId', + 'content', + 'createdAt', + 'kind', + 'title', + ]; + }; + readonly kind: 'mongo-validator'; + readonly validationAction: 'error'; + readonly validationLevel: 'strict'; + }; + }; + readonly users: { + readonly kind: 'mongo-collection'; + readonly validator: { + readonly jsonSchema: { + readonly additionalProperties: false; + readonly bsonType: 'object'; + readonly properties: { + readonly _id: { readonly bsonType: 'objectId' }; + readonly address: { + readonly oneOf: readonly [ + { readonly bsonType: 'null' }, + { + readonly additionalProperties: false; + readonly bsonType: 'object'; + readonly properties: { + readonly city: { readonly bsonType: 'string' }; + readonly country: { readonly bsonType: 'string' }; + readonly street: { readonly bsonType: 'string' }; + readonly zip: { readonly bsonType: readonly ['null', 'string'] }; + }; + readonly required: readonly ['city', 'country', 'street']; + }, + ]; + }; + readonly bio: { readonly bsonType: readonly ['null', 'string'] }; + readonly email: { readonly bsonType: 'string' }; + readonly name: { readonly bsonType: 'string' }; + readonly role: { + readonly bsonType: 'string'; + readonly enum: readonly ['admin', 'author', 'reader']; + }; + }; + readonly required: readonly ['_id', 'email', 'name', 'role']; + }; + readonly kind: 'mongo-validator'; + readonly validationAction: 'error'; + readonly validationLevel: 'strict'; + }; + }; + }; + }; + }; + }; + readonly storageHash: StorageHash; + }>, + 'roots' | 'domain' +> & { + readonly target: 'mongo'; + readonly targetFamily: 'mongo'; + readonly roots: { + readonly posts: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'Post' }; + readonly users: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'User' }; + }; + readonly domain: { + readonly namespaces: { + readonly __unbound__: { + readonly models: { + readonly Article: { + readonly fields: { + readonly summary: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly collection: 'posts'; + readonly fields: { readonly summary: { readonly field: 'summary' } }; + }; + readonly base: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Post'; + }; + }; + readonly Post: { + readonly fields: { + readonly authorId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; + }; + readonly content: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly createdAt: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; + }; + readonly kind: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly title: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + readonly relations: { + readonly author: { + readonly to: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'User'; + }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['authorId']; + readonly targetFields: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly collection: 'posts'; + readonly fields: { + readonly authorId: { readonly field: 'authorId' }; + readonly content: { readonly field: 'content' }; + readonly createdAt: { readonly field: 'createdAt' }; + readonly id: { readonly field: '_id' }; + readonly kind: { readonly field: 'kind' }; + readonly title: { readonly field: 'title' }; + }; + }; + readonly discriminator: { readonly field: 'kind' }; + readonly variants: { + readonly article: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Article'; + }; + readonly tutorial: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Tutorial'; + }; + }; + }; + readonly Tutorial: { + readonly fields: { + readonly difficulty: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly duration: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly collection: 'posts'; + readonly fields: { + readonly difficulty: { readonly field: 'difficulty' }; + readonly duration: { readonly field: 'duration' }; + }; + }; + readonly base: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Post'; + }; + }; + readonly User: { + readonly fields: { + readonly address: { + readonly nullable: true; + readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + }; + readonly bio: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; + }; + readonly name: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly role: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + readonly relations: { + readonly posts: { + readonly to: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Post'; + }; + readonly cardinality: '1:N'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['authorId']; + }; + }; + }; + readonly storage: { + readonly collection: 'users'; + readonly fields: { + readonly address: { readonly field: 'address' }; + readonly bio: { readonly field: 'bio' }; + readonly email: { readonly field: 'email' }; + readonly id: { readonly field: '_id' }; + readonly name: { readonly field: 'name' }; + readonly role: { readonly field: 'role' }; + }; + }; + }; + }; + readonly valueObjects: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + }; + readonly enum: { + readonly UserRole: { + readonly codecId: 'mongo/string@1'; + readonly members: readonly [ + { readonly name: 'Admin'; readonly value: 'admin' }, + { readonly name: 'Author'; readonly value: 'author' }, + { readonly name: 'Reader'; readonly value: 'reader' }, + ]; + }; + }; + readonly enumMemberTypes?: { + readonly UserRole: readonly [ + { readonly name: 'Admin'; readonly value: 'admin' }, + { readonly name: 'Author'; readonly value: 'author' }, + { readonly name: 'Reader'; readonly value: 'reader' }, + ]; + }; + }; + }; + }; + readonly capabilities: {}; + readonly extensions: {}; + readonly meta: {}; + readonly valueObjects: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + }; + readonly profileHash: ProfileHash; +}; + +export type Contract = MongoContractWithTypeMaps; diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/snapshots/62671fbf016c515d808ac613743fddbf3db80384c1997251eac9d6f5fc063590/contract.json b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/snapshots/62671fbf016c515d808ac613743fddbf3db80384c1997251eac9d6f5fc063590/contract.json new file mode 100644 index 000000000000..e2172d20550f --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/snapshots/62671fbf016c515d808ac613743fddbf3db80384c1997251eac9d6f5fc063590/contract.json @@ -0,0 +1 @@ +{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{},"domain":{"namespaces":{"__unbound__":{"enum":{"UserRole":{"codecId":"mongo/string@1","members":[{"name":"Admin","value":"admin"},{"name":"Author","value":"author"},{"name":"Reader","value":"reader"}]}},"models":{"Article":{"base":{"model":"Post","namespace":"__unbound__"},"fields":{"summary":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"posts","fields":{"summary":{"field":"summary"}}}},"Post":{"discriminator":{"field":"kind"},"fields":{"authorId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"content":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"createdAt":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{"author":{"cardinality":"N:1","nullable":false,"on":{"localFields":["authorId"],"targetFields":["id"]},"to":{"model":"User","namespace":"__unbound__"}}},"storage":{"collection":"posts","fields":{"authorId":{"field":"authorId"},"content":{"field":"content"},"createdAt":{"field":"createdAt"},"id":{"field":"_id"},"kind":{"field":"kind"},"title":{"field":"title"}}},"variants":{"article":{"model":"Article","namespace":"__unbound__"},"tutorial":{"model":"Tutorial","namespace":"__unbound__"}}},"Tutorial":{"base":{"model":"Post","namespace":"__unbound__"},"fields":{"difficulty":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"duration":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"posts","fields":{"difficulty":{"field":"difficulty"},"duration":{"field":"duration"}}}},"User":{"fields":{"address":{"nullable":true,"type":{"kind":"valueObject","name":"Address"}},"bio":{"nullable":true,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"role":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"UserRole","namespaceId":"__unbound__","plane":"domain"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["authorId"]},"to":{"model":"Post","namespace":"__unbound__"}}},"storage":{"collection":"users","fields":{"address":{"field":"address"},"bio":{"field":"bio"},"email":{"field":"email"},"id":{"field":"_id"},"name":{"field":"name"},"role":{"field":"role"}}}}},"valueObjects":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}}}}}},"extensions":{},"meta":{},"profileHash":"251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3","roots":{"posts":{"model":"Post","namespace":"__unbound__"},"users":{"model":"User","namespace":"__unbound__"}},"schemaVersion":"1","storage":{"namespaces":{"__unbound__":{"entries":{"collection":{"posts":{"indexes":[{"keys":[{"direction":1,"field":"summary"}],"kind":"mongo-index","partialFilterExpression":{"kind":"article"},"unique":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"bsonType":"object","oneOf":[{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"kind":{"enum":["article"]},"summary":{"bsonType":"string"},"title":{"bsonType":"string"}},"required":["kind","summary"]},{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"difficulty":{"bsonType":"string"},"duration":{"bsonType":"int"},"kind":{"enum":["tutorial"]},"title":{"bsonType":"string"}},"required":["difficulty","duration","kind"]}],"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"kind":{"bsonType":"string"},"title":{"bsonType":"string"}},"required":["_id","authorId","content","createdAt","kind","title"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"users":{"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"address":{"oneOf":[{"bsonType":"null"},{"additionalProperties":false,"bsonType":"object","properties":{"city":{"bsonType":"string"},"country":{"bsonType":"string"},"street":{"bsonType":"string"},"zip":{"bsonType":["null","string"]}},"required":["city","country","street"]}]},"bio":{"bsonType":["null","string"]},"email":{"bsonType":"string"},"name":{"bsonType":"string"},"role":{"bsonType":"string","enum":["admin","author","reader"]}},"required":["_id","email","name","role"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}}},"valueSet":{"UserRole":{"kind":"valueSet","values":["admin","author","reader"]}}},"id":"__unbound__","kind":"mongo-database"}},"storageHash":"62671fbf016c515d808ac613743fddbf3db80384c1997251eac9d6f5fc063590"},"target":"mongo","targetFamily":"mongo"} diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/snapshots/d264cabd6a02546054a776ed3c04438b9a22989f406c49f5590f7f2101156eb0/contract.d.ts b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/snapshots/d264cabd6a02546054a776ed3c04438b9a22989f406c49f5590f7f2101156eb0/contract.d.ts new file mode 100644 index 000000000000..af70721dd428 --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/snapshots/d264cabd6a02546054a776ed3c04438b9a22989f406c49f5590f7f2101156eb0/contract.d.ts @@ -0,0 +1,504 @@ +// ⚠️ GENERATED FILE - DO NOT EDIT +// This file is automatically generated by 'prisma contract emit'. +// To regenerate, run: prisma contract emit +import type { + CodecTypes as MongoCodecTypes, + ParameterizedOutputTypesForTypeParams as MongoParameterizedOutputTypes, +} from '@prisma/orm-mongo/target/codec-types'; + +import type { + MongoCollection, + MongoContractWithTypeMaps, + MongoTypeMaps, + RelationKeys, +} from '@prisma/orm-mongo/family-contract'; +import type { + Contract as ContractType, + ExecutionHashBase, + FieldTypesFromDomain, + NamespaceId, + ProfileHashBase, + StorageHashBase, + ValueObjectTypesFromDomain, +} from '@prisma/orm-mongo/contract/types'; + +export type StorageHash = + StorageHashBase<'d264cabd6a02546054a776ed3c04438b9a22989f406c49f5590f7f2101156eb0'>; +export type ExecutionHash = ExecutionHashBase; +export type ProfileHash = + ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; + +export type CodecTypes = MongoCodecTypes; + +type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; +type ValueObjectOutputTypes = ValueObjectTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output' +>; +type ValueObjectInputTypes = ValueObjectTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input' +>; +export type AddressOutput = ValueObjectOutputTypes['__unbound__']['Address']; +export type AddressInput = ValueObjectInputTypes['__unbound__']['Address']; +type FieldTypeOverrides = { + readonly __unbound__: { + readonly models: { + readonly User: { + readonly role: { + readonly output: 'admin' | 'author' | 'reader'; + readonly input: 'admin' | 'author' | 'reader'; + }; + }; + }; + }; +}; +export type FieldOutputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output' +>; +export type FieldInputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input' +>; + +export namespace Models { + export type unbound_Article = { + authorId: FieldOutputTypes['__unbound__']['Post']['authorId']; + content: FieldOutputTypes['__unbound__']['Post']['content']; + createdAt: FieldOutputTypes['__unbound__']['Post']['createdAt']; + id: FieldOutputTypes['__unbound__']['Post']['id']; + kind: 'article'; + title: FieldOutputTypes['__unbound__']['Post']['title']; + summary: FieldOutputTypes['__unbound__']['Article']['summary']; + author: unbound_User; + readonly [RelationKeys]?: 'author'; + }; + export type unbound_Post = { + authorId: FieldOutputTypes['__unbound__']['Post']['authorId']; + content: FieldOutputTypes['__unbound__']['Post']['content']; + createdAt: FieldOutputTypes['__unbound__']['Post']['createdAt']; + id: FieldOutputTypes['__unbound__']['Post']['id']; + kind: 'article' | 'tutorial'; + title: FieldOutputTypes['__unbound__']['Post']['title']; + author: unbound_User; + readonly [RelationKeys]?: 'author'; + }; + export type unbound_Tutorial = { + authorId: FieldOutputTypes['__unbound__']['Post']['authorId']; + content: FieldOutputTypes['__unbound__']['Post']['content']; + createdAt: FieldOutputTypes['__unbound__']['Post']['createdAt']; + id: FieldOutputTypes['__unbound__']['Post']['id']; + kind: 'tutorial'; + title: FieldOutputTypes['__unbound__']['Post']['title']; + difficulty: FieldOutputTypes['__unbound__']['Tutorial']['difficulty']; + duration: FieldOutputTypes['__unbound__']['Tutorial']['duration']; + author: unbound_User; + readonly [RelationKeys]?: 'author'; + }; + export type unbound_User = { + address: FieldOutputTypes['__unbound__']['User']['address']; + bio: FieldOutputTypes['__unbound__']['User']['bio']; + email: FieldOutputTypes['__unbound__']['User']['email']; + id: FieldOutputTypes['__unbound__']['User']['id']; + name: FieldOutputTypes['__unbound__']['User']['name']; + role: FieldOutputTypes['__unbound__']['User']['role']; + posts: unbound_AnyPost[]; + readonly [RelationKeys]?: 'posts'; + }; + export type unbound_AnyPost = unbound_Article | unbound_Tutorial; +} + +export declare const models: { + __unbound__: { + Article: Models.unbound_Article; + Post: Models.unbound_Post; + Tutorial: Models.unbound_Tutorial; + User: Models.unbound_User; + AnyPost: Models.unbound_AnyPost; + }; +}; + +export type TypeMaps = MongoTypeMaps; + +type ContractBase = Omit< + ContractType<{ + readonly namespaces: { + readonly __unbound__: { + readonly id: '__unbound__'; + readonly kind: 'mongo-database'; + readonly entries: { + readonly collection: { + readonly posts: { + readonly indexes: readonly [ + { + readonly keys: readonly [{ readonly direction: 1; readonly field: 'authorId' }]; + readonly kind: 'mongo-index'; + }, + { + readonly keys: readonly [ + { readonly direction: -1; readonly field: 'createdAt' }, + { readonly direction: 1; readonly field: 'authorId' }, + ]; + readonly kind: 'mongo-index'; + }, + { + readonly keys: readonly [{ readonly direction: 1; readonly field: 'summary' }]; + readonly kind: 'mongo-index'; + readonly partialFilterExpression: { readonly kind: 'article' }; + readonly unique: true; + }, + ]; + readonly kind: 'mongo-collection'; + readonly validator: { + readonly jsonSchema: { + readonly bsonType: 'object'; + readonly oneOf: readonly [ + { + readonly additionalProperties: false; + readonly properties: { + readonly _id: { readonly bsonType: 'objectId' }; + readonly authorId: { readonly bsonType: 'objectId' }; + readonly content: { readonly bsonType: 'string' }; + readonly createdAt: { readonly bsonType: 'date' }; + readonly kind: { readonly enum: readonly ['article'] }; + readonly summary: { readonly bsonType: 'string' }; + readonly title: { readonly bsonType: 'string' }; + }; + readonly required: readonly ['kind', 'summary']; + }, + { + readonly additionalProperties: false; + readonly properties: { + readonly _id: { readonly bsonType: 'objectId' }; + readonly authorId: { readonly bsonType: 'objectId' }; + readonly content: { readonly bsonType: 'string' }; + readonly createdAt: { readonly bsonType: 'date' }; + readonly difficulty: { readonly bsonType: 'string' }; + readonly duration: { readonly bsonType: 'int' }; + readonly kind: { readonly enum: readonly ['tutorial'] }; + readonly title: { readonly bsonType: 'string' }; + }; + readonly required: readonly ['difficulty', 'duration', 'kind']; + }, + ]; + readonly properties: { + readonly _id: { readonly bsonType: 'objectId' }; + readonly authorId: { readonly bsonType: 'objectId' }; + readonly content: { readonly bsonType: 'string' }; + readonly createdAt: { readonly bsonType: 'date' }; + readonly kind: { readonly bsonType: 'string' }; + readonly title: { readonly bsonType: 'string' }; + }; + readonly required: readonly [ + '_id', + 'authorId', + 'content', + 'createdAt', + 'kind', + 'title', + ]; + }; + readonly kind: 'mongo-validator'; + readonly validationAction: 'error'; + readonly validationLevel: 'strict'; + }; + }; + readonly users: { + readonly kind: 'mongo-collection'; + readonly validator: { + readonly jsonSchema: { + readonly additionalProperties: false; + readonly bsonType: 'object'; + readonly properties: { + readonly _id: { readonly bsonType: 'objectId' }; + readonly address: { + readonly oneOf: readonly [ + { readonly bsonType: 'null' }, + { + readonly additionalProperties: false; + readonly bsonType: 'object'; + readonly properties: { + readonly city: { readonly bsonType: 'string' }; + readonly country: { readonly bsonType: 'string' }; + readonly street: { readonly bsonType: 'string' }; + readonly zip: { readonly bsonType: readonly ['null', 'string'] }; + }; + readonly required: readonly ['city', 'country', 'street']; + }, + ]; + }; + readonly bio: { readonly bsonType: readonly ['null', 'string'] }; + readonly email: { readonly bsonType: 'string' }; + readonly name: { readonly bsonType: 'string' }; + readonly role: { + readonly bsonType: 'string'; + readonly enum: readonly ['admin', 'author', 'reader']; + }; + }; + readonly required: readonly ['_id', 'email', 'name', 'role']; + }; + readonly kind: 'mongo-validator'; + readonly validationAction: 'error'; + readonly validationLevel: 'strict'; + }; + }; + }; + }; + }; + }; + readonly storageHash: StorageHash; + }>, + 'roots' | 'domain' +> & { + readonly target: 'mongo'; + readonly targetFamily: 'mongo'; + readonly roots: { + readonly posts: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'Post' }; + readonly users: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'User' }; + }; + readonly domain: { + readonly namespaces: { + readonly __unbound__: { + readonly models: { + readonly Article: { + readonly fields: { + readonly summary: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly collection: 'posts'; + readonly fields: { readonly summary: { readonly field: 'summary' } }; + }; + readonly base: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Post'; + }; + }; + readonly Post: { + readonly fields: { + readonly authorId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; + }; + readonly content: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly createdAt: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; + }; + readonly kind: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly title: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + readonly relations: { + readonly author: { + readonly to: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'User'; + }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['authorId']; + readonly targetFields: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly collection: 'posts'; + readonly fields: { + readonly authorId: { readonly field: 'authorId' }; + readonly content: { readonly field: 'content' }; + readonly createdAt: { readonly field: 'createdAt' }; + readonly id: { readonly field: '_id' }; + readonly kind: { readonly field: 'kind' }; + readonly title: { readonly field: 'title' }; + }; + }; + readonly discriminator: { readonly field: 'kind' }; + readonly variants: { + readonly article: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Article'; + }; + readonly tutorial: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Tutorial'; + }; + }; + }; + readonly Tutorial: { + readonly fields: { + readonly difficulty: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly duration: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly collection: 'posts'; + readonly fields: { + readonly difficulty: { readonly field: 'difficulty' }; + readonly duration: { readonly field: 'duration' }; + }; + }; + readonly base: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Post'; + }; + }; + readonly User: { + readonly fields: { + readonly address: { + readonly nullable: true; + readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + }; + readonly bio: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; + }; + readonly name: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly role: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + readonly relations: { + readonly posts: { + readonly to: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Post'; + }; + readonly cardinality: '1:N'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['authorId']; + }; + }; + }; + readonly storage: { + readonly collection: 'users'; + readonly fields: { + readonly address: { readonly field: 'address' }; + readonly bio: { readonly field: 'bio' }; + readonly email: { readonly field: 'email' }; + readonly id: { readonly field: '_id' }; + readonly name: { readonly field: 'name' }; + readonly role: { readonly field: 'role' }; + }; + }; + }; + }; + readonly valueObjects: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + }; + readonly enum: { + readonly UserRole: { + readonly codecId: 'mongo/string@1'; + readonly members: readonly [ + { readonly name: 'Admin'; readonly value: 'admin' }, + { readonly name: 'Author'; readonly value: 'author' }, + { readonly name: 'Reader'; readonly value: 'reader' }, + ]; + }; + }; + readonly enumMemberTypes?: { + readonly UserRole: readonly [ + { readonly name: 'Admin'; readonly value: 'admin' }, + { readonly name: 'Author'; readonly value: 'author' }, + { readonly name: 'Reader'; readonly value: 'reader' }, + ]; + }; + }; + }; + }; + readonly capabilities: {}; + readonly extensions: {}; + readonly meta: {}; + readonly valueObjects: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + }; + readonly profileHash: ProfileHash; +}; + +export type Contract = MongoContractWithTypeMaps; diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/snapshots/d264cabd6a02546054a776ed3c04438b9a22989f406c49f5590f7f2101156eb0/contract.json b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/snapshots/d264cabd6a02546054a776ed3c04438b9a22989f406c49f5590f7f2101156eb0/contract.json new file mode 100644 index 000000000000..a19d2d831a90 --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/snapshots/d264cabd6a02546054a776ed3c04438b9a22989f406c49f5590f7f2101156eb0/contract.json @@ -0,0 +1 @@ +{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{},"domain":{"namespaces":{"__unbound__":{"enum":{"UserRole":{"codecId":"mongo/string@1","members":[{"name":"Admin","value":"admin"},{"name":"Author","value":"author"},{"name":"Reader","value":"reader"}]}},"models":{"Article":{"base":{"model":"Post","namespace":"__unbound__"},"fields":{"summary":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"posts","fields":{"summary":{"field":"summary"}}}},"Post":{"discriminator":{"field":"kind"},"fields":{"authorId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"content":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"createdAt":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{"author":{"cardinality":"N:1","nullable":false,"on":{"localFields":["authorId"],"targetFields":["id"]},"to":{"model":"User","namespace":"__unbound__"}}},"storage":{"collection":"posts","fields":{"authorId":{"field":"authorId"},"content":{"field":"content"},"createdAt":{"field":"createdAt"},"id":{"field":"_id"},"kind":{"field":"kind"},"title":{"field":"title"}}},"variants":{"article":{"model":"Article","namespace":"__unbound__"},"tutorial":{"model":"Tutorial","namespace":"__unbound__"}}},"Tutorial":{"base":{"model":"Post","namespace":"__unbound__"},"fields":{"difficulty":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"duration":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"posts","fields":{"difficulty":{"field":"difficulty"},"duration":{"field":"duration"}}}},"User":{"fields":{"address":{"nullable":true,"type":{"kind":"valueObject","name":"Address"}},"bio":{"nullable":true,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"role":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"UserRole","namespaceId":"__unbound__","plane":"domain"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["authorId"]},"to":{"model":"Post","namespace":"__unbound__"}}},"storage":{"collection":"users","fields":{"address":{"field":"address"},"bio":{"field":"bio"},"email":{"field":"email"},"id":{"field":"_id"},"name":{"field":"name"},"role":{"field":"role"}}}}},"valueObjects":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}}}}}},"extensions":{},"meta":{},"profileHash":"251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3","roots":{"posts":{"model":"Post","namespace":"__unbound__"},"users":{"model":"User","namespace":"__unbound__"}},"schemaVersion":"1","storage":{"namespaces":{"__unbound__":{"entries":{"collection":{"posts":{"indexes":[{"keys":[{"direction":1,"field":"authorId"}],"kind":"mongo-index"},{"keys":[{"direction":-1,"field":"createdAt"},{"direction":1,"field":"authorId"}],"kind":"mongo-index"},{"keys":[{"direction":1,"field":"summary"}],"kind":"mongo-index","partialFilterExpression":{"kind":"article"},"unique":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"bsonType":"object","oneOf":[{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"kind":{"enum":["article"]},"summary":{"bsonType":"string"},"title":{"bsonType":"string"}},"required":["kind","summary"]},{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"difficulty":{"bsonType":"string"},"duration":{"bsonType":"int"},"kind":{"enum":["tutorial"]},"title":{"bsonType":"string"}},"required":["difficulty","duration","kind"]}],"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"kind":{"bsonType":"string"},"title":{"bsonType":"string"}},"required":["_id","authorId","content","createdAt","kind","title"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"users":{"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"address":{"oneOf":[{"bsonType":"null"},{"additionalProperties":false,"bsonType":"object","properties":{"city":{"bsonType":"string"},"country":{"bsonType":"string"},"street":{"bsonType":"string"},"zip":{"bsonType":["null","string"]}},"required":["city","country","street"]}]},"bio":{"bsonType":["null","string"]},"email":{"bsonType":"string"},"name":{"bsonType":"string"},"role":{"bsonType":"string","enum":["admin","author","reader"]}},"required":["_id","email","name","role"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}}},"valueSet":{"UserRole":{"kind":"valueSet","values":["admin","author","reader"]}}},"id":"__unbound__","kind":"mongo-database"}},"storageHash":"d264cabd6a02546054a776ed3c04438b9a22989f406c49f5590f7f2101156eb0"},"target":"mongo","targetFamily":"mongo"} diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/snapshots/da1339e341177f79b37f765f08200844d7cb4d59d26fe27fe4d95b0112b0c2cd/contract.d.ts b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/snapshots/da1339e341177f79b37f765f08200844d7cb4d59d26fe27fe4d95b0112b0c2cd/contract.d.ts new file mode 100644 index 000000000000..4d5e1f4928c0 --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/snapshots/da1339e341177f79b37f765f08200844d7cb4d59d26fe27fe4d95b0112b0c2cd/contract.d.ts @@ -0,0 +1,455 @@ +// ⚠️ GENERATED FILE - DO NOT EDIT +// This file is automatically generated by 'prisma contract emit'. +// To regenerate, run: prisma contract emit +import type { + CodecTypes as MongoCodecTypes, + ParameterizedOutputTypesForTypeParams as MongoParameterizedOutputTypes, +} from '@prisma/orm-mongo/target/codec-types'; + +import type { + MongoCollection, + MongoContractWithTypeMaps, + MongoTypeMaps, + RelationKeys, +} from '@prisma/orm-mongo/family-contract'; +import type { + Contract as ContractType, + ExecutionHashBase, + FieldTypesFromDomain, + NamespaceId, + ProfileHashBase, + StorageHashBase, + ValueObjectTypesFromDomain, +} from '@prisma/orm-mongo/contract/types'; + +export type StorageHash = + StorageHashBase<'da1339e341177f79b37f765f08200844d7cb4d59d26fe27fe4d95b0112b0c2cd'>; +export type ExecutionHash = ExecutionHashBase; +export type ProfileHash = + ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; + +export type CodecTypes = MongoCodecTypes; + +type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; +type ValueObjectOutputTypes = ValueObjectTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output' +>; +type ValueObjectInputTypes = ValueObjectTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input' +>; +export type AddressOutput = ValueObjectOutputTypes['__unbound__']['Address']; +export type AddressInput = ValueObjectInputTypes['__unbound__']['Address']; +type FieldTypeOverrides = {}; +export type FieldOutputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output' +>; +export type FieldInputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input' +>; + +export namespace Models { + export type unbound_Article = { + authorId: FieldOutputTypes['__unbound__']['Post']['authorId']; + content: FieldOutputTypes['__unbound__']['Post']['content']; + createdAt: FieldOutputTypes['__unbound__']['Post']['createdAt']; + id: FieldOutputTypes['__unbound__']['Post']['id']; + kind: 'article'; + title: FieldOutputTypes['__unbound__']['Post']['title']; + summary: FieldOutputTypes['__unbound__']['Article']['summary']; + author: unbound_User; + readonly [RelationKeys]?: 'author'; + }; + export type unbound_Post = { + authorId: FieldOutputTypes['__unbound__']['Post']['authorId']; + content: FieldOutputTypes['__unbound__']['Post']['content']; + createdAt: FieldOutputTypes['__unbound__']['Post']['createdAt']; + id: FieldOutputTypes['__unbound__']['Post']['id']; + kind: 'article' | 'tutorial'; + title: FieldOutputTypes['__unbound__']['Post']['title']; + author: unbound_User; + readonly [RelationKeys]?: 'author'; + }; + export type unbound_Tutorial = { + authorId: FieldOutputTypes['__unbound__']['Post']['authorId']; + content: FieldOutputTypes['__unbound__']['Post']['content']; + createdAt: FieldOutputTypes['__unbound__']['Post']['createdAt']; + id: FieldOutputTypes['__unbound__']['Post']['id']; + kind: 'tutorial'; + title: FieldOutputTypes['__unbound__']['Post']['title']; + difficulty: FieldOutputTypes['__unbound__']['Tutorial']['difficulty']; + duration: FieldOutputTypes['__unbound__']['Tutorial']['duration']; + author: unbound_User; + readonly [RelationKeys]?: 'author'; + }; + export type unbound_User = { + address: FieldOutputTypes['__unbound__']['User']['address']; + bio: FieldOutputTypes['__unbound__']['User']['bio']; + email: FieldOutputTypes['__unbound__']['User']['email']; + id: FieldOutputTypes['__unbound__']['User']['id']; + name: FieldOutputTypes['__unbound__']['User']['name']; + posts: unbound_AnyPost[]; + readonly [RelationKeys]?: 'posts'; + }; + export type unbound_AnyPost = unbound_Article | unbound_Tutorial; +} + +export declare const models: { + __unbound__: { + Article: Models.unbound_Article; + Post: Models.unbound_Post; + Tutorial: Models.unbound_Tutorial; + User: Models.unbound_User; + AnyPost: Models.unbound_AnyPost; + }; +}; + +export type TypeMaps = MongoTypeMaps; + +type ContractBase = Omit< + ContractType<{ + readonly namespaces: { + readonly __unbound__: { + readonly id: '__unbound__'; + readonly kind: 'mongo-database'; + readonly entries: { + readonly collection: { + readonly posts: { + readonly indexes: readonly [ + { + readonly keys: readonly [{ readonly direction: 1; readonly field: 'summary' }]; + readonly kind: 'mongo-index'; + readonly partialFilterExpression: { readonly kind: 'article' }; + readonly unique: true; + }, + ]; + readonly kind: 'mongo-collection'; + readonly validator: { + readonly jsonSchema: { + readonly bsonType: 'object'; + readonly oneOf: readonly [ + { + readonly additionalProperties: false; + readonly properties: { + readonly _id: { readonly bsonType: 'objectId' }; + readonly authorId: { readonly bsonType: 'objectId' }; + readonly content: { readonly bsonType: 'string' }; + readonly createdAt: { readonly bsonType: 'date' }; + readonly kind: { readonly enum: readonly ['article'] }; + readonly summary: { readonly bsonType: 'string' }; + readonly title: { readonly bsonType: 'string' }; + }; + readonly required: readonly ['kind', 'summary']; + }, + { + readonly additionalProperties: false; + readonly properties: { + readonly _id: { readonly bsonType: 'objectId' }; + readonly authorId: { readonly bsonType: 'objectId' }; + readonly content: { readonly bsonType: 'string' }; + readonly createdAt: { readonly bsonType: 'date' }; + readonly difficulty: { readonly bsonType: 'string' }; + readonly duration: { readonly bsonType: 'int' }; + readonly kind: { readonly enum: readonly ['tutorial'] }; + readonly title: { readonly bsonType: 'string' }; + }; + readonly required: readonly ['difficulty', 'duration', 'kind']; + }, + ]; + readonly properties: { + readonly _id: { readonly bsonType: 'objectId' }; + readonly authorId: { readonly bsonType: 'objectId' }; + readonly content: { readonly bsonType: 'string' }; + readonly createdAt: { readonly bsonType: 'date' }; + readonly kind: { readonly bsonType: 'string' }; + readonly title: { readonly bsonType: 'string' }; + }; + readonly required: readonly [ + '_id', + 'authorId', + 'content', + 'createdAt', + 'kind', + 'title', + ]; + }; + readonly kind: 'mongo-validator'; + readonly validationAction: 'error'; + readonly validationLevel: 'strict'; + }; + }; + readonly users: { + readonly kind: 'mongo-collection'; + readonly validator: { + readonly jsonSchema: { + readonly additionalProperties: false; + readonly bsonType: 'object'; + readonly properties: { + readonly _id: { readonly bsonType: 'objectId' }; + readonly address: { + readonly oneOf: readonly [ + { readonly bsonType: 'null' }, + { + readonly additionalProperties: false; + readonly bsonType: 'object'; + readonly properties: { + readonly city: { readonly bsonType: 'string' }; + readonly country: { readonly bsonType: 'string' }; + readonly street: { readonly bsonType: 'string' }; + readonly zip: { readonly bsonType: readonly ['null', 'string'] }; + }; + readonly required: readonly ['city', 'country', 'street']; + }, + ]; + }; + readonly bio: { readonly bsonType: readonly ['null', 'string'] }; + readonly email: { readonly bsonType: 'string' }; + readonly name: { readonly bsonType: 'string' }; + }; + readonly required: readonly ['_id', 'email', 'name']; + }; + readonly kind: 'mongo-validator'; + readonly validationAction: 'error'; + readonly validationLevel: 'strict'; + }; + }; + }; + }; + }; + }; + readonly storageHash: StorageHash; + }>, + 'roots' | 'domain' +> & { + readonly target: 'mongo'; + readonly targetFamily: 'mongo'; + readonly roots: { + readonly posts: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'Post' }; + readonly users: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'User' }; + }; + readonly domain: { + readonly namespaces: { + readonly __unbound__: { + readonly models: { + readonly Article: { + readonly fields: { + readonly summary: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly collection: 'posts'; + readonly fields: { readonly summary: { readonly field: 'summary' } }; + }; + readonly base: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Post'; + }; + }; + readonly Post: { + readonly fields: { + readonly authorId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; + }; + readonly content: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly createdAt: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; + }; + readonly kind: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly title: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + readonly relations: { + readonly author: { + readonly to: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'User'; + }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['authorId']; + readonly targetFields: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly collection: 'posts'; + readonly fields: { + readonly authorId: { readonly field: 'authorId' }; + readonly content: { readonly field: 'content' }; + readonly createdAt: { readonly field: 'createdAt' }; + readonly id: { readonly field: '_id' }; + readonly kind: { readonly field: 'kind' }; + readonly title: { readonly field: 'title' }; + }; + }; + readonly discriminator: { readonly field: 'kind' }; + readonly variants: { + readonly article: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Article'; + }; + readonly tutorial: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Tutorial'; + }; + }; + }; + readonly Tutorial: { + readonly fields: { + readonly difficulty: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly duration: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly collection: 'posts'; + readonly fields: { + readonly difficulty: { readonly field: 'difficulty' }; + readonly duration: { readonly field: 'duration' }; + }; + }; + readonly base: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Post'; + }; + }; + readonly User: { + readonly fields: { + readonly address: { + readonly nullable: true; + readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + }; + readonly bio: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; + }; + readonly name: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + readonly relations: { + readonly posts: { + readonly to: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Post'; + }; + readonly cardinality: '1:N'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['authorId']; + }; + }; + }; + readonly storage: { + readonly collection: 'users'; + readonly fields: { + readonly address: { readonly field: 'address' }; + readonly bio: { readonly field: 'bio' }; + readonly email: { readonly field: 'email' }; + readonly id: { readonly field: '_id' }; + readonly name: { readonly field: 'name' }; + }; + }; + }; + }; + readonly valueObjects: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + }; + }; + }; + }; + readonly capabilities: {}; + readonly extensions: {}; + readonly meta: {}; + readonly valueObjects: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + }; + readonly profileHash: ProfileHash; +}; + +export type Contract = MongoContractWithTypeMaps; diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/snapshots/da1339e341177f79b37f765f08200844d7cb4d59d26fe27fe4d95b0112b0c2cd/contract.json b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/snapshots/da1339e341177f79b37f765f08200844d7cb4d59d26fe27fe4d95b0112b0c2cd/contract.json new file mode 100644 index 000000000000..948bf482e3ca --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/migrations/snapshots/da1339e341177f79b37f765f08200844d7cb4d59d26fe27fe4d95b0112b0c2cd/contract.json @@ -0,0 +1 @@ +{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{},"domain":{"namespaces":{"__unbound__":{"models":{"Article":{"base":{"model":"Post","namespace":"__unbound__"},"fields":{"summary":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"posts","fields":{"summary":{"field":"summary"}}}},"Post":{"discriminator":{"field":"kind"},"fields":{"authorId":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"content":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"createdAt":{"nullable":false,"type":{"codecId":"mongo/date@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{"author":{"cardinality":"N:1","nullable":false,"on":{"localFields":["authorId"],"targetFields":["id"]},"to":{"model":"User","namespace":"__unbound__"}}},"storage":{"collection":"posts","fields":{"authorId":{"field":"authorId"},"content":{"field":"content"},"createdAt":{"field":"createdAt"},"id":{"field":"_id"},"kind":{"field":"kind"},"title":{"field":"title"}}},"variants":{"article":{"model":"Article","namespace":"__unbound__"},"tutorial":{"model":"Tutorial","namespace":"__unbound__"}}},"Tutorial":{"base":{"model":"Post","namespace":"__unbound__"},"fields":{"difficulty":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"duration":{"nullable":false,"type":{"codecId":"mongo/int32@1","kind":"scalar"}}},"relations":{},"storage":{"collection":"posts","fields":{"difficulty":{"field":"difficulty"},"duration":{"field":"duration"}}}},"User":{"fields":{"address":{"nullable":true,"type":{"kind":"valueObject","name":"Address"}},"bio":{"nullable":true,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"mongo/objectId@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["authorId"]},"to":{"model":"Post","namespace":"__unbound__"}}},"storage":{"collection":"users","fields":{"address":{"field":"address"},"bio":{"field":"bio"},"email":{"field":"email"},"id":{"field":"_id"},"name":{"field":"name"}}}}},"valueObjects":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"mongo/string@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"mongo/string@1","kind":"scalar"}}}}}}}},"extensions":{},"meta":{},"profileHash":"251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3","roots":{"posts":{"model":"Post","namespace":"__unbound__"},"users":{"model":"User","namespace":"__unbound__"}},"schemaVersion":"1","storage":{"namespaces":{"__unbound__":{"entries":{"collection":{"posts":{"indexes":[{"keys":[{"direction":1,"field":"summary"}],"kind":"mongo-index","partialFilterExpression":{"kind":"article"},"unique":true}],"kind":"mongo-collection","validator":{"jsonSchema":{"bsonType":"object","oneOf":[{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"kind":{"enum":["article"]},"summary":{"bsonType":"string"},"title":{"bsonType":"string"}},"required":["kind","summary"]},{"additionalProperties":false,"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"difficulty":{"bsonType":"string"},"duration":{"bsonType":"int"},"kind":{"enum":["tutorial"]},"title":{"bsonType":"string"}},"required":["difficulty","duration","kind"]}],"properties":{"_id":{"bsonType":"objectId"},"authorId":{"bsonType":"objectId"},"content":{"bsonType":"string"},"createdAt":{"bsonType":"date"},"kind":{"bsonType":"string"},"title":{"bsonType":"string"}},"required":["_id","authorId","content","createdAt","kind","title"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}},"users":{"kind":"mongo-collection","validator":{"jsonSchema":{"additionalProperties":false,"bsonType":"object","properties":{"_id":{"bsonType":"objectId"},"address":{"oneOf":[{"bsonType":"null"},{"additionalProperties":false,"bsonType":"object","properties":{"city":{"bsonType":"string"},"country":{"bsonType":"string"},"street":{"bsonType":"string"},"zip":{"bsonType":["null","string"]}},"required":["city","country","street"]}]},"bio":{"bsonType":["null","string"]},"email":{"bsonType":"string"},"name":{"bsonType":"string"}},"required":["_id","email","name"]},"kind":"mongo-validator","validationAction":"error","validationLevel":"strict"}}}},"id":"__unbound__","kind":"mongo-database"}},"storageHash":"da1339e341177f79b37f765f08200844d7cb4d59d26fe27fe4d95b0112b0c2cd"},"target":"mongo","targetFamily":"mongo"} diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/src/contract.d.ts b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/src/contract.d.ts new file mode 100644 index 000000000000..af70721dd428 --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/src/contract.d.ts @@ -0,0 +1,504 @@ +// ⚠️ GENERATED FILE - DO NOT EDIT +// This file is automatically generated by 'prisma contract emit'. +// To regenerate, run: prisma contract emit +import type { + CodecTypes as MongoCodecTypes, + ParameterizedOutputTypesForTypeParams as MongoParameterizedOutputTypes, +} from '@prisma/orm-mongo/target/codec-types'; + +import type { + MongoCollection, + MongoContractWithTypeMaps, + MongoTypeMaps, + RelationKeys, +} from '@prisma/orm-mongo/family-contract'; +import type { + Contract as ContractType, + ExecutionHashBase, + FieldTypesFromDomain, + NamespaceId, + ProfileHashBase, + StorageHashBase, + ValueObjectTypesFromDomain, +} from '@prisma/orm-mongo/contract/types'; + +export type StorageHash = + StorageHashBase<'d264cabd6a02546054a776ed3c04438b9a22989f406c49f5590f7f2101156eb0'>; +export type ExecutionHash = ExecutionHashBase; +export type ProfileHash = + ProfileHashBase<'251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3'>; + +export type CodecTypes = MongoCodecTypes; + +type ParameterizedOutputTypeFunctions = MongoParameterizedOutputTypes; +type ValueObjectOutputTypes = ValueObjectTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output' +>; +type ValueObjectInputTypes = ValueObjectTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input' +>; +export type AddressOutput = ValueObjectOutputTypes['__unbound__']['Address']; +export type AddressInput = ValueObjectInputTypes['__unbound__']['Address']; +type FieldTypeOverrides = { + readonly __unbound__: { + readonly models: { + readonly User: { + readonly role: { + readonly output: 'admin' | 'author' | 'reader'; + readonly input: 'admin' | 'author' | 'reader'; + }; + }; + }; + }; +}; +export type FieldOutputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output' +>; +export type FieldInputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input' +>; + +export namespace Models { + export type unbound_Article = { + authorId: FieldOutputTypes['__unbound__']['Post']['authorId']; + content: FieldOutputTypes['__unbound__']['Post']['content']; + createdAt: FieldOutputTypes['__unbound__']['Post']['createdAt']; + id: FieldOutputTypes['__unbound__']['Post']['id']; + kind: 'article'; + title: FieldOutputTypes['__unbound__']['Post']['title']; + summary: FieldOutputTypes['__unbound__']['Article']['summary']; + author: unbound_User; + readonly [RelationKeys]?: 'author'; + }; + export type unbound_Post = { + authorId: FieldOutputTypes['__unbound__']['Post']['authorId']; + content: FieldOutputTypes['__unbound__']['Post']['content']; + createdAt: FieldOutputTypes['__unbound__']['Post']['createdAt']; + id: FieldOutputTypes['__unbound__']['Post']['id']; + kind: 'article' | 'tutorial'; + title: FieldOutputTypes['__unbound__']['Post']['title']; + author: unbound_User; + readonly [RelationKeys]?: 'author'; + }; + export type unbound_Tutorial = { + authorId: FieldOutputTypes['__unbound__']['Post']['authorId']; + content: FieldOutputTypes['__unbound__']['Post']['content']; + createdAt: FieldOutputTypes['__unbound__']['Post']['createdAt']; + id: FieldOutputTypes['__unbound__']['Post']['id']; + kind: 'tutorial'; + title: FieldOutputTypes['__unbound__']['Post']['title']; + difficulty: FieldOutputTypes['__unbound__']['Tutorial']['difficulty']; + duration: FieldOutputTypes['__unbound__']['Tutorial']['duration']; + author: unbound_User; + readonly [RelationKeys]?: 'author'; + }; + export type unbound_User = { + address: FieldOutputTypes['__unbound__']['User']['address']; + bio: FieldOutputTypes['__unbound__']['User']['bio']; + email: FieldOutputTypes['__unbound__']['User']['email']; + id: FieldOutputTypes['__unbound__']['User']['id']; + name: FieldOutputTypes['__unbound__']['User']['name']; + role: FieldOutputTypes['__unbound__']['User']['role']; + posts: unbound_AnyPost[]; + readonly [RelationKeys]?: 'posts'; + }; + export type unbound_AnyPost = unbound_Article | unbound_Tutorial; +} + +export declare const models: { + __unbound__: { + Article: Models.unbound_Article; + Post: Models.unbound_Post; + Tutorial: Models.unbound_Tutorial; + User: Models.unbound_User; + AnyPost: Models.unbound_AnyPost; + }; +}; + +export type TypeMaps = MongoTypeMaps; + +type ContractBase = Omit< + ContractType<{ + readonly namespaces: { + readonly __unbound__: { + readonly id: '__unbound__'; + readonly kind: 'mongo-database'; + readonly entries: { + readonly collection: { + readonly posts: { + readonly indexes: readonly [ + { + readonly keys: readonly [{ readonly direction: 1; readonly field: 'authorId' }]; + readonly kind: 'mongo-index'; + }, + { + readonly keys: readonly [ + { readonly direction: -1; readonly field: 'createdAt' }, + { readonly direction: 1; readonly field: 'authorId' }, + ]; + readonly kind: 'mongo-index'; + }, + { + readonly keys: readonly [{ readonly direction: 1; readonly field: 'summary' }]; + readonly kind: 'mongo-index'; + readonly partialFilterExpression: { readonly kind: 'article' }; + readonly unique: true; + }, + ]; + readonly kind: 'mongo-collection'; + readonly validator: { + readonly jsonSchema: { + readonly bsonType: 'object'; + readonly oneOf: readonly [ + { + readonly additionalProperties: false; + readonly properties: { + readonly _id: { readonly bsonType: 'objectId' }; + readonly authorId: { readonly bsonType: 'objectId' }; + readonly content: { readonly bsonType: 'string' }; + readonly createdAt: { readonly bsonType: 'date' }; + readonly kind: { readonly enum: readonly ['article'] }; + readonly summary: { readonly bsonType: 'string' }; + readonly title: { readonly bsonType: 'string' }; + }; + readonly required: readonly ['kind', 'summary']; + }, + { + readonly additionalProperties: false; + readonly properties: { + readonly _id: { readonly bsonType: 'objectId' }; + readonly authorId: { readonly bsonType: 'objectId' }; + readonly content: { readonly bsonType: 'string' }; + readonly createdAt: { readonly bsonType: 'date' }; + readonly difficulty: { readonly bsonType: 'string' }; + readonly duration: { readonly bsonType: 'int' }; + readonly kind: { readonly enum: readonly ['tutorial'] }; + readonly title: { readonly bsonType: 'string' }; + }; + readonly required: readonly ['difficulty', 'duration', 'kind']; + }, + ]; + readonly properties: { + readonly _id: { readonly bsonType: 'objectId' }; + readonly authorId: { readonly bsonType: 'objectId' }; + readonly content: { readonly bsonType: 'string' }; + readonly createdAt: { readonly bsonType: 'date' }; + readonly kind: { readonly bsonType: 'string' }; + readonly title: { readonly bsonType: 'string' }; + }; + readonly required: readonly [ + '_id', + 'authorId', + 'content', + 'createdAt', + 'kind', + 'title', + ]; + }; + readonly kind: 'mongo-validator'; + readonly validationAction: 'error'; + readonly validationLevel: 'strict'; + }; + }; + readonly users: { + readonly kind: 'mongo-collection'; + readonly validator: { + readonly jsonSchema: { + readonly additionalProperties: false; + readonly bsonType: 'object'; + readonly properties: { + readonly _id: { readonly bsonType: 'objectId' }; + readonly address: { + readonly oneOf: readonly [ + { readonly bsonType: 'null' }, + { + readonly additionalProperties: false; + readonly bsonType: 'object'; + readonly properties: { + readonly city: { readonly bsonType: 'string' }; + readonly country: { readonly bsonType: 'string' }; + readonly street: { readonly bsonType: 'string' }; + readonly zip: { readonly bsonType: readonly ['null', 'string'] }; + }; + readonly required: readonly ['city', 'country', 'street']; + }, + ]; + }; + readonly bio: { readonly bsonType: readonly ['null', 'string'] }; + readonly email: { readonly bsonType: 'string' }; + readonly name: { readonly bsonType: 'string' }; + readonly role: { + readonly bsonType: 'string'; + readonly enum: readonly ['admin', 'author', 'reader']; + }; + }; + readonly required: readonly ['_id', 'email', 'name', 'role']; + }; + readonly kind: 'mongo-validator'; + readonly validationAction: 'error'; + readonly validationLevel: 'strict'; + }; + }; + }; + }; + }; + }; + readonly storageHash: StorageHash; + }>, + 'roots' | 'domain' +> & { + readonly target: 'mongo'; + readonly targetFamily: 'mongo'; + readonly roots: { + readonly posts: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'Post' }; + readonly users: { readonly namespace: '__unbound__' & NamespaceId; readonly model: 'User' }; + }; + readonly domain: { + readonly namespaces: { + readonly __unbound__: { + readonly models: { + readonly Article: { + readonly fields: { + readonly summary: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly collection: 'posts'; + readonly fields: { readonly summary: { readonly field: 'summary' } }; + }; + readonly base: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Post'; + }; + }; + readonly Post: { + readonly fields: { + readonly authorId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; + }; + readonly content: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly createdAt: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/date@1' }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; + }; + readonly kind: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly title: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + readonly relations: { + readonly author: { + readonly to: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'User'; + }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['authorId']; + readonly targetFields: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly collection: 'posts'; + readonly fields: { + readonly authorId: { readonly field: 'authorId' }; + readonly content: { readonly field: 'content' }; + readonly createdAt: { readonly field: 'createdAt' }; + readonly id: { readonly field: '_id' }; + readonly kind: { readonly field: 'kind' }; + readonly title: { readonly field: 'title' }; + }; + }; + readonly discriminator: { readonly field: 'kind' }; + readonly variants: { + readonly article: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Article'; + }; + readonly tutorial: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Tutorial'; + }; + }; + }; + readonly Tutorial: { + readonly fields: { + readonly difficulty: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly duration: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/int32@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly collection: 'posts'; + readonly fields: { + readonly difficulty: { readonly field: 'difficulty' }; + readonly duration: { readonly field: 'duration' }; + }; + }; + readonly base: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Post'; + }; + }; + readonly User: { + readonly fields: { + readonly address: { + readonly nullable: true; + readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + }; + readonly bio: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/objectId@1' }; + }; + readonly name: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly role: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + readonly relations: { + readonly posts: { + readonly to: { + readonly namespace: '__unbound__' & NamespaceId; + readonly model: 'Post'; + }; + readonly cardinality: '1:N'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['authorId']; + }; + }; + }; + readonly storage: { + readonly collection: 'users'; + readonly fields: { + readonly address: { readonly field: 'address' }; + readonly bio: { readonly field: 'bio' }; + readonly email: { readonly field: 'email' }; + readonly id: { readonly field: '_id' }; + readonly name: { readonly field: 'name' }; + readonly role: { readonly field: 'role' }; + }; + }; + }; + }; + readonly valueObjects: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + }; + readonly enum: { + readonly UserRole: { + readonly codecId: 'mongo/string@1'; + readonly members: readonly [ + { readonly name: 'Admin'; readonly value: 'admin' }, + { readonly name: 'Author'; readonly value: 'author' }, + { readonly name: 'Reader'; readonly value: 'reader' }, + ]; + }; + }; + readonly enumMemberTypes?: { + readonly UserRole: readonly [ + { readonly name: 'Admin'; readonly value: 'admin' }, + { readonly name: 'Author'; readonly value: 'author' }, + { readonly name: 'Reader'; readonly value: 'reader' }, + ]; + }; + }; + }; + }; + readonly capabilities: {}; + readonly extensions: {}; + readonly meta: {}; + readonly valueObjects: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'mongo/string@1' }; + }; + }; + }; + }; + readonly profileHash: ProfileHash; +}; + +export type Contract = MongoContractWithTypeMaps; diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/src/contract.json b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/src/contract.json new file mode 100644 index 000000000000..8e6eaca148f4 --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/mongo/src/contract.json @@ -0,0 +1,580 @@ +{ + "schemaVersion": "1", + "targetFamily": "mongo", + "target": "mongo", + "profileHash": "251b3ce23f6c9f561892e7c1af9d2cc941a13d64ba1aa7226b90036b09568cc3", + "roots": { + "posts": { + "model": "Post", + "namespace": "__unbound__" + }, + "users": { + "model": "User", + "namespace": "__unbound__" + } + }, + "domain": { + "namespaces": { + "__unbound__": { + "enum": { + "UserRole": { + "codecId": "mongo/string@1", + "members": [ + { + "name": "Admin", + "value": "admin" + }, + { + "name": "Author", + "value": "author" + }, + { + "name": "Reader", + "value": "reader" + } + ] + } + }, + "models": { + "Article": { + "base": { + "model": "Post", + "namespace": "__unbound__" + }, + "fields": { + "summary": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + } + }, + "relations": {}, + "storage": { + "collection": "posts", + "fields": { + "summary": { + "field": "summary" + } + } + } + }, + "Post": { + "discriminator": { + "field": "kind" + }, + "fields": { + "authorId": { + "nullable": false, + "type": { + "codecId": "mongo/objectId@1", + "kind": "scalar" + } + }, + "content": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "createdAt": { + "nullable": false, + "type": { + "codecId": "mongo/date@1", + "kind": "scalar" + } + }, + "id": { + "nullable": false, + "type": { + "codecId": "mongo/objectId@1", + "kind": "scalar" + } + }, + "kind": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "title": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + } + }, + "relations": { + "author": { + "cardinality": "N:1", + "nullable": false, + "on": { + "localFields": [ + "authorId" + ], + "targetFields": [ + "id" + ] + }, + "to": { + "model": "User", + "namespace": "__unbound__" + } + } + }, + "storage": { + "collection": "posts", + "fields": { + "authorId": { + "field": "authorId" + }, + "content": { + "field": "content" + }, + "createdAt": { + "field": "createdAt" + }, + "id": { + "field": "_id" + }, + "kind": { + "field": "kind" + }, + "title": { + "field": "title" + } + } + }, + "variants": { + "article": { + "model": "Article", + "namespace": "__unbound__" + }, + "tutorial": { + "model": "Tutorial", + "namespace": "__unbound__" + } + } + }, + "Tutorial": { + "base": { + "model": "Post", + "namespace": "__unbound__" + }, + "fields": { + "difficulty": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "duration": { + "nullable": false, + "type": { + "codecId": "mongo/int32@1", + "kind": "scalar" + } + } + }, + "relations": {}, + "storage": { + "collection": "posts", + "fields": { + "difficulty": { + "field": "difficulty" + }, + "duration": { + "field": "duration" + } + } + } + }, + "User": { + "fields": { + "address": { + "nullable": true, + "type": { + "kind": "valueObject", + "name": "Address" + } + }, + "bio": { + "nullable": true, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "email": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "id": { + "nullable": false, + "type": { + "codecId": "mongo/objectId@1", + "kind": "scalar" + } + }, + "name": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "role": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + }, + "valueSet": { + "entityKind": "enum", + "entityName": "UserRole", + "namespaceId": "__unbound__", + "plane": "domain" + } + } + }, + "relations": { + "posts": { + "cardinality": "1:N", + "on": { + "localFields": [ + "id" + ], + "targetFields": [ + "authorId" + ] + }, + "to": { + "model": "Post", + "namespace": "__unbound__" + } + } + }, + "storage": { + "collection": "users", + "fields": { + "address": { + "field": "address" + }, + "bio": { + "field": "bio" + }, + "email": { + "field": "email" + }, + "id": { + "field": "_id" + }, + "name": { + "field": "name" + }, + "role": { + "field": "role" + } + } + } + } + }, + "valueObjects": { + "Address": { + "fields": { + "city": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "country": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "street": { + "nullable": false, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + }, + "zip": { + "nullable": true, + "type": { + "codecId": "mongo/string@1", + "kind": "scalar" + } + } + } + } + } + } + } + }, + "storage": { + "namespaces": { + "__unbound__": { + "entries": { + "collection": { + "posts": { + "indexes": [ + { + "keys": [ + { + "direction": 1, + "field": "authorId" + } + ], + "kind": "mongo-index" + }, + { + "keys": [ + { + "direction": -1, + "field": "createdAt" + }, + { + "direction": 1, + "field": "authorId" + } + ], + "kind": "mongo-index" + }, + { + "keys": [ + { + "direction": 1, + "field": "summary" + } + ], + "kind": "mongo-index", + "partialFilterExpression": { + "kind": "article" + }, + "unique": true + } + ], + "kind": "mongo-collection", + "validator": { + "jsonSchema": { + "bsonType": "object", + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "_id": { + "bsonType": "objectId" + }, + "authorId": { + "bsonType": "objectId" + }, + "content": { + "bsonType": "string" + }, + "createdAt": { + "bsonType": "date" + }, + "kind": { + "enum": [ + "article" + ] + }, + "summary": { + "bsonType": "string" + }, + "title": { + "bsonType": "string" + } + }, + "required": [ + "kind", + "summary" + ] + }, + { + "additionalProperties": false, + "properties": { + "_id": { + "bsonType": "objectId" + }, + "authorId": { + "bsonType": "objectId" + }, + "content": { + "bsonType": "string" + }, + "createdAt": { + "bsonType": "date" + }, + "difficulty": { + "bsonType": "string" + }, + "duration": { + "bsonType": "int" + }, + "kind": { + "enum": [ + "tutorial" + ] + }, + "title": { + "bsonType": "string" + } + }, + "required": [ + "difficulty", + "duration", + "kind" + ] + } + ], + "properties": { + "_id": { + "bsonType": "objectId" + }, + "authorId": { + "bsonType": "objectId" + }, + "content": { + "bsonType": "string" + }, + "createdAt": { + "bsonType": "date" + }, + "kind": { + "bsonType": "string" + }, + "title": { + "bsonType": "string" + } + }, + "required": [ + "_id", + "authorId", + "content", + "createdAt", + "kind", + "title" + ] + }, + "kind": "mongo-validator", + "validationAction": "error", + "validationLevel": "strict" + } + }, + "users": { + "kind": "mongo-collection", + "validator": { + "jsonSchema": { + "additionalProperties": false, + "bsonType": "object", + "properties": { + "_id": { + "bsonType": "objectId" + }, + "address": { + "oneOf": [ + { + "bsonType": "null" + }, + { + "additionalProperties": false, + "bsonType": "object", + "properties": { + "city": { + "bsonType": "string" + }, + "country": { + "bsonType": "string" + }, + "street": { + "bsonType": "string" + }, + "zip": { + "bsonType": [ + "null", + "string" + ] + } + }, + "required": [ + "city", + "country", + "street" + ] + } + ] + }, + "bio": { + "bsonType": [ + "null", + "string" + ] + }, + "email": { + "bsonType": "string" + }, + "name": { + "bsonType": "string" + }, + "role": { + "bsonType": "string", + "enum": [ + "admin", + "author", + "reader" + ] + } + }, + "required": [ + "_id", + "email", + "name", + "role" + ] + }, + "kind": "mongo-validator", + "validationAction": "error", + "validationLevel": "strict" + } + } + }, + "valueSet": { + "UserRole": { + "kind": "valueSet", + "values": [ + "admin", + "author", + "reader" + ] + } + } + }, + "id": "__unbound__", + "kind": "mongo-database" + } + }, + "storageHash": "d264cabd6a02546054a776ed3c04438b9a22989f406c49f5590f7f2101156eb0" + }, + "capabilities": {}, + "extensions": {}, + "meta": {}, + "_generated": { + "warning": "⚠️ GENERATED FILE - DO NOT EDIT", + "message": "This file is automatically generated by \"prisma contract emit\".", + "regenerate": "To regenerate, run: prisma contract emit" + } +} \ No newline at end of file diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/sql/migrations/app/20260422T0720_initial/migration.json b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/sql/migrations/app/20260422T0720_initial/migration.json new file mode 100644 index 000000000000..21aa5ae9040c --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/sql/migrations/app/20260422T0720_initial/migration.json @@ -0,0 +1,7 @@ +{ + "from": null, + "to": "58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b", + "providedInvariants": [], + "createdAt": "2026-06-19T15:33:14.992Z", + "migrationHash": "bde42f97a6cbf4ca06e4507692274c8e3ece1e1b0672f050bb78a6a2eaa0ff0d" +} diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/sql/migrations/app/20260422T0720_initial/migration.ts b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/sql/migrations/app/20260422T0720_initial/migration.ts new file mode 100644 index 000000000000..5f113150356b --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/sql/migrations/app/20260422T0720_initial/migration.ts @@ -0,0 +1,215 @@ +#!/usr/bin/env -S node +import { + checkExpression, + col, + fn, + lit, + Migration, + MigrationCLI, + primaryKey, + rawSql, + unique, +} from '@prisma/orm-postgres/target/migration'; +import type { Contract as End } from '../../snapshots/58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b/contract'; +import endContract from '../../snapshots/58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b/contract.json' with { + type: 'json', +}; + +export default class M extends Migration { + override readonly endContractJson = endContract; + + override get operations() { + return [ + rawSql({ + id: 'extension.vector', + label: 'Enable extension "vector"', + summary: 'Ensures the vector extension is available for pgvector operations', + operationClass: 'additive', + target: { id: 'postgres' }, + precheck: [ + { + description: 'verify extension "vector" is not already enabled', + sql: "SELECT NOT EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'vector')", + expectation: { expects: 'absent', object: 'extension "vector"' }, + }, + ], + execute: [ + { + description: 'create extension "vector"', + sql: 'CREATE EXTENSION IF NOT EXISTS vector', + }, + ], + postcheck: [ + { + description: 'confirm extension "vector" is enabled', + sql: "SELECT EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'vector')", + expectation: { expects: 'present', object: 'extension "vector"' }, + }, + ], + }), + this.createTable({ + schema: 'public', + table: 'bug', + columns: [ + col('id', 'uuid', { notNull: true }), + col('severity', 'text', { notNull: true }), + col('stepsToRepro', 'text'), + ], + constraints: [primaryKey(['id'])], + }), + this.createTable({ + schema: 'public', + table: 'feature', + columns: [ + col('id', 'uuid', { notNull: true }), + col('priority', 'text', { notNull: true }), + col('targetRelease', 'text'), + ], + constraints: [primaryKey(['id'])], + }), + this.createTable({ + schema: 'public', + table: 'tag', + columns: [col('id', 'uuid', { notNull: true }), col('label', 'text', { notNull: true })], + constraints: [primaryKey(['id']), unique(['label'], { name: 'tag_label_key' })], + }), + this.createTable({ + schema: 'public', + table: 'user', + columns: [ + col('address', 'jsonb'), + col('createdAt', 'timestamptz', { notNull: true, default: fn('now()') }), + col('displayName', 'text', { notNull: true }), + col('email', 'text', { notNull: true }), + col('id', 'uuid', { notNull: true }), + col('kind', 'text', { notNull: true }), + ], + constraints: [ + primaryKey(['id']), + checkExpression('user_kind_check_836d43ef', "\"kind\" IN ('admin', 'user')"), + ], + }), + this.createTable({ + schema: 'public', + table: 'post', + columns: [ + col('createdAt', 'timestamptz', { notNull: true, default: fn('now()') }), + col('embedding', 'vector(1536)'), + col('id', 'uuid', { notNull: true }), + col('priority', 'int4', { notNull: true, default: lit(0) }), + col('title', 'text', { notNull: true }), + col('userId', 'uuid', { notNull: true }), + ], + constraints: [ + primaryKey(['id']), + checkExpression('post_priority_check_b236ace9', '"priority" IN (0, 1, 2)'), + ], + }), + this.createTable({ + schema: 'public', + table: 'task', + columns: [ + col('createdAt', 'timestamptz', { notNull: true, default: fn('now()') }), + col('description', 'text'), + col('id', 'uuid', { notNull: true }), + col('status', 'text', { notNull: true, default: lit('open') }), + col('title', 'text', { notNull: true }), + col('type', 'text', { notNull: true }), + col('userId', 'uuid', { notNull: true }), + ], + constraints: [primaryKey(['id'])], + }), + this.createTable({ + schema: 'public', + table: 'post_tag', + columns: [ + col('postId', 'uuid', { notNull: true }), + col('tagId', 'uuid', { notNull: true }), + ], + constraints: [primaryKey(['postId', 'tagId'])], + }), + this.addForeignKey({ + schema: 'public', + table: 'post', + foreignKey: { + name: 'post_userId_fkey', + columns: ['userId'], + references: { schema: 'public', table: 'user', columns: ['id'] }, + }, + }), + this.createIndex({ + schema: 'public', + table: 'post', + index: 'post_userId_idx_a489d58a', + columns: ['userId'], + }), + this.addForeignKey({ + schema: 'public', + table: 'task', + foreignKey: { + name: 'task_userId_fkey', + columns: ['userId'], + references: { schema: 'public', table: 'user', columns: ['id'] }, + }, + }), + this.createIndex({ + schema: 'public', + table: 'task', + index: 'task_userId_idx_a489d58a', + columns: ['userId'], + }), + this.addForeignKey({ + schema: 'public', + table: 'bug', + foreignKey: { + name: 'bug_id_fkey', + columns: ['id'], + references: { schema: 'public', table: 'task', columns: ['id'] }, + onDelete: 'cascade', + }, + }), + this.addForeignKey({ + schema: 'public', + table: 'feature', + foreignKey: { + name: 'feature_id_fkey', + columns: ['id'], + references: { schema: 'public', table: 'task', columns: ['id'] }, + onDelete: 'cascade', + }, + }), + this.createIndex({ + schema: 'public', + table: 'post_tag', + index: 'post_tag_postId_idx_a7a72715', + columns: ['postId'], + }), + this.createIndex({ + schema: 'public', + table: 'post_tag', + index: 'post_tag_tagId_idx_86854244', + columns: ['tagId'], + }), + this.addForeignKey({ + schema: 'public', + table: 'post_tag', + foreignKey: { + name: 'post_tag_postId_fkey', + columns: ['postId'], + references: { schema: 'public', table: 'post', columns: ['id'] }, + }, + }), + this.addForeignKey({ + schema: 'public', + table: 'post_tag', + foreignKey: { + name: 'post_tag_tagId_fkey', + columns: ['tagId'], + references: { schema: 'public', table: 'tag', columns: ['id'] }, + }, + }), + ]; + } +} + +MigrationCLI.run(import.meta.url, M); diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/sql/migrations/app/20260422T0720_initial/ops.json b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/sql/migrations/app/20260422T0720_initial/ops.json new file mode 100644 index 000000000000..3843b04f54cb --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/sql/migrations/app/20260422T0720_initial/ops.json @@ -0,0 +1,810 @@ +[ + { + "id": "extension.vector", + "label": "Enable extension \"vector\"", + "summary": "Ensures the vector extension is available for pgvector operations", + "operationClass": "additive", + "target": { + "id": "postgres" + }, + "precheck": [ + { + "description": "verify extension \"vector\" is not already enabled", + "sql": "SELECT NOT EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'vector')", + "expectation": { + "expects": "absent", + "object": "extension \"vector\"" + } + } + ], + "execute": [ + { + "description": "create extension \"vector\"", + "sql": "CREATE EXTENSION IF NOT EXISTS vector" + } + ], + "postcheck": [ + { + "description": "confirm extension \"vector\" is enabled", + "sql": "SELECT EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'vector')", + "expectation": { + "expects": "present", + "object": "extension \"vector\"" + } + } + ] + }, + { + "id": "table.bug", + "label": "Create table \"bug\"", + "summary": "Creates table \"bug\"", + "operationClass": "additive", + "target": { + "id": "postgres", + "details": { + "schema": "public", + "objectType": "table", + "name": "bug" + } + }, + "precheck": [ + { + "description": "ensure table \"bug\" does not exist", + "sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"", + "params": ["\"public\".\"bug\""], + "expectation": { + "expects": "absent", + "object": "table \"bug\"" + } + } + ], + "execute": [ + { + "description": "create table \"bug\"", + "sql": "CREATE TABLE \"public\".\"bug\" (\n \"id\" uuid NOT NULL,\n \"severity\" text NOT NULL,\n \"stepsToRepro\" text,\n PRIMARY KEY (\"id\")\n)", + "params": [] + } + ], + "postcheck": [ + { + "description": "verify table \"bug\" exists", + "sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_class\" AS \"c\" WHERE (\"c\".\"oid\" = to_regclass($1) AND (\"c\".\"relkind\" = $2 OR \"c\".\"relkind\" = $3))) AS \"result\"", + "params": ["\"public\".\"bug\"", "r", "p"], + "expectation": { + "expects": "present", + "object": "table \"bug\"" + } + } + ] + }, + { + "id": "table.feature", + "label": "Create table \"feature\"", + "summary": "Creates table \"feature\"", + "operationClass": "additive", + "target": { + "id": "postgres", + "details": { + "schema": "public", + "objectType": "table", + "name": "feature" + } + }, + "precheck": [ + { + "description": "ensure table \"feature\" does not exist", + "sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"", + "params": ["\"public\".\"feature\""], + "expectation": { + "expects": "absent", + "object": "table \"feature\"" + } + } + ], + "execute": [ + { + "description": "create table \"feature\"", + "sql": "CREATE TABLE \"public\".\"feature\" (\n \"id\" uuid NOT NULL,\n \"priority\" text NOT NULL,\n \"targetRelease\" text,\n PRIMARY KEY (\"id\")\n)", + "params": [] + } + ], + "postcheck": [ + { + "description": "verify table \"feature\" exists", + "sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_class\" AS \"c\" WHERE (\"c\".\"oid\" = to_regclass($1) AND (\"c\".\"relkind\" = $2 OR \"c\".\"relkind\" = $3))) AS \"result\"", + "params": ["\"public\".\"feature\"", "r", "p"], + "expectation": { + "expects": "present", + "object": "table \"feature\"" + } + } + ] + }, + { + "id": "table.tag", + "label": "Create table \"tag\"", + "summary": "Creates table \"tag\"", + "operationClass": "additive", + "target": { + "id": "postgres", + "details": { + "schema": "public", + "objectType": "table", + "name": "tag" + } + }, + "precheck": [ + { + "description": "ensure table \"tag\" does not exist", + "sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"", + "params": ["\"public\".\"tag\""], + "expectation": { + "expects": "absent", + "object": "table \"tag\"" + } + } + ], + "execute": [ + { + "description": "create table \"tag\"", + "sql": "CREATE TABLE \"public\".\"tag\" (\n \"id\" uuid NOT NULL,\n \"label\" text NOT NULL,\n PRIMARY KEY (\"id\"),\n CONSTRAINT \"tag_label_key\" UNIQUE (\"label\")\n)", + "params": [] + } + ], + "postcheck": [ + { + "description": "verify table \"tag\" exists", + "sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_class\" AS \"c\" WHERE (\"c\".\"oid\" = to_regclass($1) AND (\"c\".\"relkind\" = $2 OR \"c\".\"relkind\" = $3))) AS \"result\"", + "params": ["\"public\".\"tag\"", "r", "p"], + "expectation": { + "expects": "present", + "object": "table \"tag\"" + } + } + ] + }, + { + "id": "table.user", + "label": "Create table \"user\"", + "summary": "Creates table \"user\"", + "operationClass": "additive", + "target": { + "id": "postgres", + "details": { + "schema": "public", + "objectType": "table", + "name": "user" + } + }, + "precheck": [ + { + "description": "ensure table \"user\" does not exist", + "sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"", + "params": ["\"public\".\"user\""], + "expectation": { + "expects": "absent", + "object": "table \"user\"" + } + } + ], + "execute": [ + { + "description": "create table \"user\"", + "sql": "CREATE TABLE \"public\".\"user\" (\n \"address\" jsonb,\n \"createdAt\" timestamptz DEFAULT (now()) NOT NULL,\n \"displayName\" text NOT NULL,\n \"email\" text NOT NULL,\n \"id\" uuid NOT NULL,\n \"kind\" text NOT NULL,\n PRIMARY KEY (\"id\"),\n CONSTRAINT \"user_kind_check_836d43ef\" CHECK (\"kind\" IN ('admin', 'user'))\n)", + "params": [] + } + ], + "postcheck": [ + { + "description": "verify table \"user\" exists", + "sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_class\" AS \"c\" WHERE (\"c\".\"oid\" = to_regclass($1) AND (\"c\".\"relkind\" = $2 OR \"c\".\"relkind\" = $3))) AS \"result\"", + "params": ["\"public\".\"user\"", "r", "p"], + "expectation": { + "expects": "present", + "object": "table \"user\"" + } + } + ] + }, + { + "id": "table.post", + "label": "Create table \"post\"", + "summary": "Creates table \"post\"", + "operationClass": "additive", + "target": { + "id": "postgres", + "details": { + "schema": "public", + "objectType": "table", + "name": "post" + } + }, + "precheck": [ + { + "description": "ensure table \"post\" does not exist", + "sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"", + "params": ["\"public\".\"post\""], + "expectation": { + "expects": "absent", + "object": "table \"post\"" + } + } + ], + "execute": [ + { + "description": "create table \"post\"", + "sql": "CREATE TABLE \"public\".\"post\" (\n \"createdAt\" timestamptz DEFAULT (now()) NOT NULL,\n \"embedding\" vector(1536),\n \"id\" uuid NOT NULL,\n \"priority\" int4 DEFAULT 0 NOT NULL,\n \"title\" text NOT NULL,\n \"userId\" uuid NOT NULL,\n PRIMARY KEY (\"id\"),\n CONSTRAINT \"post_priority_check_b236ace9\" CHECK (\"priority\" IN (0, 1, 2))\n)", + "params": [] + } + ], + "postcheck": [ + { + "description": "verify table \"post\" exists", + "sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_class\" AS \"c\" WHERE (\"c\".\"oid\" = to_regclass($1) AND (\"c\".\"relkind\" = $2 OR \"c\".\"relkind\" = $3))) AS \"result\"", + "params": ["\"public\".\"post\"", "r", "p"], + "expectation": { + "expects": "present", + "object": "table \"post\"" + } + } + ] + }, + { + "id": "table.task", + "label": "Create table \"task\"", + "summary": "Creates table \"task\"", + "operationClass": "additive", + "target": { + "id": "postgres", + "details": { + "schema": "public", + "objectType": "table", + "name": "task" + } + }, + "precheck": [ + { + "description": "ensure table \"task\" does not exist", + "sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"", + "params": ["\"public\".\"task\""], + "expectation": { + "expects": "absent", + "object": "table \"task\"" + } + } + ], + "execute": [ + { + "description": "create table \"task\"", + "sql": "CREATE TABLE \"public\".\"task\" (\n \"createdAt\" timestamptz DEFAULT (now()) NOT NULL,\n \"description\" text,\n \"id\" uuid NOT NULL,\n \"status\" text DEFAULT 'open' NOT NULL,\n \"title\" text NOT NULL,\n \"type\" text NOT NULL,\n \"userId\" uuid NOT NULL,\n PRIMARY KEY (\"id\")\n)", + "params": [] + } + ], + "postcheck": [ + { + "description": "verify table \"task\" exists", + "sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_class\" AS \"c\" WHERE (\"c\".\"oid\" = to_regclass($1) AND (\"c\".\"relkind\" = $2 OR \"c\".\"relkind\" = $3))) AS \"result\"", + "params": ["\"public\".\"task\"", "r", "p"], + "expectation": { + "expects": "present", + "object": "table \"task\"" + } + } + ] + }, + { + "id": "table.post_tag", + "label": "Create table \"post_tag\"", + "summary": "Creates table \"post_tag\"", + "operationClass": "additive", + "target": { + "id": "postgres", + "details": { + "schema": "public", + "objectType": "table", + "name": "post_tag" + } + }, + "precheck": [ + { + "description": "ensure table \"post_tag\" does not exist", + "sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"", + "params": ["\"public\".\"post_tag\""], + "expectation": { + "expects": "absent", + "object": "table \"post_tag\"" + } + } + ], + "execute": [ + { + "description": "create table \"post_tag\"", + "sql": "CREATE TABLE \"public\".\"post_tag\" (\n \"postId\" uuid NOT NULL,\n \"tagId\" uuid NOT NULL,\n PRIMARY KEY (\"postId\", \"tagId\")\n)", + "params": [] + } + ], + "postcheck": [ + { + "description": "verify table \"post_tag\" exists", + "sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_class\" AS \"c\" WHERE (\"c\".\"oid\" = to_regclass($1) AND (\"c\".\"relkind\" = $2 OR \"c\".\"relkind\" = $3))) AS \"result\"", + "params": ["\"public\".\"post_tag\"", "r", "p"], + "expectation": { + "expects": "present", + "object": "table \"post_tag\"" + } + } + ] + }, + { + "id": "foreignKey.post.post_userId_fkey", + "label": "Add foreign key \"post_userId_fkey\" on \"post\"", + "operationClass": "additive", + "target": { + "id": "postgres", + "details": { + "schema": "public", + "objectType": "foreignKey", + "name": "post_userId_fkey", + "table": "post" + } + }, + "precheck": [ + { + "description": "ensure FK \"post_userId_fkey\" does not exist", + "sql": "SELECT NOT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"", + "params": ["post_userId_fkey", "public", "\"public\".\"post\""], + "expectation": { + "expects": "absent", + "object": "FK \"post_userId_fkey\" on \"post\"" + } + } + ], + "execute": [ + { + "description": "add FK \"post_userId_fkey\"", + "sql": "ALTER TABLE \"public\".\"post\"\nADD CONSTRAINT \"post_userId_fkey\"\nFOREIGN KEY (\"userId\")\nREFERENCES \"public\".\"user\" (\"id\")" + } + ], + "postcheck": [ + { + "description": "verify FK \"post_userId_fkey\" exists with the planned columns, referenced table and actions", + "sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3) AND \"c\".\"contype\" = $4 AND (SELECT coalesce(jsonb_agg(key_attribute.attname ORDER BY key_column.position), '[]'::jsonb) FROM unnest(\"c\".\"conkey\"::int2[]) WITH ORDINALITY AS key_column(attnum, position) JOIN pg_attribute key_attribute ON key_attribute.attrelid = \"c\".\"conrelid\" AND key_attribute.attnum = key_column.attnum) = ($5)::jsonb AND \"c\".\"confrelid\" = to_regclass($6) AND (SELECT coalesce(jsonb_agg(key_attribute.attname ORDER BY key_column.position), '[]'::jsonb) FROM unnest(\"c\".\"confkey\"::int2[]) WITH ORDINALITY AS key_column(attnum, position) JOIN pg_attribute key_attribute ON key_attribute.attrelid = \"c\".\"confrelid\" AND key_attribute.attnum = key_column.attnum) = ($7)::jsonb AND \"c\".\"confdeltype\" = $8 AND \"c\".\"confupdtype\" = $9)) AS \"result\"", + "params": [ + "post_userId_fkey", + "public", + "\"public\".\"post\"", + "f", + "[\"userId\"]", + "\"public\".\"user\"", + "[\"id\"]", + "a", + "a" + ] + } + ] + }, + { + "id": "index.post.post_userId_idx_a489d58a", + "label": "Create index \"post_userId_idx_a489d58a\" on \"post\"", + "operationClass": "additive", + "target": { + "id": "postgres", + "details": { + "schema": "public", + "objectType": "index", + "name": "post_userId_idx_a489d58a", + "table": "post" + } + }, + "precheck": [ + { + "description": "ensure index \"post_userId_idx_a489d58a\" does not exist", + "sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"", + "params": ["\"public\".\"post_userId_idx_a489d58a\""], + "expectation": { + "expects": "absent", + "object": "index \"post_userId_idx_a489d58a\"" + } + } + ], + "execute": [ + { + "description": "create index \"post_userId_idx_a489d58a\"", + "sql": "CREATE INDEX \"post_userId_idx_a489d58a\" ON \"public\".\"post\" (\"userId\")", + "params": [] + } + ], + "postcheck": [ + { + "description": "verify index \"post_userId_idx_a489d58a\" exists on \"post\" with the planned definition", + "sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_class\" AS \"c\" INNER JOIN \"pg_index\" AS \"i\" ON \"i\".\"indexrelid\" = \"c\".\"oid\" INNER JOIN \"pg_am\" AS \"m\" ON \"m\".\"oid\" = \"c\".\"relam\" WHERE (\"c\".\"oid\" = to_regclass($1) AND (\"c\".\"relkind\" = $2 OR \"c\".\"relkind\" = $3) AND \"i\".\"indrelid\" = to_regclass($4) AND NOT (\"i\".\"indisunique\") AND \"m\".\"amname\" = $5 AND (\"i\".\"indexprs\" IS NULL AND (SELECT coalesce(jsonb_agg(key_attribute.attname ORDER BY key_column.position), '[]'::jsonb) FROM unnest(\"i\".\"indkey\"::int2[]) WITH ORDINALITY AS key_column(attnum, position) JOIN pg_attribute key_attribute ON key_attribute.attrelid = \"i\".\"indrelid\" AND key_attribute.attnum = key_column.attnum) = ($6)::jsonb) AND \"i\".\"indpred\" IS NULL)) AS \"result\"", + "params": [ + "\"public\".\"post_userId_idx_a489d58a\"", + "i", + "I", + "\"public\".\"post\"", + "btree", + "[\"userId\"]" + ] + } + ] + }, + { + "id": "foreignKey.task.task_userId_fkey", + "label": "Add foreign key \"task_userId_fkey\" on \"task\"", + "operationClass": "additive", + "target": { + "id": "postgres", + "details": { + "schema": "public", + "objectType": "foreignKey", + "name": "task_userId_fkey", + "table": "task" + } + }, + "precheck": [ + { + "description": "ensure FK \"task_userId_fkey\" does not exist", + "sql": "SELECT NOT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"", + "params": ["task_userId_fkey", "public", "\"public\".\"task\""], + "expectation": { + "expects": "absent", + "object": "FK \"task_userId_fkey\" on \"task\"" + } + } + ], + "execute": [ + { + "description": "add FK \"task_userId_fkey\"", + "sql": "ALTER TABLE \"public\".\"task\"\nADD CONSTRAINT \"task_userId_fkey\"\nFOREIGN KEY (\"userId\")\nREFERENCES \"public\".\"user\" (\"id\")" + } + ], + "postcheck": [ + { + "description": "verify FK \"task_userId_fkey\" exists with the planned columns, referenced table and actions", + "sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3) AND \"c\".\"contype\" = $4 AND (SELECT coalesce(jsonb_agg(key_attribute.attname ORDER BY key_column.position), '[]'::jsonb) FROM unnest(\"c\".\"conkey\"::int2[]) WITH ORDINALITY AS key_column(attnum, position) JOIN pg_attribute key_attribute ON key_attribute.attrelid = \"c\".\"conrelid\" AND key_attribute.attnum = key_column.attnum) = ($5)::jsonb AND \"c\".\"confrelid\" = to_regclass($6) AND (SELECT coalesce(jsonb_agg(key_attribute.attname ORDER BY key_column.position), '[]'::jsonb) FROM unnest(\"c\".\"confkey\"::int2[]) WITH ORDINALITY AS key_column(attnum, position) JOIN pg_attribute key_attribute ON key_attribute.attrelid = \"c\".\"confrelid\" AND key_attribute.attnum = key_column.attnum) = ($7)::jsonb AND \"c\".\"confdeltype\" = $8 AND \"c\".\"confupdtype\" = $9)) AS \"result\"", + "params": [ + "task_userId_fkey", + "public", + "\"public\".\"task\"", + "f", + "[\"userId\"]", + "\"public\".\"user\"", + "[\"id\"]", + "a", + "a" + ] + } + ] + }, + { + "id": "index.task.task_userId_idx_a489d58a", + "label": "Create index \"task_userId_idx_a489d58a\" on \"task\"", + "operationClass": "additive", + "target": { + "id": "postgres", + "details": { + "schema": "public", + "objectType": "index", + "name": "task_userId_idx_a489d58a", + "table": "task" + } + }, + "precheck": [ + { + "description": "ensure index \"task_userId_idx_a489d58a\" does not exist", + "sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"", + "params": ["\"public\".\"task_userId_idx_a489d58a\""], + "expectation": { + "expects": "absent", + "object": "index \"task_userId_idx_a489d58a\"" + } + } + ], + "execute": [ + { + "description": "create index \"task_userId_idx_a489d58a\"", + "sql": "CREATE INDEX \"task_userId_idx_a489d58a\" ON \"public\".\"task\" (\"userId\")", + "params": [] + } + ], + "postcheck": [ + { + "description": "verify index \"task_userId_idx_a489d58a\" exists on \"task\" with the planned definition", + "sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_class\" AS \"c\" INNER JOIN \"pg_index\" AS \"i\" ON \"i\".\"indexrelid\" = \"c\".\"oid\" INNER JOIN \"pg_am\" AS \"m\" ON \"m\".\"oid\" = \"c\".\"relam\" WHERE (\"c\".\"oid\" = to_regclass($1) AND (\"c\".\"relkind\" = $2 OR \"c\".\"relkind\" = $3) AND \"i\".\"indrelid\" = to_regclass($4) AND NOT (\"i\".\"indisunique\") AND \"m\".\"amname\" = $5 AND (\"i\".\"indexprs\" IS NULL AND (SELECT coalesce(jsonb_agg(key_attribute.attname ORDER BY key_column.position), '[]'::jsonb) FROM unnest(\"i\".\"indkey\"::int2[]) WITH ORDINALITY AS key_column(attnum, position) JOIN pg_attribute key_attribute ON key_attribute.attrelid = \"i\".\"indrelid\" AND key_attribute.attnum = key_column.attnum) = ($6)::jsonb) AND \"i\".\"indpred\" IS NULL)) AS \"result\"", + "params": [ + "\"public\".\"task_userId_idx_a489d58a\"", + "i", + "I", + "\"public\".\"task\"", + "btree", + "[\"userId\"]" + ] + } + ] + }, + { + "id": "foreignKey.bug.bug_id_fkey", + "label": "Add foreign key \"bug_id_fkey\" on \"bug\"", + "operationClass": "additive", + "target": { + "id": "postgres", + "details": { + "schema": "public", + "objectType": "foreignKey", + "name": "bug_id_fkey", + "table": "bug" + } + }, + "precheck": [ + { + "description": "ensure FK \"bug_id_fkey\" does not exist", + "sql": "SELECT NOT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"", + "params": ["bug_id_fkey", "public", "\"public\".\"bug\""], + "expectation": { + "expects": "absent", + "object": "FK \"bug_id_fkey\" on \"bug\"" + } + } + ], + "execute": [ + { + "description": "add FK \"bug_id_fkey\"", + "sql": "ALTER TABLE \"public\".\"bug\"\nADD CONSTRAINT \"bug_id_fkey\"\nFOREIGN KEY (\"id\")\nREFERENCES \"public\".\"task\" (\"id\")\nON DELETE CASCADE" + } + ], + "postcheck": [ + { + "description": "verify FK \"bug_id_fkey\" exists with the planned columns, referenced table and actions", + "sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3) AND \"c\".\"contype\" = $4 AND (SELECT coalesce(jsonb_agg(key_attribute.attname ORDER BY key_column.position), '[]'::jsonb) FROM unnest(\"c\".\"conkey\"::int2[]) WITH ORDINALITY AS key_column(attnum, position) JOIN pg_attribute key_attribute ON key_attribute.attrelid = \"c\".\"conrelid\" AND key_attribute.attnum = key_column.attnum) = ($5)::jsonb AND \"c\".\"confrelid\" = to_regclass($6) AND (SELECT coalesce(jsonb_agg(key_attribute.attname ORDER BY key_column.position), '[]'::jsonb) FROM unnest(\"c\".\"confkey\"::int2[]) WITH ORDINALITY AS key_column(attnum, position) JOIN pg_attribute key_attribute ON key_attribute.attrelid = \"c\".\"confrelid\" AND key_attribute.attnum = key_column.attnum) = ($7)::jsonb AND \"c\".\"confdeltype\" = $8 AND \"c\".\"confupdtype\" = $9)) AS \"result\"", + "params": [ + "bug_id_fkey", + "public", + "\"public\".\"bug\"", + "f", + "[\"id\"]", + "\"public\".\"task\"", + "[\"id\"]", + "c", + "a" + ] + } + ] + }, + { + "id": "foreignKey.feature.feature_id_fkey", + "label": "Add foreign key \"feature_id_fkey\" on \"feature\"", + "operationClass": "additive", + "target": { + "id": "postgres", + "details": { + "schema": "public", + "objectType": "foreignKey", + "name": "feature_id_fkey", + "table": "feature" + } + }, + "precheck": [ + { + "description": "ensure FK \"feature_id_fkey\" does not exist", + "sql": "SELECT NOT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"", + "params": ["feature_id_fkey", "public", "\"public\".\"feature\""], + "expectation": { + "expects": "absent", + "object": "FK \"feature_id_fkey\" on \"feature\"" + } + } + ], + "execute": [ + { + "description": "add FK \"feature_id_fkey\"", + "sql": "ALTER TABLE \"public\".\"feature\"\nADD CONSTRAINT \"feature_id_fkey\"\nFOREIGN KEY (\"id\")\nREFERENCES \"public\".\"task\" (\"id\")\nON DELETE CASCADE" + } + ], + "postcheck": [ + { + "description": "verify FK \"feature_id_fkey\" exists with the planned columns, referenced table and actions", + "sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3) AND \"c\".\"contype\" = $4 AND (SELECT coalesce(jsonb_agg(key_attribute.attname ORDER BY key_column.position), '[]'::jsonb) FROM unnest(\"c\".\"conkey\"::int2[]) WITH ORDINALITY AS key_column(attnum, position) JOIN pg_attribute key_attribute ON key_attribute.attrelid = \"c\".\"conrelid\" AND key_attribute.attnum = key_column.attnum) = ($5)::jsonb AND \"c\".\"confrelid\" = to_regclass($6) AND (SELECT coalesce(jsonb_agg(key_attribute.attname ORDER BY key_column.position), '[]'::jsonb) FROM unnest(\"c\".\"confkey\"::int2[]) WITH ORDINALITY AS key_column(attnum, position) JOIN pg_attribute key_attribute ON key_attribute.attrelid = \"c\".\"confrelid\" AND key_attribute.attnum = key_column.attnum) = ($7)::jsonb AND \"c\".\"confdeltype\" = $8 AND \"c\".\"confupdtype\" = $9)) AS \"result\"", + "params": [ + "feature_id_fkey", + "public", + "\"public\".\"feature\"", + "f", + "[\"id\"]", + "\"public\".\"task\"", + "[\"id\"]", + "c", + "a" + ] + } + ] + }, + { + "id": "index.post_tag.post_tag_postId_idx_a7a72715", + "label": "Create index \"post_tag_postId_idx_a7a72715\" on \"post_tag\"", + "operationClass": "additive", + "target": { + "id": "postgres", + "details": { + "schema": "public", + "objectType": "index", + "name": "post_tag_postId_idx_a7a72715", + "table": "post_tag" + } + }, + "precheck": [ + { + "description": "ensure index \"post_tag_postId_idx_a7a72715\" does not exist", + "sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"", + "params": ["\"public\".\"post_tag_postId_idx_a7a72715\""], + "expectation": { + "expects": "absent", + "object": "index \"post_tag_postId_idx_a7a72715\"" + } + } + ], + "execute": [ + { + "description": "create index \"post_tag_postId_idx_a7a72715\"", + "sql": "CREATE INDEX \"post_tag_postId_idx_a7a72715\" ON \"public\".\"post_tag\" (\"postId\")", + "params": [] + } + ], + "postcheck": [ + { + "description": "verify index \"post_tag_postId_idx_a7a72715\" exists on \"post_tag\" with the planned definition", + "sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_class\" AS \"c\" INNER JOIN \"pg_index\" AS \"i\" ON \"i\".\"indexrelid\" = \"c\".\"oid\" INNER JOIN \"pg_am\" AS \"m\" ON \"m\".\"oid\" = \"c\".\"relam\" WHERE (\"c\".\"oid\" = to_regclass($1) AND (\"c\".\"relkind\" = $2 OR \"c\".\"relkind\" = $3) AND \"i\".\"indrelid\" = to_regclass($4) AND NOT (\"i\".\"indisunique\") AND \"m\".\"amname\" = $5 AND (\"i\".\"indexprs\" IS NULL AND (SELECT coalesce(jsonb_agg(key_attribute.attname ORDER BY key_column.position), '[]'::jsonb) FROM unnest(\"i\".\"indkey\"::int2[]) WITH ORDINALITY AS key_column(attnum, position) JOIN pg_attribute key_attribute ON key_attribute.attrelid = \"i\".\"indrelid\" AND key_attribute.attnum = key_column.attnum) = ($6)::jsonb) AND \"i\".\"indpred\" IS NULL)) AS \"result\"", + "params": [ + "\"public\".\"post_tag_postId_idx_a7a72715\"", + "i", + "I", + "\"public\".\"post_tag\"", + "btree", + "[\"postId\"]" + ] + } + ] + }, + { + "id": "index.post_tag.post_tag_tagId_idx_86854244", + "label": "Create index \"post_tag_tagId_idx_86854244\" on \"post_tag\"", + "operationClass": "additive", + "target": { + "id": "postgres", + "details": { + "schema": "public", + "objectType": "index", + "name": "post_tag_tagId_idx_86854244", + "table": "post_tag" + } + }, + "precheck": [ + { + "description": "ensure index \"post_tag_tagId_idx_86854244\" does not exist", + "sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"", + "params": ["\"public\".\"post_tag_tagId_idx_86854244\""], + "expectation": { + "expects": "absent", + "object": "index \"post_tag_tagId_idx_86854244\"" + } + } + ], + "execute": [ + { + "description": "create index \"post_tag_tagId_idx_86854244\"", + "sql": "CREATE INDEX \"post_tag_tagId_idx_86854244\" ON \"public\".\"post_tag\" (\"tagId\")", + "params": [] + } + ], + "postcheck": [ + { + "description": "verify index \"post_tag_tagId_idx_86854244\" exists on \"post_tag\" with the planned definition", + "sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_class\" AS \"c\" INNER JOIN \"pg_index\" AS \"i\" ON \"i\".\"indexrelid\" = \"c\".\"oid\" INNER JOIN \"pg_am\" AS \"m\" ON \"m\".\"oid\" = \"c\".\"relam\" WHERE (\"c\".\"oid\" = to_regclass($1) AND (\"c\".\"relkind\" = $2 OR \"c\".\"relkind\" = $3) AND \"i\".\"indrelid\" = to_regclass($4) AND NOT (\"i\".\"indisunique\") AND \"m\".\"amname\" = $5 AND (\"i\".\"indexprs\" IS NULL AND (SELECT coalesce(jsonb_agg(key_attribute.attname ORDER BY key_column.position), '[]'::jsonb) FROM unnest(\"i\".\"indkey\"::int2[]) WITH ORDINALITY AS key_column(attnum, position) JOIN pg_attribute key_attribute ON key_attribute.attrelid = \"i\".\"indrelid\" AND key_attribute.attnum = key_column.attnum) = ($6)::jsonb) AND \"i\".\"indpred\" IS NULL)) AS \"result\"", + "params": [ + "\"public\".\"post_tag_tagId_idx_86854244\"", + "i", + "I", + "\"public\".\"post_tag\"", + "btree", + "[\"tagId\"]" + ] + } + ] + }, + { + "id": "foreignKey.post_tag.post_tag_postId_fkey", + "label": "Add foreign key \"post_tag_postId_fkey\" on \"post_tag\"", + "operationClass": "additive", + "target": { + "id": "postgres", + "details": { + "schema": "public", + "objectType": "foreignKey", + "name": "post_tag_postId_fkey", + "table": "post_tag" + } + }, + "precheck": [ + { + "description": "ensure FK \"post_tag_postId_fkey\" does not exist", + "sql": "SELECT NOT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"", + "params": ["post_tag_postId_fkey", "public", "\"public\".\"post_tag\""], + "expectation": { + "expects": "absent", + "object": "FK \"post_tag_postId_fkey\" on \"post_tag\"" + } + } + ], + "execute": [ + { + "description": "add FK \"post_tag_postId_fkey\"", + "sql": "ALTER TABLE \"public\".\"post_tag\"\nADD CONSTRAINT \"post_tag_postId_fkey\"\nFOREIGN KEY (\"postId\")\nREFERENCES \"public\".\"post\" (\"id\")" + } + ], + "postcheck": [ + { + "description": "verify FK \"post_tag_postId_fkey\" exists with the planned columns, referenced table and actions", + "sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3) AND \"c\".\"contype\" = $4 AND (SELECT coalesce(jsonb_agg(key_attribute.attname ORDER BY key_column.position), '[]'::jsonb) FROM unnest(\"c\".\"conkey\"::int2[]) WITH ORDINALITY AS key_column(attnum, position) JOIN pg_attribute key_attribute ON key_attribute.attrelid = \"c\".\"conrelid\" AND key_attribute.attnum = key_column.attnum) = ($5)::jsonb AND \"c\".\"confrelid\" = to_regclass($6) AND (SELECT coalesce(jsonb_agg(key_attribute.attname ORDER BY key_column.position), '[]'::jsonb) FROM unnest(\"c\".\"confkey\"::int2[]) WITH ORDINALITY AS key_column(attnum, position) JOIN pg_attribute key_attribute ON key_attribute.attrelid = \"c\".\"confrelid\" AND key_attribute.attnum = key_column.attnum) = ($7)::jsonb AND \"c\".\"confdeltype\" = $8 AND \"c\".\"confupdtype\" = $9)) AS \"result\"", + "params": [ + "post_tag_postId_fkey", + "public", + "\"public\".\"post_tag\"", + "f", + "[\"postId\"]", + "\"public\".\"post\"", + "[\"id\"]", + "a", + "a" + ] + } + ] + }, + { + "id": "foreignKey.post_tag.post_tag_tagId_fkey", + "label": "Add foreign key \"post_tag_tagId_fkey\" on \"post_tag\"", + "operationClass": "additive", + "target": { + "id": "postgres", + "details": { + "schema": "public", + "objectType": "foreignKey", + "name": "post_tag_tagId_fkey", + "table": "post_tag" + } + }, + "precheck": [ + { + "description": "ensure FK \"post_tag_tagId_fkey\" does not exist", + "sql": "SELECT NOT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"", + "params": ["post_tag_tagId_fkey", "public", "\"public\".\"post_tag\""], + "expectation": { + "expects": "absent", + "object": "FK \"post_tag_tagId_fkey\" on \"post_tag\"" + } + } + ], + "execute": [ + { + "description": "add FK \"post_tag_tagId_fkey\"", + "sql": "ALTER TABLE \"public\".\"post_tag\"\nADD CONSTRAINT \"post_tag_tagId_fkey\"\nFOREIGN KEY (\"tagId\")\nREFERENCES \"public\".\"tag\" (\"id\")" + } + ], + "postcheck": [ + { + "description": "verify FK \"post_tag_tagId_fkey\" exists with the planned columns, referenced table and actions", + "sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3) AND \"c\".\"contype\" = $4 AND (SELECT coalesce(jsonb_agg(key_attribute.attname ORDER BY key_column.position), '[]'::jsonb) FROM unnest(\"c\".\"conkey\"::int2[]) WITH ORDINALITY AS key_column(attnum, position) JOIN pg_attribute key_attribute ON key_attribute.attrelid = \"c\".\"conrelid\" AND key_attribute.attnum = key_column.attnum) = ($5)::jsonb AND \"c\".\"confrelid\" = to_regclass($6) AND (SELECT coalesce(jsonb_agg(key_attribute.attname ORDER BY key_column.position), '[]'::jsonb) FROM unnest(\"c\".\"confkey\"::int2[]) WITH ORDINALITY AS key_column(attnum, position) JOIN pg_attribute key_attribute ON key_attribute.attrelid = \"c\".\"confrelid\" AND key_attribute.attnum = key_column.attnum) = ($7)::jsonb AND \"c\".\"confdeltype\" = $8 AND \"c\".\"confupdtype\" = $9)) AS \"result\"", + "params": [ + "post_tag_tagId_fkey", + "public", + "\"public\".\"post_tag\"", + "f", + "[\"tagId\"]", + "\"public\".\"tag\"", + "[\"id\"]", + "a", + "a" + ] + } + ] + } +] diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/sql/migrations/snapshots/58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b/contract.d.ts b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/sql/migrations/snapshots/58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b/contract.d.ts new file mode 100644 index 000000000000..64fd0c6ba064 --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/sql/migrations/snapshots/58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b/contract.d.ts @@ -0,0 +1,1337 @@ +// ⚠️ GENERATED FILE - DO NOT EDIT +// This file is automatically generated by 'prisma contract emit'. +// To regenerate, run: prisma contract emit +import type { + CodecTypes as PgVectorTypes, + ParameterizedOutputTypesForTypeParams as PgVectorParameterizedOutputTypes, +} from '@prisma/orm-extension-pgvector/codec-types'; +import type { QueryOperationTypes as PgVectorQueryOperationTypes } from '@prisma/orm-extension-pgvector/operation-types'; +import type { + CodecTypes as PgTypes, + ParameterizedOutputTypesForTypeParams as PgParameterizedOutputTypes, +} from '@prisma/orm-postgres/target/codec-types'; +import type { QueryOperationTypes as PgTargetQueryOps } from '@prisma/orm-postgres/target/operation-types'; + +import type { + ColumnTypesFromStorage, + ContractWithTypeMaps, + RelationKeys, + TypeMaps as TypeMapsType, +} from '@prisma/orm-postgres/family-contract/types'; +import type { + Contract as ContractType, + ExecutionHashBase, + FieldTypesFromDomain, + NamespaceId, + ProfileHashBase, + StorageHashBase, + ValueObjectTypesFromDomain, +} from '@prisma/orm-postgres/contract/types'; + +export type StorageHash = + StorageHashBase<'58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b'>; +export type ExecutionHash = + ExecutionHashBase<'eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655'>; +export type ProfileHash = + ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; + +export type CodecTypes = PgTypes & PgVectorTypes; +export type LaneCodecTypes = CodecTypes; +export type QueryOperationTypes = PgTargetQueryOps & + PgVectorQueryOperationTypes; +export type AggregateTypes = { + readonly avg: { + readonly byCodec: { + readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + }; + }; + readonly avgDecimal: { + readonly byCodec: { + readonly 'pg/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + }; + }; + readonly count: { + readonly byCodec: {}; + readonly withoutInput: { readonly output: 'pg/int8number@1'; readonly nullable: false }; + readonly anyInput: { readonly output: 'pg/int8number@1'; readonly nullable: false }; + }; + readonly countBigInt: { + readonly byCodec: {}; + readonly withoutInput: { readonly output: 'pg/int8@1'; readonly nullable: false }; + readonly anyInput: { readonly output: 'pg/int8@1'; readonly nullable: false }; + }; + readonly max: { + readonly byCodec: { + readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true }; + readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true }; + readonly 'pg/date-temporal@1': { + readonly output: 'pg/date-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true }; + readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; + readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { + readonly output: 'pg/time-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-string@1': { + readonly output: 'pg/timestamp-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-temporal@1': { + readonly output: 'pg/timestamp-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-date@1': { + readonly output: 'pg/timestamptz-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-string@1': { + readonly output: 'pg/timestamptz-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-temporal@1': { + readonly output: 'pg/timestamptz-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true }; + readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true }; + readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + }; + }; + readonly min: { + readonly byCodec: { + readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true }; + readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true }; + readonly 'pg/date-temporal@1': { + readonly output: 'pg/date-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true }; + readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; + readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { + readonly output: 'pg/time-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-string@1': { + readonly output: 'pg/timestamp-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-temporal@1': { + readonly output: 'pg/timestamp-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-date@1': { + readonly output: 'pg/timestamptz-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-string@1': { + readonly output: 'pg/timestamptz-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-temporal@1': { + readonly output: 'pg/timestamptz-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true }; + readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true }; + readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + }; + }; + readonly stddev: { + readonly byCodec: { + readonly 'pg/float@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + }; + }; + readonly sum: { + readonly byCodec: { + readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + }; + }; + readonly sumBigInt: { + readonly byCodec: { + readonly 'pg/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'sql/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + }; + }; +}; +type DefaultLiteralValue = CodecId extends keyof CodecTypes + ? Encoded extends CodecTypes[CodecId]['json'] + ? Encoded + : CodecTypes[CodecId]['json'] + : Encoded; +type ParameterizedOutputTypeFunctions = + PgParameterizedOutputTypes | PgVectorParameterizedOutputTypes; +type ValueObjectOutputTypes = ValueObjectTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output' +>; +type ValueObjectInputTypes = ValueObjectTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input' +>; +export type AddressOutput = ValueObjectOutputTypes['public']['Address']; +export type AddressInput = ValueObjectInputTypes['public']['Address']; +type FieldTypeOverrides = { + readonly public: { + readonly models: { + readonly Post: { + readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 }; + }; + readonly User: { + readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; + }; + }; + }; +}; +export type FieldOutputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output' +>; +export type FieldInputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input' +>; +type ColumnTypeOverrides = { + readonly public: { + readonly post: { readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 } }; + readonly user: { + readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; + }; + }; +}; +export type StorageColumnTypes = ColumnTypesFromStorage< + ContractBase['storage'], + CodecTypes, + ParameterizedOutputTypeFunctions, + ColumnTypeOverrides, + 'output' +>; +export type StorageColumnInputTypes = ColumnTypesFromStorage< + ContractBase['storage'], + CodecTypes, + ParameterizedOutputTypeFunctions, + ColumnTypeOverrides, + 'input' +>; + +export namespace Models { + export type public_Bug = { + createdAt: FieldOutputTypes['public']['Task']['createdAt']; + description: FieldOutputTypes['public']['Task']['description']; + id: FieldOutputTypes['public']['Task']['id']; + status: FieldOutputTypes['public']['Task']['status']; + title: FieldOutputTypes['public']['Task']['title']; + type: 'bug'; + userId: FieldOutputTypes['public']['Task']['userId']; + severity: FieldOutputTypes['public']['Bug']['severity']; + stepsToRepro: FieldOutputTypes['public']['Bug']['stepsToRepro']; + user: public_User; + readonly [RelationKeys]?: 'user'; + }; + export type public_Feature = { + createdAt: FieldOutputTypes['public']['Task']['createdAt']; + description: FieldOutputTypes['public']['Task']['description']; + id: FieldOutputTypes['public']['Task']['id']; + status: FieldOutputTypes['public']['Task']['status']; + title: FieldOutputTypes['public']['Task']['title']; + type: 'feature'; + userId: FieldOutputTypes['public']['Task']['userId']; + priority: FieldOutputTypes['public']['Feature']['priority']; + targetRelease: FieldOutputTypes['public']['Feature']['targetRelease']; + user: public_User; + readonly [RelationKeys]?: 'user'; + }; + export type public_Post = { + createdAt: FieldOutputTypes['public']['Post']['createdAt']; + embedding: FieldOutputTypes['public']['Post']['embedding']; + id: FieldOutputTypes['public']['Post']['id']; + priority: FieldOutputTypes['public']['Post']['priority']; + title: FieldOutputTypes['public']['Post']['title']; + userId: FieldOutputTypes['public']['Post']['userId']; + tags: public_Tag[]; + user: public_User; + readonly [RelationKeys]?: 'tags' | 'user'; + }; + export type public_PostTag = { + postId: FieldOutputTypes['public']['PostTag']['postId']; + tagId: FieldOutputTypes['public']['PostTag']['tagId']; + post: public_Post; + tag: public_Tag; + readonly [RelationKeys]?: 'post' | 'tag'; + }; + export type public_Tag = { + id: FieldOutputTypes['public']['Tag']['id']; + label: FieldOutputTypes['public']['Tag']['label']; + posts: public_Post[]; + readonly [RelationKeys]?: 'posts'; + }; + export type public_Task = { + createdAt: FieldOutputTypes['public']['Task']['createdAt']; + description: FieldOutputTypes['public']['Task']['description']; + id: FieldOutputTypes['public']['Task']['id']; + status: FieldOutputTypes['public']['Task']['status']; + title: FieldOutputTypes['public']['Task']['title']; + type: 'bug' | 'feature'; + userId: FieldOutputTypes['public']['Task']['userId']; + user: public_User; + readonly [RelationKeys]?: 'user'; + }; + export type public_User = { + address: FieldOutputTypes['public']['User']['address']; + createdAt: FieldOutputTypes['public']['User']['createdAt']; + displayName: FieldOutputTypes['public']['User']['displayName']; + email: FieldOutputTypes['public']['User']['email']; + id: FieldOutputTypes['public']['User']['id']; + kind: FieldOutputTypes['public']['User']['kind']; + posts: public_Post[]; + tasks: public_AnyTask[]; + readonly [RelationKeys]?: 'posts' | 'tasks'; + }; + export type public_AnyTask = public_Bug | public_Feature; +} + +export declare const models: { + public: { + Bug: Models.public_Bug; + Feature: Models.public_Feature; + Post: Models.public_Post; + PostTag: Models.public_PostTag; + Tag: Models.public_Tag; + Task: Models.public_Task; + User: Models.public_User; + AnyTask: Models.public_AnyTask; + }; +}; + +export type TypeMaps = TypeMapsType< + CodecTypes, + QueryOperationTypes, + FieldOutputTypes, + FieldInputTypes, + StorageColumnTypes, + StorageColumnInputTypes, + AggregateTypes +>; + +type ContractBase = Omit< + ContractType<{ + readonly namespaces: { + readonly public: { + readonly id: 'public'; + readonly kind: 'postgres-schema'; + readonly entries: { + readonly table: { + readonly bug: { + columns: { + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly severity: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly stepsToRepro: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: true; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'bug'; + readonly columns: readonly ['id']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'task'; + readonly columns: readonly ['id']; + }; + }, + ]; + }; + readonly feature: { + columns: { + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly priority: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly targetRelease: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: true; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'feature'; + readonly columns: readonly ['id']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'task'; + readonly columns: readonly ['id']; + }; + }, + ]; + }; + readonly post: { + columns: { + readonly createdAt: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-temporal@1'; + readonly nullable: false; + readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; + readonly many: false; + }; + readonly embedding: { + readonly dataType: 'pgvector/vector'; + readonly codecId: 'pg/vector@1'; + readonly nullable: true; + readonly typeParams: { readonly length: 1536 }; + readonly many: false; + }; + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly priority: { + readonly dataType: 'pg/int4'; + readonly codecId: 'pg/int4@1'; + readonly nullable: false; + readonly default: { + readonly kind: 'literal'; + readonly value: DefaultLiteralValue<'pg/int4@1', 0>; + }; + readonly many: false; + }; + readonly title: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly userId: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly [ + { + readonly name: 'post_userId_idx_a489d58a'; + readonly prefix: 'post_userId_idx'; + readonly columns: readonly ['userId']; + readonly unique: false; + }, + ]; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'post'; + readonly columns: readonly ['userId']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'user'; + readonly columns: readonly ['id']; + }; + }, + ]; + }; + readonly post_tag: { + columns: { + readonly postId: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly tagId: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['postId', 'tagId'] }; + uniques: readonly []; + indexes: readonly [ + { + readonly name: 'post_tag_postId_idx_a7a72715'; + readonly prefix: 'post_tag_postId_idx'; + readonly columns: readonly ['postId']; + readonly unique: false; + }, + { + readonly name: 'post_tag_tagId_idx_86854244'; + readonly prefix: 'post_tag_tagId_idx'; + readonly columns: readonly ['tagId']; + readonly unique: false; + }, + ]; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'post_tag'; + readonly columns: readonly ['postId']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'post'; + readonly columns: readonly ['id']; + }; + }, + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'post_tag'; + readonly columns: readonly ['tagId']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'tag'; + readonly columns: readonly ['id']; + }; + }, + ]; + }; + readonly tag: { + columns: { + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly label: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly [{ readonly columns: readonly ['label'] }]; + indexes: readonly []; + foreignKeys: readonly []; + }; + readonly task: { + columns: { + readonly createdAt: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-temporal@1'; + readonly nullable: false; + readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; + readonly many: false; + }; + readonly description: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: true; + readonly many: false; + }; + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly status: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly default: { + readonly kind: 'literal'; + readonly value: DefaultLiteralValue<'pg/text@1', 'open'>; + }; + readonly many: false; + }; + readonly title: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly type: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly userId: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly [ + { + readonly name: 'task_userId_idx_a489d58a'; + readonly prefix: 'task_userId_idx'; + readonly columns: readonly ['userId']; + readonly unique: false; + }, + ]; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'task'; + readonly columns: readonly ['userId']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'user'; + readonly columns: readonly ['id']; + }; + }, + ]; + }; + readonly user: { + columns: { + readonly address: { + readonly dataType: 'pg/jsonb'; + readonly codecId: 'pg/jsonb@1'; + readonly nullable: true; + readonly many: false; + }; + readonly createdAt: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-temporal@1'; + readonly nullable: false; + readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; + readonly many: false; + }; + readonly displayName: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly email: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly kind: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly []; + }; + }; + readonly valueSet: { + readonly Priority: { readonly kind: 'valueSet'; readonly values: readonly [0, 1, 2] }; + readonly user_type: { + readonly kind: 'valueSet'; + readonly values: readonly ['admin', 'user']; + }; + }; + }; + }; + }; + readonly storageHash: StorageHash; + }>, + 'roots' | 'domain' +> & { + readonly target: 'postgres'; + readonly targetFamily: 'sql'; + readonly roots: { + readonly post: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; + readonly post_tag: { readonly namespace: 'public' & NamespaceId; readonly model: 'PostTag' }; + readonly tag: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; + readonly task: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; + readonly user: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; + }; + readonly domain: { + readonly namespaces: { + readonly public: { + readonly models: { + readonly Bug: { + readonly fields: { + readonly severity: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly stepsToRepro: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'bug'; + readonly namespaceId: 'public'; + readonly fields: { + readonly severity: { readonly column: 'severity' }; + readonly stepsToRepro: { readonly column: 'stepsToRepro' }; + }; + }; + readonly base: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; + }; + readonly Feature: { + readonly fields: { + readonly priority: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly targetRelease: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'feature'; + readonly namespaceId: 'public'; + readonly fields: { + readonly priority: { readonly column: 'priority' }; + readonly targetRelease: { readonly column: 'targetRelease' }; + }; + }; + readonly base: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; + }; + readonly Post: { + readonly fields: { + readonly createdAt: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-temporal@1'; + }; + }; + readonly embedding: { + readonly nullable: true; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/vector@1'; + readonly typeParams: { readonly length: 1536 }; + }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly priority: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int4@1' }; + }; + readonly title: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly userId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + }; + readonly relations: { + readonly tags: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; + readonly cardinality: 'N:M'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['postId']; + }; + readonly through: { + readonly table: 'post_tag'; + readonly namespaceId: 'public'; + readonly parentColumns: readonly ['postId']; + readonly childColumns: readonly ['tagId']; + readonly targetColumns: readonly ['id']; + }; + }; + readonly user: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['userId']; + readonly targetFields: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly table: 'post'; + readonly namespaceId: 'public'; + readonly fields: { + readonly createdAt: { readonly column: 'createdAt' }; + readonly embedding: { readonly column: 'embedding' }; + readonly id: { readonly column: 'id' }; + readonly priority: { readonly column: 'priority' }; + readonly title: { readonly column: 'title' }; + readonly userId: { readonly column: 'userId' }; + }; + }; + }; + readonly PostTag: { + readonly fields: { + readonly postId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly tagId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + }; + readonly relations: { + readonly post: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['postId']; + readonly targetFields: readonly ['id']; + }; + }; + readonly tag: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['tagId']; + readonly targetFields: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly table: 'post_tag'; + readonly namespaceId: 'public'; + readonly fields: { + readonly postId: { readonly column: 'postId' }; + readonly tagId: { readonly column: 'tagId' }; + }; + }; + }; + readonly Tag: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly label: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: { + readonly posts: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; + readonly cardinality: 'N:M'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['tagId']; + }; + readonly through: { + readonly table: 'post_tag'; + readonly namespaceId: 'public'; + readonly parentColumns: readonly ['tagId']; + readonly childColumns: readonly ['postId']; + readonly targetColumns: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly table: 'tag'; + readonly namespaceId: 'public'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly label: { readonly column: 'label' }; + }; + }; + }; + readonly Task: { + readonly fields: { + readonly createdAt: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-temporal@1'; + }; + }; + readonly description: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly status: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly title: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly type: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly userId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + }; + readonly relations: { + readonly user: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['userId']; + readonly targetFields: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly table: 'task'; + readonly namespaceId: 'public'; + readonly fields: { + readonly createdAt: { readonly column: 'createdAt' }; + readonly description: { readonly column: 'description' }; + readonly id: { readonly column: 'id' }; + readonly status: { readonly column: 'status' }; + readonly title: { readonly column: 'title' }; + readonly type: { readonly column: 'type' }; + readonly userId: { readonly column: 'userId' }; + }; + }; + readonly discriminator: { readonly field: 'type' }; + readonly variants: { + readonly bug: { readonly namespace: 'public' & NamespaceId; readonly model: 'Bug' }; + readonly feature: { + readonly namespace: 'public' & NamespaceId; + readonly model: 'Feature'; + }; + }; + }; + readonly User: { + readonly fields: { + readonly address: { + readonly nullable: true; + readonly type: { readonly kind: 'valueObject'; readonly name: 'Address' }; + }; + readonly createdAt: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-temporal@1'; + }; + }; + readonly displayName: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly kind: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: { + readonly posts: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; + readonly cardinality: '1:N'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['userId']; + }; + }; + readonly tasks: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; + readonly cardinality: '1:N'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['userId']; + }; + }; + }; + readonly storage: { + readonly table: 'user'; + readonly namespaceId: 'public'; + readonly fields: { + readonly address: { readonly column: 'address' }; + readonly createdAt: { readonly column: 'createdAt' }; + readonly displayName: { readonly column: 'displayName' }; + readonly email: { readonly column: 'email' }; + readonly id: { readonly column: 'id' }; + readonly kind: { readonly column: 'kind' }; + }; + }; + }; + }; + readonly valueObjects: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + }; + }; + readonly enum: { + readonly Priority: { + readonly codecId: 'pg/int4@1'; + readonly members: readonly [ + { readonly name: 'Low'; readonly value: 0 }, + { readonly name: 'High'; readonly value: 1 }, + { readonly name: 'Urgent'; readonly value: 2 }, + ]; + }; + readonly user_type: { + readonly codecId: 'pg/text@1'; + readonly members: readonly [ + { readonly name: 'admin'; readonly value: 'admin' }, + { readonly name: 'user'; readonly value: 'user' }, + ]; + }; + }; + readonly enumMemberTypes?: { + readonly Priority: readonly [ + { readonly name: 'Low'; readonly value: 0 }, + { readonly name: 'High'; readonly value: 1 }, + { readonly name: 'Urgent'; readonly value: 2 }, + ]; + readonly user_type: readonly [ + { readonly name: 'admin'; readonly value: 'admin' }, + { readonly name: 'user'; readonly value: 'user' }, + ]; + }; + }; + }; + }; + readonly capabilities: { + readonly postgres: { + readonly distinctOn: true; + readonly forKeyShare: true; + readonly forNoKeyUpdate: true; + readonly jsonAgg: true; + readonly lateral: true; + readonly limit: true; + readonly orderBy: true; + readonly 'pgvector.cosine': true; + readonly returning: true; + }; + readonly sql: { + readonly checkConstraint: true; + readonly defaultInInsert: true; + readonly enums: true; + readonly forShare: true; + readonly forUpdate: true; + readonly insertOnConflictSkip: true; + readonly insertOnConflictWithoutTarget: true; + readonly lateral: true; + readonly lockNowait: true; + readonly lockOf: true; + readonly lockSkipLocked: true; + readonly returning: true; + readonly scalarList: true; + }; + }; + readonly extensions: { + readonly 'demo/engagement-stats': { + readonly familyId: 'sql'; + readonly id: 'demo/engagement-stats'; + readonly kind: 'extension'; + readonly targetId: 'postgres'; + readonly types: { + readonly aggregateDescriptors: readonly [ + { + readonly input: { readonly kind: 'trait'; readonly trait: 'numeric' }; + readonly lower: unknown; + readonly nullable: true; + readonly operation: 'stddev'; + readonly output: { readonly codecId: 'pg/numeric@1'; readonly kind: 'codec' }; + }, + ]; + }; + readonly version: '0.0.1'; + }; + readonly pgvector: { + readonly capabilities: { readonly postgres: { readonly 'pgvector.cosine': true } }; + readonly familyId: 'sql'; + readonly id: 'pgvector'; + readonly kind: 'extension'; + readonly targetId: 'postgres'; + readonly types: { + readonly codecTypes: { + readonly import: { + readonly alias: 'PgVectorTypes'; + readonly named: 'CodecTypes'; + readonly package: '@internal/extension-pgvector/codec-types'; + }; + readonly parameterizedOutputTypes: { + readonly alias: 'PgVectorParameterizedOutputTypes'; + readonly named: 'ParameterizedOutputTypesForTypeParams'; + readonly package: '@internal/extension-pgvector/codec-types'; + }; + }; + readonly operationTypes: { + readonly import: { + readonly alias: 'PgVectorOperationTypes'; + readonly named: 'OperationTypes'; + readonly package: '@internal/extension-pgvector/operation-types'; + }; + }; + readonly queryOperationTypes: { + readonly import: { + readonly alias: 'PgVectorQueryOperationTypes'; + readonly named: 'QueryOperationTypes'; + readonly package: '@internal/extension-pgvector/operation-types'; + }; + }; + }; + readonly version: '0.0.1'; + }; + }; + readonly execution: { + readonly executionHash: ExecutionHash; + readonly mutations: { + readonly defaults: readonly [ + { + readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; + readonly ref: { + readonly entry: 'post'; + readonly field: 'id'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; + readonly ref: { + readonly entry: 'tag'; + readonly field: 'id'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; + readonly ref: { + readonly entry: 'task'; + readonly field: 'id'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; + readonly ref: { + readonly entry: 'user'; + readonly field: 'id'; + readonly namespace: 'public'; + }; + }, + ]; + }; + }; + readonly meta: {}; + readonly valueObjects: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + }; + }; + readonly profileHash: ProfileHash; +}; + +export type Contract = ContractWithTypeMaps; + +export type Namespaces = Contract['storage']['namespaces']; diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/sql/migrations/snapshots/58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b/contract.json b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/sql/migrations/snapshots/58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b/contract.json new file mode 100644 index 000000000000..fa8fc31297bc --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/sql/migrations/snapshots/58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b/contract.json @@ -0,0 +1 @@ +{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{"postgres":{"distinctOn":true,"forKeyShare":true,"forNoKeyUpdate":true,"jsonAgg":true,"lateral":true,"limit":true,"orderBy":true,"pgvector.cosine":true,"returning":true},"sql":{"checkConstraint":true,"defaultInInsert":true,"enums":true,"forShare":true,"forUpdate":true,"insertOnConflictSkip":true,"insertOnConflictWithoutTarget":true,"lateral":true,"lockNowait":true,"lockOf":true,"lockSkipLocked":true,"returning":true,"scalarList":true}},"domain":{"namespaces":{"public":{"enum":{"Priority":{"codecId":"pg/int4@1","members":[{"name":"Low","value":0},{"name":"High","value":1},{"name":"Urgent","value":2}]},"user_type":{"codecId":"pg/text@1","members":[{"name":"admin","value":"admin"},{"name":"user","value":"user"}]}},"models":{"Bug":{"base":{"model":"Task","namespace":"public"},"fields":{"severity":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"stepsToRepro":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"severity":{"column":"severity"},"stepsToRepro":{"column":"stepsToRepro"}},"namespaceId":"public","table":"bug"}},"Feature":{"base":{"model":"Task","namespace":"public"},"fields":{"priority":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"targetRelease":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"priority":{"column":"priority"},"targetRelease":{"column":"targetRelease"}},"namespaceId":"public","table":"feature"}},"Post":{"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"embedding":{"nullable":true,"type":{"codecId":"pg/vector@1","kind":"scalar","typeParams":{"length":1536}}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"priority":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"Priority","namespaceId":"public","plane":"domain"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"tags":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["postId"]},"through":{"childColumns":["tagId"],"namespaceId":"public","parentColumns":["postId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Tag","namespace":"public"}},"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"embedding":{"column":"embedding"},"id":{"column":"id"},"priority":{"column":"priority"},"title":{"column":"title"},"userId":{"column":"userId"}},"namespaceId":"public","table":"post"}},"PostTag":{"fields":{"postId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"tagId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"post":{"cardinality":"N:1","nullable":false,"on":{"localFields":["postId"],"targetFields":["id"]},"to":{"model":"Post","namespace":"public"}},"tag":{"cardinality":"N:1","nullable":false,"on":{"localFields":["tagId"],"targetFields":["id"]},"to":{"model":"Tag","namespace":"public"}}},"storage":{"fields":{"postId":{"column":"postId"},"tagId":{"column":"tagId"}},"namespaceId":"public","table":"post_tag"}},"Tag":{"fields":{"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"label":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{"posts":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["tagId"]},"through":{"childColumns":["postId"],"namespaceId":"public","parentColumns":["tagId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Post","namespace":"public"}}},"storage":{"fields":{"id":{"column":"id"},"label":{"column":"label"}},"namespaceId":"public","table":"tag"}},"Task":{"discriminator":{"field":"type"},"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"description":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"status":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"type":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"description":{"column":"description"},"id":{"column":"id"},"status":{"column":"status"},"title":{"column":"title"},"type":{"column":"type"},"userId":{"column":"userId"}},"namespaceId":"public","table":"task"},"variants":{"bug":{"model":"Bug","namespace":"public"},"feature":{"model":"Feature","namespace":"public"}}},"User":{"fields":{"address":{"nullable":true,"type":{"kind":"valueObject","name":"Address"}},"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"displayName":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"user_type","namespaceId":"public","plane":"domain"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Post","namespace":"public"}},"tasks":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Task","namespace":"public"}}},"storage":{"fields":{"address":{"column":"address"},"createdAt":{"column":"createdAt"},"displayName":{"column":"displayName"},"email":{"column":"email"},"id":{"column":"id"},"kind":{"column":"kind"}},"namespaceId":"public","table":"user"}}},"valueObjects":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}}}}}}},"execution":{"executionHash":"eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655","mutations":{"defaults":[{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"post","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"tag","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"task","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"user","field":"id","namespace":"public"}}]}},"extensions":{"demo/engagement-stats":{"familyId":"sql","id":"demo/engagement-stats","kind":"extension","targetId":"postgres","types":{"aggregateDescriptors":[{"input":{"kind":"trait","trait":"numeric"},"nullable":true,"operation":"stddev","output":{"codecId":"pg/numeric@1","kind":"codec"}}]},"version":"0.0.1"},"pgvector":{"capabilities":{"postgres":{"pgvector.cosine":true}},"familyId":"sql","id":"pgvector","kind":"extension","targetId":"postgres","types":{"codecTypes":{"import":{"alias":"PgVectorTypes","named":"CodecTypes","package":"@internal/extension-pgvector/codec-types"},"parameterizedOutputTypes":{"alias":"PgVectorParameterizedOutputTypes","named":"ParameterizedOutputTypesForTypeParams","package":"@internal/extension-pgvector/codec-types"}},"operationTypes":{"import":{"alias":"PgVectorOperationTypes","named":"OperationTypes","package":"@internal/extension-pgvector/operation-types"}},"queryOperationTypes":{"import":{"alias":"PgVectorQueryOperationTypes","named":"QueryOperationTypes","package":"@internal/extension-pgvector/operation-types"}}},"version":"0.0.1"}},"meta":{},"profileHash":"3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2","roots":{"post":{"model":"Post","namespace":"public"},"post_tag":{"model":"PostTag","namespace":"public"},"tag":{"model":"Tag","namespace":"public"},"task":{"model":"Task","namespace":"public"},"user":{"model":"User","namespace":"public"}},"schemaVersion":"1","storage":{"namespaces":{"public":{"entries":{"table":{"bug":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"severity":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"stepsToRepro":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"bug"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"feature":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"priority":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"targetRelease":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"feature"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"post":{"checks":[{"expression":"\"priority\" IN (0, 1, 2)","name":"post_priority_check_b236ace9","prefix":"post_priority_check"}],"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"embedding":{"codecId":"pg/vector@1","dataType":"pgvector/vector","nullable":true,"typeParams":{"length":1536}},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"priority":{"codecId":"pg/int4@1","dataType":"pg/int4","default":{"kind":"literal","value":0},"nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Priority","namespaceId":"public","plane":"storage"}},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"post_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"post"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"post_userId_idx_a489d58a","prefix":"post_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"post_tag":{"columns":{"postId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"tagId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"post_tag_postId_idx_a7a72715"},"source":{"columns":["postId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"post"}},{"index":{"name":"post_tag_tagId_idx_86854244"},"source":{"columns":["tagId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"tag"}}],"indexes":[{"columns":["postId"],"name":"post_tag_postId_idx_a7a72715","prefix":"post_tag_postId_idx","unique":false},{"columns":["tagId"],"name":"post_tag_tagId_idx_86854244","prefix":"post_tag_tagId_idx","unique":false}],"primaryKey":{"columns":["postId","tagId"]},"uniques":[]},"tag":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"label":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[{"columns":["label"]}]},"task":{"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"description":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"status":{"codecId":"pg/text@1","dataType":"pg/text","default":{"kind":"literal","value":"open"},"nullable":false},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"type":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"task_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"task"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"task_userId_idx_a489d58a","prefix":"task_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"user":{"checks":[{"expression":"\"kind\" IN ('admin', 'user')","name":"user_kind_check_836d43ef","prefix":"user_kind_check"}],"columns":{"address":{"codecId":"pg/jsonb@1","dataType":"pg/jsonb","nullable":true},"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"displayName":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"email":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"kind":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"user_type","namespaceId":"public","plane":"storage"}}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]}},"valueSet":{"Priority":{"kind":"valueSet","values":[0,1,2]},"user_type":{"kind":"valueSet","values":["admin","user"]}}},"id":"public","kind":"postgres-schema"}},"storageHash":"58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b"},"target":"postgres","targetFamily":"sql"} diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/test-helpers.ts b/test/integration/test/upgrade-instructions/value-objects-in-storage/test-helpers.ts new file mode 100644 index 000000000000..e15c68879573 --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/test-helpers.ts @@ -0,0 +1,73 @@ +import { spawnSync } from 'node:child_process'; +import { + cpSync, + mkdirSync, + mkdtempSync, + readdirSync, + readFileSync, + rmSync, + writeFileSync, +} from 'node:fs'; +import { tmpdir } from 'node:os'; +import { fileURLToPath } from 'node:url'; +import { dirname, join, relative } from 'pathe'; + +const SCRIPT_PATHS = { + app: 'upgrade-instructions/pending/value-objects-in-storage/app/scripts/value-objects-in-storage/value-objects-in-storage.ts', +} as const; + +const here = dirname(fileURLToPath(import.meta.url)); +const repoRoot = join(here, '../../../../..'); +export const fixturesDir = join(here, 'fixtures'); +const appScript = join(repoRoot, SCRIPT_PATHS.app); +const workDirs: string[] = []; + +export type Tree = Record; + +export function removeWorkDirs(): void { + for (const dir of workDirs.splice(0)) rmSync(dir, { recursive: true, force: true }); +} + +export function readTree(root: string, dir = root): Tree { + const files: Tree = {}; + for (const entry of readdirSync(dir, { withFileTypes: true })) { + const path = join(dir, entry.name); + if (entry.isDirectory()) Object.assign(files, readTree(root, path)); + else if (entry.isFile()) files[relative(root, path)] = readFileSync(path, 'utf8'); + } + return files; +} + +export function fixtureTree(side: 'before' | 'after'): Tree { + return readTree(join(fixturesDir, side)); +} + +export function copyFixture(side: 'before' | 'after'): string { + const root = mkdtempSync(join(tmpdir(), 'value-objects-in-storage-')); + workDirs.push(root); + cpSync(join(fixturesDir, side), root, { recursive: true }); + return root; +} + +export function editJson( + root: string, + path: string, + edit: (json: unknown) => unknown, + writtenPath = path, +): void { + const json: unknown = JSON.parse(readFileSync(join(root, path), 'utf8')); + const target = join(root, writtenPath); + mkdirSync(dirname(target), { recursive: true }); + writeFileSync(target, `${JSON.stringify(edit(json), null, 2)}\n`); +} + +export interface Run { + readonly status: number | null; + readonly stdout: string; + readonly stderr: string; +} + +export function runScript(root: string): Run { + const result = spawnSync(process.execPath, [appScript, root], { encoding: 'utf8' }); + return { status: result.status, stdout: result.stdout, stderr: result.stderr }; +} diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/value-objects-in-storage.test.ts b/test/integration/test/upgrade-instructions/value-objects-in-storage/value-objects-in-storage.test.ts new file mode 100644 index 000000000000..357f860a9406 --- /dev/null +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/value-objects-in-storage.test.ts @@ -0,0 +1,248 @@ +import { readFileSync } from 'node:fs'; +import { computeStorageHash } from '@internal/contract/hashing'; +import { MongoContractSerializer } from '@internal/family-mongo/ir'; +import { mongoContractCanonicalizationHooks } from '@internal/mongo-contract/canonicalization-hooks'; +import { sqlContractCanonicalizationHooks } from '@internal/sql-contract/canonicalization-hooks'; +import { PostgresContractSerializer } from '@internal/target-postgres/runtime'; +import { join } from 'pathe'; +import { afterAll, describe, expect, it } from 'vitest'; +import { + copyFixture, + editJson, + fixturesDir, + fixtureTree, + readTree, + removeWorkDirs, + runScript, +} from './test-helpers'; + +afterAll(removeWorkDirs); + +const SQL_BEFORE = '58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b'; +const SQL_AFTER = '1cb334a7b32d249d4fea0934dbbaa48c473b5946cd77ee1ce7442272479d6cf3'; +const SQL_SNAPSHOT = `sql/migrations/snapshots/${SQL_BEFORE}/contract.json`; + +interface StoredContract { + readonly targetFamily: string; + readonly target: string; + readonly storage: Record & { readonly storageHash: string }; +} + +function readContract(path: string): StoredContract { + return JSON.parse(readFileSync(path, 'utf8')); +} + +function recomputedHash(contract: StoredContract): string { + const { storageHash: _stored, ...storage } = contract.storage; + return computeStorageHash({ + target: contract.target, + targetFamily: contract.targetFamily, + storage, + ...(contract.targetFamily === 'mongo' + ? mongoContractCanonicalizationHooks + : sqlContractCanonicalizationHooks), + }); +} + +describe('a project whose contracts keep value objects in the domain plane', () => { + const root = copyFixture('before'); + const run = runScript(root); + + it('moves the value objects into storage and renames each snapshot to its new storage hash', () => { + expect({ run, tree: readTree(root) }).toEqual({ + run: { + status: 0, + stdout: [ + 'Rewrote 10 files and renamed 4 snapshot directories. Storage hashes changed (old -> new):', + ` ${SQL_BEFORE} -> ${SQL_AFTER}`, + ' 62671fbf016c515d808ac613743fddbf3db80384c1997251eac9d6f5fc063590 -> 95332b0bd6fbbfab4462d882c037a574c92e51897249c333ba681b12c4d603c9', + ' d264cabd6a02546054a776ed3c04438b9a22989f406c49f5590f7f2101156eb0 -> 406b672b68a6b01f04c0d6f910f5fefa4ac16d48b26e6c91b43856cd42c8cc5d', + ' da1339e341177f79b37f765f08200844d7cb4d59d26fe27fe4d95b0112b0c2cd -> 396942f6c9ee5f946d7f67b397929bab327fe84dd8f7f80e87b8097f416fb901', + '', + ].join('\n'), + stderr: '', + }, + tree: fixtureTree('after'), + }); + }); + + it('changes nothing on a second run', () => { + expect(runScript(root)).toEqual({ + status: 0, + stdout: 'No contract keeps value objects in the domain plane; nothing changed.\n', + stderr: '', + }); + }); +}); + +describe('the rewritten contracts', () => { + const after = fixtureTree('after'); + const contracts = Object.keys(after) + .filter((path) => path.endsWith('contract.json')) + .map((path) => ({ path, contract: readContract(join(fixturesDir, 'after', path)) })); + + it('store the hash their storage recomputes to, and each snapshot sits under it', () => { + expect( + contracts.map(({ path, contract }) => ({ + path, + stored: contract.storage.storageHash, + recomputed: recomputedHash(contract), + directory: path.includes('/snapshots/') ? path.split('/').at(-2) : undefined, + })), + ).toEqual( + contracts.map(({ path, contract }) => ({ + path, + stored: contract.storage.storageHash, + recomputed: contract.storage.storageHash, + directory: path.includes('/snapshots/') ? contract.storage.storageHash : undefined, + })), + ); + }); + + it('are read by this release', () => { + const read = contracts.map(({ path, contract }) => { + const json: unknown = contract; + const deserialized = + contract.targetFamily === 'mongo' + ? new MongoContractSerializer().deserializeContract(json) + : new PostgresContractSerializer().deserializeContract(json); + return { path, storageHash: deserialized.storage.storageHash }; + }); + expect(read).toEqual( + contracts.map(({ path, contract }) => ({ path, storageHash: contract.storage.storageHash })), + ); + }); + + it('give the SQL discriminator a value set and point its column at it', () => { + const sql = readContract( + join(fixturesDir, 'after', `sql/migrations/snapshots/${SQL_AFTER}/contract.json`), + ); + expect(sql.storage).toMatchObject({ + namespaces: { + public: { + entries: { + valueSet: { 'Task.type': { kind: 'valueSet', values: ['bug', 'feature'] } }, + table: { + task: { + columns: { + type: { + valueSet: { + plane: 'storage', + namespaceId: 'public', + entityKind: 'valueSet', + entityName: 'Task.type', + }, + }, + }, + }, + }, + }, + }, + }, + }); + }); +}); + +describe('a project the script refuses to rewrite', () => { + function refused(edit: (root: string) => void) { + const root = copyFixture('before'); + edit(root); + const before = readTree(root); + const run = runScript(root); + return { run, unchanged: JSON.stringify(readTree(root)) === JSON.stringify(before) }; + } + + it('stops on a snapshot whose stored hash does not match its content', () => { + const result = refused((root) => + editJson(root, SQL_SNAPSHOT, (json) => { + const contract = json as { + storage: { + namespaces: { + public: { + entries: { + table: Record }>; + }; + }; + }; + }; + }; + const column = contract.storage.namespaces.public.entries.table['task']?.columns['title']; + if (column !== undefined) column.nullable = true; + return contract; + }), + ); + expect(result).toMatchObject({ + run: { + status: 1, + stdout: '', + stderr: expect.stringContaining( + `${SQL_SNAPSHOT}: the stored storage hash ${SQL_BEFORE} does not match the contract's content`, + ), + }, + unchanged: true, + }); + }); + + it('stops on a field that holds a value object the contract does not declare', () => { + const result = refused((root) => + editJson(root, SQL_SNAPSHOT, (json) => { + const contract = json as { + domain: { + namespaces: { + public: { + models: Record }>; + }; + }; + }; + }; + const field = contract.domain.namespaces.public.models['User']?.fields['address']; + if (field !== undefined) field.type.name = 'Missing'; + return contract; + }), + ); + expect(result).toEqual({ + run: { + status: 1, + stdout: '', + stderr: `${SQL_SNAPSHOT}: field User.address holds the value object Missing, which the contract does not declare; restore the file from version control, or re-emit it, and run the script again\n`, + }, + unchanged: true, + }); + }); + + it('stops on a discriminator whose value set name an enum already has', () => { + const result = refused((root) => + editJson( + root, + SQL_SNAPSHOT, + (json) => { + const contract = json as StoredContract & { + storage: { namespaces: { public: { entries: { valueSet: Record } } } }; + }; + contract.storage.namespaces.public.entries.valueSet['Task.type'] = { + kind: 'valueSet', + values: ['bug', 'feature'], + }; + const hash = recomputedHash(contract); + return { ...contract, storage: { ...contract.storage, storageHash: hash } }; + }, + 'sql/src/contract.json', + ), + ); + expect(result.run).toMatchObject({ + status: 1, + stderr: expect.stringContaining( + 'sql/src/contract.json: the discriminator Task.type writes the value set Task.type in storage namespace public, which already holds a value set of that name; rename the enum', + ), + }); + }); + + it('says when it finds no contract', () => { + const root = join(copyFixture('before'), 'sql', 'migrations', 'app'); + expect(runScript(root)).toEqual({ + status: 0, + stdout: `No SQL or MongoDB contract was found under ${root}; nothing changed.\n`, + stderr: '', + }); + }); +}); diff --git a/upgrade-instructions/pending/value-objects-in-storage/app/scripts/value-objects-in-storage/value-objects-in-storage.ts b/upgrade-instructions/pending/value-objects-in-storage/app/scripts/value-objects-in-storage/value-objects-in-storage.ts new file mode 100644 index 000000000000..7108205dcaab --- /dev/null +++ b/upgrade-instructions/pending/value-objects-in-storage/app/scripts/value-objects-in-storage/value-objects-in-storage.ts @@ -0,0 +1,1123 @@ +#!/usr/bin/env node +/** + * Moves the value objects of every SQL and MongoDB contract from the domain plane into storage. Contracts emitted before this release keep value objects at `domain.namespaces..valueObjects` and type a field holding one as `{ kind: 'valueObject', name }`; this release keeps them at `storage.namespaces..entries.valueObject` and types the field by the value object's storage coordinate. In each contract the script moves every value object into the storage namespace of the same id, points every field at it by coordinate, and points a member typed by an enum at the enum's storage value set. In a SQL contract it also writes the value set of each discriminator, keyed `.` and holding the variants' values, and points the discriminator column at it, unless an enum already types that column. It makes the same changes in `contract.d.ts`. + * + * Storage now holds the value objects, so each rewritten contract has a new storage hash. The script renames each snapshot directory to its new hash and rewrites every migration, ref, `migration.ts` and `contract.d.ts` that names an old hash, and each migration's `migrationHash`. It does not add the checks PostgreSQL now derives for value object and discriminator columns: a snapshot records what its migrations created, and they created no such check. The next `prisma migration plan` adds them. + * + * Usage: node value-objects-in-storage.ts [project-root] + * + * The project root defaults to the working directory. The script reads and writes files only. It needs no database, network or configured stack, and a project whose contracts keep no value object in the domain plane is left unchanged. When it stops (a file it must read is not valid JSON, a contract whose stored hash does not match its content, a field naming a value object the contract does not declare, a value object whose namespace storage does not have, a member enum without a storage value set, a discriminator value set that already exists, or a snapshot directory that already exists with different content) it changes no file, prints one line per case and exits 1. When it finishes it prints how many files and snapshot directories it changed and each storage hash it replaced. + * + * The storage hash and migration hash rules are copied from `@internal/contract`, `@internal/sql-contract`, `@internal/mongo-contract` and `@internal/migration-tools` because a project's strict `node_modules` does not expose them. + */ +import { createHash } from 'node:crypto'; +import { + chmodSync, + type Dirent, + existsSync, + mkdirSync, + readdirSync, + readFileSync, + realpathSync, + renameSync, + rmSync, + statSync, + writeFileSync, +} from 'node:fs'; +import { basename, dirname, join, relative, resolve, sep } from 'node:path'; + +type Json = null | boolean | number | string | Json[] | JsonRecord; +interface JsonRecord { + [key: string]: Json; +} + +type Family = 'sql' | 'mongo'; + +const SKIPPED_DIRECTORIES = new Set(['node_modules', 'dist', 'build']); +const HASH = /^[0-9a-f]{64}$/; +const TEMPORARY_SUFFIX = '.value-objects-in-storage-tmp'; +const BYTE_ORDER_MARK = '\uFEFF'; + +function isRecord(value: unknown): value is JsonRecord { + if (typeof value !== 'object' || value === null || Array.isArray(value)) return false; + const prototype: unknown = Object.getPrototypeOf(value); + return prototype === Object.prototype || prototype === null; +} + +function withoutByteOrderMark(text: string): string { + return text.startsWith(BYTE_ORDER_MARK) ? text.slice(BYTE_ORDER_MARK.length) : text; +} + +function parseJson(text: string): Json | undefined { + try { + const value: Json = JSON.parse(withoutByteOrderMark(text)); + return value; + } catch { + return undefined; + } +} + +function sha256(text: string): string { + return createHash('sha256').update(text).digest('hex'); +} + +function compareCodeUnits(a: string, b: string): number { + return a < b ? -1 : a > b ? 1 : 0; +} + +function sortKeys(value: Json): Json { + if (Array.isArray(value)) return value.map(sortKeys); + if (!isRecord(value)) return value; + const sorted: JsonRecord = {}; + for (const key of Object.keys(value).sort(compareCodeUnits)) { + const child = value[key]; + if (child !== undefined) sorted[key] = sortKeys(child); + } + return sorted; +} + +function canonicalizeJson(value: Json): string { + return JSON.stringify(sortKeys(value)); +} + +type PathSegment = string | readonly string[]; + +function matchesPath(path: readonly string[], pattern: readonly PathSegment[]): boolean { + if (path.length !== pattern.length) return false; + return pattern.every((segment, index) => { + const value = path[index]; + if (value === undefined) return false; + if (segment === '*') return true; + return typeof segment === 'string' ? value === segment : segment.includes(value); + }); +} + +const SQL_PRESERVED_EMPTY: readonly (readonly PathSegment[])[] = [ + ['storage', 'namespaces', '*', 'entries', 'table'], + ['storage', 'namespaces', '*', 'entries', 'table', '*'], + ['storage', 'namespaces', '*', 'entries', 'table', '*', ['uniques', 'indexes', 'foreignKeys']], + ['storage', 'namespaces', '*', 'entries', 'table', '*', 'indexes', 'unique'], + ['storage', 'namespaces', '*', 'entries', 'table', '*', 'columns', '*', 'default', 'value'], +]; +const COLUMN_DEFAULT_VALUE: readonly PathSegment[] = [ + 'storage', + 'namespaces', + '*', + 'entries', + 'table', + '*', + 'columns', + '*', + 'default', + 'value', +]; +const MONGO_PRESERVED_EMPTY: readonly (readonly PathSegment[])[] = [ + ['storage', 'namespaces', '*', 'entries', 'collection'], + ['storage', 'namespaces', '*', 'entries', 'collection', '*'], +]; +const FRAMEWORK_PRESERVED_EMPTY: readonly (readonly PathSegment[])[] = [ + ['domain', 'namespaces'], + ['domain', 'namespaces', '*'], + ['domain', 'namespaces', '*', 'models'], + ['domain', 'namespaces', '*', 'models', '*', 'relations'], + ['domain', 'namespaces', '*', 'models', '*', 'storage'], + ['storage', 'namespaces'], + ['storage', 'namespaces', '*', 'entries'], + ['roots'], + ['extensions'], + ['extensions', '*'], + ['capabilities'], + ['meta'], + ['execution', 'mutations', 'defaults'], +]; +const TOP_LEVEL_ORDER = [ + 'schemaVersion', + 'canonicalVersion', + 'targetFamily', + 'target', + 'profileHash', + 'roots', + 'domain', + 'storage', + 'execution', + 'capabilities', + 'extensions', + 'defaultControlPolicy', + 'meta', +]; + +function isDefaultValue(value: Json): boolean { + if (value === false) return true; + if (Array.isArray(value)) return value.length === 0; + return isRecord(value) && Object.keys(value).length === 0; +} + +function isFamilyPreservedEmpty(family: Family, path: readonly string[]): boolean { + if (family === 'mongo') { + return ( + path.at(-1) === 'additionalProperties' || + (path.includes('jsonSchema') && (path.at(-2) === 'properties' || path.at(-1) === 'items')) || + MONGO_PRESERVED_EMPTY.some((pattern) => matchesPath(path, pattern)) + ); + } + if ( + path.length >= COLUMN_DEFAULT_VALUE.length && + matchesPath(path.slice(0, COLUMN_DEFAULT_VALUE.length), COLUMN_DEFAULT_VALUE) + ) + return true; + return SQL_PRESERVED_EMPTY.some((pattern) => matchesPath(path, pattern)); +} + +function isPreservedEmpty(family: Family, path: readonly string[], key: string): boolean { + if (key === 'nullable') return true; + if (key === 'elementNullable' && path.at(-2) === 'many') return true; + if (FRAMEWORK_PRESERVED_EMPTY.some((pattern) => matchesPath(path, pattern))) return true; + return isFamilyPreservedEmpty(family, path); +} + +function omitDefaults(family: Family, value: Json, path: readonly string[]): Json { + if (Array.isArray(value)) return value.map((item) => omitDefaults(family, item, path)); + if (!isRecord(value)) return value; + const result: JsonRecord = {}; + for (const [key, child] of Object.entries(value)) { + const childPath = [...path, key]; + if (key === '_generated') continue; + if (key === 'generated' && child === false) continue; + if ((key === 'onDelete' || key === 'onUpdate') && child === 'noAction') continue; + if (isDefaultValue(child) && !isPreservedEmpty(family, childPath, key)) continue; + result[key] = omitDefaults(family, child, childPath); + } + return result; +} + +function compareByName(a: Json, b: Json): number { + const nameA = isRecord(a) && typeof a['name'] === 'string' ? a['name'] : ''; + const nameB = isRecord(b) && typeof b['name'] === 'string' ? b['name'] : ''; + return compareCodeUnits(nameA, nameB); +} + +function sortTableArrays(storage: Json): Json { + if (!isRecord(storage)) return storage; + const namespaces = storage['namespaces']; + if (!isRecord(namespaces)) return storage; + const sortedNamespaces: JsonRecord = {}; + for (const [namespaceId, namespace] of Object.entries(namespaces)) { + const entries = isRecord(namespace) ? namespace['entries'] : undefined; + const tables = isRecord(entries) ? entries['table'] : undefined; + if (!isRecord(namespace) || !isRecord(entries) || !isRecord(tables)) { + sortedNamespaces[namespaceId] = namespace; + continue; + } + const sortedTables: JsonRecord = {}; + for (const [tableName, table] of Object.entries(tables)) { + if (!isRecord(table)) { + sortedTables[tableName] = table; + continue; + } + const sortedTable: JsonRecord = { ...table }; + for (const key of ['checks', 'indexes', 'uniques']) { + const list = table[key]; + if (Array.isArray(list)) sortedTable[key] = [...list].sort(compareByName); + } + sortedTables[tableName] = sortedTable; + } + sortedNamespaces[namespaceId] = { ...namespace, entries: { ...entries, table: sortedTables } }; + } + return { ...storage, namespaces: sortedNamespaces }; +} + +function orderTopLevel(value: JsonRecord): JsonRecord { + const ordered: JsonRecord = {}; + const remaining = new Set(Object.keys(value)); + for (const key of TOP_LEVEL_ORDER) { + const child = value[key]; + if (child !== undefined) { + ordered[key] = child; + remaining.delete(key); + } + } + for (const key of [...remaining].sort(compareCodeUnits)) { + const child = value[key]; + if (child !== undefined) ordered[key] = child; + } + return ordered; +} + +function omitNamespaceKinds(storage: JsonRecord): JsonRecord { + const namespaces = storage['namespaces']; + if (!isRecord(namespaces)) return storage; + const stripped: JsonRecord = {}; + for (const [namespaceId, namespace] of Object.entries(namespaces)) { + if (isRecord(namespace)) { + const { kind: _kind, ...rest } = namespace; + stripped[namespaceId] = rest; + } else { + stripped[namespaceId] = namespace; + } + } + return { ...storage, namespaces: stripped }; +} + +function computeStorageHash(family: Family, contract: JsonRecord): string { + const storage = isRecord(contract['storage']) ? contract['storage'] : {}; + const { storageHash: _published, ...withoutHash } = storage; + const hashed: JsonRecord = { + schemaVersion: '1', + targetFamily: typeof contract['targetFamily'] === 'string' ? contract['targetFamily'] : '', + target: typeof contract['target'] === 'string' ? contract['target'] : '', + profileHash: '', + roots: {}, + domain: { namespaces: {} }, + storage: omitNamespaceKinds(withoutHash), + extensions: {}, + capabilities: {}, + meta: {}, + }; + const withoutDefaults = omitDefaults(family, hashed, []); + if (!isRecord(withoutDefaults)) return ''; + const withSortedStorage = + family === 'sql' + ? { ...withoutDefaults, storage: sortTableArrays(withoutDefaults['storage'] ?? {}) } + : withoutDefaults; + const sorted = sortKeys(withSortedStorage); + return isRecord(sorted) ? sha256(JSON.stringify(orderTopLevel(sorted), null, 2)) : ''; +} + +function computeMigrationHash(metadata: JsonRecord, ops: Json): string { + const { migrationHash: _migrationHash, ...withoutHash } = metadata; + const parts = [canonicalizeJson(withoutHash), canonicalizeJson(ops)].map(sha256); + return sha256(canonicalizeJson(parts)); +} + +interface ProjectFiles { + readonly json: readonly string[]; + readonly migrationJson: readonly string[]; + readonly refs: readonly string[]; + readonly migrationTs: readonly string[]; + readonly leftovers: readonly string[]; +} + +function isDirectory(path: string): boolean { + try { + return statSync(path).isDirectory(); + } catch { + return false; + } +} + +function isFile(path: string): boolean { + try { + return statSync(path).isFile(); + } catch { + return false; + } +} + +/** A file, or a link to one: the script reads and writes through links to files. */ +function isFileEntry(dir: string, entry: Dirent): boolean { + return entry.isFile() || (entry.isSymbolicLink() && isFile(join(dir, entry.name))); +} + +/** A file the script reads, by the path it was reached through: its own path rather than a link's. */ +interface FoundFile { + readonly path: string; + readonly linked: boolean; + readonly inMigrationPackage: boolean; +} + +function listFiles(root: string): ProjectFiles { + const leftovers: string[] = []; + const visited = new Set(); + const found = new Map(); + const visit = (dir: string): void => { + const realDir = realpathSync(dir); + if (visited.has(realDir)) return; + visited.add(realDir); + const entries = readdirSync(dir, { withFileTypes: true }).sort((a, b) => + compareCodeUnits(a.name, b.name), + ); + const inMigrationPackage = entries.some( + (entry) => entry.name === 'migration.json' && isFileEntry(dir, entry), + ); + for (const entry of entries) { + const path = join(dir, entry.name); + if (entry.name.endsWith(TEMPORARY_SUFFIX)) { + leftovers.push(path); + continue; + } + if (entry.isDirectory() || (entry.isSymbolicLink() && isDirectory(path))) { + if (!SKIPPED_DIRECTORIES.has(entry.name) && !entry.name.startsWith('.')) visit(path); + continue; + } + if (!isFileEntry(dir, entry)) continue; + const realFile = realpathSync(path); + const linked = entry.isSymbolicLink(); + const known = found.get(realFile); + if (known === undefined || (known.linked && !linked)) + found.set(realFile, { path, linked, inMigrationPackage }); + } + }; + visit(root); + const files = [...found.values()]; + const named = (test: (file: FoundFile, name: string) => boolean): string[] => + files.filter((file) => test(file, basename(file.path))).map((file) => file.path); + return { + json: named((_file, name) => name.endsWith('.json')), + migrationJson: named((_file, name) => name === 'migration.json'), + refs: named((file, name) => name.endsWith('.json') && basename(dirname(file.path)) === 'refs'), + migrationTs: named((file, name) => name === 'migration.ts' && file.inMigrationPackage), + leftovers, + }; +} + +function isSnapshotContract(path: string): boolean { + const snapshotDir = dirname(path); + return ( + basename(path) === 'contract.json' && + HASH.test(basename(snapshotDir)) && + basename(dirname(snapshotDir)) === 'snapshots' + ); +} + +function replaceQuotedHashes(text: string, hashes: ReadonlyMap): string { + return text.replace(/"([0-9a-f]{64})"/g, (match: string, hash: string) => { + const next = hashes.get(hash); + return next === undefined ? match : `"${next}"`; + }); +} + +function replaceHashTokens(text: string, hashes: ReadonlyMap): string { + return text.replace( + /(? hashes.get(hash) ?? hash, + ); +} + +function readDirectory(dir: string): Map { + const files = new Map(); + if (!existsSync(dir)) return files; + for (const entry of readdirSync(dir, { withFileTypes: true })) + if (isFileEntry(dir, entry) && !entry.name.endsWith(TEMPORARY_SUFFIX)) + files.set(entry.name, readFileSync(join(dir, entry.name), 'utf8')); + return files; +} + +function sameFiles(a: ReadonlyMap, b: ReadonlyMap): boolean { + return a.size === b.size && [...a].every(([name, content]) => b.get(name) === content); +} + +function temporaryPath(path: string): string { + return join(dirname(path), `.${basename(path)}${TEMPORARY_SUFFIX}`); +} + +function writeWithModeOf(path: string, text: string, modeSource: string): void { + writeFileSync(path, text); + if (existsSync(modeSource)) chmodSync(path, statSync(modeSource).mode & 0o7777); +} + +function writeFile(path: string, text: string): void { + const target = realpathSync(path); + const temporary = temporaryPath(target); + writeWithModeOf(temporary, text, target); + renameSync(temporary, target); +} + +function writeDirectory(dir: string, files: ReadonlyMap, sourceDir: string): void { + const temporary = temporaryPath(dir); + mkdirSync(temporary); + for (const [name, text] of files) + writeWithModeOf(join(temporary, name), text, join(sourceDir, name)); + renameSync(temporary, dir); +} + +function removeDirectory(dir: string): void { + const temporary = temporaryPath(dir); + renameSync(dir, temporary); + rmSync(temporary, { recursive: true, force: true }); +} + +function count(amount: number, singular: string, plural: string): string { + return `${amount} ${amount === 1 ? singular : plural}`; +} + +function recordsOf(value: Json | undefined): [string, JsonRecord][] { + return isRecord(value) + ? Object.entries(value).filter((entry): entry is [string, JsonRecord] => isRecord(entry[1])) + : []; +} + +function coordinate(namespaceId: string, entityKind: string, entityName: string): JsonRecord { + return { plane: 'storage', namespaceId, entityKind, entityName }; +} + +interface Rewrite { + readonly contract: JsonRecord; + readonly changed: boolean; + /** The storage namespace each moved value object now lives in, by name. */ + readonly valueObjectNamespaces: ReadonlyMap; + readonly problems: readonly string[]; +} + +/** Whether the contract keeps value objects in the domain plane, or refers to one by name. */ +function holdsOldValueObjects(contract: JsonRecord): boolean { + const domain = isRecord(contract['domain']) ? contract['domain'] : {}; + for (const [, namespace] of recordsOf(domain['namespaces'])) { + if (Object.hasOwn(namespace, 'valueObjects')) return true; + for (const [, model] of recordsOf(namespace['models'])) + for (const [, field] of recordsOf(model['fields'])) { + const type = field['type']; + if (isRecord(type) && type['kind'] === 'valueObject' && typeof type['name'] === 'string') + return true; + } + } + return false; +} + +/** A field that names a value object by `name`, pointed at it by its storage coordinate. */ +function withValueObjectCoordinate( + field: JsonRecord, + namespaceOf: (name: string) => string | undefined, + where: string, + problems: string[], +): JsonRecord { + const type = field['type']; + if (!isRecord(type) || type['kind'] !== 'valueObject' || typeof type['name'] !== 'string') + return field; + const namespaceId = namespaceOf(type['name']); + if (namespaceId === undefined) { + problems.push( + `${where} holds the value object ${type['name']}, which the contract does not declare`, + ); + return field; + } + return { + ...field, + type: { kind: 'valueObject', ...coordinate(namespaceId, 'valueObject', type['name']) }, + }; +} + +function valueSetsOf(storageNamespace: JsonRecord): JsonRecord { + const entries = isRecord(storageNamespace['entries']) ? storageNamespace['entries'] : {}; + return isRecord(entries['valueSet']) ? entries['valueSet'] : {}; +} + +/** A value object member, with an enum reference pointed at the enum's storage value set. */ +function rewrittenMember( + member: JsonRecord, + storageNamespaces: JsonRecord, + where: string, + problems: string[], +): JsonRecord { + const valueSet = member['valueSet']; + if (!isRecord(valueSet) || valueSet['plane'] === 'storage') return member; + const namespaceId = valueSet['namespaceId']; + const name = valueSet['entityName']; + const storageNamespace = + typeof namespaceId === 'string' ? storageNamespaces[namespaceId] : undefined; + if ( + typeof namespaceId !== 'string' || + typeof name !== 'string' || + !isRecord(storageNamespace) || + !Object.hasOwn(valueSetsOf(storageNamespace), name) + ) { + problems.push( + `${where} is typed by the enum ${String(name)}, which has no value set in storage namespace ${String(namespaceId)}`, + ); + return member; + } + return { ...member, valueSet: { ...valueSet, ...coordinate(namespaceId, 'valueSet', name) } }; +} + +/** Writes each discriminator's value set, `.`, in its base table's namespace, and points the column at it, unless an enum already types the column. */ +function writeDiscriminatorValueSets( + domainNamespaces: JsonRecord, + storageNamespaces: JsonRecord, + problems: string[], +): boolean { + let changed = false; + for (const [modelNamespaceId, namespace] of recordsOf(domainNamespaces)) { + for (const [modelName, model] of recordsOf(namespace['models'])) { + const discriminator = model['discriminator']; + const variants = model['variants']; + const storage = model['storage']; + if (!isRecord(discriminator) || !isRecord(variants) || !isRecord(storage)) continue; + const field = discriminator['field']; + const fields = isRecord(storage['fields']) ? storage['fields'] : {}; + const fieldStorage = typeof field === 'string' ? fields[field] : undefined; + const columnName = isRecord(fieldStorage) ? fieldStorage['column'] : undefined; + const tableName = storage['table']; + const namespaceId = + typeof storage['namespaceId'] === 'string' ? storage['namespaceId'] : modelNamespaceId; + const storageNamespace = storageNamespaces[namespaceId]; + const entries = isRecord(storageNamespace) ? storageNamespace['entries'] : undefined; + const tables = isRecord(entries) ? entries['table'] : undefined; + const table = + isRecord(tables) && typeof tableName === 'string' ? tables[tableName] : undefined; + const columns = isRecord(table) ? table['columns'] : undefined; + const column = + isRecord(columns) && typeof columnName === 'string' ? columns[columnName] : undefined; + if ( + typeof field !== 'string' || + typeof columnName !== 'string' || + !isRecord(entries) || + !isRecord(columns) || + !isRecord(column) || + column['valueSet'] !== undefined + ) + continue; + const name = `${modelName}.${field}`; + const valueSets = isRecord(entries['valueSet']) ? entries['valueSet'] : {}; + if (Object.hasOwn(valueSets, name)) { + problems.push( + `the discriminator ${name} writes the value set ${name} in storage namespace ${namespaceId}, which already holds a value set of that name; rename the enum`, + ); + continue; + } + entries['valueSet'] = { + ...valueSets, + [name]: { kind: 'valueSet', values: Object.keys(variants).sort(compareCodeUnits) }, + }; + columns[columnName] = { ...column, valueSet: coordinate(namespaceId, 'valueSet', name) }; + changed = true; + } + } + return changed; +} + +/** The contract with its value objects moved into storage, and on SQL each discriminator's value set written. */ +function rewriteContract(contract: JsonRecord, family: Family): Rewrite { + const unchanged: Rewrite = { + contract, + changed: false, + valueObjectNamespaces: new Map(), + problems: [], + }; + if (!holdsOldValueObjects(contract)) return unchanged; + const copy = parseJson(JSON.stringify(contract)); + if (!isRecord(copy) || !isRecord(copy['domain']) || !isRecord(copy['storage'])) return unchanged; + const domainNamespaces = isRecord(copy['domain']['namespaces']) + ? copy['domain']['namespaces'] + : {}; + const storageNamespaces = isRecord(copy['storage']['namespaces']) + ? copy['storage']['namespaces'] + : {}; + const problems: string[] = []; + const valueObjectNamespaces = new Map(); + const declaredIn = new Map>(); + for (const [namespaceId, namespace] of recordsOf(domainNamespaces)) + for (const [name] of recordsOf(namespace['valueObjects'])) + declaredIn.set(name, new Set([...(declaredIn.get(name) ?? []), namespaceId])); + const namespaceFor = + (ownNamespaceId: string) => + (name: string): string | undefined => { + const namespaces = declaredIn.get(name); + if (namespaces === undefined) return undefined; + if (namespaces.has(ownNamespaceId)) return ownNamespaceId; + return namespaces.size === 1 ? [...namespaces][0] : undefined; + }; + + for (const [namespaceId, namespace] of recordsOf(domainNamespaces)) { + const valueObjects = namespace['valueObjects']; + delete namespace['valueObjects']; + for (const [modelName, model] of recordsOf(namespace['models'])) { + const fields = model['fields']; + for (const [fieldName, field] of recordsOf(fields)) + if (isRecord(fields)) + fields[fieldName] = withValueObjectCoordinate( + field, + namespaceFor(namespaceId), + `field ${modelName}.${fieldName}`, + problems, + ); + } + if (!isRecord(valueObjects)) continue; + const storageNamespace = storageNamespaces[namespaceId]; + if (!isRecord(storageNamespace)) { + problems.push( + `the value objects of domain namespace ${namespaceId} have no storage namespace ${namespaceId} to move to`, + ); + continue; + } + const moved: JsonRecord = {}; + for (const [name, valueObject] of recordsOf(valueObjects)) { + valueObjectNamespaces.set(name, namespaceId); + const members: JsonRecord = {}; + for (const [memberName, member] of recordsOf(valueObject['fields'])) { + const where = `value object member ${name}.${memberName}`; + members[memberName] = rewrittenMember( + withValueObjectCoordinate(member, namespaceFor(namespaceId), where, problems), + storageNamespaces, + where, + problems, + ); + } + moved[name] = { ...valueObject, fields: members }; + } + const entries = isRecord(storageNamespace['entries']) ? storageNamespace['entries'] : {}; + storageNamespace['entries'] = { ...entries, valueObject: moved }; + } + if (family === 'sql') writeDiscriminatorValueSets(domainNamespaces, storageNamespaces, problems); + if (Object.hasOwn(copy, 'valueObjects')) delete copy['valueObjects']; + return { contract: copy, changed: true, valueObjectNamespaces, problems }; +} + +function withStorageHash(contract: JsonRecord, hash: string): JsonRecord { + const storage = contract['storage']; + if (!isRecord(storage) || typeof storage['storageHash'] !== 'string') return contract; + return { ...contract, storage: { ...storage, storageHash: hash } }; +} + +function lineEndingOf(text: string): string { + return text.includes('\r\n') ? '\r\n' : '\n'; +} + +/** The contract written the way the file held it: a snapshot's canonical line, or indented JSON, with the file's byte order mark and line ending. */ +function writtenForm(contract: JsonRecord, original: JsonRecord, text: string): string { + const mark = text.startsWith(BYTE_ORDER_MARK) ? BYTE_ORDER_MARK : ''; + const body = withoutByteOrderMark(text); + const lineEnding = lineEndingOf(body); + const finalNewline = body.endsWith('\n') ? lineEnding : ''; + if (body === `${canonicalizeJson(original)}${finalNewline}`) + return `${mark}${canonicalizeJson(contract)}${finalNewline}`; + const indented = JSON.stringify(contract, null, 2).replaceAll('\n', lineEnding); + return `${mark}${indented}${finalNewline}`; +} + +interface Block { + readonly start: number; + end: number; + readonly label: string; + readonly parent: Block | undefined; +} + +function scanBlocks(text: string): Block[] { + const blocks: Block[] = []; + const open: Block[] = []; + let quote: string | undefined; + for (let index = 0; index < text.length; index++) { + const char = text[index]; + if (quote !== undefined) { + if (char === '\\') index++; + else if (char === quote) quote = undefined; + continue; + } + if (char === '/' && text[index + 1] === '/') { + const end = text.indexOf('\n', index); + index = end === -1 ? text.length : end; + continue; + } + if (char === '/' && text[index + 1] === '*') { + const end = text.indexOf('*/', index + 2); + index = end === -1 ? text.length : end + 1; + continue; + } + if (char === "'" || char === '"' || char === '`') { + quote = char; + continue; + } + if (char === '{') { + const key = + /(?:readonly\s+)?([\p{ID_Start}_$][\p{ID_Continue}$]*|'[^']*'|"[^"]*")\s*\??\s*:\s*$/u.exec( + text.slice(Math.max(0, index - 200), index), + ); + const block: Block = { + start: index, + end: text.length, + label: key?.[1]?.replace(/^['"]|['"]$/g, '') ?? '', + parent: open.at(-1), + }; + open.push(block); + blocks.push(block); + } else if (char === '}') { + const block = open.pop(); + if (block !== undefined) block.end = index; + } + } + return blocks; +} + +function lineStart(text: string, position: number): number { + return text.lastIndexOf('\n', position - 1) + 1; +} + +/** The end of the member a block closes: past its `;` and the line break after it. */ +function memberEnd(text: string, block: Block): number { + const rest = /^\s*;?[ \t]*(?:\r?\n)?/.exec(text.slice(block.end + 1)); + return block.end + 1 + (rest?.[0].length ?? 0); +} + +function indentOf(text: string, position: number): string { + return /^[ \t]*/.exec(text.slice(lineStart(text, position)))?.[0] ?? ''; +} + +function childBlocks(blocks: readonly Block[], parent: Block): Block[] { + return blocks.filter((block) => block.parent === parent); +} + +function isUnder(block: Block, labels: readonly string[]): boolean { + let current = block.parent; + for (const label of labels) { + if (current?.label !== label) return false; + current = current.parent; + } + return true; +} + +/** A block re-indented from `from` to `to`, its first line left as it starts. */ +function reindent(literal: string, from: string, to: string): string { + return literal + .split('\n') + .map((line, index) => + index === 0 || !line.startsWith(from) ? line : `${to}${line.slice(from.length)}`, + ) + .join('\n'); +} + +interface Edit { + readonly start: number; + readonly end: number; + readonly text: string; +} + +const STORAGE_NAMESPACES = "ContractBase['storage']['namespaces']"; +const DTS_LINE_WIDTH = 100; + +/** + * The same move in `contract.d.ts`: each `valueObjects` type of a domain namespace goes into its storage namespace's `entries` as `valueObject`, a member's enum reference names the storage value set, a field's value object type names its coordinate, the top-level `valueObjects` type goes, and the value object and field type aliases read storage. The discriminator value sets are not typed: a re-emit types them. + */ +function rewriteDts( + text: string, + valueObjectNamespaces: ReadonlyMap, + hashes: ReadonlyMap, +): string { + const lineEnding = lineEndingOf(text); + const blocks = scanBlocks(text); + const edits: Edit[] = []; + const moved = new Map(); + for (const block of blocks) { + if (block.label !== 'valueObjects') continue; + const namespace = block.parent; + if (namespace !== undefined && isUnder(namespace, ['namespaces', 'domain'])) { + const literal = text + .slice(block.start, block.end + 1) + .replace(/readonly plane: (['"])domain\1/g, "readonly plane: 'storage'") + .replace(/readonly entityKind: (['"])enum\1/g, "readonly entityKind: 'valueSet'"); + moved.set(namespace.label, reindent(literal, indentOf(text, block.start), '')); + edits.push({ start: lineStart(text, block.start), end: memberEnd(text, block), text: '' }); + } else if ( + namespace !== undefined && + childBlocks(blocks, namespace).some((sibling) => sibling.label === 'domain') + ) { + edits.push({ start: lineStart(text, block.start), end: memberEnd(text, block), text: '' }); + } + } + for (const block of blocks) { + if (block.label !== 'entries' || block.parent === undefined) continue; + if (!isUnder(block.parent, ['namespaces']) || isUnder(block.parent, ['namespaces', 'domain'])) + continue; + const literal = moved.get(block.parent.label); + if (literal === undefined) continue; + const indent = `${indentOf(text, block.start)} `; + const closing = lineStart(text, block.end); + edits.push({ + start: closing, + end: closing, + text: `${indent}readonly valueObject: ${reindent(literal, '', indent)};${lineEnding}`, + }); + } + let result = text; + for (const edit of edits.sort((a, b) => b.start - a.start)) + result = `${result.slice(0, edit.start)}${edit.text}${result.slice(edit.end)}`; + result = result + .replace( + /\{\s*readonly kind: (['"])valueObject\1;\s*readonly name: (['"])([^'"]*)\2;?\s*\}/g, + ( + match: string, + _quote: string, + _nameQuote: string, + name: string, + offset: number, + whole: string, + ) => { + const namespaceId = valueObjectNamespaces.get(name); + return namespaceId === undefined + ? match + : coordinateTypeLiteral(namespaceId, name, whole, offset, lineEnding); + }, + ) + .replace( + /ValueObjectTypesFromDomain<(\s*)ContractBase\['domain'\]\['namespaces'\]/g, + `ValueObjectTypesFromStorage<$1${STORAGE_NAMESPACES}`, + ) + .replace(/\bValueObjectTypesFromDomain\b/g, 'ValueObjectTypesFromStorage') + .replace( + /(FieldTypesFromDomain<[^<>]*?(['"])(?:output|input)\2)(\s*)>/g, + (_match: string, head: string, _quote: string, space: string) => + space.includes('\n') + ? `${head},${lineEnding} ${STORAGE_NAMESPACES}${lineEnding}>` + : `${head}, ${STORAGE_NAMESPACES}>`, + ); + return replaceHashTokens(result, hashes); +} + +/** A value object field's coordinate type, on one line when it fits the emitter's line width, else one member per line. */ +function coordinateTypeLiteral( + namespaceId: string, + name: string, + text: string, + offset: number, + lineEnding: string, +): string { + const members = [ + "readonly kind: 'valueObject'", + "readonly plane: 'storage'", + `readonly namespaceId: '${namespaceId}'`, + "readonly entityKind: 'valueObject'", + `readonly entityName: '${name}'`, + ]; + const single = `{ ${members.join('; ')} }`; + if (offset - lineStart(text, offset) + single.length + 1 <= DTS_LINE_WIDTH) return single; + const indent = indentOf(text, offset); + const lines = members.map((member) => `${indent} ${member};`); + return `{${lineEnding}${lines.join(lineEnding)}${lineEnding}${indent}}`; +} + +interface ContractPlan { + readonly path: string; + readonly snapshot: boolean; + readonly oldHash: string; + readonly newHash: string; + readonly content: string; + readonly valueObjectNamespaces: ReadonlyMap; +} + +interface MigrationOps { + readonly path: string; + /** `undefined` when the package has no `ops.json` or it is not valid JSON. */ + readonly ops: Json | undefined; + readonly unreadable: boolean; +} + +function readOps(migrationDir: string): MigrationOps { + const path = join(migrationDir, 'ops.json'); + if (!existsSync(path)) return { path, ops: undefined, unreadable: false }; + const ops = parseJson(readFileSync(path, 'utf8')); + return { path, ops, unreadable: ops === undefined }; +} + +function familyOf(contract: JsonRecord): Family | undefined { + const family = contract['targetFamily']; + return family === 'sql' || family === 'mongo' ? family : undefined; +} + +function summary( + root: string, + contractsFound: number, + files: number, + directories: number, + hashes: ReadonlyMap, +): readonly string[] { + if (contractsFound === 0) + return [`No SQL or MongoDB contract was found under ${root}; nothing changed.`]; + if (files === 0 && directories === 0) + return ['No contract keeps value objects in the domain plane; nothing changed.']; + const renamed = count(directories, 'snapshot directory', 'snapshot directories'); + const changes = `Rewrote ${count(files, 'file', 'files')} and renamed ${renamed}.`; + if (hashes.size === 0) return [changes]; + return [ + `${changes} Storage hashes changed (old -> new):`, + ...[...hashes] + .sort(([a], [b]) => compareCodeUnits(a, b)) + .map(([oldHash, newHash]) => ` ${oldHash} -> ${newHash}`), + ]; +} + +const JSON_FILES_THAT_MUST_PARSE = new Set(['contract.json', 'migration.json']); + +function main({ + root, + errors, +}: { + readonly root: string; + readonly errors: readonly string[]; +}): number { + if (errors.length > 0) { + process.stderr.write(`${errors.join('\n')}\n`); + return 1; + } + const display = (path: string): string => relative(root, path).split(sep).join('/'); + const files = listFiles(root); + const plans: ContractPlan[] = []; + const stops: string[] = []; + let contractsFound = 0; + + for (const path of files.json) { + const text = readFileSync(path, 'utf8'); + const contract = parseJson(text); + if ( + contract === undefined && + (JSON_FILES_THAT_MUST_PARSE.has(basename(path)) || files.refs.includes(path)) + ) { + stops.push(`${display(path)}: not valid JSON`); + continue; + } + if (!isRecord(contract) || typeof contract['target'] !== 'string') continue; + const family = familyOf(contract); + if (family === undefined || !isRecord(contract['storage'])) continue; + contractsFound++; + const rewrite = rewriteContract(contract, family); + if (!rewrite.changed) continue; + const snapshot = isSnapshotContract(path); + const stored = contract['storage']['storageHash']; + const oldHash = snapshot ? basename(dirname(path)) : typeof stored === 'string' ? stored : ''; + const computed = computeStorageHash(family, contract); + if (computed !== oldHash || (typeof stored === 'string' && stored !== oldHash)) { + stops.push( + `${display(path)}: the stored storage hash ${oldHash} does not match the contract's content, which hashes to ${computed}; restore the file from version control, or re-emit it, and run the script again`, + ); + continue; + } + if (rewrite.problems.length > 0) { + for (const problem of rewrite.problems) + stops.push( + `${display(path)}: ${problem}; restore the file from version control, or re-emit it, and run the script again`, + ); + continue; + } + const newHash = computeStorageHash(family, rewrite.contract); + plans.push({ + path, + snapshot, + oldHash, + newHash, + content: writtenForm(withStorageHash(rewrite.contract, newHash), contract, text), + valueObjectNamespaces: rewrite.valueObjectNamespaces, + }); + } + + const hashes = new Map(); + for (const plan of [...plans].sort((a, b) => Number(b.snapshot) - Number(a.snapshot))) + if (plan.oldHash !== plan.newHash && !hashes.has(plan.oldHash)) + hashes.set(plan.oldHash, plan.newHash); + + const newDirectories = new Map>(); + const directorySources = new Map(); + const sourceDirectories = new Set(); + const removals = new Set(); + const snapshotRewrites: [string, string][] = []; + const referenceWrites = new Map(); + const contractWrites = new Map(); + for (const plan of plans) { + const dtsPath = plan.snapshot + ? join(dirname(plan.path), 'contract.d.ts') + : `${plan.path.slice(0, -'.json'.length)}.d.ts`; + const dtsHashes = new Map([...hashes, [plan.oldHash, plan.newHash]]); + const dts = existsSync(dtsPath) + ? rewriteDts(readFileSync(dtsPath, 'utf8'), plan.valueObjectNamespaces, dtsHashes) + : undefined; + if (!plan.snapshot) { + if (dts !== undefined) referenceWrites.set(dtsPath, dts); + contractWrites.set(plan.path, plan.content); + continue; + } + const oldDir = dirname(plan.path); + const newDir = join(dirname(oldDir), plan.newHash); + sourceDirectories.add(oldDir); + if (newDir === oldDir) { + if (dts !== undefined) snapshotRewrites.push([dtsPath, dts]); + snapshotRewrites.push([plan.path, plan.content]); + continue; + } + removals.add(oldDir); + const content = readDirectory(oldDir); + content.set('contract.json', plan.content); + if (dts !== undefined) content.set('contract.d.ts', dts); + const planned = newDirectories.get(newDir); + if (planned !== undefined && !sameFiles(planned, content)) { + stops.push(`${display(newDir)}: snapshot directory already exists with different content`); + continue; + } + newDirectories.set(newDir, content); + directorySources.set(newDir, oldDir); + } + for (const [dir, content] of newDirectories) { + if (sourceDirectories.has(dir) || !existsSync(dir)) continue; + if (sameFiles(readDirectory(dir), content)) newDirectories.delete(dir); + else stops.push(`${display(dir)}: snapshot directory already exists with different content`); + } + + for (const path of files.migrationJson) { + const text = readFileSync(path, 'utf8'); + const metadata = parseJson(text); + if (!isRecord(metadata)) continue; + const from = metadata['from']; + const to = metadata['to']; + const nextFrom = typeof from === 'string' ? (hashes.get(from) ?? from) : from; + const nextTo = typeof to === 'string' ? (hashes.get(to) ?? to) : to; + if (nextFrom === from && nextTo === to) continue; + const { path: opsPath, ops, unreadable } = readOps(dirname(path)); + if (unreadable) { + stops.push(`${display(opsPath)}: not valid JSON`); + continue; + } + const oldMigrationHash = metadata['migrationHash']; + let next = replaceQuotedHashes(text, hashes); + if ( + ops !== undefined && + typeof oldMigrationHash === 'string' && + nextFrom !== undefined && + nextTo !== undefined + ) { + const migrationHash = computeMigrationHash({ ...metadata, from: nextFrom, to: nextTo }, ops); + next = next.replace(`"${oldMigrationHash}"`, `"${migrationHash}"`); + } + referenceWrites.set(path, next); + } + for (const path of files.refs) { + const text = readFileSync(path, 'utf8'); + const next = replaceQuotedHashes(text, hashes); + if (next !== text) referenceWrites.set(path, next); + } + for (const path of files.migrationTs) { + const text = readFileSync(path, 'utf8'); + const next = replaceHashTokens(text, hashes); + if (next !== text) referenceWrites.set(path, next); + } + + if (stops.length > 0) { + process.stderr.write(`${[...new Set(stops)].join('\n')}\n`); + return 1; + } + + try { + for (const path of files.leftovers) rmSync(path, { recursive: true, force: true }); + for (const [dir, content] of newDirectories) + writeDirectory(dir, content, directorySources.get(dir) ?? dir); + for (const [path, text] of snapshotRewrites) writeFile(path, text); + for (const [path, text] of referenceWrites) writeFile(path, text); + for (const [path, text] of contractWrites) writeFile(path, text); + for (const dir of removals) removeDirectory(dir); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + process.stderr.write( + `${message}; the upgrade stopped partway, run the script again to finish it\n`, + ); + return 1; + } + const rewrittenFiles = snapshotRewrites.length + referenceWrites.size + contractWrites.size; + const lines = summary(root, contractsFound, rewrittenFiles, removals.size, hashes); + process.stdout.write(`${lines.join('\n')}\n`); + return 0; +} + +function parseRoot(args: readonly string[]): { readonly root: string; readonly errors: string[] } { + const errors = args.filter((arg) => arg.startsWith('-')).map((arg) => `${arg}: unknown option`); + const positional = args.filter((arg) => !arg.startsWith('-')); + if (positional.length > 1) errors.push(`expected one project root, got ${positional.length}`); + const root = resolve(positional[0] ?? process.cwd()); + if (errors.length === 0 && !isDirectory(root)) errors.push(`${root}: not a directory`); + if (errors.length > 0) errors.push('Usage: node value-objects-in-storage.ts [project-root]'); + return { root, errors }; +} + +process.exitCode = main(parseRoot(process.argv.slice(2))); From d4acafa48a692f90ac1319c0361154573cdc7280 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 16:16:17 +0200 Subject: [PATCH 054/104] TML-3537: upgrade instructions for value objects in storage, for apps and for extensions Signed-off-by: willbot Signed-off-by: Will Madden --- .../app/instructions.md | 90 +++++++++++++++++++ .../extension/instructions.md | 52 +++++++++++ 2 files changed, 142 insertions(+) create mode 100644 upgrade-instructions/pending/value-objects-in-storage/app/instructions.md create mode 100644 upgrade-instructions/pending/value-objects-in-storage/extension/instructions.md diff --git a/upgrade-instructions/pending/value-objects-in-storage/app/instructions.md b/upgrade-instructions/pending/value-objects-in-storage/app/instructions.md new file mode 100644 index 000000000000..f900cca6e96b --- /dev/null +++ b/upgrade-instructions/pending/value-objects-in-storage/app/instructions.md @@ -0,0 +1,90 @@ +--- +changes: + - id: value-objects-in-storage + summary: | + A contract keeps its value objects in storage, at `storage.namespaces..entries.valueObject`, and a field holding one names it by its storage coordinate. On SQL, a discriminator column names a value set, `.`, of its variants' values. A `contract.json` that keeps `valueObjects` in the domain plane is refused with `CONTRACT.VALIDATION_FAILED`, saying to re-emit it. Run the colocated script on the project: it rewrites every contract and snapshot, renames the snapshot directories to their new storage hashes, and rewrites the migrations, refs, `migration.ts` files and `contract.d.ts` files that name them. Then sign each database and re-emit the contract. + detection: + glob: "**/*.json" + matches: + - '"valueObjects"\s*:' + script: ./scripts/value-objects-in-storage/value-objects-in-storage.ts + - id: value-object-types-from-storage + summary: | + `ValueObjectTypesFromDomain`, exported by `@prisma/orm-mongo/contract/types`, is now `ValueObjectTypesFromStorage` and takes the contract's storage namespaces. A committed `contract.d.ts` that imports it compiles again once it is re-emitted. Code of your own that imports it changes the name and passes `Contract['storage']['namespaces']`. + detection: + glob: "**/*.{ts,mts,cts,tsx}" + matches: + - '\bValueObjectTypesFromDomain\b' + - id: postgres-value-object-checks + summary: | + On PostgreSQL, the contract derives a check for every value object column and every discriminator column of a managed table. The next `prisma migration plan` adds them, and `prisma db migrate` fails on a row that does not fit, including a row whose value object has a key the value object does not declare, which the SQL ORM never refused. Find such rows before you apply the migration. + - id: discriminator-typed-by-variant-values + summary: | + A SQL discriminator field is typed by its variants' values, such as `'bug' | 'feature'`, instead of `string`. Code that assigns such a field to a `string` still compiles; a type assertion that expects `string` changes to the union. +--- + +# Value objects are storage entities + +## `value-objects-in-storage` + +Commit your work first, so the script's changes can be reviewed and undone with git. Do this before you re-emit the contract: `prisma db sign` must see the contract the database was created from before a re-emit adds the new checks. Then run the script from the project root: + +```sh +node /scripts/value-objects-in-storage/value-objects-in-storage.ts +``` + +Node 24 or later runs the TypeScript script directly; it needs no `tsx`. It reads and writes files only and needs no database. It rewrites every `*.json` file under the root that parses as a SQL or MongoDB contract keeping value objects in the domain plane, skipping `node_modules`, `dist`, `build` and directories whose name starts with a dot. In each contract it: + +- moves each value object from `domain.namespaces..valueObjects` to `storage.namespaces..entries.valueObject`; +- writes each field that holds a value object as `{ kind: 'valueObject', plane: 'storage', namespaceId, entityKind: 'valueObject', entityName }` instead of `{ kind: 'valueObject', name }`; +- points a value object member typed by an enum at the enum's storage value set (`plane: 'storage'`, `entityKind: 'valueSet'`); +- on SQL, writes the value set of each discriminator, keyed `.` in the base table's namespace and holding the variants' values, sorted, and points the discriminator column at it. A column an enum types keeps the enum's value set. + +Storage now holds the value objects, so each contract it rewrites has a new storage hash. The script renames each snapshot directory to the new hash and rewrites every `migration.json` (with its `migrationHash`), ref, `migration.ts` and `contract.d.ts` that names an old one. It does not add the PostgreSQL checks described in `postgres-value-object-checks`: a snapshot records what its migrations created, and none created those checks. + +When it finishes, it prints how many files it rewrote, how many snapshot directories it renamed, and each storage hash it replaced (` -> `). A project already in the new form is left unchanged, and the script says that nothing changed. It changes no file and exits 1 when a file it must read is not valid JSON, when a contract's stored hash does not match its content, when a field holds a value object the contract does not declare, when a member's enum has no storage value set, when a discriminator's value set name is already taken by an enum, or when a renamed snapshot directory already exists with different content. If it stops on an error partway, for example on a full disk, run it again: it finishes the upgrade. + +The script rewrites a snapshot's `contract.d.ts` for the moved value objects. It does not add the types of the discriminator value sets; `prisma contract emit` writes those for the emitted contract. + +Then: + +1. Sign each database (development, staging, production) with the rewritten contract, before you re-emit: `prisma db sign --db "$DATABASE_URL"`. Its marker still holds the old storage hash, so until it is signed `prisma db migrate` refuses with `MIGRATION.MARKER_MISMATCH` and `prisma db verify` with `CONTRACT.MARKER_MISMATCH`. +2. Re-emit the contract: `prisma contract emit`. +3. On PostgreSQL, plan and apply the migration that adds the new checks, as `postgres-value-object-checks` describes. On MongoDB there is nothing to plan: the collection validators do not change. + +## `value-object-types-from-storage` + +```ts +// before +import type { ValueObjectTypesFromDomain } from '@prisma/orm-mongo/contract/types'; +type Outputs = ValueObjectTypesFromDomain; + +// after +import type { ValueObjectTypesFromStorage } from '@prisma/orm-mongo/contract/types'; +type Outputs = ValueObjectTypesFromStorage; +``` + +## `postgres-value-object-checks` + +For each column that holds a value object, the contract derives a check named `
__value_object_`. It accepts a JSON object with every required member, each of its JSON type, no other key, and for a list column an array of such objects; a nullable column also accepts SQL `NULL` and JSON `null`. For each discriminator column it derives `
__check_`, which accepts only the variants' values. A table whose control policy is not managed gets neither check. + +After you re-emit the contract, `prisma migration plan` writes a migration that adds the checks. Its DDL preview prints each check's expression. Before you run `prisma db migrate`, find the rows that would fail by selecting the rows where the expression is not true, for example: + +```sql +SELECT id, address FROM "public"."user" +WHERE NOT ("address" IS NULL OR jsonb_path_match("address", 'strict … ') IS TRUE); + +SELECT id, type FROM "public"."task" WHERE NOT ("type" IN ('bug', 'feature')); +``` + +Copy the expression from the preview between `CHECK (` and the closing `)`. Fix or delete each row the query returns, for example by removing the extra keys with `UPDATE "public"."user" SET address = address - 'extra' WHERE address ? 'extra'`, then apply the migration. A migration that fails on a row leaves the operations before it applied; fix the rows and run `prisma db migrate` again. + +## `discriminator-typed-by-variant-values` + +```ts +// before +expectTypeOf(task.type).toEqualTypeOf(); + +// after +expectTypeOf(task.type).toEqualTypeOf<'bug' | 'feature'>(); +``` diff --git a/upgrade-instructions/pending/value-objects-in-storage/extension/instructions.md b/upgrade-instructions/pending/value-objects-in-storage/extension/instructions.md new file mode 100644 index 000000000000..d318433fad0b --- /dev/null +++ b/upgrade-instructions/pending/value-objects-in-storage/extension/instructions.md @@ -0,0 +1,52 @@ +--- +changes: + - id: mongo-result-shapes-read-storage + summary: | + `contractModelToMongoResultShape` and `contractFieldToMongoFieldShape` from `@prisma/orm-mongo/query-builder` take the contract's storage, `{ storage: contract.storage }` and `contract.storage`, instead of its value objects. `compileMongoQuery` from `@prisma/orm-mongo/orm` takes the contract's storage as its fifth argument. `MongoValueObjects` is removed. This replaces the rc.14 advice to pass `valueObjects`. + detection: + glob: "**/*.{ts,mts,cts,tsx}" + matches: + - '\bMongoValueObjects\b' + - '\bcontractModelToMongoResultShape\s*\([\s\S]*?\bvalueObjects\b' + - '\bcontractFieldToMongoFieldShape\s*\(' + - '\bcompileMongoQuery\s*\(' + - id: postgres-check-expression-types-move + summary: | + `PostgresCheckKind`, `PostgresCheckExpressionInput` and `PostgresCheckExpressionCandidate` are removed from `@prisma/orm-postgres/target/types`. Use `CheckExpressionInput` and `CheckExpressionCandidate` from `@prisma/orm-postgres/family-contract/check-expressions` (also exported at the same path by `@prisma/orm-mysql` and `@prisma/orm-sqlite`). A candidate's `kind` is `'membership'`, `'elementNotNull'` or the new `'valueObject'`. + detection: + glob: "**/*.{ts,mts,cts,tsx}" + matches: + - '\bPostgresCheck(?:Kind|ExpressionInput|ExpressionCandidate)\b' +--- + +# Value objects are storage entities + +A contract keeps its value objects in storage, at `storage.namespaces..entries.valueObject`, and a domain field names its value object by a storage coordinate. Code that read `domain.namespaces..valueObjects` reads the storage namespaces instead. + +## `mongo-result-shapes-read-storage` + +```ts +// before +contractModelToMongoResultShape(model, { includes: { author: authorShape }, valueObjects }); +contractFieldToMongoFieldShape(field, valueObjects); +compileMongoQuery(collection, state, storageHash, model, valueObjects); + +// after +contractModelToMongoResultShape(model, { includes: { author: authorShape }, storage: contract.storage }); +contractFieldToMongoFieldShape(field, contract.storage); +compileMongoQuery(collection, state, storageHash, model, contract.storage); +``` + +A value object field's shape is a document of its fields when `storage` holds the value object the field's coordinate names. Drop any `MongoValueObjects` type annotation; each parameter takes an object with the contract's storage `namespaces`. + +## `postgres-check-expression-types-move` + +```ts +// before +import type { PostgresCheckExpressionInput } from '@prisma/orm-postgres/target/types'; + +// after +import type { CheckExpressionInput } from '@prisma/orm-postgres/family-contract/check-expressions'; +``` + +`postgresRenderCheckExpressions` stays in `@prisma/orm-postgres/target/types`. Its input also takes `valueObjectField`, the value object a column holds, or `undefined`. From 0bee2193941db06e893783da3a8b164dcebff1ca Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 16:19:58 +0200 Subject: [PATCH 055/104] TML-3537: docs describe value objects as storage entities and the SQL discriminator value set ADR 278 records what was built: the discriminator value set is SQL only, an enum-typed discriminator keeps the enum's value set, only an exact name clash is refused, the value object check is one jsonpath predicate, and value object fields have no stored names. ADR 172 and ADR 178 point to it. Signed-off-by: willbot Signed-off-by: Will Madden --- ... 172 - Contract domain-storage separation.md | 2 ++ .../ADR 178 - Value objects in the contract.md | 6 ++++++ ... a discriminator column names a value set.md | 17 ++++++++--------- .../subsystems/1. Data Contract.md | 12 ++++++------ .../subsystems/10. MongoDB Family.md | 4 ++-- .../subsystems/2. Contract Emitter & Types.md | 4 ++-- skills/prisma-8/references/contract.md | 4 +++- 7 files changed, 29 insertions(+), 20 deletions(-) diff --git a/docs/architecture docs/adrs/ADR 172 - Contract domain-storage separation.md b/docs/architecture docs/adrs/ADR 172 - Contract domain-storage separation.md index b6c1e9f05642..eae15d2a8324 100644 --- a/docs/architecture docs/adrs/ADR 172 - Contract domain-storage separation.md +++ b/docs/architecture docs/adrs/ADR 172 - Contract domain-storage separation.md @@ -139,6 +139,8 @@ The contract has three levels, each serving a different consumer: 2. **Model storage bridge** (`model.storage`) — how domain fields connect to persistence. Sits on the model to preserve co-location. SQL carries field-to-column mappings because field names and column names can differ; Mongo carries the collection name and, in `model.storage.fields`, the key each field is stored under (`{ "field": "" }`, an entry for every field), because field names and stored names can differ too (`id` → `_id`, `createdAt` → `_created_at`). 3. **Top-level storage** (`storage`) — the database schema itself. SQL: every table, every column with its native type, nullability constraint, default, plus indexes and foreign keys. Mongo: collection metadata (indexes, validators). Consumed by migration tooling, schema introspection, and DDL generation. +**Note:** value objects are not at the domain level. They live in storage, at `storage.namespaces..entries.valueObject`, and a domain field that holds one refers to it by its storage coordinate, because their fields are the keys every row stores ([ADR 278](ADR%20278%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md)). + `model.storage` sits on the model (not in a separate section) to preserve co-location. In SQL, field-to-column mappings like `"name": { "column": "display_name" }` need their table context nearby — separating them would leave column references dangling. For a consumer that only cares about the domain, `model.storage` is a clearly scoped block to skip. The separation is logical, not physical. ### Redundancy between levels diff --git a/docs/architecture docs/adrs/ADR 178 - Value objects in the contract.md b/docs/architecture docs/adrs/ADR 178 - Value objects in the contract.md index cff3b2e9fdd7..f8a3a8ba04a1 100644 --- a/docs/architecture docs/adrs/ADR 178 - Value objects in the contract.md +++ b/docs/architecture docs/adrs/ADR 178 - Value objects in the contract.md @@ -2,6 +2,8 @@ ## At a glance +> **Note:** this ADR describes value objects in the domain plane, where they first lived. They are now storage entities ([ADR 278](ADR%20278%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md)); the field shapes below are unchanged. + A User model with scalar fields and value object fields. Value objects are defined in a separate `valueObjects` section — they share the same field descriptor shape (`{ nullable, codecId }`) as models, but the framework makes fundamentally different promises about them. ```json @@ -103,6 +105,8 @@ This extends the framework commitment hierarchy: ### Value objects are a top-level contract section +> **Superseded:** value objects now live in the storage plane, at `storage.namespaces..entries.valueObject`, and a domain field refers to one by its storage coordinate. See [ADR 278](ADR%20278%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md). + Value objects live in a `valueObjects` section alongside `models`. They use the same field shape (`{ nullable, codecId }`) as model fields. Refer to the [At a glance](#at-a-glance) example for the complete structure. **Why not inside `models`?** The `models` section carries an implicit promise: everything here is a full framework citizen. Mixing in value objects conflates two concepts with different guarantees. Consumers iterating `models` (the ORM building its graph, migration tooling planning DDL, validation checking integrity constraints) would need to filter by kind. @@ -168,6 +172,8 @@ You say `boundingBox.topLeft`, not `boundingBox[0]`. Length constraints on homog ### Value objects need no special storage mapping +> **Superseded:** the value object's definition is now a storage entity, so a change to its fields changes the storage hash. See [ADR 278](ADR%20278%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md). + Value object fields use `storage.fields` like any other field. The storage layer maps domain field names to physical locations without knowing whether the field holds a scalar or a composite: **Mongo:** diff --git a/docs/architecture docs/adrs/ADR 278 - Value objects are storage entities, and a discriminator column names a value set.md b/docs/architecture docs/adrs/ADR 278 - Value objects are storage entities, and a discriminator column names a value set.md index 45d3028100ab..4394b7a04850 100644 --- a/docs/architecture docs/adrs/ADR 278 - Value objects are storage entities, and a discriminator column names a value set.md +++ b/docs/architecture docs/adrs/ADR 278 - Value objects are storage entities, and a discriminator column names a value set.md @@ -1,11 +1,9 @@ # ADR 278 — Value objects are storage entities, and a discriminator column names a value set -**Status:** Accepted, implementation pending +**Status:** Accepted **Date:** 2026-10-21 **Domain:** Contract, Migration System -The contract gains the storage entity for value objects and the discriminator value set in a later change of this work; the contract today still keeps value objects in the domain plane. - ## At a glance A model stores a value object in one column on SQL, and as a subdocument on MongoDB: @@ -70,7 +68,7 @@ Renaming `street` changes the storage entity, so it changes `storageHash`. The p ## Decision 1. **A value object is a storage entity.** It lives at `storage.namespaces..entries.valueObject.`, in both families, with the same definition it had in the domain plane: `fields`, each with `nullable`, `type` and, for a list, `many`. A domain field that holds one refers to it by entity coordinate, as a field typed by an enum refers to its value set. A value object field that holds another value object refers to it the same way. -2. **A discriminator column names a value set.** The build writes one value set per base model that has variants, holding each variant's discriminator value as the column stores it, and the base table's discriminator column refers to it, as an enum column refers to its value set ([ADR 157](ADR%20157%20-%20Execution%20enums.md)). +2. **A SQL discriminator column names a value set.** The SQL build writes one value set per base model that has variants, holding each variant's discriminator value as the column stores it, and the base table's discriminator column refers to it, as an enum column refers to its value set ([ADR 157](ADR%20157%20-%20Execution%20enums.md)). MongoDB writes none: its collection validator already pins each variant's discriminator value, so a change to one already changes the storage hash. Both are enforced where the database can enforce them, as an enum's value set is: on a managed Postgres table the build derives a check for the discriminator column and for a column that holds a value object, and on MongoDB the collection validator lists the value object's fields and the discriminator's values. @@ -90,15 +88,15 @@ Storage already holds stored facts the database does not enforce. SQLite derives ## How -**Value objects.** The storage entity keeps the field definition the domain plane used, so every reader of a value object's fields reads the same shape, from the storage plane. Scalar members carry their codec and, when an enum types them, a `valueSet` reference. A member that is a value object refers to its storage entity by coordinate, so a value object that contains itself, such as a `NavItem` whose `children` are `NavItem`s, is one entity that refers to itself. On SQL the column that holds a value object stays a JSON column; on MongoDB the validator is derived from the storage entity, as it is from the definition today. +**Value objects.** The storage entity keeps the field definition the domain plane used, so every reader of a value object's fields reads the same shape, from the storage plane. Its fields have no stored names of their own: each is stored under its own name, in both families. Scalar members carry their codec and, when an enum types them, a `valueSet` reference to the enum's storage value set, since storage never refers to the domain plane. A member that is a value object refers to its storage entity by coordinate, so a value object that contains itself, such as a `NavItem` whose `children` are `NavItem`s, is one entity that refers to itself. On SQL the column that holds a value object stays a JSON column; on MongoDB the validator is derived from the storage entity, as it is from the definition today. -**Discriminators.** The value set is keyed `.`, such as `Task.type`. An enum's name cannot contain a dot, so the key never collides with an enum's value set. A variant stored in its own table changes nothing here: its rows are still told apart by the base table's column. The ORM writes a row only through a variant, so every row's value names a variant, and the derived check (`"type" IN ('bug', 'feature')`) rejects only a value no variant names, as an enum's check does. +**Discriminators.** The value set is keyed `.`, such as `Task.type`, and holds the values sorted, so the order variants are declared in does not change the hash. A discriminator an enum types keeps the enum's value set and check: the column has one value set reference, and the enum already restricts it. Enum value sets are keyed by the enum's name, so only an enum named exactly like a discriminator's key, in the same storage namespace, clashes with it. No authoring surface writes such a name, since PSL names are identifiers and the SQL TypeScript builder has no polymorphism; the build refuses it all the same, naming both and saying to rename the enum. Enum names are not otherwise restricted. A variant stored in its own table changes nothing here: its rows are still told apart by the base table's column. The ORM writes a row only through a variant, so every row's value names a variant, and the derived check (`"type" IN ('bug', 'feature')`) rejects only a value no variant names, as an enum's check does. ```json "type": { "codecId": "pg/text@1", "dataType": "pg/text", "nullable": false, "valueSet": { "entityKind": "valueSet", "entityName": "Task.type", "namespaceId": "public", "plane": "storage" } } ``` -**Enforcement on Postgres.** A column that holds a value object gets a derived check, as an enum column does, named `
__value_object`. It is one strict `jsonpath` predicate, written so that an unknown result fails: +**Enforcement on Postgres.** On a managed table, a column that holds a value object gets a derived check, as an enum column does, named `
__value_object`. It is one strict `jsonpath` predicate, written so that an unknown result fails, rather than the key-set operators `?&` and `-`: ```sql jsonb_path_match("address", 'strict $.type() == "object" @@ -106,7 +104,7 @@ jsonb_path_match("address", 'strict $.type() == "object" && !exists($.keyvalue() ? (@.key != "street"))') IS TRUE ``` -The value must be an object, or for a list an array of objects. Each required member must be present; a nullable member may be absent or JSON `null`; no other key may be. A scalar member must have the JSON type its codec writes, an enum member must hold a value of its value set, and a nested value object is checked the same way, inline. A nullable column also accepts SQL `NULL` and JSON `null`. Members are read through `keyvalue()`, so an absent member is never an error, and strict mode keeps a comparison from unwrapping an array. A value object that contains itself is checked in full down to where it refers back into itself, and any object is accepted below, as the MongoDB validator does. A member whose codec has no known JSON type is checked for presence only. The check is opaque and content-named ([ADR 244](ADR%20244%20-%20Check%20constraints%20are%20opaque%20wire-named%20expressions.md)), so any change to the value object changes the check's name, and the diff drops the old check and adds the new one. +The value must be an object, or for a list an array of objects. Each required member must be present; a nullable member may be absent or JSON `null`; no other key may be. A scalar member must have the JSON type its codec writes, an enum member must hold a value of its value set, and a nested value object is checked the same way, inline. A nullable column also accepts SQL `NULL` and JSON `null`. Members are read through `keyvalue()`, so an absent member is never an error, and strict mode keeps a comparison from unwrapping an array. A value object that contains itself is checked in full down to where it refers back into itself, and any object is accepted below, as the MongoDB validator does. A `Decimal` member must be a JSON string, as the SQL ORM writes it. A member whose codec has no known JSON type is checked to be present and, when it is required, not JSON `null`; a JSON member may hold any value. The check is opaque and content-named ([ADR 244](ADR%20244%20-%20Check%20constraints%20are%20opaque%20wire-named%20expressions.md)), so any change to the value object changes the check's name, and the diff drops the old check and adds the new one. **Why every change is an operation.** The storage hash identifies a database state. Two different hashes are two different database states, so the migration between them does something to the database. Because the value object and the discriminator values are enforced, a change to either is a change to a check or a validator, which is an operation. A plan that changes the storage hash and produces no operation is therefore unexpected: it means the database cannot tell the two states apart. Such cases will still arise, so the system handles them rather than refusing: `migration plan` writes the package with no operations and says so, naming what changed, and `db migrate` applies it by moving the marker to the new hash. @@ -117,7 +115,8 @@ The value must be an object, or for a list an array of objects. Each required me - Any change to a value object, and any change to a variant's discriminator value, changes `storageHash` and the enforcing check or validator. The planner asks about the ones that lose data and plans their rewrite, as for any other storage change. - A removed variant that has no columns of its own is visible to the planner through the value set. - Whether the rows are rewritten, and how efficiently, is a separate decision. This one only makes the change visible. -- `valueObjects` is no longer a domain namespace key. +- `valueObjects` is no longer a domain namespace key. A contract that keeps it there is refused, with a message saying to re-emit it. +- Moving the value objects changes the storage hash of every contract that has them, including a project's committed migration snapshots. The upgrade rewrites the snapshots, renames their directories and updates the migrations that name them; the databases are then signed with the new hashes. On Postgres, the next migration adds the derived checks, and a row that does not fit, such as one whose value object has a key it does not declare, fails it. ## Alternatives considered diff --git a/docs/architecture docs/subsystems/1. Data Contract.md b/docs/architecture docs/subsystems/1. Data Contract.md index 140c4dc5ecb8..b8906668719d 100644 --- a/docs/architecture docs/subsystems/1. Data Contract.md +++ b/docs/architecture docs/subsystems/1. Data Contract.md @@ -138,8 +138,7 @@ At a high level. The contract encodes what exists (models, storage), how it can "fields": { "id": { "column": "id" }, "userId": { "column": "user_id" } } } } - }, - "valueObjects": {} + } } } }, @@ -183,8 +182,8 @@ At a high level. The contract encodes what exists (models, storage), how it can **Notes:** - **roots** map ORM accessor names to a model coordinate — an object pair `{ namespace, model }` (e.g., `"users": { "namespace": "public", "model": "User" }`) -- **domain** holds the application concepts the user defines, under `domain.namespaces..{ models, valueObjects }`. A model carries fields with `{ nullable, codecId }`, relations whose `to` is an object pair `{ namespace, model }`, and a `storage` bridge to the physical layer. The framework domain plane has **no** `types` member — codec aliases live on the SQL storage plane. -- **storage** holds the family-owned persistence projection. Each namespace carries an open `entries` dictionary keyed by entity kind — `storage.namespaces..entries[entityKind][entityName]` — plus plane-level metadata (`storageHash`). Built-in entity kinds are `table` (SQL family), `collection` (Mongo family) and `valueSet` (both families); pack-contributed kinds (e.g. `native_enum` for Postgres enum types) register via the serializer registry. Cross-namespace foreign-key references are object pairs `{ namespaceId, tableName, columns }`. +- **domain** holds the application concepts the user defines, under `domain.namespaces..models`. A model carries fields with `{ nullable, codecId }`, relations whose `to` is an object pair `{ namespace, model }`, and a `storage` bridge to the physical layer. The framework domain plane has **no** `types` member — codec aliases live on the SQL storage plane. +- **storage** holds the family-owned persistence projection. Each namespace carries an open `entries` dictionary keyed by entity kind — `storage.namespaces..entries[entityKind][entityName]` — plus plane-level metadata (`storageHash`). Built-in entity kinds are `table` (SQL family), `collection` (Mongo family), and `valueSet` and `valueObject` (both families); pack-contributed kinds (e.g. `native_enum` for Postgres enum types) register via the serializer registry. Cross-namespace foreign-key references are object pairs `{ namespaceId, tableName, columns }`. - **capabilities** surface target features that affect compilation/lowering (e.g., lateral) - **Types (.d.ts)** expose branded TS types for contract type identifiers; at runtime, codecs provided by packs/adapters handle encode/decode ([ADR 114 — Extension codecs & branded types](../adrs/ADR%20114%20-%20Extension%20codecs%20&%20branded%20types.md)) - Authoring provenance (paths/source IDs/spans) is **diagnostics-only**; it is not embedded in canonical artifacts @@ -230,7 +229,8 @@ The contract's domain-level structure generalizes across database families. The - **`model.fields`** — domain-level field descriptors as `Record`. Both `nullable` and `codecId` are domain concepts. "Family-agnostic" describes the structure, not the values — codec ID values differ across families but the structure is identical. See [ADR 172](../adrs/ADR%20172%20-%20Contract%20domain-storage%20separation.md). - **Polymorphism** — `discriminator` + `variants` on the base model, `base` on each variant. Persistence strategy (STI vs MTI) is emergent from storage mappings. Uses specialization/generalization terminology. See [ADR 173](../adrs/ADR%20173%20-%20Polymorphism%20via%20discriminator%20and%20variants.md). - **Model ownership** — an owned model declares `"owner": "ParentModel"`, a domain fact about aggregate membership. Its data is co-located with the owner's storage (embedded document in Mongo, JSONB column in SQL). The parent's `storage.relations` maps the relation to its physical location. Relations to owned models are plain graph edges. See [ADR 177](../adrs/ADR%20177%20-%20Ownership%20replaces%20relation%20strategy.md). -- **Value objects** — typed data structures without identity, lifecycle, or framework capabilities (no query entry points, no hooks, no referential integrity). Defined under `domain.namespaces..valueObjects` alongside `models`, using the same `{ nullable, codecId }` field shape. Fields reference value objects via `type` (mutually exclusive with `codecId`); unions of types use `union`. Value object support in SQL is capability-gated (requires JSONB-compatible columns). See [ADR 178](../adrs/ADR%20178%20-%20Value%20objects%20in%20the%20contract.md), [ADR 179](../adrs/ADR%20179%20-%20Union%20field%20types.md). +- **Value objects** — typed data structures without identity, lifecycle, or framework capabilities (no query entry points, no hooks, no referential integrity). They are storage entities, at `storage.namespaces..entries.valueObject.` in both families, because their fields are the keys every row or subdocument stores, so a change to them changes the storage hash. A value object's `fields` use the field shape models use; a member typed by an enum refers to the enum's storage value set. A domain field that holds one has the type `{ kind: 'valueObject', plane: 'storage', namespaceId, entityKind: 'valueObject', entityName }`, the value object's storage coordinate, and a member that holds another value object refers to it the same way. Readers resolve the coordinate with `valueObjectAt` from `@internal/contract`. A contract that keeps `valueObjects` in a domain namespace, or refers to a value object by `name`, is refused with a message saying to re-emit it. Value object support in SQL is capability-gated (requires JSONB-compatible columns). See [ADR 278](../adrs/ADR%20278%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md), [ADR 178](../adrs/ADR%20178%20-%20Value%20objects%20in%20the%20contract.md), [ADR 179](../adrs/ADR%20179%20-%20Union%20field%20types.md). +- **Discriminator value sets (SQL)** — the SQL build writes one value set per base model with variants, keyed `.` in the base table's namespace and holding the variants' values, sorted; the discriminator column refers to it. A discriminator column an enum types keeps the enum's value set. An enum with exactly that name in the same namespace is refused. MongoDB writes no such value set: its collection validator already pins each variant's value. See [ADR 278](../adrs/ADR%20278%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md). ### Core entities vs pack extensions @@ -321,7 +321,7 @@ A `pg/int4@1` enum `Int4Level` with members `Low = 1` and `High = 10`, on column A list column gets `<@` containment instead of `IN`. In the same fixture, `levels FloatMixed[]`, whose members are written `1.5` and `NaN` and stored as `1.5` and `"NaN"`, gets `array_remove("levels", NULL) <@ '{1.5,"NaN"}'`, plus the derived check `array_position("levels", NULL) IS NULL` because its elements are not nullable. -On a `managed` Postgres table two more columns derive checks ([ADR 278](../adrs/ADR%20278%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md)). A discriminator column with no enum gets a membership check, `
__check`, holding the values of its `.` value set. A column that holds a value object gets a check named `
__value_object`: one strict `jsonpath` predicate that the column value is an object (or, for a list, an array of objects) with each required member present, no other key, each scalar member of its JSON type, each enum member one of its value set's values, and each nested value object checked the same way. A nullable column accepts SQL `NULL` and JSON `null`; the ORM writes SQL `NULL` for an absent value. A member whose codec has no known JSON type, and a `Json` member, is checked for presence only. Below the point where a value object refers back into itself, any object is accepted, as the MongoDB validator does. +On a `managed` Postgres table two more columns derive checks ([ADR 278](../adrs/ADR%20278%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md)). A discriminator column with no enum gets a membership check, `
__check`, holding the values of its `.` value set. A column that holds a value object gets a check named `
__value_object`: one strict `jsonpath` predicate that the column value is an object (or, for a list, an array of objects) with each required member present, no other key, each scalar member of its JSON type, each enum member one of its value set's values, and each nested value object checked the same way. A nullable column accepts SQL `NULL` and JSON `null`; the ORM writes SQL `NULL` for an absent value. A `Decimal` member must be a JSON string, as the ORM writes it. A required member whose codec has no known JSON type must be present and not JSON `null`; a `Json` member may hold any value. Below the point where a value object refers back into itself, any object is accepted, as the MongoDB validator does. `contract infer` produces no value objects and no polymorphism, so on a live table with these checks it prints each one as an authored `@@check` with its exact name in `map:`, as it does for an enum membership check. diff --git a/docs/architecture docs/subsystems/10. MongoDB Family.md b/docs/architecture docs/subsystems/10. MongoDB Family.md index be183b947b72..c9ce54da9187 100644 --- a/docs/architecture docs/subsystems/10. MongoDB Family.md +++ b/docs/architecture docs/subsystems/10. MongoDB Family.md @@ -86,7 +86,7 @@ A few things to notice before reading further: - **`roots`** declares which models are ORM entry points — `db.users` and `db.posts`. Comment is not in `roots` because it's only reachable through Post. - **`owner: "Post"`** on the Comment model means Comment belongs to Post's aggregate. Its data lives inside Post documents. The relation from Post to Comment (`{ "to": "Comment", "cardinality": "1:N" }`) is a plain graph edge — no storage annotation. The physical location is in Post's `storage.relations`: `"comments": { "field": "comments" }`. -- **Comment has no `collection`**, because it doesn't own one. Its data lives within Post's storage. Note that Comment is still an entity with its own id — it has identity even when embedded. Value objects (data without identity, like an address or a money amount) are a separate concept with their own contract section — see [ADR 178](../adrs/ADR%20178%20-%20Value%20objects%20in%20the%20contract.md). +- **Comment has no `collection`**, because it doesn't own one. Its data lives within Post's storage. Note that Comment is still an entity with its own id — it has identity even when embedded. Value objects (data without identity, like an address or a money amount) are a separate concept: storage entities at `storage.namespaces..entries.valueObject`, which a field refers to by storage coordinate — see [ADR 178](../adrs/ADR%20178%20-%20Value%20objects%20in%20the%20contract.md) and [ADR 278](../adrs/ADR%20278%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md). - **Field structure is identical to SQL.** Each field is `{ nullable, codecId }`. A Mongo field says `"mongo/string@1"` where SQL would say `"pg/text@1"`, but the shape is the same. - **Fields keep their own names; storage records where each is stored.** As SQL records each field's column in `model.storage.fields`, a Mongo model records each field's stored name there: `{ "": { "field": "" } }`, an entry for every field. `id ObjectId @id @map("_id")` in PSL, or `field.objectId().mongo({ field: '_id' })` in the TypeScript builder, gives the field `id` stored as `_id`. A field without `@map` is stored under its own name. Relations and the discriminator name fields; indexes, validators and execution defaults name stored names. An embed relation is stored under its `storage.relations` entry, or its own name when it has none. A value object has no storage block: its fields are stored under their own names, and PSL and the TypeScript builder refuse a stored name on them. `validateMongoStorage` checks that every field has an entry and that no two fields or embed relations of a model, with its base for a variant, share a stored name. The helpers in `@internal/mongo-contract` (`ownStoredNamesOf`, `ownStoredNameOf`, `ownFieldStoredAs`, `ownEmbedStoredNamesOf`, `ownDocumentIdFieldOf`, `storedNamesOf` with inherited fields, `modelAndBasesOf`) and the types `OwnStoredNameOf` and `OwnFieldStoredAs` translate between the two, and every reader goes through them: the validator derivation, the planner, the ORM and the pipeline builder. @@ -126,7 +126,7 @@ A referenced relation (to a model with its own collection) carries `on` join det This also introduces the concept of **aggregate roots**: models that own their own collection and can be queried directly. In SQL, every model has its own table, so this distinction is invisible. In MongoDB, some models are owned by a parent and can only be reached through it. The contract makes this explicit with a top-level `roots` section that maps ORM accessor names (like `db.users`) to model names (like `User`). If a model isn't in `roots`, it's not an independent entry point. See [ADR 174](../adrs/ADR%20174%20-%20Aggregate%20roots%20and%20relation%20strategies.md). -Each model has exactly one canonical storage location — either its own collection (aggregate root) or inside a parent's document (owned). A model can't be both. When developers need data from one aggregate root available inside another for read performance (the MongoDB "extended reference" pattern), the embedded copy is a denormalized snapshot — a value object — not the model itself. The canonical Post lives in the `posts` collection; a `{ title, slug }` summary embedded in a User document is a different thing, even though it shares some fields. Value objects are defined in the contract's `valueObjects` section — see [ADR 178](../adrs/ADR%20178%20-%20Value%20objects%20in%20the%20contract.md). See also [design principle #5](#design-principles). +Each model has exactly one canonical storage location — either its own collection (aggregate root) or inside a parent's document (owned). A model can't be both. When developers need data from one aggregate root available inside another for read performance (the MongoDB "extended reference" pattern), the embedded copy is a denormalized snapshot — a value object — not the model itself. The canonical Post lives in the `posts` collection; a `{ title, slug }` summary embedded in a User document is a different thing, even though it shares some fields. Value objects are storage entities, at `storage.namespaces..entries.valueObject`, and the collection validator is derived from them — see [ADR 178](../adrs/ADR%20178%20-%20Value%20objects%20in%20the%20contract.md) and [ADR 278](../adrs/ADR%20278%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md). A change to a value object's fields changes the storage hash and the validator. MongoDB writes no discriminator value set: the validator already pins each variant's discriminator value. See also [design principle #5](#design-principles). In the ORM, owned relations auto-project into the parent row — no separate query is issued. Referenced relations use `$lookup` pipeline stages or multi-query stitching, similar to how SQL handles joins. diff --git a/docs/architecture docs/subsystems/2. Contract Emitter & Types.md b/docs/architecture docs/subsystems/2. Contract Emitter & Types.md index 211173eaf814..399b53361956 100644 --- a/docs/architecture docs/subsystems/2. Contract Emitter & Types.md +++ b/docs/architecture docs/subsystems/2. Contract Emitter & Types.md @@ -259,7 +259,7 @@ type ContractBase = Omit< readonly target: 'postgres'; readonly targetFamily: 'sql'; readonly roots: { readonly users: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' } }; - readonly domain: { readonly namespaces: { readonly public: { readonly models: { readonly User: { /* … fields / relations / storage bridge … */ } }; readonly valueObjects: {} } } }; + readonly domain: { readonly namespaces: { readonly public: { readonly models: { readonly User: { /* … fields / relations / storage bridge … */ } } } } }; readonly capabilities: { /* … */ }; readonly profileHash: ProfileHash; }; @@ -269,7 +269,7 @@ export type Contract = ContractWithTypeMaps; export type Namespaces = Contract['storage']['namespaces']; ``` -The emitter writes the field type maps of every family, and the SQL family its storage column type maps, as applications of `FieldTypesFromDomain` (`@internal/contract/types`) and `ColumnTypesFromStorage` (`@internal/sql-contract/types`) to the emitted contract's own shape. A contract written with `defineContract`, in either family, applies the same generics to its built shape, so the two paths share one set of rules: an override first, then, on the output side, the pack's type for a field with type parameters, then the codec's output or input type, an object of a value object's own field types, or the union of a union field's members, then list and dictionary wrapping and nullability. Each value object's `Output` and `Input` alias is an entry of `ValueObjectTypesFromDomain`. The emitter writes only what a type cannot compute: `FieldTypeOverrides` and `ColumnTypeOverrides` hold the literal union of a value set (each value rendered by its codec's `renderValueLiteral`; a codec that renders no literal, such as one that sets `keepsWrittenForm`, leaves the codec's type) and text a codec renders with `renderOutputType`. Each enum member's type comes from the same `renderValueLiteral`, or is the codec's output type when the codec renders no literal. `Models` members index `FieldOutputTypes`. +The emitter writes the field type maps of every family, and the SQL family its storage column type maps, as applications of `FieldTypesFromDomain` (`@internal/contract/types`) and `ColumnTypesFromStorage` (`@internal/sql-contract/types`) to the emitted contract's own shape. A contract written with `defineContract`, in either family, applies the same generics to its built shape, so the two paths share one set of rules: an override first, then, on the output side, the pack's type for a field with type parameters, then the codec's output or input type, an object of a value object's own field types, or the union of a union field's members, then list and dictionary wrapping and nullability. Value objects are storage entities ([ADR 278](../adrs/ADR%20278%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md)): `contract.d.ts` writes them in the storage namespaces type, under `entries.valueObject`, and types a field that holds one by its storage coordinate. Each value object's `Output` and `Input` alias is an entry of `ValueObjectTypesFromStorage`, applied to `ContractBase['storage']['namespaces']`. `FieldTypesFromDomain` takes the storage namespaces as an optional sixth type parameter, where it finds the value object a field's coordinate names; the emitter passes it only when the contract has value objects, so a contract without them emits the same text as before. A SQL discriminator field is typed by the literal union of its value set, through `FieldTypeOverrides`. The emitter writes only what a type cannot compute: `FieldTypeOverrides` and `ColumnTypeOverrides` hold the literal union of a value set (each value rendered by its codec's `renderValueLiteral`; a codec that renders no literal, such as one that sets `keepsWrittenForm`, leaves the codec's type) and text a codec renders with `renderOutputType`. Each enum member's type comes from the same `renderValueLiteral`, or is the codec's output type when the codec renders no literal. `Models` members index `FieldOutputTypes`. Both planes are namespaced: storage entities live under `storage.namespaces..entries[entityKind][entityName]` (e.g. `entries['table']['user']`) and models under `domain.namespaces..models` (see [Data Contract § Two planes, namespaced](1.%20Data%20Contract.md#two-planes-namespaced) and [ADR 221](../adrs/ADR%20221%20-%20Contract%20IR%20two%20planes%20with%20uniform%20entity%20coordinate%20and%20pack-contributed%20entity%20kinds.md)). Consumers read a namespace's models directly — `Contract['domain']['namespaces']['public']['models']` (or `[keyof …['namespaces']]` for the sole-namespace case). See [ADR 223 — Target-owned default namespace](../adrs/ADR%20223%20-%20Target-owned%20default%20namespace.md) for the runtime/emitter fail-loud split. diff --git a/skills/prisma-8/references/contract.md b/skills/prisma-8/references/contract.md index d6b720eae24a..50b7830642ec 100644 --- a/skills/prisma-8/references/contract.md +++ b/skills/prisma-8/references/contract.md @@ -316,7 +316,9 @@ model User { } ``` -Emitted `contract.json` carries `domain.namespaces..valueObjects.Address` with its field descriptors, and the `address` column lands as `codecId: "pg/jsonb@1"` / `dataType: "pg/jsonb"` in `storage`. +Emitted `contract.json` carries the value object in storage, at `storage.namespaces..entries.valueObject.Address` with its field descriptors, and the domain field `address` refers to it by coordinate: `{ "kind": "valueObject", "plane": "storage", "namespaceId": "public", "entityKind": "valueObject", "entityName": "Address" }`. The `address` column lands as `codecId: "pg/jsonb@1"` / `dataType: "pg/jsonb"`. Because the value object is in storage, renaming, adding or removing one of its fields changes the storage hash, and `prisma migration plan` writes a migration for it. + +On Postgres, a managed table gets a derived check on each value object column, `
__value_object_`: the value must be an object (an array of them for a list) with every required member, each of its JSON type, and no other key. A nullable column also accepts `NULL`. On SQL, a polymorphic base's discriminator column refers to a value set `.` holding the variants' values, and Postgres derives a check from it, as for an enum. Canonical worked example: `examples/prisma-8-demo/src/prisma/contract.prisma`. From ed4d6b0df80f0f93add5c67c506da02a156e22e9 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 16:33:53 +0200 Subject: [PATCH 056/104] TML-3537: dispatch brief for journeys, the ADR number and the PR text Signed-off-by: willbot Signed-off-by: Will Madden --- .../dispatches/17-journeys-docs-pr-text.md | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 projects/migration-statements/slices/remaining-nouns/dispatches/17-journeys-docs-pr-text.md diff --git a/projects/migration-statements/slices/remaining-nouns/dispatches/17-journeys-docs-pr-text.md b/projects/migration-statements/slices/remaining-nouns/dispatches/17-journeys-docs-pr-text.md new file mode 100644 index 000000000000..212b768a8ac8 --- /dev/null +++ b/projects/migration-statements/slices/remaining-nouns/dispatches/17-journeys-docs-pr-text.md @@ -0,0 +1,77 @@ +# Dispatch 17: journeys, the ADR number, loose ends, and the PR text + +**Plan:** [`../plan-pr3.md`](../plan-pr3.md) · **Spec:** [`../spec.md`](../spec.md) "Done when" · **Builds on:** dispatch 16 · **Branch:** `tml-3537-value-objects-storage` + +## Outcome + +PR 3 is ready for the two-reviewer `/drive-code-review`. Its journeys show the new behaviour from the CLI, the ADR has a free number, and the PR text is drafted. + +### 1. The ADR number + +Main already has an ADR 278 ("Each database's packages live in one folder"), and open PRs use 307 and 308. Do these: +- Rename ours to **ADR 309**: `docs/architecture docs/adrs/ADR 309 - Value objects are storage entities, and a discriminator column names a value set.md`. +- Update every reference to our ADR across code comments, docs, the skill, the upgrade fragments and the project files. That is about 44 references. Leave references to the other ADR 278 as they are. +- Add the index row in `docs/architecture docs/ADR-INDEX.md`. +- Before you commit, check again that 309 is free on `origin/main` and in open PRs (`gh pr list --repo prisma/orm --state open --json files`). + +### 2. Journeys + +Add CLI journeys under `test/integration/test/cli-journeys/`, following the existing journeys and their helpers. Each journey runs through both `migration plan` (then `migrate`) and `db update`, and a later plan is empty (spec "Done when"). Run them on Postgres (PGlite): +- **A value object column on a managed table.** + - `migration plan` writes the value object check. + - `migrate` applies it. + - A row the ORM writes is accepted, and a row with an extra key is refused. + - `db verify` is clean. +- **Changing a value object.** Add a member, and then make a member nullable. Each one is a storage change. The plan drops the old check and adds the new one. On a table with rows that fit the new shape, the apply works. +- **A row that does not fit.** Adding the check to a table that already holds a bad row fails. The error names the constraint and changes nothing. +- **Adding a variant.** The discriminator check is replaced, and the new variant's value is accepted. +- **A storage change with no operation.** Change a value object used only by an external table's column. `migration plan` writes a migration with no operations, and `migrate` moves the marker. + +On MongoDB (mongodb-memory-server), add one journey: a value object change updates the collection validator, as before this PR. This shows that moving value objects into storage did not break Mongo. + +### 3. Loose ends from dispatch 16 + +- `test/integration/test/sql-orm-client/fixtures/contract.ts` patches a built contract to add `Address` to storage. That leaves its `storageHash` stale. Author the value object through a user-facing surface instead (`.agents/rules/no-contract-data-patching-in-tests.mdc`). Or, if no SQL TypeScript surface exists, use the emitted fixture. +- The upgrade script does not write discriminator value set types into snapshot `contract.d.ts` files. Find out whether anything reads them: typecheck an upgraded copy of `prisma-8-demo` under `wip/d16-upgrade/`. If something does, write them. If nothing does, say so in the app fragment. +- On the upgraded copy, `migration plan` asked for `--from`. Find out why. If the project's `db` ref or graph is left inconsistent by the script, fix the script. If the request is expected, say why in the fragment. +- The `mongo-demo` and `retail-store` chains fail `db migrate` on mongodb-memory-server. Check out `origin/main` into a new worktree under `wip/`, and run the same commands there to find out whether this happens on main too. Report the result with the commands. Do not fix it here. + +### 4. The PR text + +Draft the PR title and description at `wip/pr3-body.md`. Follow `skills-contrib/create-pr/SKILL.md`: +- The title is "TML-3537: " followed by a plain sentence saying what a user can now do or see. No conventional prefix. +- Open with a concrete example: a PSL value object, the `contract.json` before and after, and the Postgres check it derives. +- Then build the explanation step by step. +- State each decision before its alternatives. Put the alternatives last. +- List the upgrade steps. +- Close with the line `Agent: radagast-20` and no AI attribution line. +- Never hard-wrap the prose. + +## Rules + +- Never run `pnpm test:integration`, `pnpm test:e2e` or `pnpm test:all`. Run only named files inside `test/integration`, with `pnpm test `, and journeys with `pnpm test:journeys `. +- Use `mise exec --` for node and pnpm. Never `npx`. +- Do not write to `/tmp` or any system temp directory. Save slow output under `wip/` and read the file. +- Never use bare `git stash`. +- Write tests first. +- No comments where the code can say it. No bare `as` casts in production code. No `any`. +- Never hard-wrap Markdown prose. ADRs carry no ticket ids or dispatch names. +- Commit with `git commit -s --trailer "Signed-off-by: Will Madden "`. No AI attribution lines. Stage files explicitly. +- Do not push. Do not open a PR. + +## Done when + +- The new journeys pass. +- The enum value journeys, the namespace journeys and the plans-with-no-operations journeys still pass. +- `fixtures:check`, `lint:deps`, `lint:ratchets`, `check:error-reference` and `check:upgrade-coverage --mode pr --prev $(git merge-base HEAD origin/main) --head HEAD` pass. +- Typecheck, lint and `test --coverage` pass in every touched package. + +## Report + +Plain English, short sentences. Cover: +- the ADR renumbering; +- each journey and what it shows; +- each loose end and its answer; +- where the PR text is; +- the checks; +- the commits. From 97552843dc97a4f3754e7d7b35e75f4f55cc32e8 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 16:36:28 +0200 Subject: [PATCH 057/104] TML-3537: the value objects ADR is ADR 309, since main has an ADR 278 Signed-off-by: willbot Signed-off-by: Will Madden --- docs/architecture docs/ADR-INDEX.md | 2 ++ .../adrs/ADR 172 - Contract domain-storage separation.md | 2 +- .../adrs/ADR 178 - Value objects in the contract.md | 6 +++--- ...ibutes, and a type alias is expanded into each column.md | 2 +- ...tities, and a discriminator column names a value set.md} | 2 +- docs/architecture docs/subsystems/1. Data Contract.md | 6 +++--- docs/architecture docs/subsystems/10. MongoDB Family.md | 4 ++-- .../subsystems/2. Contract Emitter & Types.md | 2 +- .../0-foundation/contract/src/domain-envelope.ts | 2 +- .../1-framework/0-foundation/contract/src/domain-types.ts | 4 ++-- .../0-foundation/contract/src/validate-domain.ts | 2 +- .../1-foundation/mongo-contract/src/contract-schema.ts | 2 +- .../mongo-contract/src/ir/mongo-value-object.ts | 2 +- .../1-core/contract/src/ir/storage-entry-schemas.ts | 2 +- .../1-core/contract/src/ir/storage-value-object.ts | 2 +- .../2-authoring/contract-psl/src/interpreter.ts | 2 +- .../2-authoring/contract-ts/src/build-contract.ts | 2 +- .../2-authoring/contract-ts/src/contract-definition.ts | 2 +- .../dispatches/10-value-object-storage-shape.md | 6 +++--- .../dispatches/11-plans-with-no-operations.md | 2 +- .../dispatches/12-authoring-writes-storage-value-objects.md | 2 +- .../dispatches/13-emit-and-types-read-storage.md | 2 +- .../dispatches/14-runtime-and-tools-read-storage.md | 2 +- .../dispatches/15-postgres-derived-checks.md | 2 +- .../dispatches/16-re-emit-migrations-upgrade-docs.md | 4 ++-- .../migration-statements/slices/remaining-nouns/plan-pr3.md | 6 +++--- .../migration-statements/slices/remaining-nouns/plan.md | 4 ++-- .../migration-statements/slices/remaining-nouns/spec.md | 4 ++-- 28 files changed, 42 insertions(+), 40 deletions(-) rename docs/architecture docs/adrs/{ADR 278 - Value objects are storage entities, and a discriminator column names a value set.md => ADR 309 - Value objects are storage entities, and a discriminator column names a value set.md} (99%) diff --git a/docs/architecture docs/ADR-INDEX.md b/docs/architecture docs/ADR-INDEX.md index 99293733b055..52874b4fbf54 100644 --- a/docs/architecture docs/ADR-INDEX.md +++ b/docs/architecture docs/ADR-INDEX.md @@ -79,6 +79,7 @@ Open this list before you propose a new feature or surface, or judge a feature r | 272 | Packages contribute collection methods computed from a model | **Accepted.** A package reads each model of the contract (target, name, fields, relations, storage table) and contributes collection methods under names it chooses; every SQL ORM collection offers them under the reserved member `using`, such as `db.orm.public.Post.using.post_search(q)` and `db.orm.public.Post.using.embedding_nearest(v)`. The type half registers in `CollectionMethodRegistry` by declaration merging through the package's own entry points, never through the emitted `contract.d.ts`; the run-time half goes on a target or extension descriptor. Two contributions that give a model the same name are a compile error and an `ORM.ARGUMENT_INVALID` at `orm()` | [ADR 272 - Packages contribute collection methods computed from a model.md](adrs/ADR%20272%20-%20Packages%20contribute%20collection%20methods%20computed%20from%20a%20model.md) | | 273 | A column of a type no data type claims is a raw column | A column whose database type no data type claims has data type `sql/raw`, whose parameter `sql` is the type exactly as the catalog prints it, and codec `sql/raw@1`, which passes values through typed `unknown`; migrations write the text into DDL and `db verify` compares it exactly; a Prisma 8 field declares one with ``Raw(sql`...`)``, and its default is a `sql` expression; the Prisma 7 reader reads `Unsupported("...")` as a raw column with no field | [ADR 273 - A column of a type no data type claims is a raw column.md](adrs/ADR%20273%20-%20A%20column%20of%20a%20type%20no%20data%20type%20claims%20is%20a%20raw%20column.md) | | 275 | Client generators are field attributes, and a type alias is expanded into each column | **Proposed.** `@onCreate(fn())` and `@onUpdate(fn())` set the client's generators, resolved from the column's codec through a generator function registry the stack assembles, with params from the column's type parameters; `@default` means a database default only; a `types {}` alias may carry `@default`, `@onCreate` and `@onUpdate`, is expanded into each column, and is never stored, so `storage.types` keeps only types a contract space registers; field presets are removed from SQL and Mongo, PSL and the TypeScript builder, and targets contribute aliases such as `temporal.UpdatedAt` | [ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md](adrs/ADR%20275%20-%20Client%20generators%20are%20field%20attributes%2C%20and%20a%20type%20alias%20is%20expanded%20into%20each%20column.md) | +| 309 | Value objects are storage entities, and a discriminator column names a value set | **Accepted.** A value object lives at `storage.namespaces..entries.valueObject.` in both families, and a domain field refers to it by storage coordinate; a SQL discriminator column refers to a value set keyed `.`; on a managed Postgres table both derive a check; a change to either changes the storage hash, and a plan with no operations is written and applied. Partially supersedes ADR 178. | [ADR 309 - Value objects are storage entities, and a discriminator column names a value set.md](adrs/ADR%20309%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md) | ## Query System @@ -258,6 +259,7 @@ Requests answered with a no, with what to use instead. Every Accepted row is on - **ADR 157** is partially superseded by its own October 12, 2026 amendment: the idea stands, and the amendment replaces how the contract realizes it - **ADR 170** is partially superseded by ADR 275: its field-preset half; type constructors and namespacing stand - **ADR 246** is superseded by ADR 275, except its two omission rules for `executionDefaults` and `typeParams` +- **ADR 178** is partially superseded by ADR 309: value objects are storage entities, not a domain section - **ADRs 104-118** form the core extension system architecture (decorators, attributes, capabilities, packs) - **ADRs 126-127** introduce PSL top-level blocks and views as composable extensions - **ADRs 096-100** cover the no-emit workflow for TypeScript-authored contracts diff --git a/docs/architecture docs/adrs/ADR 172 - Contract domain-storage separation.md b/docs/architecture docs/adrs/ADR 172 - Contract domain-storage separation.md index eae15d2a8324..66b70dc804fa 100644 --- a/docs/architecture docs/adrs/ADR 172 - Contract domain-storage separation.md +++ b/docs/architecture docs/adrs/ADR 172 - Contract domain-storage separation.md @@ -139,7 +139,7 @@ The contract has three levels, each serving a different consumer: 2. **Model storage bridge** (`model.storage`) — how domain fields connect to persistence. Sits on the model to preserve co-location. SQL carries field-to-column mappings because field names and column names can differ; Mongo carries the collection name and, in `model.storage.fields`, the key each field is stored under (`{ "field": "" }`, an entry for every field), because field names and stored names can differ too (`id` → `_id`, `createdAt` → `_created_at`). 3. **Top-level storage** (`storage`) — the database schema itself. SQL: every table, every column with its native type, nullability constraint, default, plus indexes and foreign keys. Mongo: collection metadata (indexes, validators). Consumed by migration tooling, schema introspection, and DDL generation. -**Note:** value objects are not at the domain level. They live in storage, at `storage.namespaces..entries.valueObject`, and a domain field that holds one refers to it by its storage coordinate, because their fields are the keys every row stores ([ADR 278](ADR%20278%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md)). +**Note:** value objects are not at the domain level. They live in storage, at `storage.namespaces..entries.valueObject`, and a domain field that holds one refers to it by its storage coordinate, because their fields are the keys every row stores ([ADR 309](ADR%20309%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md)). `model.storage` sits on the model (not in a separate section) to preserve co-location. In SQL, field-to-column mappings like `"name": { "column": "display_name" }` need their table context nearby — separating them would leave column references dangling. For a consumer that only cares about the domain, `model.storage` is a clearly scoped block to skip. The separation is logical, not physical. diff --git a/docs/architecture docs/adrs/ADR 178 - Value objects in the contract.md b/docs/architecture docs/adrs/ADR 178 - Value objects in the contract.md index f8a3a8ba04a1..d2ab14db0ef7 100644 --- a/docs/architecture docs/adrs/ADR 178 - Value objects in the contract.md +++ b/docs/architecture docs/adrs/ADR 178 - Value objects in the contract.md @@ -2,7 +2,7 @@ ## At a glance -> **Note:** this ADR describes value objects in the domain plane, where they first lived. They are now storage entities ([ADR 278](ADR%20278%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md)); the field shapes below are unchanged. +> **Note:** this ADR describes value objects in the domain plane, where they first lived. They are now storage entities ([ADR 309](ADR%20309%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md)); the field shapes below are unchanged. A User model with scalar fields and value object fields. Value objects are defined in a separate `valueObjects` section — they share the same field descriptor shape (`{ nullable, codecId }`) as models, but the framework makes fundamentally different promises about them. @@ -105,7 +105,7 @@ This extends the framework commitment hierarchy: ### Value objects are a top-level contract section -> **Superseded:** value objects now live in the storage plane, at `storage.namespaces..entries.valueObject`, and a domain field refers to one by its storage coordinate. See [ADR 278](ADR%20278%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md). +> **Superseded:** value objects now live in the storage plane, at `storage.namespaces..entries.valueObject`, and a domain field refers to one by its storage coordinate. See [ADR 309](ADR%20309%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md). Value objects live in a `valueObjects` section alongside `models`. They use the same field shape (`{ nullable, codecId }`) as model fields. Refer to the [At a glance](#at-a-glance) example for the complete structure. @@ -172,7 +172,7 @@ You say `boundingBox.topLeft`, not `boundingBox[0]`. Length constraints on homog ### Value objects need no special storage mapping -> **Superseded:** the value object's definition is now a storage entity, so a change to its fields changes the storage hash. See [ADR 278](ADR%20278%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md). +> **Superseded:** the value object's definition is now a storage entity, so a change to its fields changes the storage hash. See [ADR 309](ADR%20309%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md). Value object fields use `storage.fields` like any other field. The storage layer maps domain field names to physical locations without knowing whether the field holds a scalar or a composite: diff --git a/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md b/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md index 9d0f038abb04..ff0f5edc843d 100644 --- a/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md +++ b/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md @@ -184,7 +184,7 @@ The Prisma 7 reader builds the contract directly and writes no aliases. It reads - **Breaking for schemas.** Every preset call and every `@default(())` must be rewritten. `@updatedAt` keeps being refused in Prisma 8 PSL, with a hint that names `@onCreate(now()) @onUpdate(now())` and `temporal.UpdatedAt`. - **Hashes.** Rewriting a preset or `@default(uuid())` as attributes changes no hash when the column and generators are the same. A contract with a `types {}` block or TypeScript `types` gets a new storage hash, because its columns lose `typeRef` and gain inline params. Supabase's space contract gets a new hash. pgvector's and PostGIS's do not. -- **Upgrade.** The upgrade instructions ship a script that rewrites stored contracts and migration snapshots into the expanded form and refreshes their hashes, as the removal of `@noCheck` did. Users then re-emit and run `db sign` against every database. The change has no operations; since ADR 278, `migration plan` writes such a migration with no operations, and `db migrate` applies it by moving the marker to the new hash. The instructions also give the PSL and TypeScript rewrites for presets. +- **Upgrade.** The upgrade instructions ship a script that rewrites stored contracts and migration snapshots into the expanded form and refreshes their hashes, as the removal of `@noCheck` did. Users then re-emit and run `db sign` against every database. The change has no operations; since ADR 309, `migration plan` writes such a migration with no operations, and `db migrate` applies it by moving the marker to the new hash. The instructions also give the PSL and TypeScript rewrites for presets. - **Breaking for extension authors.** A default function can no longer lower to a client generator: `ControlMutationDefaultEntry.lower` returns a database default only. An extension that generated a value through a default function, such as `slugid()`, contributes a generator function with a binding for each codec it fills instead. `printArguments` and `applicableCodecIds` are removed; a binding declares the codecs it fills and how `contract print` writes each generator. - **Diagnostics.** The `PSL_PRESET_*` diagnostics and the refusal of preset-only generators in `@default` are removed. `@default(uuid())` gets a refusal that names `@onCreate(uuid())`. - **Less code.** The `fieldPreset` descriptor kind, `instantiateAuthoringFieldPreset`, the `option` argument kind, the `select` template node, the temporal preset builders and the preset print search are removed. The language server loses its field-preset completion branch. diff --git a/docs/architecture docs/adrs/ADR 278 - Value objects are storage entities, and a discriminator column names a value set.md b/docs/architecture docs/adrs/ADR 309 - Value objects are storage entities, and a discriminator column names a value set.md similarity index 99% rename from docs/architecture docs/adrs/ADR 278 - Value objects are storage entities, and a discriminator column names a value set.md rename to docs/architecture docs/adrs/ADR 309 - Value objects are storage entities, and a discriminator column names a value set.md index 4394b7a04850..289ba4fd9e2f 100644 --- a/docs/architecture docs/adrs/ADR 278 - Value objects are storage entities, and a discriminator column names a value set.md +++ b/docs/architecture docs/adrs/ADR 309 - Value objects are storage entities, and a discriminator column names a value set.md @@ -1,4 +1,4 @@ -# ADR 278 — Value objects are storage entities, and a discriminator column names a value set +# ADR 309 — Value objects are storage entities, and a discriminator column names a value set **Status:** Accepted **Date:** 2026-10-21 diff --git a/docs/architecture docs/subsystems/1. Data Contract.md b/docs/architecture docs/subsystems/1. Data Contract.md index b8906668719d..d96b4da57894 100644 --- a/docs/architecture docs/subsystems/1. Data Contract.md +++ b/docs/architecture docs/subsystems/1. Data Contract.md @@ -229,8 +229,8 @@ The contract's domain-level structure generalizes across database families. The - **`model.fields`** — domain-level field descriptors as `Record`. Both `nullable` and `codecId` are domain concepts. "Family-agnostic" describes the structure, not the values — codec ID values differ across families but the structure is identical. See [ADR 172](../adrs/ADR%20172%20-%20Contract%20domain-storage%20separation.md). - **Polymorphism** — `discriminator` + `variants` on the base model, `base` on each variant. Persistence strategy (STI vs MTI) is emergent from storage mappings. Uses specialization/generalization terminology. See [ADR 173](../adrs/ADR%20173%20-%20Polymorphism%20via%20discriminator%20and%20variants.md). - **Model ownership** — an owned model declares `"owner": "ParentModel"`, a domain fact about aggregate membership. Its data is co-located with the owner's storage (embedded document in Mongo, JSONB column in SQL). The parent's `storage.relations` maps the relation to its physical location. Relations to owned models are plain graph edges. See [ADR 177](../adrs/ADR%20177%20-%20Ownership%20replaces%20relation%20strategy.md). -- **Value objects** — typed data structures without identity, lifecycle, or framework capabilities (no query entry points, no hooks, no referential integrity). They are storage entities, at `storage.namespaces..entries.valueObject.` in both families, because their fields are the keys every row or subdocument stores, so a change to them changes the storage hash. A value object's `fields` use the field shape models use; a member typed by an enum refers to the enum's storage value set. A domain field that holds one has the type `{ kind: 'valueObject', plane: 'storage', namespaceId, entityKind: 'valueObject', entityName }`, the value object's storage coordinate, and a member that holds another value object refers to it the same way. Readers resolve the coordinate with `valueObjectAt` from `@internal/contract`. A contract that keeps `valueObjects` in a domain namespace, or refers to a value object by `name`, is refused with a message saying to re-emit it. Value object support in SQL is capability-gated (requires JSONB-compatible columns). See [ADR 278](../adrs/ADR%20278%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md), [ADR 178](../adrs/ADR%20178%20-%20Value%20objects%20in%20the%20contract.md), [ADR 179](../adrs/ADR%20179%20-%20Union%20field%20types.md). -- **Discriminator value sets (SQL)** — the SQL build writes one value set per base model with variants, keyed `.` in the base table's namespace and holding the variants' values, sorted; the discriminator column refers to it. A discriminator column an enum types keeps the enum's value set. An enum with exactly that name in the same namespace is refused. MongoDB writes no such value set: its collection validator already pins each variant's value. See [ADR 278](../adrs/ADR%20278%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md). +- **Value objects** — typed data structures without identity, lifecycle, or framework capabilities (no query entry points, no hooks, no referential integrity). They are storage entities, at `storage.namespaces..entries.valueObject.` in both families, because their fields are the keys every row or subdocument stores, so a change to them changes the storage hash. A value object's `fields` use the field shape models use; a member typed by an enum refers to the enum's storage value set. A domain field that holds one has the type `{ kind: 'valueObject', plane: 'storage', namespaceId, entityKind: 'valueObject', entityName }`, the value object's storage coordinate, and a member that holds another value object refers to it the same way. Readers resolve the coordinate with `valueObjectAt` from `@internal/contract`. A contract that keeps `valueObjects` in a domain namespace, or refers to a value object by `name`, is refused with a message saying to re-emit it. Value object support in SQL is capability-gated (requires JSONB-compatible columns). See [ADR 309](../adrs/ADR%20309%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md), [ADR 178](../adrs/ADR%20178%20-%20Value%20objects%20in%20the%20contract.md), [ADR 179](../adrs/ADR%20179%20-%20Union%20field%20types.md). +- **Discriminator value sets (SQL)** — the SQL build writes one value set per base model with variants, keyed `.` in the base table's namespace and holding the variants' values, sorted; the discriminator column refers to it. A discriminator column an enum types keeps the enum's value set. An enum with exactly that name in the same namespace is refused. MongoDB writes no such value set: its collection validator already pins each variant's value. See [ADR 309](../adrs/ADR%20309%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md). ### Core entities vs pack extensions @@ -321,7 +321,7 @@ A `pg/int4@1` enum `Int4Level` with members `Low = 1` and `High = 10`, on column A list column gets `<@` containment instead of `IN`. In the same fixture, `levels FloatMixed[]`, whose members are written `1.5` and `NaN` and stored as `1.5` and `"NaN"`, gets `array_remove("levels", NULL) <@ '{1.5,"NaN"}'`, plus the derived check `array_position("levels", NULL) IS NULL` because its elements are not nullable. -On a `managed` Postgres table two more columns derive checks ([ADR 278](../adrs/ADR%20278%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md)). A discriminator column with no enum gets a membership check, `
__check`, holding the values of its `.` value set. A column that holds a value object gets a check named `
__value_object`: one strict `jsonpath` predicate that the column value is an object (or, for a list, an array of objects) with each required member present, no other key, each scalar member of its JSON type, each enum member one of its value set's values, and each nested value object checked the same way. A nullable column accepts SQL `NULL` and JSON `null`; the ORM writes SQL `NULL` for an absent value. A `Decimal` member must be a JSON string, as the ORM writes it. A required member whose codec has no known JSON type must be present and not JSON `null`; a `Json` member may hold any value. Below the point where a value object refers back into itself, any object is accepted, as the MongoDB validator does. +On a `managed` Postgres table two more columns derive checks ([ADR 309](../adrs/ADR%20309%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md)). A discriminator column with no enum gets a membership check, `
__check`, holding the values of its `.` value set. A column that holds a value object gets a check named `
__value_object`: one strict `jsonpath` predicate that the column value is an object (or, for a list, an array of objects) with each required member present, no other key, each scalar member of its JSON type, each enum member one of its value set's values, and each nested value object checked the same way. A nullable column accepts SQL `NULL` and JSON `null`; the ORM writes SQL `NULL` for an absent value. A `Decimal` member must be a JSON string, as the ORM writes it. A required member whose codec has no known JSON type must be present and not JSON `null`; a `Json` member may hold any value. Below the point where a value object refers back into itself, any object is accepted, as the MongoDB validator does. `contract infer` produces no value objects and no polymorphism, so on a live table with these checks it prints each one as an authored `@@check` with its exact name in `map:`, as it does for an enum membership check. diff --git a/docs/architecture docs/subsystems/10. MongoDB Family.md b/docs/architecture docs/subsystems/10. MongoDB Family.md index c9ce54da9187..152e63017a3e 100644 --- a/docs/architecture docs/subsystems/10. MongoDB Family.md +++ b/docs/architecture docs/subsystems/10. MongoDB Family.md @@ -86,7 +86,7 @@ A few things to notice before reading further: - **`roots`** declares which models are ORM entry points — `db.users` and `db.posts`. Comment is not in `roots` because it's only reachable through Post. - **`owner: "Post"`** on the Comment model means Comment belongs to Post's aggregate. Its data lives inside Post documents. The relation from Post to Comment (`{ "to": "Comment", "cardinality": "1:N" }`) is a plain graph edge — no storage annotation. The physical location is in Post's `storage.relations`: `"comments": { "field": "comments" }`. -- **Comment has no `collection`**, because it doesn't own one. Its data lives within Post's storage. Note that Comment is still an entity with its own id — it has identity even when embedded. Value objects (data without identity, like an address or a money amount) are a separate concept: storage entities at `storage.namespaces..entries.valueObject`, which a field refers to by storage coordinate — see [ADR 178](../adrs/ADR%20178%20-%20Value%20objects%20in%20the%20contract.md) and [ADR 278](../adrs/ADR%20278%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md). +- **Comment has no `collection`**, because it doesn't own one. Its data lives within Post's storage. Note that Comment is still an entity with its own id — it has identity even when embedded. Value objects (data without identity, like an address or a money amount) are a separate concept: storage entities at `storage.namespaces..entries.valueObject`, which a field refers to by storage coordinate — see [ADR 178](../adrs/ADR%20178%20-%20Value%20objects%20in%20the%20contract.md) and [ADR 309](../adrs/ADR%20309%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md). - **Field structure is identical to SQL.** Each field is `{ nullable, codecId }`. A Mongo field says `"mongo/string@1"` where SQL would say `"pg/text@1"`, but the shape is the same. - **Fields keep their own names; storage records where each is stored.** As SQL records each field's column in `model.storage.fields`, a Mongo model records each field's stored name there: `{ "": { "field": "" } }`, an entry for every field. `id ObjectId @id @map("_id")` in PSL, or `field.objectId().mongo({ field: '_id' })` in the TypeScript builder, gives the field `id` stored as `_id`. A field without `@map` is stored under its own name. Relations and the discriminator name fields; indexes, validators and execution defaults name stored names. An embed relation is stored under its `storage.relations` entry, or its own name when it has none. A value object has no storage block: its fields are stored under their own names, and PSL and the TypeScript builder refuse a stored name on them. `validateMongoStorage` checks that every field has an entry and that no two fields or embed relations of a model, with its base for a variant, share a stored name. The helpers in `@internal/mongo-contract` (`ownStoredNamesOf`, `ownStoredNameOf`, `ownFieldStoredAs`, `ownEmbedStoredNamesOf`, `ownDocumentIdFieldOf`, `storedNamesOf` with inherited fields, `modelAndBasesOf`) and the types `OwnStoredNameOf` and `OwnFieldStoredAs` translate between the two, and every reader goes through them: the validator derivation, the planner, the ORM and the pipeline builder. @@ -126,7 +126,7 @@ A referenced relation (to a model with its own collection) carries `on` join det This also introduces the concept of **aggregate roots**: models that own their own collection and can be queried directly. In SQL, every model has its own table, so this distinction is invisible. In MongoDB, some models are owned by a parent and can only be reached through it. The contract makes this explicit with a top-level `roots` section that maps ORM accessor names (like `db.users`) to model names (like `User`). If a model isn't in `roots`, it's not an independent entry point. See [ADR 174](../adrs/ADR%20174%20-%20Aggregate%20roots%20and%20relation%20strategies.md). -Each model has exactly one canonical storage location — either its own collection (aggregate root) or inside a parent's document (owned). A model can't be both. When developers need data from one aggregate root available inside another for read performance (the MongoDB "extended reference" pattern), the embedded copy is a denormalized snapshot — a value object — not the model itself. The canonical Post lives in the `posts` collection; a `{ title, slug }` summary embedded in a User document is a different thing, even though it shares some fields. Value objects are storage entities, at `storage.namespaces..entries.valueObject`, and the collection validator is derived from them — see [ADR 178](../adrs/ADR%20178%20-%20Value%20objects%20in%20the%20contract.md) and [ADR 278](../adrs/ADR%20278%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md). A change to a value object's fields changes the storage hash and the validator. MongoDB writes no discriminator value set: the validator already pins each variant's discriminator value. See also [design principle #5](#design-principles). +Each model has exactly one canonical storage location — either its own collection (aggregate root) or inside a parent's document (owned). A model can't be both. When developers need data from one aggregate root available inside another for read performance (the MongoDB "extended reference" pattern), the embedded copy is a denormalized snapshot — a value object — not the model itself. The canonical Post lives in the `posts` collection; a `{ title, slug }` summary embedded in a User document is a different thing, even though it shares some fields. Value objects are storage entities, at `storage.namespaces..entries.valueObject`, and the collection validator is derived from them — see [ADR 178](../adrs/ADR%20178%20-%20Value%20objects%20in%20the%20contract.md) and [ADR 309](../adrs/ADR%20309%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md). A change to a value object's fields changes the storage hash and the validator. MongoDB writes no discriminator value set: the validator already pins each variant's discriminator value. See also [design principle #5](#design-principles). In the ORM, owned relations auto-project into the parent row — no separate query is issued. Referenced relations use `$lookup` pipeline stages or multi-query stitching, similar to how SQL handles joins. diff --git a/docs/architecture docs/subsystems/2. Contract Emitter & Types.md b/docs/architecture docs/subsystems/2. Contract Emitter & Types.md index 399b53361956..e2a45b02e082 100644 --- a/docs/architecture docs/subsystems/2. Contract Emitter & Types.md +++ b/docs/architecture docs/subsystems/2. Contract Emitter & Types.md @@ -269,7 +269,7 @@ export type Contract = ContractWithTypeMaps; export type Namespaces = Contract['storage']['namespaces']; ``` -The emitter writes the field type maps of every family, and the SQL family its storage column type maps, as applications of `FieldTypesFromDomain` (`@internal/contract/types`) and `ColumnTypesFromStorage` (`@internal/sql-contract/types`) to the emitted contract's own shape. A contract written with `defineContract`, in either family, applies the same generics to its built shape, so the two paths share one set of rules: an override first, then, on the output side, the pack's type for a field with type parameters, then the codec's output or input type, an object of a value object's own field types, or the union of a union field's members, then list and dictionary wrapping and nullability. Value objects are storage entities ([ADR 278](../adrs/ADR%20278%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md)): `contract.d.ts` writes them in the storage namespaces type, under `entries.valueObject`, and types a field that holds one by its storage coordinate. Each value object's `Output` and `Input` alias is an entry of `ValueObjectTypesFromStorage`, applied to `ContractBase['storage']['namespaces']`. `FieldTypesFromDomain` takes the storage namespaces as an optional sixth type parameter, where it finds the value object a field's coordinate names; the emitter passes it only when the contract has value objects, so a contract without them emits the same text as before. A SQL discriminator field is typed by the literal union of its value set, through `FieldTypeOverrides`. The emitter writes only what a type cannot compute: `FieldTypeOverrides` and `ColumnTypeOverrides` hold the literal union of a value set (each value rendered by its codec's `renderValueLiteral`; a codec that renders no literal, such as one that sets `keepsWrittenForm`, leaves the codec's type) and text a codec renders with `renderOutputType`. Each enum member's type comes from the same `renderValueLiteral`, or is the codec's output type when the codec renders no literal. `Models` members index `FieldOutputTypes`. +The emitter writes the field type maps of every family, and the SQL family its storage column type maps, as applications of `FieldTypesFromDomain` (`@internal/contract/types`) and `ColumnTypesFromStorage` (`@internal/sql-contract/types`) to the emitted contract's own shape. A contract written with `defineContract`, in either family, applies the same generics to its built shape, so the two paths share one set of rules: an override first, then, on the output side, the pack's type for a field with type parameters, then the codec's output or input type, an object of a value object's own field types, or the union of a union field's members, then list and dictionary wrapping and nullability. Value objects are storage entities ([ADR 309](../adrs/ADR%20309%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md)): `contract.d.ts` writes them in the storage namespaces type, under `entries.valueObject`, and types a field that holds one by its storage coordinate. Each value object's `Output` and `Input` alias is an entry of `ValueObjectTypesFromStorage`, applied to `ContractBase['storage']['namespaces']`. `FieldTypesFromDomain` takes the storage namespaces as an optional sixth type parameter, where it finds the value object a field's coordinate names; the emitter passes it only when the contract has value objects, so a contract without them emits the same text as before. A SQL discriminator field is typed by the literal union of its value set, through `FieldTypeOverrides`. The emitter writes only what a type cannot compute: `FieldTypeOverrides` and `ColumnTypeOverrides` hold the literal union of a value set (each value rendered by its codec's `renderValueLiteral`; a codec that renders no literal, such as one that sets `keepsWrittenForm`, leaves the codec's type) and text a codec renders with `renderOutputType`. Each enum member's type comes from the same `renderValueLiteral`, or is the codec's output type when the codec renders no literal. `Models` members index `FieldOutputTypes`. Both planes are namespaced: storage entities live under `storage.namespaces..entries[entityKind][entityName]` (e.g. `entries['table']['user']`) and models under `domain.namespaces..models` (see [Data Contract § Two planes, namespaced](1.%20Data%20Contract.md#two-planes-namespaced) and [ADR 221](../adrs/ADR%20221%20-%20Contract%20IR%20two%20planes%20with%20uniform%20entity%20coordinate%20and%20pack-contributed%20entity%20kinds.md)). Consumers read a namespace's models directly — `Contract['domain']['namespaces']['public']['models']` (or `[keyof …['namespaces']]` for the sole-namespace case). See [ADR 223 — Target-owned default namespace](../adrs/ADR%20223%20-%20Target-owned%20default%20namespace.md) for the runtime/emitter fail-loud split. diff --git a/packages/1-framework/0-foundation/contract/src/domain-envelope.ts b/packages/1-framework/0-foundation/contract/src/domain-envelope.ts index 7466d96ac4f7..ff904c070daf 100644 --- a/packages/1-framework/0-foundation/contract/src/domain-envelope.ts +++ b/packages/1-framework/0-foundation/contract/src/domain-envelope.ts @@ -5,7 +5,7 @@ export { UNBOUND_DOMAIN_NAMESPACE_ID } from './default-namespace'; /** * One namespace's application-domain entities — models and optional enums * keyed by entity name within that namespace coordinate. Value objects are - * storage entities (ADR 278). + * storage entities (ADR 309). */ export interface ApplicationDomainNamespace { readonly models: Record; diff --git a/packages/1-framework/0-foundation/contract/src/domain-types.ts b/packages/1-framework/0-foundation/contract/src/domain-types.ts index 43e852759d85..9ee98546e9a7 100644 --- a/packages/1-framework/0-foundation/contract/src/domain-types.ts +++ b/packages/1-framework/0-foundation/contract/src/domain-types.ts @@ -9,7 +9,7 @@ export type ScalarFieldType = { readonly typeParams?: Record; }; -/** A field that holds a value object: the coordinate of its storage entity (ADR 278). */ +/** A field that holds a value object: the coordinate of its storage entity (ADR 309). */ export interface ValueObjectFieldType extends EntityCoordinate<'storage', 'valueObject'> { readonly kind: 'valueObject'; } @@ -94,7 +94,7 @@ export type ContractDiscriminator = { }; /** - * A field of a value object, which is a storage entity (ADR 278). Storage never refers to the domain plane, so a member an enum types refers to the enum's storage value set. + * A field of a value object, which is a storage entity (ADR 309). Storage never refers to the domain plane, so a member an enum types refers to the enum's storage value set. */ export interface ContractValueObjectField extends Omit { readonly valueSet?: EntityCoordinate<'storage', 'valueSet'> & { readonly spaceId?: string }; diff --git a/packages/1-framework/0-foundation/contract/src/validate-domain.ts b/packages/1-framework/0-foundation/contract/src/validate-domain.ts index 4c6597727a6f..eca97d8e6279 100644 --- a/packages/1-framework/0-foundation/contract/src/validate-domain.ts +++ b/packages/1-framework/0-foundation/contract/src/validate-domain.ts @@ -66,7 +66,7 @@ function* iterateIndexedModels(index: ModelIndex): IterableIterator, diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts b/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts index d0fb521affa3..3373c751a69b 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts @@ -204,7 +204,7 @@ function storageOf( } /** - * Writes the value set of each discriminator, keyed `.` in the base table's namespace and holding the variants' values as the column stores them, sorted, and points the discriminator column at it (ADR 278). A column an enum types keeps the enum's value set. An enum with the same name in the same namespace is refused, since the two value sets would share one entry. + * Writes the value set of each discriminator, keyed `.` in the base table's namespace and holding the variants' values as the column stores them, sorted, and points the discriminator column at it (ADR 309). A column an enum types keeps the enum's value set. An enum with the same name in the same namespace is refused, since the two value sets would share one entry. */ function writeDiscriminatorValueSets( definition: ContractDefinition, diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/contract-definition.ts b/packages/2-sql-family/2-authoring/contract-ts/src/contract-definition.ts index 799c2959bfc4..98fccc244c98 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/contract-definition.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/contract-definition.ts @@ -307,7 +307,7 @@ export interface ModelNode extends TableProperties { * table and a root for this model — the base owns both. */ readonly sharesBaseTable?: boolean; - /** The field that names a row's variant, on a model that has variants. Its value set is written into storage before the storage hash (ADR 278). */ + /** The field that names a row's variant, on a model that has variants. Its value set is written into storage before the storage hash (ADR 309). */ readonly discriminator?: ContractDiscriminator; /** The variants of this model, keyed by the discriminator value as the column stores it. */ readonly variants?: Readonly>; diff --git a/projects/migration-statements/slices/remaining-nouns/dispatches/10-value-object-storage-shape.md b/projects/migration-statements/slices/remaining-nouns/dispatches/10-value-object-storage-shape.md index 549c6db9c024..4e5ca5e5e491 100644 --- a/projects/migration-statements/slices/remaining-nouns/dispatches/10-value-object-storage-shape.md +++ b/projects/migration-statements/slices/remaining-nouns/dispatches/10-value-object-storage-shape.md @@ -1,6 +1,6 @@ # Dispatch 10 — value objects in the storage plane: contract shape and validators -**Plan:** [`../plan-pr3.md`](../plan-pr3.md) · **ADR:** `docs/architecture docs/adrs/ADR 278 - Value objects are storage entities, and a discriminator column names a value set.md` · **Grounding:** `wip/pr3-grounding.md` § A, B · **Branch:** `tml-3537-value-objects-storage` +**Plan:** [`../plan-pr3.md`](../plan-pr3.md) · **ADR:** `docs/architecture docs/adrs/ADR 309 - Value objects are storage entities, and a discriminator column names a value set.md` · **Grounding:** `wip/pr3-grounding.md` § A, B · **Branch:** `tml-3537-value-objects-storage` ## Outcome @@ -14,9 +14,9 @@ The contract types and validators accept a value object as a storage entity, at ## Do -1. Read ADR 221, ADR 278, ADR 178, `domain-types.ts`, `value-set-ref.ts`, `validate-domain.ts`, the SQL and Mongo storage types and their validators, the JSON schema `data-contract-sql-v1.json`, and the domain accessors (`domainValueObjectsAtDefaultNamespace` and friends). +1. Read ADR 221, ADR 309, ADR 178, `domain-types.ts`, `value-set-ref.ts`, `validate-domain.ts`, the SQL and Mongo storage types and their validators, the JSON schema `data-contract-sql-v1.json`, and the domain accessors (`domainValueObjectsAtDefaultNamespace` and friends). 2. **Tests first, red before each change:** a contract with a value object in storage and a field referring to it validates; a reference to a missing storage value object is refused with a message naming the field and the coordinate; a nested reference resolves; a value object that refers to itself validates; the old domain `valueObjects` key is refused; both families. -3. Implement the types, the validators and the accessors. Keep every name from the existing vocabulary (`valueObject` is the entity kind, as ADR 278 says). Do not invent new nouns; if a new name is unavoidable, say so in the report with candidates. +3. Implement the types, the validators and the accessors. Keep every name from the existing vocabulary (`valueObject` is the entity kind, as ADR 309 says). Do not invent new nouns; if a new name is unavoidable, say so in the report with candidates. 4. Update the JSON schema. ## Do not diff --git a/projects/migration-statements/slices/remaining-nouns/dispatches/11-plans-with-no-operations.md b/projects/migration-statements/slices/remaining-nouns/dispatches/11-plans-with-no-operations.md index a883e1318004..f29c6210bf70 100644 --- a/projects/migration-statements/slices/remaining-nouns/dispatches/11-plans-with-no-operations.md +++ b/projects/migration-statements/slices/remaining-nouns/dispatches/11-plans-with-no-operations.md @@ -1,6 +1,6 @@ # Dispatch 11 — a plan with no operations is written and applied -**Plan:** [`../plan-pr3.md`](../plan-pr3.md) · **ADR:** ADR 278, "Why every change is an operation" · **Grounding:** `wip/pr3-grounding.md` § F · **Branch:** `tml-3537-value-objects-storage` +**Plan:** [`../plan-pr3.md`](../plan-pr3.md) · **ADR:** ADR 309, "Why every change is an operation" · **Grounding:** `wip/pr3-grounding.md` § F · **Branch:** `tml-3537-value-objects-storage` ## Outcome diff --git a/projects/migration-statements/slices/remaining-nouns/dispatches/12-authoring-writes-storage-value-objects.md b/projects/migration-statements/slices/remaining-nouns/dispatches/12-authoring-writes-storage-value-objects.md index 4ca1400bec6a..1cfd011c9f3d 100644 --- a/projects/migration-statements/slices/remaining-nouns/dispatches/12-authoring-writes-storage-value-objects.md +++ b/projects/migration-statements/slices/remaining-nouns/dispatches/12-authoring-writes-storage-value-objects.md @@ -1,6 +1,6 @@ # Dispatch 12 — authoring writes value objects and discriminator value sets into storage -**Plan:** [`../plan-pr3.md`](../plan-pr3.md) · **ADR:** ADR 278 · **Grounding:** `wip/pr3-grounding.md` § A (SQL family, Mongo family), C, E · **Builds on:** dispatch 10 (`d607bde854`, `1792de2a67`, `965c0cf100`) · **Branch:** `tml-3537-value-objects-storage` +**Plan:** [`../plan-pr3.md`](../plan-pr3.md) · **ADR:** ADR 309 · **Grounding:** `wip/pr3-grounding.md` § A (SQL family, Mongo family), C, E · **Builds on:** dispatch 10 (`d607bde854`, `1792de2a67`, `965c0cf100`) · **Branch:** `tml-3537-value-objects-storage` ## Outcome diff --git a/projects/migration-statements/slices/remaining-nouns/dispatches/13-emit-and-types-read-storage.md b/projects/migration-statements/slices/remaining-nouns/dispatches/13-emit-and-types-read-storage.md index 346cfbaa078e..b1843dcdd616 100644 --- a/projects/migration-statements/slices/remaining-nouns/dispatches/13-emit-and-types-read-storage.md +++ b/projects/migration-statements/slices/remaining-nouns/dispatches/13-emit-and-types-read-storage.md @@ -1,6 +1,6 @@ # Dispatch 13 — `contract.d.ts` reads value objects from storage -**Plan:** [`../plan-pr3.md`](../plan-pr3.md) · **ADR:** ADR 278 · **Grounding:** `wip/pr3-grounding.md` § A (foundation `field-value-types.ts`, framework emitter, SQL and Mongo emitters), C · **Builds on:** dispatch 10 · **Branch:** `tml-3537-value-objects-storage` +**Plan:** [`../plan-pr3.md`](../plan-pr3.md) · **ADR:** ADR 309 · **Grounding:** `wip/pr3-grounding.md` § A (foundation `field-value-types.ts`, framework emitter, SQL and Mongo emitters), C · **Builds on:** dispatch 10 · **Branch:** `tml-3537-value-objects-storage` ## Outcome diff --git a/projects/migration-statements/slices/remaining-nouns/dispatches/14-runtime-and-tools-read-storage.md b/projects/migration-statements/slices/remaining-nouns/dispatches/14-runtime-and-tools-read-storage.md index 621d44a4bab3..6c3b9378495b 100644 --- a/projects/migration-statements/slices/remaining-nouns/dispatches/14-runtime-and-tools-read-storage.md +++ b/projects/migration-statements/slices/remaining-nouns/dispatches/14-runtime-and-tools-read-storage.md @@ -1,6 +1,6 @@ # Dispatch 14 — runtimes and tools read value objects through the field's storage reference -**Plan:** [`../plan-pr3.md`](../plan-pr3.md) · **ADR:** ADR 278 · **Grounding:** `wip/pr3-grounding.md` § A (CLI, Postgres target, Mongo query builders) · **Builds on:** dispatches 12 and 13 · **Branch:** `tml-3537-value-objects-storage` +**Plan:** [`../plan-pr3.md`](../plan-pr3.md) · **ADR:** ADR 309 · **Grounding:** `wip/pr3-grounding.md` § A (CLI, Postgres target, Mongo query builders) · **Builds on:** dispatches 12 and 13 · **Branch:** `tml-3537-value-objects-storage` ## Outcome diff --git a/projects/migration-statements/slices/remaining-nouns/dispatches/15-postgres-derived-checks.md b/projects/migration-statements/slices/remaining-nouns/dispatches/15-postgres-derived-checks.md index 90580841ac43..f1dc29ec37b3 100644 --- a/projects/migration-statements/slices/remaining-nouns/dispatches/15-postgres-derived-checks.md +++ b/projects/migration-statements/slices/remaining-nouns/dispatches/15-postgres-derived-checks.md @@ -1,6 +1,6 @@ # Dispatch 15 — Postgres derives a check for the discriminator column and for each value object column -**Plan:** [`../plan-pr3.md`](../plan-pr3.md) · **ADR:** ADR 278 decisions 2 and 3, and the plan's decisions on the `jsonpath` predicate and on value objects that contain themselves · **Grounding:** `wip/pr3-grounding.md` § D · **Builds on:** dispatch 12 · **Branch:** `tml-3537-value-objects-storage` +**Plan:** [`../plan-pr3.md`](../plan-pr3.md) · **ADR:** ADR 309 decisions 2 and 3, and the plan's decisions on the `jsonpath` predicate and on value objects that contain themselves · **Grounding:** `wip/pr3-grounding.md` § D · **Builds on:** dispatch 12 · **Branch:** `tml-3537-value-objects-storage` ## Outcome diff --git a/projects/migration-statements/slices/remaining-nouns/dispatches/16-re-emit-migrations-upgrade-docs.md b/projects/migration-statements/slices/remaining-nouns/dispatches/16-re-emit-migrations-upgrade-docs.md index d1f500c6da5f..6c31d6e2028b 100644 --- a/projects/migration-statements/slices/remaining-nouns/dispatches/16-re-emit-migrations-upgrade-docs.md +++ b/projects/migration-statements/slices/remaining-nouns/dispatches/16-re-emit-migrations-upgrade-docs.md @@ -1,6 +1,6 @@ # Dispatch 16 — re-emit, example migrations, the upgrade script, and docs -**Plan:** [`../plan-pr3.md`](../plan-pr3.md) · **ADR:** ADR 278 · **Grounding:** `wip/pr3-grounding.md` § C and risks 6–8 · **Builds on:** dispatches 11, 14 and 15, and the merge of main (`4aaa36d9bc`) · **Branch:** `tml-3537-value-objects-storage` +**Plan:** [`../plan-pr3.md`](../plan-pr3.md) · **ADR:** ADR 309 · **Grounding:** `wip/pr3-grounding.md` § C and risks 6–8 · **Builds on:** dispatches 11, 14 and 15, and the merge of main (`4aaa36d9bc`) · **Branch:** `tml-3537-value-objects-storage` ## Outcome @@ -46,7 +46,7 @@ The whole repository is on the new contract shape, users can move their projects ### 4. Docs -- **ADR 278:** check that it matches what was built. Notes on what changed from its first text, for example the jsonpath predicate and the discriminator value set on SQL only, go in the ADR's own words, with no ticket or dispatch ids. +- **ADR 309:** check that it matches what was built. Notes on what changed from its first text, for example the jsonpath predicate and the discriminator value set on SQL only, go in the ADR's own words, with no ticket or dispatch ids. - **ADR 172 and ADR 178:** add a short note where they say value objects live in the domain plane. - **Data Contract subsystem doc and MongoDB Family doc:** update them for the new shape. - **Contract Emitter & Types subsystem doc**, around lines 262 and 272: diff --git a/projects/migration-statements/slices/remaining-nouns/plan-pr3.md b/projects/migration-statements/slices/remaining-nouns/plan-pr3.md index 7b5f9d2da0dd..a2b6686a82ca 100644 --- a/projects/migration-statements/slices/remaining-nouns/plan-pr3.md +++ b/projects/migration-statements/slices/remaining-nouns/plan-pr3.md @@ -1,10 +1,10 @@ # Slice plan — 3b, PR 3: value objects are storage entities, and a discriminator column names a value set -**Spec:** [`spec.md`](./spec.md) decision 1 · **ADR:** ADR 278 (approved by Will, 2026-10-21) · **Linear:** [TML-3537](https://linear.app/prisma-company/issue/TML-3537) · **Branch:** `tml-3537-value-objects-storage`, from `origin/main` · **Grounding:** `wip/pr3-grounding.md` (gitignored, in this worktree; code references at main 9979a2d6ca) +**Spec:** [`spec.md`](./spec.md) decision 1 · **ADR:** ADR 309 (approved by Will, 2026-10-21) · **Linear:** [TML-3537](https://linear.app/prisma-company/issue/TML-3537) · **Branch:** `tml-3537-value-objects-storage`, from `origin/main` · **Grounding:** `wip/pr3-grounding.md` (gitignored, in this worktree; code references at main 9979a2d6ca) This PR changes the contract shape. It adds no statements; PR 4 adds the value object and variant statements on top of it. -## Decisions taken while planning (record each in ADR 278 in this PR) +## Decisions taken while planning (record each in ADR 309 in this PR) - **The discriminator value set is SQL only.** On MongoDB the collection validator already pins each variant's discriminator value, so a change there already changes the storage hash. - **An enum-typed discriminator keeps the enum's value set and check.** The column has one `valueSet` slot, and the enum already restricts it; a variant value outside the enum is already refused. @@ -28,7 +28,7 @@ As for PR 2: typecheck; lint and `test --coverage` in every touched package; `li | 13 | Emit and types: `contract.d.ts` types read value objects from the storage plane, with a defaulted type parameter so contracts without value objects emit the same text | 10 | | 14 | Runtime and tools read value objects through the domain field's reference: the SQL and Mongo ORMs and query builders, the CLI statement resolver, `contract print`, `contract infer`, verification | 12, 13 | | 15 | Postgres derived checks: the discriminator membership check, and the value object `jsonpath` check for every shape SQL authoring produces, starting from a test per shape on PGlite; a new SQL fixture with nested and list members; contract print and infer recognise the new checks | 12 | -| 16 | Re-emit every fixture and committed contract; the `prisma-8-demo` migrations gain the operations that create the new checks; the app and extension upgrade fragments with a script that rewrites a user's contract snapshots and keeps their manifests' hashes consistent, validated by running it on a project with a real migration history; ADR 278, ADR 172 and ADR 178 notes, Data Contract and MongoDB Family docs, the skill | 11, 14, 15 | +| 16 | Re-emit every fixture and committed contract; the `prisma-8-demo` migrations gain the operations that create the new checks; the app and extension upgrade fragments with a script that rewrites a user's contract snapshots and keeps their manifests' hashes consistent, validated by running it on a project with a real migration history; ADR 309, ADR 172 and ADR 178 notes, Data Contract and MongoDB Family docs, the skill | 11, 14, 15 | | 17 | Journeys, docs and the PR text | 16 | **Order:** 10 and 11 first (11 is independent); then 12 and 13; then 14 and 15; then 16; then 17. Then the two-reviewer `/drive-code-review`, fixes, manual QA, the PR. diff --git a/projects/migration-statements/slices/remaining-nouns/plan.md b/projects/migration-statements/slices/remaining-nouns/plan.md index 4b68caa0a62a..3403fd289963 100644 --- a/projects/migration-statements/slices/remaining-nouns/plan.md +++ b/projects/migration-statements/slices/remaining-nouns/plan.md @@ -6,7 +6,7 @@ Four pull requests, in the order the spec gives. This plan covers PR 1. Each lat ## PR 1 — namespaces, and the access question (merged, prisma/orm#30823) -**Branch:** `tml-3537-namespaces`, from `tml-3537-remaining-nouns-spec` (the spec and ADR 278 ride in this PR). Postgres only for the namespace work; the access rename touches every target. +**Branch:** `tml-3537-namespaces`, from `tml-3537-remaining-nouns-spec` (the spec and ADR 309 ride in this PR). Postgres only for the namespace work; the access rename touches every target. ### Gate for every dispatch @@ -36,7 +36,7 @@ Four pull requests, in the order the spec gives. This plan covers PR 1. Each lat - **Renaming the target's default namespace (`public`).** The spec allows it when no other contract space declares it. Dispatch 4 checks whether Postgres lets an ordinary database owner run `ALTER SCHEMA public RENAME` on PGlite and on a managed Postgres; if not, the planner refuses it with advice, and the spec's edge case row is corrected. - **Names of the three facade methods.** Named after the SQL they run, as `createSchema` and `renameTable` are. The reviewer checks them against the existing facade. -- **For PR 3: three points on ADR 278 from PR 1's system-design review.** (1) The storage value object keeps domain field names; state that value object fields have no mapped names, or store the stored key. (2) The discriminator value set is keyed `.` and relies on enum names never containing a dot; use a key the column's coordinate determines, or state the rule where entity names are validated. (3) The ADR has no **Status** or **Date** header, unlike its neighbours. +- **For PR 3: three points on ADR 309 from PR 1's system-design review.** (1) The storage value object keeps domain field names; state that value object fields have no mapped names, or store the stored key. (2) The discriminator value set is keyed `.` and relies on enum names never containing a dot; use a key the column's coordinate determines, or state the rule where entity names are validated. (3) The ADR has no **Status** or **Date** header, unlike its neighbours. ## PR 2 — enum values diff --git a/projects/migration-statements/slices/remaining-nouns/spec.md b/projects/migration-statements/slices/remaining-nouns/spec.md index 737cb0f4074e..0e087c2f49d8 100644 --- a/projects/migration-statements/slices/remaining-nouns/spec.md +++ b/projects/migration-statements/slices/remaining-nouns/spec.md @@ -37,7 +37,7 @@ Namespace items are Postgres only: SQLite has no namespaces. Everything else wor ## Decisions to check -1. **Value objects become storage entities, and a discriminator column names a value set** (agreed with Will, 2026-10-21; [ADR 278](../../../../docs/architecture%20docs/adrs/ADR%20278%20-%20Value%20objects%20are%20storage%20entities,%20and%20a%20discriminator%20column%20names%20a%20value%20set.md)). Today a value object field rename, a discriminator value change and a variant removal with no own columns change only the domain plane. The storage hash covers only the storage plane, so it stays the same, and the migration system cannot see the change: `migration plan` reports "No changes detected", a same-hash migration fails the integrity check, and snapshots are keyed by storage hash with the first write kept. A value object's fields and a discriminator's values are stored data the planner must see, so they belong in storage: +1. **Value objects become storage entities, and a discriminator column names a value set** (agreed with Will, 2026-10-21; [ADR 309](../../../../docs/architecture%20docs/adrs/ADR%20309%20-%20Value%20objects%20are%20storage%20entities,%20and%20a%20discriminator%20column%20names%20a%20value%20set.md)). Today a value object field rename, a discriminator value change and a variant removal with no own columns change only the domain plane. The storage hash covers only the storage plane, so it stays the same, and the migration system cannot see the change: `migration plan` reports "No changes detected", a same-hash migration fails the integrity check, and snapshots are keyed by storage hash with the first write kept. A value object's fields and a discriminator's values are stored data the planner must see, so they belong in storage: - **Value objects** move from `domain.namespaces..valueObjects` to `storage.namespaces..entries.valueObject`, in both families, keeping their definition. A domain field refers to one by entity coordinate, as a field typed by an enum refers to its value set. No copy. - **Discriminator values** are a value set keyed `.`, which the base table's discriminator column names, with a derived check on Postgres as an enum column has. - **Both are enforced:** on Postgres a value object column gets a derived check on its keys (`?&` and `-` for a flat shape, `jsonpath` for nesting and lists); on MongoDB the validator already lists them. So a change is normally a check or validator change. Will, 2026-10-21: migrations are expected never to be empty, because two storage hashes are two database states, but edge cases will happen, and the system handles them: `migration plan` writes the package with no operations and says what changed, and `db migrate` moves the marker. The "no operations" refusal goes for a plan whose storage hash changed. @@ -103,7 +103,7 @@ Every operation the planner writes is also something a user could write in `migr | --- | --- | --- | | 1 | Namespaces: move, rename, delete, and a removed namespace becomes a question. The access question wording and rename | — | | 2 | Enum values: rename and delete in scalar and list columns, on Postgres and SQLite | 1 merged | -| 3 | Value objects move to the storage plane and discriminator columns name a value set, both with derived checks on Postgres (decision 1), with ADR 278. First step: confirm a Postgres check can enforce every value object shape SQL authoring produces, nesting and lists included; raise any shape it cannot. A plan whose hash changed but has no operations is written and applied, not refused. Contracts re-emitted, with an upgrade fragment. No statements yet | 2 merged | +| 3 | Value objects move to the storage plane and discriminator columns name a value set, both with derived checks on Postgres (decision 1), with ADR 309. First step: confirm a Postgres check can enforce every value object shape SQL authoring produces, nesting and lists included; raise any shape it cannot. A plan whose hash changed but has no operations is written and applied, not refused. Contracts re-emitted, with an upgrade fragment. No statements yet | 2 merged | | 4 | Value object field rename and delete, including enum values inside JSON. Variant convert and delete | 3 merged | PR 3 is separate because it changes the contract shape and every contract with value objects or variants, and needs its own review, as slice 4p's first PR did. Slice 4b reuses PR 4's value object statement support for MongoDB. From 58e2d8953bb4d96b630195712d6cc1562fe7d35f Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 16:44:42 +0200 Subject: [PATCH 058/104] TML-3537: journeys show the value object and discriminator checks on Postgres and the value object validator on MongoDB Signed-off-by: willbot Signed-off-by: Will Madden --- test/integration/test/cli-journeys/README.md | 2 + .../value-object-checks.e2e.test.ts | 483 ++++++++++++++++++ .../value-object-validator.mongo.e2e.test.ts | 226 ++++++++ .../contract-external-auth-users.ts | 2 +- 4 files changed, 712 insertions(+), 1 deletion(-) create mode 100644 test/integration/test/cli-journeys/value-object-checks.e2e.test.ts create mode 100644 test/integration/test/cli-journeys/value-object-validator.mongo.e2e.test.ts diff --git a/test/integration/test/cli-journeys/README.md b/test/integration/test/cli-journeys/README.md index 9bccc010b349..d2be35f4f077 100644 --- a/test/integration/test/cli-journeys/README.md +++ b/test/integration/test/cli-journeys/README.md @@ -43,6 +43,8 @@ pnpm test:journeys | `namespace-statements.e2e.test.ts` | **Namespace statements (Postgres)**: N1 renames namespace `billing` to `finance` with `migration plan --rename billing:finance`, re-runs `migration.ts` byte for byte and keeps the rows; N2 removes `billing` and `db update` asks about each model, then `--delete billing` drops the tables and the schema; N3 plans the same delete with `migration plan` and re-runs its `migration.ts` | | `rename-statements-migration.e2e.test.ts` | **Rename statements on the command line (Postgres)**: seed `Profile` with rows, a unique field, a secondary index, a check, row-level security with a policy, and a foreign key from `Post`; the destination renames the model to `User` and its field `name` to `fullName`. Journey S1 plans `migration plan --rename Profile:User --rename User.name:User.fullName`: only widening renames of the table, the column and the objects named after them, both statements reported as applied, and re-running the written `migration.ts` reproduces `ops.json` and `migration.json` byte for byte; after `migrate` the rows and every object are under the new names, a further plan is empty and `db verify --schema-only` is clean. Journey S2 applies the same statements with `db update` and no consent, checks that `db update --dry-run` without statements then plans nothing, and refuses the statements on a second run (`MIGRATION.STATEMENT_UNRESOLVED`). Journey S3 covers the errors: unresolved names, a field named through its old model (the message gives the corrected statement), and `db update --db ` with no snapshot to resolve against (`MIGRATION.STATEMENT_ORIGIN_UNKNOWN`, advising `--advance-ref`) | | `rename-statements-migration.sqlite.e2e.test.ts` | The SQLite twin of journeys S1 and S2, driven through a file database and the SQLite facade config: the table and column renames, the index on the column dropped and created under its destination name, the foreign key from `Post` following the table, the byte-identical re-emit, the empty `db update --dry-run` after the renames, and the second `db update` refused | +| `value-object-checks.e2e.test.ts` | **Value object and discriminator checks (Postgres)**, each journey through `migration plan` and `migrate`, and through `db update`: V1 creates the check on a value object column; a row the ORM writes is accepted, and a row with an extra key or a missing member is refused naming the check. V2 adds a member, then makes one nullable: each drops the old check and adds the new one on a table whose rows fit. V3 adds a required member to a table holding a row without it: the apply fails naming the check and changes nothing. V4 adds a variant: the discriminator check is replaced and the new value is accepted. V5 changes a value object only an external table holds: the storage hash changes, the migration has no operations, and the apply moves the marker. Each ends with `db verify --schema-only` clean and a further plan empty | +| `value-object-validator.mongo.e2e.test.ts` | **A value object change on MongoDB**: adding a member to a value object updates the collection validator, through `migration plan` and `migrate` (M1) and through `db update` (M2); `db verify` is clean and a further plan is empty | ### Graph features and refs diff --git a/test/integration/test/cli-journeys/value-object-checks.e2e.test.ts b/test/integration/test/cli-journeys/value-object-checks.e2e.test.ts new file mode 100644 index 000000000000..6309cc635f1e --- /dev/null +++ b/test/integration/test/cli-journeys/value-object-checks.e2e.test.ts @@ -0,0 +1,483 @@ +/** + * Value objects and discriminators are storage entities, and Postgres enforces them with derived checks. + * + * `User.address` holds an `Address` value object in a `jsonb` column. On a managed table the build derives a check on the column, `user_address_value_object_`, which accepts the documents the ORM writes and refuses any other shape. `Task` is a base model whose variants are stored in their own tables; its discriminator column `type` gets a check on the variants' values, `task_type_check_`. + * + * Every journey runs twice: once through `migration plan` and `migrate`, once through `db update`. Each ends with `db verify --schema-only` clean and a further plan empty. + * + * - V1: the first migration creates the value object check. A row the ORM writes is accepted; a row with a key `Address` does not declare is refused, naming the check. + * - V2: adding a member, and then making a member nullable, each drop the old check and add the new one, on a table whose rows fit the new shape. + * - V3: adding a required member to a table that holds a row without it fails at apply, names the check, and changes nothing. + * - V4: adding a variant replaces the discriminator check, and the new variant's value is accepted. + * - V5: changing a value object that only an external table's column holds changes the storage hash and needs no operation: `migration plan` writes a migration with no operations, and `migrate` moves the marker. + */ + +import { readFileSync, writeFileSync } from 'node:fs'; +import type { Contract } from '@prisma/orm-postgres/contract/types'; +import type { SqlStorage } from '@prisma/orm-postgres/family-contract/types'; +import postgres from '@prisma/orm-postgres/runtime'; +import { contractFromJson } from '@prisma/orm-postgres/static'; +import { join } from 'pathe'; +import { describe, expect, it } from 'vitest'; +import { withTempDir } from '../utils/cli-test-helpers'; +import { + engineError, + getMigrationDirs, + type JourneyContext, + latestMigrationDirName, + parseJsonOutput, + planMigrationAndSelfEmit, + readEmittedContractStorageHash, + runContractEmit, + runDbUpdate, + runDbVerify, + runMigrate, + runMigrationPlan, + setupJourney, + sql, + timeouts, + useDevDatabase, +} from '../utils/journey-test-helpers'; + +type Via = 'migrate' | 'db update'; +const VIAS: readonly Via[] = ['migrate', 'db update']; + +function userContract(addressMembers: string): string { + return `// use prisma-8 + +type Address { +${addressMembers}} + +model User { + id Int @id + address Address? + @@map("user") +} +`; +} + +const ADDRESS = userContract(' street String\n city String\n'); +const ADDRESS_WITH_ZIP = userContract(' street String\n city String\n zip String?\n'); +const ADDRESS_WITH_OPTIONAL_CITY = userContract( + ' street String\n city String?\n zip String?\n', +); +const ADDRESS_WITH_COUNTRY = userContract(' street String\n city String\n country String\n'); + +function taskContract(variants: readonly string[]): string { + const models = variants.map( + (name) => ` +model ${name} { + note String? + @@base(Task, "${name.toLowerCase()}") + @@map("${name.toLowerCase()}") +} +`, + ); + return `// use prisma-8 + +model Task { + id Int @id + title String + type String + @@discriminator(type) + @@map("task") +} +${models.join('')}`; +} + +const ONE_VARIANT = taskContract(['Bug']); +const TWO_VARIANTS = taskContract(['Bug', 'Feature']); + +const AUTH_PROFILES_SQL = 'CREATE TABLE public.auth_profiles (id integer PRIMARY KEY, home jsonb)'; + +function externalContract(homeMembers: string): string { + return `// use prisma-8 + +type Home { +${homeMembers}} + +model User { + id Int @id + @@map("user") +} + +model AuthProfile { + id Int @id + home Home? + @@map("auth_profiles") + @@control(external) +} +`; +} + +const HOME = externalContract(' city String\n'); +const HOME_WITH_ZIP = externalContract(' city String\n zip String?\n'); + +interface PlannedOperation { + readonly id: string; + readonly label: string; +} + +async function emit(ctx: JourneyContext, psl: string, label: string): Promise { + writeFileSync(join(ctx.testDir, 'contract.prisma'), psl, 'utf-8'); + const emitted = await runContractEmit(ctx); + expect(emitted.exitCode, `${label}: emit: ${emitted.stderr}`).toBe(0); +} + +function journey(db: { readonly connectionString: string }, createTempDir: () => string) { + return setupJourney({ + connectionString: db.connectionString, + createTempDir, + contractMode: 'psl', + }); +} + +/** Plans from the latest migration, or from an empty database before the first one. */ +function fromLatest(ctx: JourneyContext): readonly string[] { + return getMigrationDirs(ctx).length === 0 ? [] : ['--from', latestMigrationDirName(ctx)]; +} + +/** Applies the emitted contract through `via` and returns the labels of the operations it planned. */ +async function apply(ctx: JourneyContext, via: Via, label: string): Promise { + if (via === 'migrate') { + const plan = await planMigrationAndSelfEmit(ctx, [ + ...fromLatest(ctx), + '--name', + label.replace(/\W/g, '-'), + '--json', + ]); + expect(plan.exitCode, `${label}: plan: ${plan.stderr}`).toBe(0); + const migrate = await runMigrate(ctx); + expect(migrate.exitCode, `${label}: migrate: ${migrate.stderr}`).toBe(0); + return plannedLabels(ctx); + } + const update = await runDbUpdate(ctx, ['--json']); + expect(update.exitCode, `${label}: db update: ${JSON.stringify(engineError(update))}`).toBe(0); + return parseJsonOutput<{ plan: { operations: readonly PlannedOperation[] } }>( + update, + ).plan.operations.map((operation) => operation.label); +} + +function plannedLabels(ctx: JourneyContext): readonly string[] { + const ops = JSON.parse( + readFileSync( + join(ctx.testDir, 'migrations/app', latestMigrationDirName(ctx), 'ops.json'), + 'utf-8', + ), + ) as readonly PlannedOperation[]; + return ops.map((operation) => operation.label); +} + +async function expectSettled(ctx: JourneyContext, via: Via, label: string): Promise { + const verify = await runDbVerify(ctx, ['--schema-only']); + expect(verify.exitCode, `${label}: db verify --schema-only: ${verify.stderr}`).toBe(0); + if (via === 'migrate') { + const count = getMigrationDirs(ctx).length; + const fresh = await runMigrationPlan(ctx, [...fromLatest(ctx), '--json']); + expect(fresh.exitCode, `${label}: plan with no change: ${fresh.stderr}`).toBe(0); + expect(parseJsonOutput<{ noOp: boolean }>(fresh).noOp, `${label}: plan is empty`).toBe(true); + expect(getMigrationDirs(ctx), `${label}: nothing written`).toHaveLength(count); + return; + } + const dryRun = await runDbUpdate(ctx, ['--dry-run', '--json']); + expect(dryRun.exitCode, `${label}: db update --dry-run: ${dryRun.stderr}`).toBe(0); + expect( + parseJsonOutput<{ plan: { operations: readonly PlannedOperation[] } }>(dryRun).plan.operations, + `${label}: nothing left to plan`, + ).toEqual([]); +} + +async function checkNames(connectionString: string, table: string): Promise { + const { rows } = await sql( + connectionString, + `SELECT conname FROM pg_constraint WHERE conrelid = $1::regclass AND contype = 'c' ORDER BY conname`, + [`public."${table}"`], + ); + return rows.map((row) => String(row['conname'])); +} + +async function markerHash(connectionString: string): Promise { + const { rows } = await sql( + connectionString, + "SELECT core_hash FROM prisma_contract.marker WHERE space = 'app'", + ); + return rows[0]?.['core_hash']; +} + +async function insertError(connectionString: string, query: string): Promise { + try { + await sql(connectionString, query); + } catch (error) { + return error instanceof Error ? error.message : String(error); + } + return 'the insert was accepted'; +} + +interface UserOrm { + readonly public: { + readonly User: { create(row: Record): Promise }; + }; +} + +/** Writes a user through the ORM of the emitted contract. */ +async function createUserThroughOrm( + ctx: JourneyContext, + connectionString: string, + row: Record, +): Promise { + const contractJson = JSON.parse(readFileSync(join(ctx.testDir, 'contract.json'), 'utf-8')); + const client = postgres({ + contract: contractFromJson>(contractJson), + url: connectionString, + }); + const runtime = await client.connect(); + try { + return await (client.orm as unknown as UserOrm).public.User.create(row); + } finally { + await runtime.close(); + } +} + +withTempDir(({ createTempDir }) => { + describe.each(VIAS)('V1 (%s): a value object column gets a check', (via) => { + const db = useDevDatabase(); + + it( + 'creates the check, accepts a row the ORM writes and refuses a row with an extra key', + async () => { + const ctx = journey(db, createTempDir); + await emit(ctx, ADDRESS, 'V1.01'); + const labels = await apply(ctx, via, 'V1.02'); + expect(labels.join('\n'), 'V1.02: the table is created').toContain('Create table "user"'); + if (via === 'migrate') { + expect( + readFileSync( + join(ctx.testDir, 'migrations/app', latestMigrationDirName(ctx), 'ops.json'), + 'utf-8', + ), + 'V1.02: the migration writes the check', + ).toMatch(/CONSTRAINT \\"user_address_value_object_[0-9a-f]{8}\\" CHECK/); + } + expect(await checkNames(db.connectionString, 'user'), 'V1.02: the check').toEqual([ + expect.stringMatching(/^user_address_value_object_[0-9a-f]{8}$/), + ]); + + expect( + await createUserThroughOrm(ctx, db.connectionString, { + id: 1, + address: { street: 'Main St', city: 'Oslo' }, + }), + 'V1.03: the ORM writes an address', + ).toMatchObject({ id: 1, address: { street: 'Main St', city: 'Oslo' } }); + await sql(db.connectionString, `INSERT INTO "user" (id, address) VALUES (2, NULL)`); + + const [check] = await checkNames(db.connectionString, 'user'); + expect( + await insertError( + db.connectionString, + `INSERT INTO "user" (id, address) VALUES (3, '{"street": "Main St", "city": "Oslo", "floor": 2}')`, + ), + 'V1.04: an extra key is refused', + ).toContain(`violates check constraint "${check}"`); + expect( + await insertError( + db.connectionString, + `INSERT INTO "user" (id, address) VALUES (3, '{"street": "Main St"}')`, + ), + 'V1.05: a missing member is refused', + ).toContain(`violates check constraint "${check}"`); + + const verify = await runDbVerify(ctx); + expect(verify.exitCode, `V1.06: db verify: ${verify.stderr}`).toBe(0); + await expectSettled(ctx, via, 'V1.07'); + }, + timeouts.spinUpPpgDev, + ); + }); + + describe.each(VIAS)('V2 (%s): changing a value object replaces its check', (via) => { + const db = useDevDatabase(); + + it( + 'drops the old check and adds the new one when a member is added, then when one becomes nullable', + async () => { + const ctx = journey(db, createTempDir); + await emit(ctx, ADDRESS, 'V2.01'); + await apply(ctx, via, 'V2.02'); + await sql( + db.connectionString, + `INSERT INTO "user" (id, address) VALUES (1, '{"street": "Main St", "city": "Oslo"}')`, + ); + const [first] = await checkNames(db.connectionString, 'user'); + + await emit(ctx, ADDRESS_WITH_ZIP, 'V2.03'); + expect(await apply(ctx, via, 'V2.04'), 'V2.04: a member added').toEqual([ + `Drop check constraint "${first}" on "user"`, + expect.stringMatching( + /^Add check constraint "user_address_value_object_[0-9a-f]{8}" on "user"$/, + ), + ]); + const [second] = await checkNames(db.connectionString, 'user'); + expect(second, 'V2.04: a new check').not.toBe(first); + await sql( + db.connectionString, + `INSERT INTO "user" (id, address) VALUES (2, '{"street": "Elm St", "city": "Bergen", "zip": "5003"}')`, + ); + await expectSettled(ctx, via, 'V2.05'); + + await emit(ctx, ADDRESS_WITH_OPTIONAL_CITY, 'V2.06'); + expect(await apply(ctx, via, 'V2.07'), 'V2.07: a member made nullable').toEqual([ + `Drop check constraint "${second}" on "user"`, + expect.stringMatching( + /^Add check constraint "user_address_value_object_[0-9a-f]{8}" on "user"$/, + ), + ]); + await sql( + db.connectionString, + `INSERT INTO "user" (id, address) VALUES (3, '{"street": "Elm St", "city": null}')`, + ); + expect( + (await sql(db.connectionString, 'SELECT id FROM "user" ORDER BY id')).rows, + 'V2.07: the rows are kept', + ).toEqual([{ id: 1 }, { id: 2 }, { id: 3 }]); + await expectSettled(ctx, via, 'V2.08'); + }, + timeouts.spinUpPpgDev, + ); + }); + + describe.each(VIAS)('V3 (%s): a row that does not fit the new check', (via) => { + const db = useDevDatabase(); + + it( + 'fails the apply, names the check and changes nothing', + async () => { + const ctx = journey(db, createTempDir); + await emit(ctx, ADDRESS, 'V3.01'); + await apply(ctx, via, 'V3.02'); + await sql( + db.connectionString, + `INSERT INTO "user" (id, address) VALUES (1, '{"street": "Main St", "city": "Oslo"}')`, + ); + const before = await checkNames(db.connectionString, 'user'); + const marker = await markerHash(db.connectionString); + + await emit(ctx, ADDRESS_WITH_COUNTRY, 'V3.03'); + let failed: Parameters[0]; + if (via === 'migrate') { + const plan = await planMigrationAndSelfEmit(ctx, [ + ...fromLatest(ctx), + '--name', + 'country', + ]); + expect(plan.exitCode, `V3.04: plan: ${plan.stderr}`).toBe(0); + failed = await runMigrate(ctx, ['--json']); + } else { + failed = await runDbUpdate(ctx, ['--json']); + } + expect(failed.exitCode, 'V3.04: the apply fails').not.toBe(0); + expect(JSON.stringify(engineError(failed)), 'V3.04: the failure names the check').toMatch( + /user_address_value_object_[0-9a-f]{8}/, + ); + + expect(await checkNames(db.connectionString, 'user'), 'V3.05: the old check stays').toEqual( + before, + ); + expect(await markerHash(db.connectionString), 'V3.05: the marker stays').toBe(marker); + expect( + (await sql(db.connectionString, 'SELECT id, address FROM "user"')).rows, + 'V3.05: the row stays', + ).toEqual([{ id: 1, address: { street: 'Main St', city: 'Oslo' } }]); + }, + timeouts.spinUpPpgDev, + ); + }); + + describe.each(VIAS)('V4 (%s): adding a variant', (via) => { + const db = useDevDatabase(); + + it( + "replaces the discriminator check, and the new variant's value is accepted", + async () => { + const ctx = journey(db, createTempDir); + await emit(ctx, ONE_VARIANT, 'V4.01'); + await apply(ctx, via, 'V4.02'); + const [first] = await checkNames(db.connectionString, 'task'); + expect(first, 'V4.02: the discriminator check').toMatch(/^task_type_check_[0-9a-f]{8}$/); + await sql( + db.connectionString, + `INSERT INTO "task" (id, title, type) VALUES (1, 'a', 'bug')`, + ); + expect( + await insertError( + db.connectionString, + `INSERT INTO "task" (id, title, type) VALUES (2, 'b', 'feature')`, + ), + 'V4.03: a value no variant names is refused', + ).toContain(`violates check constraint "${first}"`); + + await emit(ctx, TWO_VARIANTS, 'V4.04'); + const labels = await apply(ctx, via, 'V4.05'); + expect( + labels.filter((label) => label.includes('check constraint')), + 'V4.05: the check is replaced', + ).toEqual([ + `Drop check constraint "${first}" on "task"`, + expect.stringMatching(/^Add check constraint "task_type_check_[0-9a-f]{8}" on "task"$/), + ]); + await sql( + db.connectionString, + `INSERT INTO "task" (id, title, type) VALUES (2, 'b', 'feature')`, + ); + expect( + (await sql(db.connectionString, 'SELECT id, type FROM "task" ORDER BY id')).rows, + 'V4.06: both variants are stored', + ).toEqual([ + { id: 1, type: 'bug' }, + { id: 2, type: 'feature' }, + ]); + await expectSettled(ctx, via, 'V4.07'); + }, + timeouts.spinUpPpgDev, + ); + }); + + describe.each(VIAS)('V5 (%s): a value object only an external table holds', (via) => { + const db = useDevDatabase({ onReady: (cs) => sql(cs, AUTH_PROFILES_SQL) }); + + it( + 'changes the storage hash with no operation, and the apply moves the marker', + async () => { + const ctx = journey(db, createTempDir); + await emit(ctx, HOME, 'V5.01'); + await apply(ctx, via, 'V5.02'); + expect(await checkNames(db.connectionString, 'auth_profiles'), 'V5.02: no check').toEqual( + [], + ); + const before = readEmittedContractStorageHash({ ...ctx, outputDir: ctx.testDir }); + + await emit(ctx, HOME_WITH_ZIP, 'V5.03'); + const after = readEmittedContractStorageHash({ ...ctx, outputDir: ctx.testDir }); + expect(after, 'V5.03: the storage hash changes').not.toBe(before); + if (via === 'migrate') { + const plan = await runMigrationPlan(ctx, [...fromLatest(ctx), '--name', 'zip', '--json']); + expect(plan.exitCode, `V5.04: plan: ${plan.stderr}`).toBe(0); + expect( + parseJsonOutput<{ operations: readonly unknown[]; summary: string }>(plan), + 'V5.04: no operations', + ).toMatchObject({ + operations: [], + summary: expect.stringContaining('What changed: valueObject "public.Home".'), + }); + const migrate = await runMigrate(ctx); + expect(migrate.exitCode, `V5.05: migrate: ${migrate.stderr}`).toBe(0); + } else { + expect(await apply(ctx, via, 'V5.04'), 'V5.04: no operations').toEqual([]); + } + expect(await markerHash(db.connectionString), 'V5.05: the marker').toBe(after); + await expectSettled(ctx, via, 'V5.06'); + }, + timeouts.spinUpPpgDev, + ); + }); +}); diff --git a/test/integration/test/cli-journeys/value-object-validator.mongo.e2e.test.ts b/test/integration/test/cli-journeys/value-object-validator.mongo.e2e.test.ts new file mode 100644 index 000000000000..070c1b71617a --- /dev/null +++ b/test/integration/test/cli-journeys/value-object-validator.mongo.e2e.test.ts @@ -0,0 +1,226 @@ +/** + * A value object change updates the MongoDB collection validator, now that value objects are storage entities. + * + * `User.address` holds an `Address` subdocument, and the `users` validator lists its members. Adding `zip` to `Address` changes the storage hash and the validator. Journey M1 plans the change with `migration plan` and applies it with `migrate`; journey M2 applies it with `db update`. Each ends with `db verify` clean and a further plan empty. + */ + +import { mkdirSync, writeFileSync } from 'node:fs'; +import { rm } from 'node:fs/promises'; +import { join } from 'node:path'; +import { timeouts } from '@repo/test-utils'; +import { type Db, MongoClient } from 'mongodb'; +import { MongoMemoryReplSet } from 'mongodb-memory-server'; +import { afterAll, afterEach, beforeAll, describe, expect, it } from 'vitest'; +import { fixtureAppDir } from '../utils/cli-test-helpers'; +import { + getMigrationDirs, + type JourneyContext, + latestMigrationDirName, + parseJsonOutput, + readEmittedContractStorageHash, + runContractEmit, + runDbUpdate, + runDbVerify, + runMigrate, + runMigrationPlan, +} from '../utils/journey-test-helpers'; + +function contractWith(addressMembers: string): string { + return `// use prisma-8 + +type Address { +${addressMembers}} + +model User { + id ObjectId @id @map("_id") + address Address? + @@map("users") +} +`; +} + +const ADDRESS = contractWith(' street String\n city String\n'); +const ADDRESS_WITH_ZIP = contractWith(' street String\n city String\n zip String?\n'); + +function withDatabase(baseUri: string, dbName: string): string { + const url = new URL(baseUri); + url.pathname = `/${dbName}`; + return url.toString(); +} + +function setupProject(connectionString: string): JourneyContext { + const testDir = join( + fixtureAppDir, + `test-mongo-value-object-${Date.now()}-${Math.random().toString(36).slice(2)}`, + ); + const outputDir = join(testDir, 'output'); + mkdirSync(outputDir, { recursive: true }); + mkdirSync(join(testDir, 'migrations'), { recursive: true }); + writeFileSync( + join(testDir, 'package.json'), + JSON.stringify({ + name: 'mongo-value-object-journey', + private: true, + type: 'module', + dependencies: { '@prisma/orm-mongo': 'workspace:0.16.0' }, + }), + 'utf-8', + ); + const configPath = join(testDir, 'prisma.config.ts'); + writeFileSync( + configPath, + [ + "import { defineConfig as ormConfig } from '@prisma/orm-mongo/config';", + "import { definePrismaConfig } from '@prisma/cli-engine';", + '', + 'export default definePrismaConfig({', + ' orm: ormConfig({', + " contract: './contract.prisma',", + " output: 'output',", + ` db: { connection: ${JSON.stringify(connectionString)} },`, + ' }),', + '});', + '', + ].join('\n'), + ); + return { testDir, configPath, outputDir }; +} + +async function emit(ctx: JourneyContext, psl: string, label: string): Promise { + writeFileSync(join(ctx.testDir, 'contract.prisma'), psl, 'utf-8'); + const emitted = await runContractEmit(ctx); + expect(emitted.exitCode, `${label}: emit: ${emitted.stderr}`).toBe(0); +} + +interface AddressSchema { + readonly properties?: Record; + readonly required?: readonly string[]; +} + +/** The members the `users` validator lists for `address`, and which of them it requires. */ +async function addressInValidator(db: Db): Promise<{ members: string[]; required: string[] }> { + const [collection] = await db.listCollections({ name: 'users' }).toArray(); + const schema = ( + collection as { + options?: { validator?: { $jsonSchema?: { properties?: Record } } }; + } + )?.options?.validator?.$jsonSchema?.properties?.['address']; + const objectSchemas = [schema, ...((schema as { oneOf?: unknown[] })?.oneOf ?? [])].filter( + (entry): entry is AddressSchema => + typeof entry === 'object' && entry !== null && 'properties' in entry, + ); + const [object] = objectSchemas; + return { + members: Object.keys(object?.properties ?? {}).sort(), + required: [...(object?.required ?? [])].sort(), + }; +} + +async function markerHash(db: Db): Promise { + const marker = await db + .collection('_prisma_migrations') + .findOne({ space: 'app', storageHash: { $exists: true } }); + return marker?.['storageHash']; +} + +describe('Journeys (MongoDB): a value object change updates the collection validator', { + timeout: timeouts.spinUpMongoMemoryServer, +}, () => { + let replSet: MongoMemoryReplSet; + let client: MongoClient; + const created = new Set(); + + beforeAll(async () => { + replSet = await MongoMemoryReplSet.create({ + instanceOpts: [ + { launchTimeout: timeouts.spinUpMongoMemoryServer, storageEngine: 'wiredTiger' }, + ], + replSet: { count: 1, storageEngine: 'wiredTiger' }, + }); + client = new MongoClient(replSet.getUri()); + await client.connect(); + }, timeouts.spinUpMongoMemoryServer); + + afterEach(async () => { + for (const dir of created) { + await rm(dir, { recursive: true, force: true }).catch(() => {}); + } + created.clear(); + }); + + afterAll(async () => { + await client?.close().catch(() => {}); + await replSet?.stop().catch(() => {}); + }, timeouts.spinUpMongoMemoryServer); + + it('M1: migration plan and migrate add the member to the validator', async () => { + const dbName = 'mongo_value_object_plan'; + const db = client.db(dbName); + const ctx = setupProject(withDatabase(replSet.getUri(), dbName)); + created.add(ctx.testDir); + await emit(ctx, ADDRESS, 'M1.01'); + const initial = await runMigrationPlan(ctx, ['--name', 'initial']); + expect(initial.exitCode, `M1.02: plan initial: ${initial.stderr}`).toBe(0); + expect((await runMigrate(ctx)).exitCode, 'M1.02: migrate initial').toBe(0); + expect(await addressInValidator(db), 'M1.02: the validator').toEqual({ + members: ['city', 'street'], + required: ['city', 'street'], + }); + await db.collection('users').insertOne({ address: { street: 'Main St', city: 'Oslo' } }); + const origin = latestMigrationDirName(ctx); + + await emit(ctx, ADDRESS_WITH_ZIP, 'M1.03'); + const plan = await runMigrationPlan(ctx, ['--from', origin, '--name', 'zip', '--json']); + expect(plan.exitCode, `M1.04: plan: ${plan.stderr}`).toBe(0); + expect( + parseJsonOutput<{ operations: readonly { label: string }[] }>(plan).operations.map( + ({ label }) => label, + ), + 'M1.04: the validator is updated', + ).toEqual([expect.stringContaining('validator')]); + const migrate = await runMigrate(ctx); + expect(migrate.exitCode, `M1.05: migrate: ${migrate.stderr}`).toBe(0); + + expect(await addressInValidator(db), 'M1.05: the validator lists zip').toEqual({ + members: ['city', 'street', 'zip'], + required: ['city', 'street'], + }); + expect(await markerHash(db), 'M1.05: the marker').toBe(readEmittedContractStorageHash(ctx)); + await db + .collection('users') + .insertOne({ address: { street: 'Elm St', city: 'Bergen', zip: '5003' } }); + const verify = await runDbVerify(ctx); + expect(verify.exitCode, `M1.06: db verify: ${verify.stderr}`).toBe(0); + const count = getMigrationDirs(ctx).length; + const fresh = await runMigrationPlan(ctx, ['--from', latestMigrationDirName(ctx), '--json']); + expect(parseJsonOutput<{ noOp: boolean }>(fresh).noOp, 'M1.07: plan is empty').toBe(true); + expect(getMigrationDirs(ctx), 'M1.07: nothing written').toHaveLength(count); + }); + + it('M2: db update adds the member to the validator', async () => { + const dbName = 'mongo_value_object_update'; + const db = client.db(dbName); + const ctx = setupProject(withDatabase(replSet.getUri(), dbName)); + created.add(ctx.testDir); + await emit(ctx, ADDRESS, 'M2.01'); + const initial = await runDbUpdate(ctx); + expect(initial.exitCode, `M2.02: db update initial: ${initial.stderr}`).toBe(0); + await db.collection('users').insertOne({ address: { street: 'Main St', city: 'Oslo' } }); + + await emit(ctx, ADDRESS_WITH_ZIP, 'M2.03'); + const update = await runDbUpdate(ctx, ['--json']); + expect(update.exitCode, `M2.04: db update: ${update.stderr}`).toBe(0); + expect(await addressInValidator(db), 'M2.04: the validator lists zip').toEqual({ + members: ['city', 'street', 'zip'], + required: ['city', 'street'], + }); + expect(await markerHash(db), 'M2.04: the marker').toBe(readEmittedContractStorageHash(ctx)); + const verify = await runDbVerify(ctx); + expect(verify.exitCode, `M2.05: db verify: ${verify.stderr}`).toBe(0); + const dryRun = await runDbUpdate(ctx, ['--dry-run', '--json']); + expect( + parseJsonOutput<{ plan: { operations: readonly unknown[] } }>(dryRun).plan.operations, + 'M2.06: nothing left to plan', + ).toEqual([]); + }); +}); diff --git a/test/integration/test/fixtures/cli/cli-e2e-test-app/fixtures/mongo-cli-journeys/contract-external-auth-users.ts b/test/integration/test/fixtures/cli/cli-e2e-test-app/fixtures/mongo-cli-journeys/contract-external-auth-users.ts index 152950801af0..8dd620c6fd58 100644 --- a/test/integration/test/fixtures/cli/cli-e2e-test-app/fixtures/mongo-cli-journeys/contract-external-auth-users.ts +++ b/test/integration/test/fixtures/cli/cli-e2e-test-app/fixtures/mongo-cli-journeys/contract-external-auth-users.ts @@ -1,4 +1,4 @@ -import { defineContract, field, index, model } from '@internal/mongo/contract-builder'; +import { defineContract, field, model } from '@internal/mongo/contract-builder'; const User = model('User', { collection: 'users', From ac65eca0cbf8aec914aef408835eefb2899676c8 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 16:56:55 +0200 Subject: [PATCH 059/104] TML-3537: the SQL ORM fixture no longer patches a value object into its built contract, so its storage hash is the one the builder computed Signed-off-by: willbot Signed-off-by: Will Madden --- .../test/sql-orm-client/codec-async.test.ts | 2 +- .../test/sql-orm-client/fixtures/contract.ts | 65 +------------------ .../fixtures/generated/contract.d.ts | 50 ++------------ .../fixtures/generated/contract.json | 34 +--------- 4 files changed, 8 insertions(+), 143 deletions(-) diff --git a/test/integration/test/sql-orm-client/codec-async.test.ts b/test/integration/test/sql-orm-client/codec-async.test.ts index bd3a53e8f2a3..48a2a59bc3e6 100644 --- a/test/integration/test/sql-orm-client/codec-async.test.ts +++ b/test/integration/test/sql-orm-client/codec-async.test.ts @@ -3,7 +3,7 @@ * * `Post.embedding` flows through the `pg/vector@1` codec from `@internal/extension-pgvector`. The pgvector codec's `encode` and `decode` are authored synchronously, but the codec base in `framework-components` lifts them to Promise-returning at the boundary, so this column exercises the runtime's async dispatch path on every read and write. * - * `User.address` flows through `pg/jsonb@1` (a built-in `adapter-postgres` codec, also lifted to async by the same boundary) backed by the `Address` value object. Adding a second codec with a different wire shape gives us "mixed sync/async codec columns" in a single integration run. + * `User.address` flows through `pg/jsonb@1` (a built-in `adapter-postgres` codec, also lifted to async by the same boundary). Adding a second codec with a different wire shape gives us "mixed sync/async codec columns" in a single integration run. * * The tests below verify: * diff --git a/test/integration/test/sql-orm-client/fixtures/contract.ts b/test/integration/test/sql-orm-client/fixtures/contract.ts index dc8eada6def8..4da5fccffde5 100644 --- a/test/integration/test/sql-orm-client/fixtures/contract.ts +++ b/test/integration/test/sql-orm-client/fixtures/contract.ts @@ -167,7 +167,7 @@ const User = UserBase.relations({ ], })); -const baseContract = defineContract({ +export const contract = defineContract({ extensions: { pgvector }, models: { User, @@ -183,66 +183,3 @@ const baseContract = defineContract({ ProjectLink, }, }); - -const defaultNamespace = baseContract.domain.namespaces['public']!; -const userModel = defaultNamespace.models['User']!; -const publicStorage = baseContract.storage.namespaces['public']!; - -const textMember = (nullable: Nullable) => ({ - many: false as const, - nullable, - type: { kind: 'scalar' as const, codecId: 'pg/text@1' as const }, -}); - -export const contract = { - ...baseContract, - storage: { - ...baseContract.storage, - namespaces: { - ...baseContract.storage.namespaces, - public: { - ...publicStorage, - entries: { - ...publicStorage.entries, - valueObject: { - Address: { - fields: { - street: textMember(false), - city: textMember(false), - zip: textMember(true), - }, - }, - }, - }, - }, - }, - }, - domain: { - namespaces: { - ...baseContract.domain.namespaces, - public: { - ...defaultNamespace, - models: { - ...defaultNamespace.models, - User: { - ...userModel, - fields: { - ...userModel.fields, - address: { - many: false as const, - nullable: true as const, - type: { - kind: 'valueObject' as const, - plane: 'storage' as const, - namespaceId: 'public' as const, - entityKind: 'valueObject' as const, - entityName: 'Address' as const, - }, - }, - }, - }, - }, - }, - }, - }, -}; diff --git a/test/integration/test/sql-orm-client/fixtures/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/generated/contract.d.ts index 21a93183df43..6a539adf4199 100644 --- a/test/integration/test/sql-orm-client/fixtures/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/generated/contract.d.ts @@ -25,7 +25,6 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromStorage, } from '@internal/contract/types'; export type StorageHash = @@ -245,38 +244,21 @@ type DefaultLiteralValue = CodecId extends keyo : Encoded; type ParameterizedOutputTypeFunctions = PgParameterizedOutputTypes | PgVectorParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromStorage< - ContractBase['storage']['namespaces'], - CodecTypes, - ParameterizedOutputTypeFunctions, - FieldTypeOverrides, - 'output' ->; -type ValueObjectInputTypes = ValueObjectTypesFromStorage< - ContractBase['storage']['namespaces'], - CodecTypes, - ParameterizedOutputTypeFunctions, - FieldTypeOverrides, - 'input' ->; -export type AddressOutput = ValueObjectOutputTypes['public']['Address']; -export type AddressInput = ValueObjectInputTypes['public']['Address']; + type FieldTypeOverrides = {}; export type FieldOutputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output', - ContractBase['storage']['namespaces'] + 'output' >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input', - ContractBase['storage']['namespaces'] + 'input' >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< @@ -821,24 +803,6 @@ type ContractBase = Omit< ]; }; }; - readonly valueObject: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - }; - }; }; }; }; @@ -1157,13 +1121,7 @@ type ContractBase = Omit< readonly fields: { readonly address: { readonly nullable: true; - readonly type: { - readonly kind: 'valueObject'; - readonly plane: 'storage'; - readonly namespaceId: 'public'; - readonly entityKind: 'valueObject'; - readonly entityName: 'Address'; - }; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/jsonb@1' }; }; readonly email: { readonly nullable: false; diff --git a/test/integration/test/sql-orm-client/fixtures/generated/contract.json b/test/integration/test/sql-orm-client/fixtures/generated/contract.json index d753a0f62643..2dc5dd47490e 100644 --- a/test/integration/test/sql-orm-client/fixtures/generated/contract.json +++ b/test/integration/test/sql-orm-client/fixtures/generated/contract.json @@ -505,11 +505,8 @@ "address": { "nullable": true, "type": { - "entityKind": "valueObject", - "entityName": "Address", - "kind": "valueObject", - "namespaceId": "public", - "plane": "storage" + "codecId": "pg/jsonb@1", + "kind": "scalar" } }, "email": { @@ -1295,33 +1292,6 @@ } ] } - }, - "valueObject": { - "Address": { - "fields": { - "city": { - "nullable": false, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" - } - }, - "street": { - "nullable": false, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" - } - }, - "zip": { - "nullable": true, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" - } - } - } - } } }, "id": "public", From b4d1c86900a71adee629a38948b4bcd80f6ab6c4 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 17:02:00 +0200 Subject: [PATCH 060/104] TML-3537: the SQL ORM value object type tests read an emitted PSL fixture, since the SQL TypeScript builder has no value objects Signed-off-by: willbot Signed-off-by: Will Madden --- .../3-extensions/sql-orm-client/package.json | 4 +- .../test/codec-async.types.test-d.ts | 17 +- .../test/codec-field-parity.types.test-d.ts | 70 ++- .../test/fixtures/generated/contract.d.ts | 50 +- .../test/fixtures/generated/contract.json | 34 +- .../value-objects/generated/contract.d.ts | 453 ++++++++++++++++++ .../value-objects/generated/contract.json | 172 +++++++ .../test/simplify-deep.test-d.ts | 31 +- .../test/value-object-inputs.test-d.ts | 2 +- .../fixtures/value-objects/contract.prisma | 18 + .../value-objects/generated/contract.d.ts | 453 ++++++++++++++++++ .../value-objects/generated/contract.json | 172 +++++++ .../fixtures/value-objects/prisma.config.ts | 9 + 13 files changed, 1349 insertions(+), 136 deletions(-) create mode 100644 packages/3-extensions/sql-orm-client/test/fixtures/value-objects/generated/contract.d.ts create mode 100644 packages/3-extensions/sql-orm-client/test/fixtures/value-objects/generated/contract.json create mode 100644 test/integration/test/sql-orm-client/fixtures/value-objects/contract.prisma create mode 100644 test/integration/test/sql-orm-client/fixtures/value-objects/generated/contract.d.ts create mode 100644 test/integration/test/sql-orm-client/fixtures/value-objects/generated/contract.json create mode 100644 test/integration/test/sql-orm-client/fixtures/value-objects/prisma.config.ts diff --git a/packages/3-extensions/sql-orm-client/package.json b/packages/3-extensions/sql-orm-client/package.json index 796c76ecef52..836e54fec5ba 100644 --- a/packages/3-extensions/sql-orm-client/package.json +++ b/packages/3-extensions/sql-orm-client/package.json @@ -8,8 +8,8 @@ "description": "ORM client for Prisma 8 — fluent, type-safe model collections", "scripts": { "build": "tsdown", - "emit": "cd ../../../test/integration && node ../../packages/1-framework/3-tooling/cli/dist/bin.mjs contract emit --config test/sql-orm-client/fixtures/prisma.config.ts && node ../../packages/1-framework/3-tooling/cli/dist/bin.mjs contract emit --config test/sql-orm-client/fixtures/polymorphism/prisma.config.ts && node ../../packages/1-framework/3-tooling/cli/dist/bin.mjs contract emit --config test/sql-orm-client/fixtures/polymorphism-self-relation/prisma.config.ts && node ../../packages/1-framework/3-tooling/cli/dist/bin.mjs contract emit --config test/sql-orm-client/fixtures/polymorphism-namespaces/prisma.config.ts && node ../../packages/1-framework/3-tooling/cli/dist/bin.mjs contract emit --config test/sql-orm-client/fixtures/soft-delete/prisma.config.ts && node ../../packages/1-framework/3-tooling/cli/dist/bin.mjs contract emit --config test/sql-orm-client/fixtures/fragment-namespace/prisma.config.ts && node ../../packages/1-framework/3-tooling/cli/dist/bin.mjs contract emit --config test/sql-orm-client/fixtures/scope-namespace/prisma.config.ts && node ../../packages/1-framework/3-tooling/cli/dist/bin.mjs contract emit --config test/sql-orm-client/fixtures/junction-namespaces/prisma.config.ts && node ../../packages/1-framework/3-tooling/cli/dist/bin.mjs contract emit --config test/sql-orm-client/fixtures/execution-defaulted-tags/prisma.config.ts && node ../../packages/1-framework/3-tooling/cli/dist/bin.mjs contract emit --config test/sql-orm-client/fixtures/mn-psl/prisma.config.ts && node ../../packages/1-framework/3-tooling/cli/dist/bin.mjs contract emit --config test/sql-orm-client/fixtures/self-relations/prisma.config.ts && node ../../packages/1-framework/3-tooling/cli/dist/bin.mjs contract emit --config test/sql-orm-client/fixtures/collection-methods/prisma.config.ts && node ../../packages/1-framework/3-tooling/cli/dist/bin.mjs contract emit --config test/sql-orm-client/fixtures/on-update-only/prisma.config.ts && cp test/sql-orm-client/fixtures/generated/contract.json test/sql-orm-client/fixtures/generated/contract.d.ts ../../packages/3-extensions/sql-orm-client/test/fixtures/generated/ && cp test/sql-orm-client/fixtures/junction-namespaces/generated/contract.json test/sql-orm-client/fixtures/junction-namespaces/generated/contract.d.ts ../../packages/3-extensions/sql-orm-client/test/fixtures/junction-namespaces/generated/ && cp test/sql-orm-client/fixtures/polymorphism/generated/contract.json test/sql-orm-client/fixtures/polymorphism/generated/contract.d.ts ../../packages/3-extensions/sql-orm-client/test/fixtures/polymorphism/generated/ && cp test/sql-orm-client/fixtures/soft-delete/generated/contract.json test/sql-orm-client/fixtures/soft-delete/generated/contract.d.ts ../../packages/3-extensions/sql-orm-client/test/fixtures/soft-delete/generated/ && cp test/sql-orm-client/fixtures/fragment-namespace/generated/contract.json test/sql-orm-client/fixtures/fragment-namespace/generated/contract.d.ts ../../packages/3-extensions/sql-orm-client/test/fixtures/fragment-namespace/generated/ && cp test/sql-orm-client/fixtures/scope-namespace/generated/contract.json test/sql-orm-client/fixtures/scope-namespace/generated/contract.d.ts ../../packages/3-extensions/sql-orm-client/test/fixtures/scope-namespace/generated/ && cp test/sql-orm-client/fixtures/collection-methods/generated/contract.json test/sql-orm-client/fixtures/collection-methods/generated/contract.d.ts ../../packages/3-extensions/sql-orm-client/test/fixtures/collection-methods/generated/ && cp test/sql-orm-client/fixtures/on-update-only/generated/contract.json test/sql-orm-client/fixtures/on-update-only/generated/contract.d.ts ../../packages/3-extensions/sql-orm-client/test/fixtures/on-update-only/generated/ && cd ../../packages/3-extensions/sql-orm-client && node scripts/strip-pgvector-fixture.mjs test/fixtures/generated/contract.d.ts", - "emit:check": "pnpm emit && git diff --exit-code test/fixtures/generated/ test/fixtures/junction-namespaces/generated/ test/fixtures/polymorphism/generated/ test/fixtures/soft-delete/generated/ test/fixtures/fragment-namespace/generated/ test/fixtures/scope-namespace/generated/ test/fixtures/collection-methods/generated/ test/fixtures/on-update-only/generated/", + "emit": "cd ../../../test/integration && node ../../packages/1-framework/3-tooling/cli/dist/bin.mjs contract emit --config test/sql-orm-client/fixtures/prisma.config.ts && node ../../packages/1-framework/3-tooling/cli/dist/bin.mjs contract emit --config test/sql-orm-client/fixtures/polymorphism/prisma.config.ts && node ../../packages/1-framework/3-tooling/cli/dist/bin.mjs contract emit --config test/sql-orm-client/fixtures/polymorphism-self-relation/prisma.config.ts && node ../../packages/1-framework/3-tooling/cli/dist/bin.mjs contract emit --config test/sql-orm-client/fixtures/polymorphism-namespaces/prisma.config.ts && node ../../packages/1-framework/3-tooling/cli/dist/bin.mjs contract emit --config test/sql-orm-client/fixtures/soft-delete/prisma.config.ts && node ../../packages/1-framework/3-tooling/cli/dist/bin.mjs contract emit --config test/sql-orm-client/fixtures/value-objects/prisma.config.ts && node ../../packages/1-framework/3-tooling/cli/dist/bin.mjs contract emit --config test/sql-orm-client/fixtures/fragment-namespace/prisma.config.ts && node ../../packages/1-framework/3-tooling/cli/dist/bin.mjs contract emit --config test/sql-orm-client/fixtures/scope-namespace/prisma.config.ts && node ../../packages/1-framework/3-tooling/cli/dist/bin.mjs contract emit --config test/sql-orm-client/fixtures/junction-namespaces/prisma.config.ts && node ../../packages/1-framework/3-tooling/cli/dist/bin.mjs contract emit --config test/sql-orm-client/fixtures/execution-defaulted-tags/prisma.config.ts && node ../../packages/1-framework/3-tooling/cli/dist/bin.mjs contract emit --config test/sql-orm-client/fixtures/mn-psl/prisma.config.ts && node ../../packages/1-framework/3-tooling/cli/dist/bin.mjs contract emit --config test/sql-orm-client/fixtures/self-relations/prisma.config.ts && node ../../packages/1-framework/3-tooling/cli/dist/bin.mjs contract emit --config test/sql-orm-client/fixtures/collection-methods/prisma.config.ts && node ../../packages/1-framework/3-tooling/cli/dist/bin.mjs contract emit --config test/sql-orm-client/fixtures/on-update-only/prisma.config.ts && cp test/sql-orm-client/fixtures/generated/contract.json test/sql-orm-client/fixtures/generated/contract.d.ts ../../packages/3-extensions/sql-orm-client/test/fixtures/generated/ && cp test/sql-orm-client/fixtures/junction-namespaces/generated/contract.json test/sql-orm-client/fixtures/junction-namespaces/generated/contract.d.ts ../../packages/3-extensions/sql-orm-client/test/fixtures/junction-namespaces/generated/ && cp test/sql-orm-client/fixtures/polymorphism/generated/contract.json test/sql-orm-client/fixtures/polymorphism/generated/contract.d.ts ../../packages/3-extensions/sql-orm-client/test/fixtures/polymorphism/generated/ && cp test/sql-orm-client/fixtures/soft-delete/generated/contract.json test/sql-orm-client/fixtures/soft-delete/generated/contract.d.ts ../../packages/3-extensions/sql-orm-client/test/fixtures/soft-delete/generated/ && cp test/sql-orm-client/fixtures/value-objects/generated/contract.json test/sql-orm-client/fixtures/value-objects/generated/contract.d.ts ../../packages/3-extensions/sql-orm-client/test/fixtures/value-objects/generated/ && cp test/sql-orm-client/fixtures/fragment-namespace/generated/contract.json test/sql-orm-client/fixtures/fragment-namespace/generated/contract.d.ts ../../packages/3-extensions/sql-orm-client/test/fixtures/fragment-namespace/generated/ && cp test/sql-orm-client/fixtures/scope-namespace/generated/contract.json test/sql-orm-client/fixtures/scope-namespace/generated/contract.d.ts ../../packages/3-extensions/sql-orm-client/test/fixtures/scope-namespace/generated/ && cp test/sql-orm-client/fixtures/collection-methods/generated/contract.json test/sql-orm-client/fixtures/collection-methods/generated/contract.d.ts ../../packages/3-extensions/sql-orm-client/test/fixtures/collection-methods/generated/ && cp test/sql-orm-client/fixtures/on-update-only/generated/contract.json test/sql-orm-client/fixtures/on-update-only/generated/contract.d.ts ../../packages/3-extensions/sql-orm-client/test/fixtures/on-update-only/generated/ && cd ../../packages/3-extensions/sql-orm-client && node scripts/strip-pgvector-fixture.mjs test/fixtures/generated/contract.d.ts", + "emit:check": "pnpm emit && git diff --exit-code test/fixtures/generated/ test/fixtures/junction-namespaces/generated/ test/fixtures/polymorphism/generated/ test/fixtures/soft-delete/generated/ test/fixtures/value-objects/generated/ test/fixtures/fragment-namespace/generated/ test/fixtures/scope-namespace/generated/ test/fixtures/collection-methods/generated/ test/fixtures/on-update-only/generated/", "test": "vitest run", "typecheck": "tsc --noEmit", "lint": "biome check . --error-on-warnings", diff --git a/packages/3-extensions/sql-orm-client/test/codec-async.types.test-d.ts b/packages/3-extensions/sql-orm-client/test/codec-async.types.test-d.ts index f232e4595941..fcc48bc59888 100644 --- a/packages/3-extensions/sql-orm-client/test/codec-async.types.test-d.ts +++ b/packages/3-extensions/sql-orm-client/test/codec-async.types.test-d.ts @@ -11,6 +11,7 @@ */ import type { Expression, ScopeField } from '@internal/sql-relational-core/expression'; +import type { JsonValue } from '@internal/utils/json'; import { expectTypeOf, test } from 'vitest'; import type { Collection } from '../src/collection'; import type { @@ -22,8 +23,9 @@ import type { UniqueConstraintCriterion, } from '../src/types'; import type { Contract } from './fixtures/generated/contract'; +import type { Contract as ValueObjectContract } from './fixtures/value-objects/generated/contract'; -// `User.address` is jsonb-backed (a value object whose fields and the field itself flow through the `pg/jsonb@1` codec); `User.invitedById` is int4 and nullable. Both codecs are lifted to async dispatch at the codec boundary regardless of whether the author wrote sync or async functions, so these columns are representative of "async codec columns" at the runtime boundary. +// `User.address` is jsonb-backed: in the value-objects fixture it holds a value object, whose fields and the field itself flow through the `pg/jsonb@1` codec. `User.invitedById` is int4 and nullable. Both codecs are lifted to async dispatch at the codec boundary regardless of whether the author wrote sync or async functions, so these columns are representative of "async codec columns" at the runtime boundary. // // See `packages/1-framework/1-core/framework-components/src/shared/codec.ts` (`Codec` interface — `encode`/`decode` return `Promise<…>`) — every author function is wrapped at the class boundary, which means every column is an "async codec" at the runtime layer. @@ -36,6 +38,9 @@ type AddressShape = { type IsPromiseLike = T extends Promise ? true : false; type UserRow = DefaultModelRow; +type ValueObjectUserRow = DefaultModelRow; +type ValueObjectUserCreate = CreateInput; +type ValueObjectUserUpdate = MutationUpdateInput; type UserCreate = CreateInput; type UserUpdate = MutationUpdateInput; type UserUnique = UniqueConstraintCriterion; @@ -56,7 +61,7 @@ test('DefaultModelRow exposes plain `T | null` for nullable pg/int4@1 columns', }); test('DefaultModelRow exposes plain object value (no Promise) for jsonb-backed value object', () => { - expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); }); test('InferRootRow for non-polymorphic model equals DefaultModelRow', () => { @@ -75,7 +80,7 @@ test('Collection.first() resolves to a plain row | null (no Promise on row fi type Row = Exclude; expectTypeOf().toEqualTypeOf(); expectTypeOf().toEqualTypeOf(); - expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); expectTypeOf>().toEqualTypeOf(); }); @@ -84,7 +89,7 @@ test('Collection async iteration yields a plain row (no Promise on row fields type IteratedRow = UserCollectionRow; expectTypeOf().toEqualTypeOf(); expectTypeOf().toEqualTypeOf(); - expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); expectTypeOf>().toEqualTypeOf(); }); @@ -104,11 +109,11 @@ test('CreateInput accepts plain `string` for pg/text@1 fields', () => { }); test('CreateInput accepts plain object for jsonb-backed value-object field', () => { - expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); }); test('MutationUpdateInput accepts plain T for jsonb-backed value-object field', () => { - expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); }); test('MutationUpdateInput accepts plain `string` for pg/text@1 fields', () => { diff --git a/packages/3-extensions/sql-orm-client/test/codec-field-parity.types.test-d.ts b/packages/3-extensions/sql-orm-client/test/codec-field-parity.types.test-d.ts index b11274c2606d..48d1186e8aa2 100644 --- a/packages/3-extensions/sql-orm-client/test/codec-field-parity.types.test-d.ts +++ b/packages/3-extensions/sql-orm-client/test/codec-field-parity.types.test-d.ts @@ -1,27 +1,33 @@ -import type { ExtractCodecTypes } from '@internal/sql-contract/types'; +import type { Contract as SqlContract } from '@internal/contract/types'; +import type { ExtractCodecTypes, SqlStorage } from '@internal/sql-contract/types'; import { describe, expectTypeOf, test } from 'vitest'; import type { CodecField, CollectionModelName, ModelAccessor } from '../src/types'; +import type { Contract as ValueObjectContract } from './fixtures/value-objects/generated/contract'; import type { TestContract } from './helpers'; +type AnyContract = SqlContract; + type Exactly = (() => T extends A ? 1 : 2) extends () => T extends B ? 1 : 2 ? true : false; -type ModelName = CollectionModelName; -type Accessor = ModelAccessor; +type ModelName = CollectionModelName; +type Accessor> = ModelAccessor; -type ScalarFieldName = { - [F in keyof Accessor & string]: Accessor[F] extends { readonly returnType: unknown } +type ScalarFieldName> = { + [F in keyof Accessor & string]: Accessor[F] extends { + readonly returnType: unknown; + } ? F : never; -}[keyof Accessor & string]; +}[keyof Accessor & string]; -type CodecFieldOf = Field extends { +type CodecFieldOf = Field extends { readonly returnType: { - readonly codecId: infer Id extends keyof ExtractCodecTypes & string; + readonly codecId: infer Id extends keyof ExtractCodecTypes & string; readonly nullable: infer Nullable extends boolean; }; } - ? CodecField + ? CodecField : never; type MethodName = Exclude; @@ -38,36 +44,52 @@ type MethodDifferences = { : `${Path}.${K}`; }[MethodName]; -type FieldDifferences = { - [F in ScalarFieldName]: [CodecFieldOf[F]>] extends [never] +type FieldDifferences> = { + [F in ScalarFieldName]: [CodecFieldOf[F]>] extends [never] ? never - : Exactly[F], keyof CodecFieldOf[F]>> extends true - ? MethodDifferences<`${M}.${F}`, Accessor[F], CodecFieldOf[F]>> + : Exactly[F], keyof CodecFieldOf[F]>> extends true + ? MethodDifferences<`${M}.${F}`, Accessor[F], CodecFieldOf[F]>> : `${M}.${F} methods`; -}[ScalarFieldName]; +}[ScalarFieldName]; + +type FieldsWithoutCodecField> = { + [F in ScalarFieldName]: [CodecFieldOf[F]>] extends [never] + ? `${M}.${F}` + : never; +}[ScalarFieldName]; -type FieldsWithoutCodecField = { - [F in ScalarFieldName]: [CodecFieldOf[F]>] extends [never] ? `${M}.${F}` : never; -}[ScalarFieldName]; +type Differences = { + [M in ModelName]: FieldDifferences; +}[ModelName]; -type Differences = { [M in ModelName]: FieldDifferences }[ModelName]; +type Unmatched = { + [M in ModelName]: FieldsWithoutCodecField; +}[ModelName]; type ValueMethod = 'eq' | 'neq' | 'in' | 'notIn'; -type RefinedValueMethods = `User.address.${ValueMethod}`; -type Unmatched = { [M in ModelName]: FieldsWithoutCodecField }[ModelName]; describe('CodecField against every scalar field of the test contract', () => { test('the comparison checks fields, not nothing', () => { - expectTypeOf>().toEqualTypeOf< + expectTypeOf>().toEqualTypeOf< 'id' | 'title' | 'userId' | 'views' | 'embedding' >(); }); test('every scalar field of the test contract has a CodecField', () => { - expectTypeOf().toBeNever(); + expectTypeOf>().toBeNever(); + }); + + test('every method takes the same parameters', () => { + expectTypeOf>().toBeNever(); + }); +}); + +describe('CodecField against a value object field', () => { + test('every scalar field has a CodecField', () => { + expectTypeOf>().toBeNever(); }); - test('every method takes the same parameters, except the value methods of a value-object field, whose input type refines its codec input', () => { - expectTypeOf().toEqualTypeOf(); + test('every method takes the same parameters, except the value methods of the value object field, whose input type refines its codec input', () => { + expectTypeOf>().toEqualTypeOf<`User.address.${ValueMethod}`>(); }); }); diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/generated/contract.d.ts b/packages/3-extensions/sql-orm-client/test/fixtures/generated/contract.d.ts index 327dea94d035..83bbf7059ac5 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/generated/contract.d.ts +++ b/packages/3-extensions/sql-orm-client/test/fixtures/generated/contract.d.ts @@ -38,7 +38,6 @@ import type { NamespaceId, ProfileHashBase, StorageHashBase, - ValueObjectTypesFromStorage, } from '@internal/contract/types'; export type StorageHash = @@ -258,38 +257,21 @@ type DefaultLiteralValue = CodecId extends keyo : Encoded; type ParameterizedOutputTypeFunctions = PgParameterizedOutputTypes | PgVectorParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromStorage< - ContractBase['storage']['namespaces'], - CodecTypes, - ParameterizedOutputTypeFunctions, - FieldTypeOverrides, - 'output' ->; -type ValueObjectInputTypes = ValueObjectTypesFromStorage< - ContractBase['storage']['namespaces'], - CodecTypes, - ParameterizedOutputTypeFunctions, - FieldTypeOverrides, - 'input' ->; -export type AddressOutput = ValueObjectOutputTypes['public']['Address']; -export type AddressInput = ValueObjectInputTypes['public']['Address']; + type FieldTypeOverrides = {}; export type FieldOutputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output', - ContractBase['storage']['namespaces'] + 'output' >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input', - ContractBase['storage']['namespaces'] + 'input' >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< @@ -834,24 +816,6 @@ type ContractBase = Omit< ]; }; }; - readonly valueObject: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - }; - }; }; }; }; @@ -1170,13 +1134,7 @@ type ContractBase = Omit< readonly fields: { readonly address: { readonly nullable: true; - readonly type: { - readonly kind: 'valueObject'; - readonly plane: 'storage'; - readonly namespaceId: 'public'; - readonly entityKind: 'valueObject'; - readonly entityName: 'Address'; - }; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/jsonb@1' }; }; readonly email: { readonly nullable: false; diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/generated/contract.json b/packages/3-extensions/sql-orm-client/test/fixtures/generated/contract.json index d753a0f62643..2dc5dd47490e 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/generated/contract.json +++ b/packages/3-extensions/sql-orm-client/test/fixtures/generated/contract.json @@ -505,11 +505,8 @@ "address": { "nullable": true, "type": { - "entityKind": "valueObject", - "entityName": "Address", - "kind": "valueObject", - "namespaceId": "public", - "plane": "storage" + "codecId": "pg/jsonb@1", + "kind": "scalar" } }, "email": { @@ -1295,33 +1292,6 @@ } ] } - }, - "valueObject": { - "Address": { - "fields": { - "city": { - "nullable": false, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" - } - }, - "street": { - "nullable": false, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" - } - }, - "zip": { - "nullable": true, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" - } - } - } - } } }, "id": "public", diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/value-objects/generated/contract.d.ts b/packages/3-extensions/sql-orm-client/test/fixtures/value-objects/generated/contract.d.ts new file mode 100644 index 000000000000..2ada6514e957 --- /dev/null +++ b/packages/3-extensions/sql-orm-client/test/fixtures/value-objects/generated/contract.d.ts @@ -0,0 +1,453 @@ +// ⚠️ GENERATED FILE - DO NOT EDIT +// This file is automatically generated by 'prisma contract emit'. +// To regenerate, run: prisma contract emit +import type { + CodecTypes as PgTypes, + ParameterizedOutputTypesForTypeParams as PgParameterizedOutputTypes, +} from '@internal/target-postgres/codec-types'; +import type { QueryOperationTypes as PgTargetQueryOps } from '@internal/target-postgres/operation-types'; + +import type { + ColumnTypesFromStorage, + ContractWithTypeMaps, + RelationKeys, + TypeMaps as TypeMapsType, +} from '@internal/sql-contract/types'; +import type { + Contract as ContractType, + ExecutionHashBase, + FieldTypesFromDomain, + NamespaceId, + ProfileHashBase, + StorageHashBase, + ValueObjectTypesFromStorage, +} from '@internal/contract/types'; + +export type StorageHash = + StorageHashBase<'e6640da7edb7ea952996d8d210526e990703e8a81f588f70b6c097dc5870a9af'>; +export type ExecutionHash = ExecutionHashBase; +export type ProfileHash = + ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; + +export type CodecTypes = PgTypes; +export type LaneCodecTypes = CodecTypes; +export type QueryOperationTypes = PgTargetQueryOps; +export type AggregateTypes = { + readonly avg: { + readonly byCodec: { + readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + }; + }; + readonly avgDecimal: { + readonly byCodec: { + readonly 'pg/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + }; + }; + readonly count: { + readonly byCodec: {}; + readonly withoutInput: { readonly output: 'pg/int8number@1'; readonly nullable: false }; + readonly anyInput: { readonly output: 'pg/int8number@1'; readonly nullable: false }; + }; + readonly countBigInt: { + readonly byCodec: {}; + readonly withoutInput: { readonly output: 'pg/int8@1'; readonly nullable: false }; + readonly anyInput: { readonly output: 'pg/int8@1'; readonly nullable: false }; + }; + readonly max: { + readonly byCodec: { + readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true }; + readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true }; + readonly 'pg/date-temporal@1': { + readonly output: 'pg/date-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true }; + readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; + readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { + readonly output: 'pg/time-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-string@1': { + readonly output: 'pg/timestamp-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-temporal@1': { + readonly output: 'pg/timestamp-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-date@1': { + readonly output: 'pg/timestamptz-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-string@1': { + readonly output: 'pg/timestamptz-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-temporal@1': { + readonly output: 'pg/timestamptz-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true }; + readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true }; + readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + }; + }; + readonly min: { + readonly byCodec: { + readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true }; + readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true }; + readonly 'pg/date-temporal@1': { + readonly output: 'pg/date-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true }; + readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; + readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { + readonly output: 'pg/time-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-string@1': { + readonly output: 'pg/timestamp-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-temporal@1': { + readonly output: 'pg/timestamp-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-date@1': { + readonly output: 'pg/timestamptz-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-string@1': { + readonly output: 'pg/timestamptz-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-temporal@1': { + readonly output: 'pg/timestamptz-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true }; + readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true }; + readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + }; + }; + readonly sum: { + readonly byCodec: { + readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + }; + }; + readonly sumBigInt: { + readonly byCodec: { + readonly 'pg/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'sql/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + }; + }; +}; +type DefaultLiteralValue = CodecId extends keyof CodecTypes + ? Encoded extends CodecTypes[CodecId]['json'] + ? Encoded + : CodecTypes[CodecId]['json'] + : Encoded; +type ParameterizedOutputTypeFunctions = PgParameterizedOutputTypes; +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output' +>; +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input' +>; +export type AddressOutput = ValueObjectOutputTypes['public']['Address']; +export type AddressInput = ValueObjectInputTypes['public']['Address']; +type FieldTypeOverrides = {}; +export type FieldOutputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output', + ContractBase['storage']['namespaces'] +>; +export type FieldInputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input', + ContractBase['storage']['namespaces'] +>; +type ColumnTypeOverrides = {}; +export type StorageColumnTypes = ColumnTypesFromStorage< + ContractBase['storage'], + CodecTypes, + ParameterizedOutputTypeFunctions, + ColumnTypeOverrides, + 'output' +>; +export type StorageColumnInputTypes = ColumnTypesFromStorage< + ContractBase['storage'], + CodecTypes, + ParameterizedOutputTypeFunctions, + ColumnTypeOverrides, + 'input' +>; + +export namespace Models { + export type public_User = { + address: FieldOutputTypes['public']['User']['address']; + id: FieldOutputTypes['public']['User']['id']; + name: FieldOutputTypes['public']['User']['name']; + readonly [RelationKeys]?: never; + }; +} + +export declare const models: { + public: { + User: Models.public_User; + }; +}; + +export type TypeMaps = TypeMapsType< + CodecTypes, + QueryOperationTypes, + FieldOutputTypes, + FieldInputTypes, + StorageColumnTypes, + StorageColumnInputTypes, + AggregateTypes +>; + +type ContractBase = Omit< + ContractType<{ + readonly namespaces: { + readonly public: { + readonly id: 'public'; + readonly kind: 'postgres-schema'; + readonly entries: { + readonly table: { + readonly users: { + columns: { + readonly address: { + readonly dataType: 'pg/jsonb'; + readonly codecId: 'pg/jsonb@1'; + readonly nullable: true; + readonly many: false; + }; + readonly id: { + readonly dataType: 'pg/int4'; + readonly codecId: 'pg/int4@1'; + readonly nullable: false; + readonly many: false; + }; + readonly name: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly []; + }; + }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + }; + }; + }; + }; + }; + readonly storageHash: StorageHash; + }>, + 'roots' | 'domain' +> & { + readonly target: 'postgres'; + readonly targetFamily: 'sql'; + readonly roots: { + readonly users: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; + }; + readonly domain: { + readonly namespaces: { + readonly public: { + readonly models: { + readonly User: { + readonly fields: { + readonly address: { + readonly nullable: true; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: 'public'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int4@1' }; + }; + readonly name: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'users'; + readonly namespaceId: 'public'; + readonly fields: { + readonly address: { readonly column: 'address' }; + readonly id: { readonly column: 'id' }; + readonly name: { readonly column: 'name' }; + }; + }; + }; + }; + }; + }; + }; + readonly capabilities: { + readonly postgres: { + readonly distinctOn: true; + readonly forKeyShare: true; + readonly forNoKeyUpdate: true; + readonly jsonAgg: true; + readonly lateral: true; + readonly limit: true; + readonly orderBy: true; + readonly returning: true; + }; + readonly sql: { + readonly checkConstraint: true; + readonly defaultInInsert: true; + readonly enums: true; + readonly forShare: true; + readonly forUpdate: true; + readonly insertOnConflictSkip: true; + readonly insertOnConflictWithoutTarget: true; + readonly lateral: true; + readonly lockNowait: true; + readonly lockOf: true; + readonly lockSkipLocked: true; + readonly returning: true; + readonly scalarList: true; + }; + }; + readonly extensions: {}; + readonly meta: {}; + readonly profileHash: ProfileHash; +}; + +export type Contract = ContractWithTypeMaps; + +export type Namespaces = Contract['storage']['namespaces']; diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/value-objects/generated/contract.json b/packages/3-extensions/sql-orm-client/test/fixtures/value-objects/generated/contract.json new file mode 100644 index 000000000000..81164ba0cbdd --- /dev/null +++ b/packages/3-extensions/sql-orm-client/test/fixtures/value-objects/generated/contract.json @@ -0,0 +1,172 @@ +{ + "schemaVersion": "1", + "targetFamily": "sql", + "target": "postgres", + "profileHash": "3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2", + "roots": { + "users": { + "model": "User", + "namespace": "public" + } + }, + "domain": { + "namespaces": { + "public": { + "models": { + "User": { + "fields": { + "address": { + "nullable": true, + "type": { + "entityKind": "valueObject", + "entityName": "Address", + "kind": "valueObject", + "namespaceId": "public", + "plane": "storage" + } + }, + "id": { + "nullable": false, + "type": { + "codecId": "pg/int4@1", + "kind": "scalar" + } + }, + "name": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + } + }, + "relations": {}, + "storage": { + "fields": { + "address": { + "column": "address" + }, + "id": { + "column": "id" + }, + "name": { + "column": "name" + } + }, + "namespaceId": "public", + "table": "users" + } + } + } + } + } + }, + "storage": { + "namespaces": { + "public": { + "entries": { + "table": { + "users": { + "checks": [ + { + "expression": "\"address\" IS NULL OR jsonb_path_match(\"address\", 'strict $.type() == \"null\" || ($.type() == \"object\" && exists($.keyvalue() ? (@.key == \"city\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"street\" && (@.value.type() == \"string\"))) && !exists($.keyvalue() ? (@.key == \"zip\" && !(@.value.type() == \"null\" || (@.value.type() == \"string\")))) && !exists($.keyvalue() ? (@.key != \"city\" && @.key != \"street\" && @.key != \"zip\")))') IS TRUE", + "name": "users_address_value_object_0c7d1c3a", + "prefix": "users_address_value_object" + } + ], + "columns": { + "address": { + "codecId": "pg/jsonb@1", + "dataType": "pg/jsonb", + "nullable": true + }, + "id": { + "codecId": "pg/int4@1", + "dataType": "pg/int4", + "nullable": false + }, + "name": { + "codecId": "pg/text@1", + "dataType": "pg/text", + "nullable": false + } + }, + "foreignKeys": [], + "indexes": [], + "primaryKey": { + "columns": [ + "id" + ] + }, + "uniques": [] + } + }, + "valueObject": { + "Address": { + "fields": { + "city": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "street": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "zip": { + "nullable": true, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + } + } + } + } + }, + "id": "public", + "kind": "postgres-schema" + } + }, + "storageHash": "e6640da7edb7ea952996d8d210526e990703e8a81f588f70b6c097dc5870a9af" + }, + "capabilities": { + "postgres": { + "distinctOn": true, + "forKeyShare": true, + "forNoKeyUpdate": true, + "jsonAgg": true, + "lateral": true, + "limit": true, + "orderBy": true, + "returning": true + }, + "sql": { + "checkConstraint": true, + "defaultInInsert": true, + "enums": true, + "forShare": true, + "forUpdate": true, + "insertOnConflictSkip": true, + "insertOnConflictWithoutTarget": true, + "lateral": true, + "lockNowait": true, + "lockOf": true, + "lockSkipLocked": true, + "returning": true, + "scalarList": true + } + }, + "extensions": {}, + "meta": {}, + "_generated": { + "warning": "⚠️ GENERATED FILE - DO NOT EDIT", + "message": "This file is automatically generated by \"prisma contract emit\".", + "regenerate": "To regenerate, run: prisma contract emit" + } +} \ No newline at end of file diff --git a/packages/3-extensions/sql-orm-client/test/simplify-deep.test-d.ts b/packages/3-extensions/sql-orm-client/test/simplify-deep.test-d.ts index a77f7f09d9e8..382c0d891525 100644 --- a/packages/3-extensions/sql-orm-client/test/simplify-deep.test-d.ts +++ b/packages/3-extensions/sql-orm-client/test/simplify-deep.test-d.ts @@ -1,3 +1,4 @@ +import type { JsonValue } from '@internal/utils/json'; import { describe, expectTypeOf, test } from 'vitest'; import { Collection } from '../src/collection'; import { createMockRuntime, getTestContext } from './helpers'; @@ -14,11 +15,7 @@ describe('Collection result types are simplified', () => { name: string; email: string; invitedById: number | null; - address: { - readonly street: string; - readonly city: string; - readonly zip: string | null; - } | null; + address: JsonValue | null; }>(); }); @@ -41,11 +38,7 @@ describe('Collection result types are simplified', () => { name: string; email: string; invitedById: number | null; - address: { - readonly street: string; - readonly city: string; - readonly zip: string | null; - } | null; + address: JsonValue | null; posts: { id: number; title: string; @@ -82,11 +75,7 @@ describe('Collection result types are simplified', () => { name: string; email: string; invitedById: number | null; - address: { - readonly street: string; - readonly city: string; - readonly zip: string | null; - } | null; + address: JsonValue | null; }>(); }); @@ -99,11 +88,7 @@ describe('Collection result types are simplified', () => { name: string; email: string; invitedById: number | null; - address: { - readonly street: string; - readonly city: string; - readonly zip: string | null; - } | null; + address: JsonValue | null; posts: { id: number; title: string; @@ -116,11 +101,7 @@ describe('Collection result types are simplified', () => { name: string; email: string; invitedById: number | null; - address: { - readonly street: string; - readonly city: string; - readonly zip: string | null; - } | null; + address: JsonValue | null; } | null; }>(); }); diff --git a/packages/3-extensions/sql-orm-client/test/value-object-inputs.test-d.ts b/packages/3-extensions/sql-orm-client/test/value-object-inputs.test-d.ts index 3c46ff53fc3d..6ffaf7ae1747 100644 --- a/packages/3-extensions/sql-orm-client/test/value-object-inputs.test-d.ts +++ b/packages/3-extensions/sql-orm-client/test/value-object-inputs.test-d.ts @@ -1,6 +1,6 @@ import { expectTypeOf, test } from 'vitest'; import type { CreateInput, DefaultModelRow, MutationUpdateInput } from '../src/types'; -import type { Contract } from './fixtures/generated/contract'; +import type { Contract } from './fixtures/value-objects/generated/contract'; type AddressShape = { readonly street: string; readonly city: string; readonly zip: string | null }; diff --git a/test/integration/test/sql-orm-client/fixtures/value-objects/contract.prisma b/test/integration/test/sql-orm-client/fixtures/value-objects/contract.prisma new file mode 100644 index 000000000000..5df35551e994 --- /dev/null +++ b/test/integration/test/sql-orm-client/fixtures/value-objects/contract.prisma @@ -0,0 +1,18 @@ +// use prisma-8 + +// Emitted fixture for the sql-orm-client value object type tests. +// `User.address` holds an `Address` value object in a jsonb column. + +type Address { + street String + city String + zip String? +} + +model User { + id Int @id + name String + address Address? + + @@map("users") +} diff --git a/test/integration/test/sql-orm-client/fixtures/value-objects/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/value-objects/generated/contract.d.ts new file mode 100644 index 000000000000..2ada6514e957 --- /dev/null +++ b/test/integration/test/sql-orm-client/fixtures/value-objects/generated/contract.d.ts @@ -0,0 +1,453 @@ +// ⚠️ GENERATED FILE - DO NOT EDIT +// This file is automatically generated by 'prisma contract emit'. +// To regenerate, run: prisma contract emit +import type { + CodecTypes as PgTypes, + ParameterizedOutputTypesForTypeParams as PgParameterizedOutputTypes, +} from '@internal/target-postgres/codec-types'; +import type { QueryOperationTypes as PgTargetQueryOps } from '@internal/target-postgres/operation-types'; + +import type { + ColumnTypesFromStorage, + ContractWithTypeMaps, + RelationKeys, + TypeMaps as TypeMapsType, +} from '@internal/sql-contract/types'; +import type { + Contract as ContractType, + ExecutionHashBase, + FieldTypesFromDomain, + NamespaceId, + ProfileHashBase, + StorageHashBase, + ValueObjectTypesFromStorage, +} from '@internal/contract/types'; + +export type StorageHash = + StorageHashBase<'e6640da7edb7ea952996d8d210526e990703e8a81f588f70b6c097dc5870a9af'>; +export type ExecutionHash = ExecutionHashBase; +export type ProfileHash = + ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; + +export type CodecTypes = PgTypes; +export type LaneCodecTypes = CodecTypes; +export type QueryOperationTypes = PgTargetQueryOps; +export type AggregateTypes = { + readonly avg: { + readonly byCodec: { + readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + }; + }; + readonly avgDecimal: { + readonly byCodec: { + readonly 'pg/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + }; + }; + readonly count: { + readonly byCodec: {}; + readonly withoutInput: { readonly output: 'pg/int8number@1'; readonly nullable: false }; + readonly anyInput: { readonly output: 'pg/int8number@1'; readonly nullable: false }; + }; + readonly countBigInt: { + readonly byCodec: {}; + readonly withoutInput: { readonly output: 'pg/int8@1'; readonly nullable: false }; + readonly anyInput: { readonly output: 'pg/int8@1'; readonly nullable: false }; + }; + readonly max: { + readonly byCodec: { + readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true }; + readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true }; + readonly 'pg/date-temporal@1': { + readonly output: 'pg/date-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true }; + readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; + readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { + readonly output: 'pg/time-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-string@1': { + readonly output: 'pg/timestamp-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-temporal@1': { + readonly output: 'pg/timestamp-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-date@1': { + readonly output: 'pg/timestamptz-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-string@1': { + readonly output: 'pg/timestamptz-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-temporal@1': { + readonly output: 'pg/timestamptz-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true }; + readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true }; + readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + }; + }; + readonly min: { + readonly byCodec: { + readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true }; + readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true }; + readonly 'pg/date-temporal@1': { + readonly output: 'pg/date-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true }; + readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; + readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { + readonly output: 'pg/time-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-string@1': { + readonly output: 'pg/timestamp-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-temporal@1': { + readonly output: 'pg/timestamp-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-date@1': { + readonly output: 'pg/timestamptz-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-string@1': { + readonly output: 'pg/timestamptz-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-temporal@1': { + readonly output: 'pg/timestamptz-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true }; + readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true }; + readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + }; + }; + readonly sum: { + readonly byCodec: { + readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + }; + }; + readonly sumBigInt: { + readonly byCodec: { + readonly 'pg/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'sql/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + }; + }; +}; +type DefaultLiteralValue = CodecId extends keyof CodecTypes + ? Encoded extends CodecTypes[CodecId]['json'] + ? Encoded + : CodecTypes[CodecId]['json'] + : Encoded; +type ParameterizedOutputTypeFunctions = PgParameterizedOutputTypes; +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output' +>; +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input' +>; +export type AddressOutput = ValueObjectOutputTypes['public']['Address']; +export type AddressInput = ValueObjectInputTypes['public']['Address']; +type FieldTypeOverrides = {}; +export type FieldOutputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output', + ContractBase['storage']['namespaces'] +>; +export type FieldInputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input', + ContractBase['storage']['namespaces'] +>; +type ColumnTypeOverrides = {}; +export type StorageColumnTypes = ColumnTypesFromStorage< + ContractBase['storage'], + CodecTypes, + ParameterizedOutputTypeFunctions, + ColumnTypeOverrides, + 'output' +>; +export type StorageColumnInputTypes = ColumnTypesFromStorage< + ContractBase['storage'], + CodecTypes, + ParameterizedOutputTypeFunctions, + ColumnTypeOverrides, + 'input' +>; + +export namespace Models { + export type public_User = { + address: FieldOutputTypes['public']['User']['address']; + id: FieldOutputTypes['public']['User']['id']; + name: FieldOutputTypes['public']['User']['name']; + readonly [RelationKeys]?: never; + }; +} + +export declare const models: { + public: { + User: Models.public_User; + }; +}; + +export type TypeMaps = TypeMapsType< + CodecTypes, + QueryOperationTypes, + FieldOutputTypes, + FieldInputTypes, + StorageColumnTypes, + StorageColumnInputTypes, + AggregateTypes +>; + +type ContractBase = Omit< + ContractType<{ + readonly namespaces: { + readonly public: { + readonly id: 'public'; + readonly kind: 'postgres-schema'; + readonly entries: { + readonly table: { + readonly users: { + columns: { + readonly address: { + readonly dataType: 'pg/jsonb'; + readonly codecId: 'pg/jsonb@1'; + readonly nullable: true; + readonly many: false; + }; + readonly id: { + readonly dataType: 'pg/int4'; + readonly codecId: 'pg/int4@1'; + readonly nullable: false; + readonly many: false; + }; + readonly name: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly []; + }; + }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + }; + }; + }; + }; + }; + readonly storageHash: StorageHash; + }>, + 'roots' | 'domain' +> & { + readonly target: 'postgres'; + readonly targetFamily: 'sql'; + readonly roots: { + readonly users: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; + }; + readonly domain: { + readonly namespaces: { + readonly public: { + readonly models: { + readonly User: { + readonly fields: { + readonly address: { + readonly nullable: true; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: 'public'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int4@1' }; + }; + readonly name: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'users'; + readonly namespaceId: 'public'; + readonly fields: { + readonly address: { readonly column: 'address' }; + readonly id: { readonly column: 'id' }; + readonly name: { readonly column: 'name' }; + }; + }; + }; + }; + }; + }; + }; + readonly capabilities: { + readonly postgres: { + readonly distinctOn: true; + readonly forKeyShare: true; + readonly forNoKeyUpdate: true; + readonly jsonAgg: true; + readonly lateral: true; + readonly limit: true; + readonly orderBy: true; + readonly returning: true; + }; + readonly sql: { + readonly checkConstraint: true; + readonly defaultInInsert: true; + readonly enums: true; + readonly forShare: true; + readonly forUpdate: true; + readonly insertOnConflictSkip: true; + readonly insertOnConflictWithoutTarget: true; + readonly lateral: true; + readonly lockNowait: true; + readonly lockOf: true; + readonly lockSkipLocked: true; + readonly returning: true; + readonly scalarList: true; + }; + }; + readonly extensions: {}; + readonly meta: {}; + readonly profileHash: ProfileHash; +}; + +export type Contract = ContractWithTypeMaps; + +export type Namespaces = Contract['storage']['namespaces']; diff --git a/test/integration/test/sql-orm-client/fixtures/value-objects/generated/contract.json b/test/integration/test/sql-orm-client/fixtures/value-objects/generated/contract.json new file mode 100644 index 000000000000..81164ba0cbdd --- /dev/null +++ b/test/integration/test/sql-orm-client/fixtures/value-objects/generated/contract.json @@ -0,0 +1,172 @@ +{ + "schemaVersion": "1", + "targetFamily": "sql", + "target": "postgres", + "profileHash": "3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2", + "roots": { + "users": { + "model": "User", + "namespace": "public" + } + }, + "domain": { + "namespaces": { + "public": { + "models": { + "User": { + "fields": { + "address": { + "nullable": true, + "type": { + "entityKind": "valueObject", + "entityName": "Address", + "kind": "valueObject", + "namespaceId": "public", + "plane": "storage" + } + }, + "id": { + "nullable": false, + "type": { + "codecId": "pg/int4@1", + "kind": "scalar" + } + }, + "name": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + } + }, + "relations": {}, + "storage": { + "fields": { + "address": { + "column": "address" + }, + "id": { + "column": "id" + }, + "name": { + "column": "name" + } + }, + "namespaceId": "public", + "table": "users" + } + } + } + } + } + }, + "storage": { + "namespaces": { + "public": { + "entries": { + "table": { + "users": { + "checks": [ + { + "expression": "\"address\" IS NULL OR jsonb_path_match(\"address\", 'strict $.type() == \"null\" || ($.type() == \"object\" && exists($.keyvalue() ? (@.key == \"city\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"street\" && (@.value.type() == \"string\"))) && !exists($.keyvalue() ? (@.key == \"zip\" && !(@.value.type() == \"null\" || (@.value.type() == \"string\")))) && !exists($.keyvalue() ? (@.key != \"city\" && @.key != \"street\" && @.key != \"zip\")))') IS TRUE", + "name": "users_address_value_object_0c7d1c3a", + "prefix": "users_address_value_object" + } + ], + "columns": { + "address": { + "codecId": "pg/jsonb@1", + "dataType": "pg/jsonb", + "nullable": true + }, + "id": { + "codecId": "pg/int4@1", + "dataType": "pg/int4", + "nullable": false + }, + "name": { + "codecId": "pg/text@1", + "dataType": "pg/text", + "nullable": false + } + }, + "foreignKeys": [], + "indexes": [], + "primaryKey": { + "columns": [ + "id" + ] + }, + "uniques": [] + } + }, + "valueObject": { + "Address": { + "fields": { + "city": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "street": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "zip": { + "nullable": true, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + } + } + } + } + }, + "id": "public", + "kind": "postgres-schema" + } + }, + "storageHash": "e6640da7edb7ea952996d8d210526e990703e8a81f588f70b6c097dc5870a9af" + }, + "capabilities": { + "postgres": { + "distinctOn": true, + "forKeyShare": true, + "forNoKeyUpdate": true, + "jsonAgg": true, + "lateral": true, + "limit": true, + "orderBy": true, + "returning": true + }, + "sql": { + "checkConstraint": true, + "defaultInInsert": true, + "enums": true, + "forShare": true, + "forUpdate": true, + "insertOnConflictSkip": true, + "insertOnConflictWithoutTarget": true, + "lateral": true, + "lockNowait": true, + "lockOf": true, + "lockSkipLocked": true, + "returning": true, + "scalarList": true + } + }, + "extensions": {}, + "meta": {}, + "_generated": { + "warning": "⚠️ GENERATED FILE - DO NOT EDIT", + "message": "This file is automatically generated by \"prisma contract emit\".", + "regenerate": "To regenerate, run: prisma contract emit" + } +} \ No newline at end of file diff --git a/test/integration/test/sql-orm-client/fixtures/value-objects/prisma.config.ts b/test/integration/test/sql-orm-client/fixtures/value-objects/prisma.config.ts new file mode 100644 index 000000000000..c19c293e813c --- /dev/null +++ b/test/integration/test/sql-orm-client/fixtures/value-objects/prisma.config.ts @@ -0,0 +1,9 @@ +import { defineConfig as ormConfig } from '@internal/postgres/config'; +import { definePrismaConfig } from '@prisma/cli-engine'; + +export default definePrismaConfig({ + orm: ormConfig({ + contract: './contract.prisma', + output: 'generated', + }), +}); From 13e112d724c7490c16c236806f7a1f077c19fc67 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 17:02:37 +0200 Subject: [PATCH 061/104] TML-3537: the skill and the older ADRs say value objects are storage entities, and the Data Contract doc names the right export of valueObjectAt Signed-off-by: willbot Signed-off-by: Will Madden --- .../ADR 173 - Polymorphism via discriminator and variants.md | 2 ++ .../adrs/ADR 178 - Value objects in the contract.md | 2 +- ...orm entity coordinate and pack-contributed entity kinds.md | 2 ++ docs/architecture docs/subsystems/1. Data Contract.md | 2 +- skills/prisma-8/references/contract.md | 4 ++-- 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/architecture docs/adrs/ADR 173 - Polymorphism via discriminator and variants.md b/docs/architecture docs/adrs/ADR 173 - Polymorphism via discriminator and variants.md index 459e0f919fb4..405729d1e853 100644 --- a/docs/architecture docs/adrs/ADR 173 - Polymorphism via discriminator and variants.md +++ b/docs/architecture docs/adrs/ADR 173 - Polymorphism via discriminator and variants.md @@ -177,6 +177,8 @@ These are independent properties. This composability is why we rejected labeled ### Value objects use the same mechanism +> **Note:** value objects are now storage entities, at `storage.namespaces..entries.valueObject`, not a domain `valueObjects` section ([ADR 309](ADR%20309%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md)). On SQL, a base model's discriminator column also refers to a value set of its variants' values. + The `discriminator`/`variants`/`base` pattern applies identically to value objects in the `valueObjects` section ([ADR 178](ADR%20178%20-%20Value%20objects%20in%20the%20contract.md)). A base value object declares a discriminator and variants; each variant declares its base and adds type-specific fields: ```json diff --git a/docs/architecture docs/adrs/ADR 178 - Value objects in the contract.md b/docs/architecture docs/adrs/ADR 178 - Value objects in the contract.md index d2ab14db0ef7..58dd4d44c551 100644 --- a/docs/architecture docs/adrs/ADR 178 - Value objects in the contract.md +++ b/docs/architecture docs/adrs/ADR 178 - Value objects in the contract.md @@ -2,7 +2,7 @@ ## At a glance -> **Note:** this ADR describes value objects in the domain plane, where they first lived. They are now storage entities ([ADR 309](ADR%20309%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md)); the field shapes below are unchanged. +> **Note:** this ADR describes value objects in the domain plane, where they first lived. They are now storage entities ([ADR 309](ADR%20309%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md)); the shapes of their members below are unchanged, and a field now refers to its value object by storage coordinate rather than by name. A User model with scalar fields and value object fields. Value objects are defined in a separate `valueObjects` section — they share the same field descriptor shape (`{ nullable, codecId }`) as models, but the framework makes fundamentally different promises about them. diff --git a/docs/architecture docs/adrs/ADR 221 - Contract IR two planes with uniform entity coordinate and pack-contributed entity kinds.md b/docs/architecture docs/adrs/ADR 221 - Contract IR two planes with uniform entity coordinate and pack-contributed entity kinds.md index f4d3775f151e..124c1f2cbe50 100644 --- a/docs/architecture docs/adrs/ADR 221 - Contract IR two planes with uniform entity coordinate and pack-contributed entity kinds.md +++ b/docs/architecture docs/adrs/ADR 221 - Contract IR two planes with uniform entity coordinate and pack-contributed entity kinds.md @@ -3,6 +3,8 @@ **Status:** Accepted **Date:** 2026-05-20 +> **Note:** value objects have since moved out of the domain plane. They are storage entities, at `storage.namespaces..entries.valueObject`, and a domain field refers to one by coordinate ([ADR 309](ADR%20309%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md)). The domain `valueObjects` key shown below no longer exists. + --- ## A concrete example diff --git a/docs/architecture docs/subsystems/1. Data Contract.md b/docs/architecture docs/subsystems/1. Data Contract.md index d96b4da57894..7c819a5b9756 100644 --- a/docs/architecture docs/subsystems/1. Data Contract.md +++ b/docs/architecture docs/subsystems/1. Data Contract.md @@ -229,7 +229,7 @@ The contract's domain-level structure generalizes across database families. The - **`model.fields`** — domain-level field descriptors as `Record`. Both `nullable` and `codecId` are domain concepts. "Family-agnostic" describes the structure, not the values — codec ID values differ across families but the structure is identical. See [ADR 172](../adrs/ADR%20172%20-%20Contract%20domain-storage%20separation.md). - **Polymorphism** — `discriminator` + `variants` on the base model, `base` on each variant. Persistence strategy (STI vs MTI) is emergent from storage mappings. Uses specialization/generalization terminology. See [ADR 173](../adrs/ADR%20173%20-%20Polymorphism%20via%20discriminator%20and%20variants.md). - **Model ownership** — an owned model declares `"owner": "ParentModel"`, a domain fact about aggregate membership. Its data is co-located with the owner's storage (embedded document in Mongo, JSONB column in SQL). The parent's `storage.relations` maps the relation to its physical location. Relations to owned models are plain graph edges. See [ADR 177](../adrs/ADR%20177%20-%20Ownership%20replaces%20relation%20strategy.md). -- **Value objects** — typed data structures without identity, lifecycle, or framework capabilities (no query entry points, no hooks, no referential integrity). They are storage entities, at `storage.namespaces..entries.valueObject.` in both families, because their fields are the keys every row or subdocument stores, so a change to them changes the storage hash. A value object's `fields` use the field shape models use; a member typed by an enum refers to the enum's storage value set. A domain field that holds one has the type `{ kind: 'valueObject', plane: 'storage', namespaceId, entityKind: 'valueObject', entityName }`, the value object's storage coordinate, and a member that holds another value object refers to it the same way. Readers resolve the coordinate with `valueObjectAt` from `@internal/contract`. A contract that keeps `valueObjects` in a domain namespace, or refers to a value object by `name`, is refused with a message saying to re-emit it. Value object support in SQL is capability-gated (requires JSONB-compatible columns). See [ADR 309](../adrs/ADR%20309%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md), [ADR 178](../adrs/ADR%20178%20-%20Value%20objects%20in%20the%20contract.md), [ADR 179](../adrs/ADR%20179%20-%20Union%20field%20types.md). +- **Value objects** — typed data structures without identity, lifecycle, or framework capabilities (no query entry points, no hooks, no referential integrity). They are storage entities, at `storage.namespaces..entries.valueObject.` in both families, because their fields are the keys every row or subdocument stores, so a change to them changes the storage hash. A value object's `fields` use the field shape models use; a member typed by an enum refers to the enum's storage value set. A domain field that holds one has the type `{ kind: 'valueObject', plane: 'storage', namespaceId, entityKind: 'valueObject', entityName }`, the value object's storage coordinate, and a member that holds another value object refers to it the same way. Readers resolve the coordinate with `valueObjectAt` from `@internal/contract/types`. A contract that keeps `valueObjects` in a domain namespace, or refers to a value object by `name`, is refused with a message saying to re-emit it. Value object support in SQL is capability-gated (requires JSONB-compatible columns). See [ADR 309](../adrs/ADR%20309%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md), [ADR 178](../adrs/ADR%20178%20-%20Value%20objects%20in%20the%20contract.md), [ADR 179](../adrs/ADR%20179%20-%20Union%20field%20types.md). - **Discriminator value sets (SQL)** — the SQL build writes one value set per base model with variants, keyed `.` in the base table's namespace and holding the variants' values, sorted; the discriminator column refers to it. A discriminator column an enum types keeps the enum's value set. An enum with exactly that name in the same namespace is refused. MongoDB writes no such value set: its collection validator already pins each variant's value. See [ADR 309](../adrs/ADR%20309%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md). ### Core entities vs pack extensions diff --git a/skills/prisma-8/references/contract.md b/skills/prisma-8/references/contract.md index 50b7830642ec..f504b66ab9f3 100644 --- a/skills/prisma-8/references/contract.md +++ b/skills/prisma-8/references/contract.md @@ -162,7 +162,7 @@ Values the client generates are written `@onCreate(fn())` and `@onUpdate(fn())`: In the TS builder, an alias is a constant passed as the field, because field builders are immutable: `const Slug = field.column(type.VarChar(64))`, `const UpdatedAt = field.column(type.TimestampString(3)).onCreate(gen.now()).onUpdate(gen.now())`, then `slug: Slug, updatedAt: UpdatedAt.default(now())`. `type.*` has a helper for every PSL type constructor, and the contributed aliases are `type.temporal.CreatedAt()` and `type.temporal.UpdatedAt()`, used as `field.column(type.temporal.UpdatedAt())`. A definition has no `types` key (`CONTRACT.TYPES_KEY_REMOVED`), and `field.namedType(...)` takes only an enum handle. -Note: scalar lists (e.g. `String[]`) are gated on the target's `sql.scalarList` capability — PostgreSQL declares it, so `tags String[]` lowers to an array column (with a generated no-NULL-elements check; write `tags String?[]` for a list whose elements may be null, which gets no such check); SQLite does not, and rejects the field with `PSL_SCALAR_LIST_UNSUPPORTED_TARGET`. Implicit Prisma-ORM many-to-many (list navigation on both sides without a join model) is rejected by the SQL interpreter — declare the join model explicitly and the ORM traverses it for you (`references/queries.md`). Composite/embeddable types (`type Address { ... }` with `address Address` on a model) are supported: the interpreter lowers them to `valueObjects` in the domain and stores them as `jsonb` columns. See *Workflow — Value objects* below. +Note: scalar lists (e.g. `String[]`) are gated on the target's `sql.scalarList` capability — PostgreSQL declares it, so `tags String[]` lowers to an array column (with a generated no-NULL-elements check; write `tags String?[]` for a list whose elements may be null, which gets no such check); SQLite does not, and rejects the field with `PSL_SCALAR_LIST_UNSUPPORTED_TARGET`. Implicit Prisma-ORM many-to-many (list navigation on both sides without a join model) is rejected by the SQL interpreter — declare the join model explicitly and the ORM traverses it for you (`references/queries.md`). Composite/embeddable types (`type Address { ... }` with `address Address` on a model) are supported: the interpreter lowers them to storage value objects, at `storage.namespaces..entries.valueObject`, and stores them as `jsonb` columns. See *Workflow — Value objects* below. ## Workflow — Edit a model / field / relation (TS builder) @@ -299,7 +299,7 @@ Querying the variants is a runtime concern — see `references/queries.md`. ## Workflow — Value objects (composite types) -The concept: `type Foo { ... }` blocks declare value-object shapes. The interpreter lowers them to `valueObjects` in the contract domain and stores them as `jsonb` columns. Nested value-object references are supported. +The concept: `type Foo { ... }` blocks declare value-object shapes. The interpreter lowers them to storage value objects, at `storage.namespaces..entries.valueObject`, and stores them as `jsonb` columns; a domain field refers to one by coordinate. Nested value-object references are supported. ```prisma type Address { From 47d1e4d460b3e85251aec77b3d5375551b94a535 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 17:05:55 +0200 Subject: [PATCH 062/104] TML-3537: the app upgrade guide says how to sign a database behind the last migration, and that a failed apply changes nothing Signed-off-by: willbot Signed-off-by: Will Madden --- .../app/instructions.md | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/upgrade-instructions/pending/value-objects-in-storage/app/instructions.md b/upgrade-instructions/pending/value-objects-in-storage/app/instructions.md index f900cca6e96b..a2b7bb6cac76 100644 --- a/upgrade-instructions/pending/value-objects-in-storage/app/instructions.md +++ b/upgrade-instructions/pending/value-objects-in-storage/app/instructions.md @@ -27,7 +27,7 @@ changes: ## `value-objects-in-storage` -Commit your work first, so the script's changes can be reviewed and undone with git. Do this before you re-emit the contract: `prisma db sign` must see the contract the database was created from before a re-emit adds the new checks. Then run the script from the project root: +Before you upgrade, apply every pending migration to each database (development, staging, production) with the release you are upgrading from, so each database is at the last migration. Then commit your work, so the script's changes can be reviewed and undone with git. Run the script before you re-emit the contract: `prisma db sign` must see the contract the database was created from before a re-emit adds the new checks. Run it from the project root: ```sh node /scripts/value-objects-in-storage/value-objects-in-storage.ts @@ -44,14 +44,24 @@ Storage now holds the value objects, so each contract it rewrites has a new stor When it finishes, it prints how many files it rewrote, how many snapshot directories it renamed, and each storage hash it replaced (` -> `). A project already in the new form is left unchanged, and the script says that nothing changed. It changes no file and exits 1 when a file it must read is not valid JSON, when a contract's stored hash does not match its content, when a field holds a value object the contract does not declare, when a member's enum has no storage value set, when a discriminator's value set name is already taken by an enum, or when a renamed snapshot directory already exists with different content. If it stops on an error partway, for example on a full disk, run it again: it finishes the upgrade. -The script rewrites a snapshot's `contract.d.ts` for the moved value objects. It does not add the types of the discriminator value sets; `prisma contract emit` writes those for the emitted contract. +The script rewrites a snapshot's `contract.d.ts` for the moved value objects. It does not add the types of the discriminator value sets. Nothing reads them from a snapshot: a `migration.ts` that imports a snapshot's `contract.d.ts` compiles without them, and `prisma contract emit` writes them for the emitted contract. Then: -1. Sign each database (development, staging, production) with the rewritten contract, before you re-emit: `prisma db sign --db "$DATABASE_URL"`. Its marker still holds the old storage hash, so until it is signed `prisma db migrate` refuses with `MIGRATION.MARKER_MISMATCH` and `prisma db verify` with `CONTRACT.MARKER_MISMATCH`. +1. Sign each database (development, staging, production) with the rewritten contract, before you re-emit: `prisma db sign --db "$DATABASE_URL"`. Its marker still holds the old storage hash, so until it is signed `prisma db migrate` refuses with `MIGRATION.MARKER_MISMATCH` and `prisma db verify` with `CONTRACT.MARKER_MISMATCH`. This works for a database at the last migration. 2. Re-emit the contract: `prisma contract emit`. 3. On PostgreSQL, plan and apply the migration that adds the new checks, as `postgres-value-object-checks` describes. On MongoDB there is nothing to plan: the collection validators do not change. +A database you could not migrate before the upgrade is behind the last migration, and `prisma db sign --db "$DATABASE_URL"` fails on it with `CONTRACT.SCHEMA_VERIFICATION_FAILED`, because it signs against the last contract. Do not follow its advice to run `prisma db update`: that skips the pending migrations. Instead, sign the database at the new hash of the hash its marker holds, then migrate it: + +```sh +prisma db sign --contract --no-advance-ref --db "$DATABASE_URL" +prisma db migrate --db "$DATABASE_URL" +prisma db verify --strict --db "$DATABASE_URL" +``` + +Find the new hash in the script's output: the marker holds the `` hash of one ` -> ` line. `--no-advance-ref` leaves your local `db` ref where it is. + ## `value-object-types-from-storage` ```ts @@ -68,7 +78,7 @@ type Outputs = ValueObjectTypesFromStorage__value_object_`. It accepts a JSON object with every required member, each of its JSON type, no other key, and for a list column an array of such objects; a nullable column also accepts SQL `NULL` and JSON `null`. For each discriminator column it derives `
__check_`, which accepts only the variants' values. A table whose control policy is not managed gets neither check. -After you re-emit the contract, `prisma migration plan` writes a migration that adds the checks. Its DDL preview prints each check's expression. Before you run `prisma db migrate`, find the rows that would fail by selecting the rows where the expression is not true, for example: +After you re-emit the contract, `prisma migration plan` writes a migration that adds the checks. A project with no `db` ref names the origin, the last migration: `prisma migration plan --from `; without it the command refuses with `MIGRATION.PLAN_ORIGIN_UNKNOWN`, as it does for any plan in such a project. Its DDL preview prints each check's expression. Before you run `prisma db migrate`, find the rows that would fail by selecting the rows where the expression is not true, for example: ```sql SELECT id, address FROM "public"."user" @@ -77,7 +87,7 @@ WHERE NOT ("address" IS NULL OR jsonb_path_match("address", 'strict … ') IS TR SELECT id, type FROM "public"."task" WHERE NOT ("type" IN ('bug', 'feature')); ``` -Copy the expression from the preview between `CHECK (` and the closing `)`. Fix or delete each row the query returns, for example by removing the extra keys with `UPDATE "public"."user" SET address = address - 'extra' WHERE address ? 'extra'`, then apply the migration. A migration that fails on a row leaves the operations before it applied; fix the rows and run `prisma db migrate` again. +Copy the expression from the preview between `CHECK (` and the closing `)`. Fix or delete each row the query returns, for example by removing the extra keys with `UPDATE "public"."user" SET address = address - 'extra' WHERE address ? 'extra'`, then apply the migration. A migration that fails on a row changes nothing, and the error names the check; fix the rows and run `prisma db migrate` again. ## `discriminator-typed-by-variant-values` From 0e8628b3d2c38655df566a7a5b71ffed64579ee5 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 17:06:31 +0200 Subject: [PATCH 063/104] TML-3537: the extension upgrade guide covers ValueObjectTypesFromStorage, the sixth argument of FieldTypesFromDomain and the new key of a value object field's override Signed-off-by: willbot Signed-off-by: Will Madden --- .../extension/instructions.md | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/upgrade-instructions/pending/value-objects-in-storage/extension/instructions.md b/upgrade-instructions/pending/value-objects-in-storage/extension/instructions.md index d318433fad0b..2ebbb70a8b04 100644 --- a/upgrade-instructions/pending/value-objects-in-storage/extension/instructions.md +++ b/upgrade-instructions/pending/value-objects-in-storage/extension/instructions.md @@ -17,6 +17,14 @@ changes: glob: "**/*.{ts,mts,cts,tsx}" matches: - '\bPostgresCheck(?:Kind|ExpressionInput|ExpressionCandidate)\b' + - id: field-types-read-storage-value-objects + summary: | + In `@prisma/orm-framework/contract/types`, `ValueObjectTypesFromDomain` is now `ValueObjectTypesFromStorage`, which takes the contract's storage namespaces. `FieldTypesFromDomain` takes the storage namespaces as a new sixth argument, `Contract['storage']['namespaces']`. It defaults to none, so code that still passes five arguments types no value object field: such a field gets no type. An override for a value object field is keyed by the value object's storage namespace, at `[storage namespace]['valueObjects'][valueObject][field]`. + detection: + glob: "**/*.{ts,mts,cts,tsx}" + matches: + - '\bValueObjectTypesFromDomain\b' + - '\bFieldTypesFromDomain\s*<' --- # Value objects are storage entities @@ -50,3 +58,19 @@ import type { CheckExpressionInput } from '@prisma/orm-postgres/family-contract/ ``` `postgresRenderCheckExpressions` stays in `@prisma/orm-postgres/target/types`. Its input also takes `valueObjectField`, the value object a column holds, or `undefined`. + +## `field-types-read-storage-value-objects` + +```ts +// before +import type { FieldTypesFromDomain, ValueObjectTypesFromDomain } from '@prisma/orm-framework/contract/types'; +type Fields = FieldTypesFromDomain; +type ValueObjects = ValueObjectTypesFromDomain; + +// after +import type { FieldTypesFromDomain, ValueObjectTypesFromStorage } from '@prisma/orm-framework/contract/types'; +type Fields = FieldTypesFromDomain; +type ValueObjects = ValueObjectTypesFromStorage; +``` + +Pass the sixth argument whenever the contract has value objects. A model field's override stays at `[namespace]['models'][model][field]`. A value object field's override moves to the namespace the value object is stored in: on SQL that is usually the model's namespace, and on MongoDB it is `__unbound__`. From 8f59742e6c89177089dedd34f692b3cc62fd71a4 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 17:07:19 +0200 Subject: [PATCH 064/104] TML-3537: the Mongo family migration fixture's storage hash is the one its storage computes to Signed-off-by: willbot Signed-off-by: Will Madden --- .../9-family/test/fixtures/migration-contract.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/2-mongo-family/9-family/test/fixtures/migration-contract.json b/packages/2-mongo-family/9-family/test/fixtures/migration-contract.json index 380cd17dce02..b2039b10d9ef 100644 --- a/packages/2-mongo-family/9-family/test/fixtures/migration-contract.json +++ b/packages/2-mongo-family/9-family/test/fixtures/migration-contract.json @@ -1783,7 +1783,7 @@ "kind": "mongo-database" } }, - "storageHash": "bd938b4f8a10c688bd32dc61ec1dd808dcf34e725f08505b39ce365a39c97e1b" + "storageHash": "efad057995b73e1a0303e93726cc60cb6f7186542712902dd615689ad7971758" }, "target": "mongo", "targetFamily": "mongo" From 4c488d558151c62783824b2373ac952cc5b019ec Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 17:07:48 +0200 Subject: [PATCH 065/104] TML-3537: a Mongo ORM test builds its update with an undefined field through a typed helper instead of a double cast Signed-off-by: willbot Signed-off-by: Will Madden --- .../orm/test/collection.mutation-defaults.test.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/2-mongo-family/5-query-builders/orm/test/collection.mutation-defaults.test.ts b/packages/2-mongo-family/5-query-builders/orm/test/collection.mutation-defaults.test.ts index 52be601ff329..3c87a7d4609a 100644 --- a/packages/2-mongo-family/5-query-builders/orm/test/collection.mutation-defaults.test.ts +++ b/packages/2-mongo-family/5-query-builders/orm/test/collection.mutation-defaults.test.ts @@ -188,6 +188,11 @@ describe('ORM create paths apply onCreate defaults', () => { }); }); +/** The payload a JavaScript caller passes with `field` set to `undefined`, which the typed input refuses. */ +function withUndefinedField(payload: Payload, field: string): Payload { + return Object.assign(payload, { [field]: undefined }); +} + describe('ORM update paths apply onUpdate defaults', () => { it('update adds the generated value to $set', async () => { const defaults = fakeMutationDefaults(); @@ -203,9 +208,7 @@ describe('ORM update paths apply onUpdate defaults', () => { it('update whose only field is undefined adds nothing', async () => { const defaults = fakeMutationDefaults(); const { executor, plans } = recordingExecutor([]); - await users(executor, defaults) - .where(byEmail) - .update({ name: undefined } as unknown as { name?: string }); + await users(executor, defaults).where(byEmail).update(withUndefinedField({}, 'name')); expect(unwrap(commandOf(plans, 'findOneAndUpdate')['update'])).toEqual({ $set: {} }); expect(defaults.calls[0]?.values).toEqual({}); }); From bc28697bf6673df8df0e642c39fe2ceb212e62b4 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 17:09:24 +0200 Subject: [PATCH 066/104] TML-3537: the upgrade script stops with one line, not a stack trace, on a directory it cannot read Signed-off-by: willbot Signed-off-by: Will Madden --- .../value-objects-in-storage.test.ts | 18 ++++++++- .../app/instructions.md | 2 +- .../value-objects-in-storage.ts | 38 +++++++++++++++++-- 3 files changed, 52 insertions(+), 6 deletions(-) diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/value-objects-in-storage.test.ts b/test/integration/test/upgrade-instructions/value-objects-in-storage/value-objects-in-storage.test.ts index 357f860a9406..d81f57a8cc72 100644 --- a/test/integration/test/upgrade-instructions/value-objects-in-storage/value-objects-in-storage.test.ts +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/value-objects-in-storage.test.ts @@ -1,4 +1,4 @@ -import { readFileSync } from 'node:fs'; +import { chmodSync, readFileSync } from 'node:fs'; import { computeStorageHash } from '@internal/contract/hashing'; import { MongoContractSerializer } from '@internal/family-mongo/ir'; import { mongoContractCanonicalizationHooks } from '@internal/mongo-contract/canonicalization-hooks'; @@ -237,6 +237,22 @@ describe('a project the script refuses to rewrite', () => { }); }); + it('stops with one line on a directory it cannot read', () => { + const root = copyFixture('before'); + const locked = join(root, 'sql', 'migrations', 'app'); + chmodSync(locked, 0o000); + try { + expect(runScript(root)).toEqual({ + status: 1, + stdout: '', + stderr: + 'sql/migrations/app: cannot read the directory (EACCES); give the script read access to it, or move it out of the project, and run the script again\n', + }); + } finally { + chmodSync(locked, 0o755); + } + }); + it('says when it finds no contract', () => { const root = join(copyFixture('before'), 'sql', 'migrations', 'app'); expect(runScript(root)).toEqual({ diff --git a/upgrade-instructions/pending/value-objects-in-storage/app/instructions.md b/upgrade-instructions/pending/value-objects-in-storage/app/instructions.md index a2b7bb6cac76..027fbefe6529 100644 --- a/upgrade-instructions/pending/value-objects-in-storage/app/instructions.md +++ b/upgrade-instructions/pending/value-objects-in-storage/app/instructions.md @@ -42,7 +42,7 @@ Node 24 or later runs the TypeScript script directly; it needs no `tsx`. It read Storage now holds the value objects, so each contract it rewrites has a new storage hash. The script renames each snapshot directory to the new hash and rewrites every `migration.json` (with its `migrationHash`), ref, `migration.ts` and `contract.d.ts` that names an old one. It does not add the PostgreSQL checks described in `postgres-value-object-checks`: a snapshot records what its migrations created, and none created those checks. -When it finishes, it prints how many files it rewrote, how many snapshot directories it renamed, and each storage hash it replaced (` -> `). A project already in the new form is left unchanged, and the script says that nothing changed. It changes no file and exits 1 when a file it must read is not valid JSON, when a contract's stored hash does not match its content, when a field holds a value object the contract does not declare, when a member's enum has no storage value set, when a discriminator's value set name is already taken by an enum, or when a renamed snapshot directory already exists with different content. If it stops on an error partway, for example on a full disk, run it again: it finishes the upgrade. +When it finishes, it prints how many files it rewrote, how many snapshot directories it renamed, and each storage hash it replaced (` -> `). A project already in the new form is left unchanged, and the script says that nothing changed. It changes no file and exits 1 when a file it must read is not valid JSON, when a contract's stored hash does not match its content, when a field holds a value object the contract does not declare, when a member's enum has no storage value set, when a discriminator's value set name is already taken by an enum, when a renamed snapshot directory already exists with different content, or when it cannot read a directory under the root. If it stops on an error partway, for example on a full disk, run it again: it finishes the upgrade. The script rewrites a snapshot's `contract.d.ts` for the moved value objects. It does not add the types of the discriminator value sets. Nothing reads them from a snapshot: a `migration.ts` that imports a snapshot's `contract.d.ts` compiles without them, and `prisma contract emit` writes them for the emitted contract. diff --git a/upgrade-instructions/pending/value-objects-in-storage/app/scripts/value-objects-in-storage/value-objects-in-storage.ts b/upgrade-instructions/pending/value-objects-in-storage/app/scripts/value-objects-in-storage/value-objects-in-storage.ts index 7108205dcaab..f5924eb74f54 100644 --- a/upgrade-instructions/pending/value-objects-in-storage/app/scripts/value-objects-in-storage/value-objects-in-storage.ts +++ b/upgrade-instructions/pending/value-objects-in-storage/app/scripts/value-objects-in-storage/value-objects-in-storage.ts @@ -6,7 +6,7 @@ * * Usage: node value-objects-in-storage.ts [project-root] * - * The project root defaults to the working directory. The script reads and writes files only. It needs no database, network or configured stack, and a project whose contracts keep no value object in the domain plane is left unchanged. When it stops (a file it must read is not valid JSON, a contract whose stored hash does not match its content, a field naming a value object the contract does not declare, a value object whose namespace storage does not have, a member enum without a storage value set, a discriminator value set that already exists, or a snapshot directory that already exists with different content) it changes no file, prints one line per case and exits 1. When it finishes it prints how many files and snapshot directories it changed and each storage hash it replaced. + * The project root defaults to the working directory. The script reads and writes files only. It needs no database, network or configured stack, and a project whose contracts keep no value object in the domain plane is left unchanged. When it stops (a file it must read is not valid JSON, a contract whose stored hash does not match its content, a field naming a value object the contract does not declare, a value object whose namespace storage does not have, a member enum without a storage value set, a discriminator value set that already exists, a snapshot directory that already exists with different content, or a directory it cannot read) it changes no file, prints one line per case and exits 1. When it finishes it prints how many files and snapshot directories it changed and each storage hash it replaced. * * The storage hash and migration hash rules are copied from `@internal/contract`, `@internal/sql-contract`, `@internal/mongo-contract` and `@internal/migration-tools` because a project's strict `node_modules` does not expose them. */ @@ -295,6 +295,7 @@ interface ProjectFiles { readonly refs: readonly string[]; readonly migrationTs: readonly string[]; readonly leftovers: readonly string[]; + readonly unreadable: readonly { readonly dir: string; readonly code: string }[]; } function isDirectory(path: string): boolean { @@ -329,13 +330,17 @@ function listFiles(root: string): ProjectFiles { const leftovers: string[] = []; const visited = new Set(); const found = new Map(); + const unreadable: { dir: string; code: string }[] = []; const visit = (dir: string): void => { const realDir = realpathSync(dir); if (visited.has(realDir)) return; visited.add(realDir); - const entries = readdirSync(dir, { withFileTypes: true }).sort((a, b) => - compareCodeUnits(a.name, b.name), - ); + const listed = listDirectory(dir); + if (!listed.ok) { + unreadable.push({ dir, code: listed.code }); + return; + } + const entries = listed.entries.sort((a, b) => compareCodeUnits(a.name, b.name)); const inMigrationPackage = entries.some( (entry) => entry.name === 'migration.json' && isFileEntry(dir, entry), ); @@ -367,9 +372,23 @@ function listFiles(root: string): ProjectFiles { refs: named((file, name) => name.endsWith('.json') && basename(dirname(file.path)) === 'refs'), migrationTs: named((file, name) => name === 'migration.ts' && file.inMigrationPackage), leftovers, + unreadable, }; } +type DirectoryListing = + | { readonly ok: true; readonly entries: Dirent[] } + | { readonly ok: false; readonly code: string }; + +function listDirectory(dir: string): DirectoryListing { + try { + return { ok: true, entries: readdirSync(dir, { withFileTypes: true }) }; + } catch (error) { + const code = error instanceof Error && 'code' in error ? String(error.code) : 'unknown error'; + return { ok: false, code }; + } +} + function isSnapshotContract(path: string): boolean { const snapshotDir = dirname(path); return ( @@ -947,6 +966,17 @@ function main({ } const display = (path: string): string => relative(root, path).split(sep).join('/'); const files = listFiles(root); + if (files.unreadable.length > 0) { + process.stderr.write( + `${files.unreadable + .map( + ({ dir, code }) => + `${display(dir)}: cannot read the directory (${code}); give the script read access to it, or move it out of the project, and run the script again`, + ) + .join('\n')}\n`, + ); + return 1; + } const plans: ContractPlan[] = []; const stops: string[] = []; let contractsFound = 0; From 5f48de903b7088d9ab01acdc97c006df61e092f9 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 17:12:48 +0200 Subject: [PATCH 067/104] TML-3537: deferred note on the SQL TypeScript builder having no value objects Signed-off-by: willbot Signed-off-by: Will Madden --- projects/migration-statements/deferred.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/projects/migration-statements/deferred.md b/projects/migration-statements/deferred.md index 5e0ee4e28280..28c647fad7ff 100644 --- a/projects/migration-statements/deferred.md +++ b/projects/migration-statements/deferred.md @@ -245,3 +245,7 @@ Found in the review of the value object storage PR, 2026-10-23. Both limits are - A `BigInt` member of a value object cannot be written: the `pg/jsonb@1` codec throws "Do not know how to serialize a BigInt". - A `Bytes` member is written as an object of byte indexes, such as `{"0":1,"1":2}`, not as bytes. + +## The SQL TypeScript builder has no value objects + +`defineContract` in `@internal/postgres/contract-builder` cannot declare a value object, so a SQL contract written in TypeScript cannot have one. The main SQL ORM test contract (`test/integration/test/sql-orm-client/fixtures/contract.ts`) used to patch a value object into its built contract; it now stores `User.address` as a plain `jsonb` column, and the value object type tests read a PSL fixture (`fixtures/value-objects`). That contract cannot move to PSL, because PSL cannot write its to-one relation without a foreign key (`Article.reviewer`). From 059a3e6a8f9f88357fd76b7287d1ac3e62d57e15 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 17:48:08 +0200 Subject: [PATCH 068/104] TML-3537: the upgrade test fixture keeps the snapshot from before the codec additions on main Signed-off-by: willbot Signed-off-by: Will Madden --- .../contract.d.ts | 32 ------------------- 1 file changed, 32 deletions(-) diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/sql/migrations/snapshots/58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b/contract.d.ts b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/sql/migrations/snapshots/58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b/contract.d.ts index d80ae3a33b2b..64fd0c6ba064 100644 --- a/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/sql/migrations/snapshots/58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b/contract.d.ts +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/fixtures/before/sql/migrations/snapshots/58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b/contract.d.ts @@ -50,10 +50,6 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/interval-string@1': { - readonly output: 'pg/interval-string@1'; - readonly nullable: true; - }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -103,10 +99,6 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/interval-string@1': { - readonly output: 'pg/interval-string@1'; - readonly nullable: true; - }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -116,14 +108,6 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; - readonly 'pg/timestamp-date@1': { - readonly output: 'pg/timestamp-date@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-instant@1': { - readonly output: 'pg/timestamp-instant@1'; - readonly nullable: true; - }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -175,10 +159,6 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/interval-string@1': { - readonly output: 'pg/interval-string@1'; - readonly nullable: true; - }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -188,14 +168,6 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; - readonly 'pg/timestamp-date@1': { - readonly output: 'pg/timestamp-date@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-instant@1': { - readonly output: 'pg/timestamp-instant@1'; - readonly nullable: true; - }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -255,10 +227,6 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/interval-string@1': { - readonly output: 'pg/interval-string@1'; - readonly nullable: true; - }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; From c6a92e741e76c1c01ced1a0315b2196823bc1614 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 17:49:04 +0200 Subject: [PATCH 069/104] TML-3537: dispatch brief for the fixes from the whole-branch review Signed-off-by: willbot Signed-off-by: Will Madden --- .../dispatches/18-pr3-review-fixes.md | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 projects/migration-statements/slices/remaining-nouns/dispatches/18-pr3-review-fixes.md diff --git a/projects/migration-statements/slices/remaining-nouns/dispatches/18-pr3-review-fixes.md b/projects/migration-statements/slices/remaining-nouns/dispatches/18-pr3-review-fixes.md new file mode 100644 index 000000000000..e6843ca31ba4 --- /dev/null +++ b/projects/migration-statements/slices/remaining-nouns/dispatches/18-pr3-review-fixes.md @@ -0,0 +1,101 @@ +# Dispatch 18 — fixes from the whole-branch review of PR 3 + +**Plan:** [`../plan-pr3.md`](../plan-pr3.md) · **ADR:** ADR 309 · **Reviews:** `../reviews/pr3/system-design-review.md` (findings A01–A17) and `../reviews/pr3/code-review.md` (findings F1–F5) · **Branch:** `tml-3537-value-objects-storage` + +F1 is fixed (`059a3e6a8f`). Each item below records the decision; do what it says. Write a failing test first for every behaviour change. One commit per item or per small group of items. + +## Contract shape and types + +1. **A01 — `valueObject` is a framework storage entity kind.** + - Record this in ADR 309. Add a matching note to ADR 221's section on pack-contributed entity kinds: value objects and the value sets their members name are kinds the framework defines and reads, because domain fields refer to them. + - Give the value object field schema one definition that both families use. If a shared schema cannot be imported by both, add a test that one input gives the same entity in both families. + - Decide `nullable` and `dict` once: + - `nullable` is required in the JSON, as SQL has it. + - `dict` is `true` or absent. The Mongo schema currently accepts `false`. + - Update the JSON schema and any fixture this changes. +2. **A03 — one name for "domain plus storage namespaces."** + - Widen `ContractWithDomain` to carry `storage: Pick`. Remove `StatementContract` and `ContractPlanes`, and replace the inline `Pick` uses with that type or one exported name for it. + - Rename the local `StorageNamespaces` aliases, so the word means one thing across the codebase. +3. **A04 and A05 — the names of the storage value object helpers.** + - Today there are three different functions named `storageValueObjects`, plus an export that only tests use. Give each a name that says what it returns, and remove the test-only export. + - Make `ValueObjectFieldType` and `ContractValueObjectField` read as what they are: the field type that refers to a value object, and a field of a value object. Rename one of them if a reader cannot tell them apart. + - Use existing vocabulary. List the names you chose in the report. +4. **A06 — the sixth type parameter of `FieldTypesFromDomain` is required.** + - Always emit `ContractBase['storage']['namespaces']`, and remove the `hasValueObjects` branch. Every `contract.d.ts` changes, so re-emit. + - Say in the type's doc comment that it reads both planes. + - Update the extension upgrade fragment. +5. **A07 — the extension fragment's override example.** Correct the namespace in the example. Say that under `valueObjects` the key is the storage namespace, while under `models` it is the domain namespace. +6. **A14 — names in the column check input.** + - The column check input reuses the member type. Give the column its own type if a member and a column can differ. + - Rename `openObject` to say what the predicate accepts there (any object), not why. Use existing vocabulary. +7. **A15 — the Mongo validator.** It looks up value objects by name only. Look them up by the field's coordinate, as every other reader now does. Add a test with two value objects of the same name in different storage namespaces, built by hand as a contract. +8. **A16 and F4 — no unions or dictionaries in SQL.** The SQL storage schema accepts union and dictionary value object members, but SQL code treats them as impossible. Refuse them in the SQL schema, with a message that names the member. Add a test that a hand-written contract with one is refused at validation, not by an internal error in `contract print`. + +## Postgres checks + +9. **A08 and F2 — a codec's JSON type is declared on the codec or data type, not in a table in the target.** + - Declare it the way the Mongo family declares `bsonTypes` on its data types (`mongo-data-type.ts`, `bsonTypesOfCodec`). The Postgres renderer reads it through the codec lookup the build already has. Remove `value-object-json-types.ts`. + - Every Postgres and SQL family codec declares one of these: its JSON type; that it holds any JSON value; or that only presence is checked. This includes the three codecs that came with the merge of main: `pg/timestamp-date@1`, `pg/timestamp-instant@1` and `pg/interval-string@1`. + - Add a test that fails when a codec declares none of them. + - Keep the presence-only rules from dispatch 15: a required member is not JSON `null`, and `Json` members hold anything. +10. **A11 — the check kind is `shape`, and its constraint suffix is `shape`.** + - The sibling kinds name what they check (`membership`, `elementNotNull`). A value object check checks the column's shape. The suffix ships in users' constraint names, so it changes now, before release. + - Update `CHECK_KIND_SUFFIX`, the docs, the error reference, the upgrade fragment and the `prisma-8-demo` migration, then re-emit. +11. **A13 — one derivation for the build and for print.** + - Put the discriminator value set derivation and the value object check derivation in one function each, in `@internal/sql-contract/check-expressions` beside `resolveValueObject`. + - The build and `contract print` both call them. The print round-trip tests then check the comparison, not two copies of the rule. +12. **F3 — the lock.** + - Add one sentence to the app upgrade fragment and to the Data Contract doc. Adding a check takes a lock on the table while Postgres reads every row. Any later change to a value object renames the check, so the check is dropped and added again, and the rows are read again. + - Add a `deferred.md` entry: adding these checks as `NOT VALID` and then running `VALIDATE CONSTRAINT`. + +## CLI and messages + +13. **A12 — `changedStorageEntities`.** + - Fill it for every migration `migration plan` writes, not only one with no operations. + - The CLI decides "written with no operations" from the operations, not from whether the field is present. + - Use the same noun as `changedElementCoordinates` where that reads better. + - Check that it does not clash with the existing `noOp` field, and say how in the README. +14. **A02 — the domain validator checks storage entities.** + - Today `validateContractDomain` checks that a value object member's storage value set exists. Move the checks of storage entries to a storage validator: in the framework if there is one, otherwise beside `validateContractDomain` under a name that says it checks storage. + - Leave the checks of the domain-to-storage reference in the domain validator. +15. **F5 — the refusal for an old snapshot.** Name the upgrade script, as the `noCheck` refusal does. A snapshot cannot be re-emitted. + +## ADR 309 and other docs + +16. **A09.** + - Add a short paragraph on why a value object does not have both a domain definition and a storage entity, the way models and tables do. Name the cost: there are no stored names without a later shape change. + - Change the Prisma 6 reader's diagnostic (`contract-prisma6/src/interpreter.ts` around line 880), so that it no longer promises `@map` support on composite type fields. + - Say that enum member names are outside the principle, and link TML-3720. +17. **A10.** + - Limit "every change is an operation" to targets that derive enforcement. + - List the known no-operation cases: + - targets with no check renderer (SQLite and MySQL); + - on Postgres, a member codec or type parameter change that keeps the same JSON type. + - Point to the no-operations report as how the system handles them. +18. **A17.** Fix the small documentation drift the review lists. + +## Rules + +- Never run `pnpm test:integration`, `pnpm test:e2e` or `pnpm test:all`. Run only named files inside `test/integration`, with `pnpm test `, and journeys with `pnpm test:journeys `. `pnpm test:packages` is allowed. +- Use `mise exec --` for node and pnpm. Never `npx`. Do not write to `/tmp` or any system temp directory. Save slow output under `wip/` and read the file. Never use bare `git stash`. +- No comments where the code can say it. No bare `as` casts in production code. No `any`. No file extensions in imports. No backwards-compatibility exports. Framework packages name no family or target. +- No invented nouns. Never hard-wrap Markdown prose. ADRs carry no ticket ids except where this brief says so (TML-3720), and no dispatch names. +- Commit with `git commit -s --trailer "Signed-off-by: Will Madden "`. No AI attribution lines. Stage files explicitly. Commit re-emitted files apart from hand-written changes. +- Do not push. Do not open a PR. + +## Done when + +- `pnpm build` and `fixtures:check` are clean. +- `pnpm test:packages` passes, apart from the tarball tests' known `@vercel/detect-agent` trust warning. +- Typecheck passes in every example. +- `lint:deps`, `lint:ratchets`, `check:error-reference` and `check:upgrade-coverage --mode pr --prev $(git merge-base HEAD origin/main) --head HEAD` pass. +- Typecheck, lint and `test --coverage` pass in every touched package. +- These pass: + - the journeys `value-object-checks.e2e.test.ts`, `value-object-validator.mongo.e2e.test.ts`, `plans-with-no-operations*.e2e.test.ts` and `enum-value-statements*.e2e.test.ts`; + - `test/integration/test/sql-orm-client/value-object-checks.test.ts`; + - `authored-contract-roundtrip.integration.test.ts`; + - the upgrade script test. + +## Report + +Plain English, short sentences: each item and what you did, the names you chose, the tests that were red first, the checks, anything you could not do and why, and the commits. From 548feb2c7f760d73c7a3fc59f6207b18391c57e9 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 17:56:52 +0200 Subject: [PATCH 070/104] TML-3537: both families validate a value object against one framework schema, which requires nullable and allows dict only as true Signed-off-by: willbot Signed-off-by: Will Madden --- ...inate and pack-contributed entity kinds.md | 2 + ... discriminator column names a value set.md | 2 + .../contract/src/exports/types.ts | 1 + .../src/storage-value-object-schema.ts | 49 ++++++++ .../test/storage-value-object-schema.test.ts | 64 ++++++++++ .../mongo-contract/src/contract-schema.ts | 33 +---- .../mongo-contract/src/entity-kinds.ts | 7 +- .../src/ir/mongo-value-object.ts | 8 +- .../mongo-contract/test/field-schema.test.ts | 12 +- .../test/value-object-schema.test.ts | 22 +++- .../1-core/contract/src/entity-kinds.ts | 7 +- .../contract/src/ir/storage-entry-schemas.ts | 17 --- .../schemas/data-contract-sql-v1.json | 115 +++++++++++++++++- 13 files changed, 268 insertions(+), 71 deletions(-) create mode 100644 packages/1-framework/0-foundation/contract/src/storage-value-object-schema.ts create mode 100644 packages/1-framework/0-foundation/contract/test/storage-value-object-schema.test.ts diff --git a/docs/architecture docs/adrs/ADR 221 - Contract IR two planes with uniform entity coordinate and pack-contributed entity kinds.md b/docs/architecture docs/adrs/ADR 221 - Contract IR two planes with uniform entity coordinate and pack-contributed entity kinds.md index 124c1f2cbe50..20f121e7472b 100644 --- a/docs/architecture docs/adrs/ADR 221 - Contract IR two planes with uniform entity coordinate and pack-contributed entity kinds.md +++ b/docs/architecture docs/adrs/ADR 221 - Contract IR two planes with uniform entity coordinate and pack-contributed entity kinds.md @@ -145,6 +145,8 @@ The built-in kinds (`tables`, `collections`) stay hardcoded inside their family The mechanism's value is that it satisfies two requirements at once: a kind is contributed at the framework level (so authoring tools — the PSL interpreter, the TS DSL, the emitter — all see it) *without* any target-specific symbol appearing in the framework or in sibling-target packages. A cheaper-looking option — keep a kind hardcoded but move the hardcode from the framework into the pack — fails the second requirement, because the slot name still leaks into the framework's authoring-tool dispatch. +**Kinds the framework defines.** Two storage kinds are not contributed but defined by the framework, because domain fields refer to them: `valueObject`, and `valueSet`, which value object members name. The framework reads them by slot key, validates a value object's fields against one schema that both families use, and types them in `contract.d.ts`. Each family still registers a descriptor and an IR class for them. See [ADR 309](ADR%20309%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md). + ## The `Namespace` interface The framework's `Namespace` interface declares only `{ id, kind }`. Family-specific slots live on family-shaped namespace types — `SqlNamespace = Namespace & { tables: …, [...packContributedSlots] }`, `MongoNamespace = Namespace & { collections: … }`. The framework type is honest about what the framework actually knows. diff --git a/docs/architecture docs/adrs/ADR 309 - Value objects are storage entities, and a discriminator column names a value set.md b/docs/architecture docs/adrs/ADR 309 - Value objects are storage entities, and a discriminator column names a value set.md index 289ba4fd9e2f..171a6e44abef 100644 --- a/docs/architecture docs/adrs/ADR 309 - Value objects are storage entities, and a discriminator column names a value set.md +++ b/docs/architecture docs/adrs/ADR 309 - Value objects are storage entities, and a discriminator column names a value set.md @@ -90,6 +90,8 @@ Storage already holds stored facts the database does not enforce. SQLite derives **Value objects.** The storage entity keeps the field definition the domain plane used, so every reader of a value object's fields reads the same shape, from the storage plane. Its fields have no stored names of their own: each is stored under its own name, in both families. Scalar members carry their codec and, when an enum types them, a `valueSet` reference to the enum's storage value set, since storage never refers to the domain plane. A member that is a value object refers to its storage entity by coordinate, so a value object that contains itself, such as a `NavItem` whose `children` are `NavItem`s, is one entity that refers to itself. On SQL the column that holds a value object stays a JSON column; on MongoDB the validator is derived from the storage entity, as it is from the definition today. +**The framework defines the entity kind.** Families and packs contribute storage entity kinds ([ADR 221](ADR%20221%20-%20Contract%20IR%20two%20planes%20with%20uniform%20entity%20coordinate%20and%20pack-contributed%20entity%20kinds.md)), but `valueObject` is defined by the framework, because a domain field refers to it and the framework resolves that reference, validates it and types it. So does `valueSet` where a value object member names one. Both families validate a value object against one schema from the framework: each field has `nullable`, always written; `type`; `many` when it is a list; `dict` only as `true`; and an optional `valueSet` reference. Each family still registers its own descriptor and IR class for the kind. + **Discriminators.** The value set is keyed `.`, such as `Task.type`, and holds the values sorted, so the order variants are declared in does not change the hash. A discriminator an enum types keeps the enum's value set and check: the column has one value set reference, and the enum already restricts it. Enum value sets are keyed by the enum's name, so only an enum named exactly like a discriminator's key, in the same storage namespace, clashes with it. No authoring surface writes such a name, since PSL names are identifiers and the SQL TypeScript builder has no polymorphism; the build refuses it all the same, naming both and saying to rename the enum. Enum names are not otherwise restricted. A variant stored in its own table changes nothing here: its rows are still told apart by the base table's column. The ORM writes a row only through a variant, so every row's value names a variant, and the derived check (`"type" IN ('bug', 'feature')`) rejects only a value no variant names, as an enum's check does. ```json diff --git a/packages/1-framework/0-foundation/contract/src/exports/types.ts b/packages/1-framework/0-foundation/contract/src/exports/types.ts index c0f9cee0db0a..d53c6c2994de 100644 --- a/packages/1-framework/0-foundation/contract/src/exports/types.ts +++ b/packages/1-framework/0-foundation/contract/src/exports/types.ts @@ -58,6 +58,7 @@ export type { ParameterizedOutputTypesFunction, } from '../parameterized-output-types'; export { type ResolvedDomainModel, resolveDomainModel } from '../resolve-domain-model'; +export { StorageValueObjectSchema } from '../storage-value-object-schema'; export { storageValueObjectsAtDefaultNamespace, valueObjectAt, diff --git a/packages/1-framework/0-foundation/contract/src/storage-value-object-schema.ts b/packages/1-framework/0-foundation/contract/src/storage-value-object-schema.ts new file mode 100644 index 000000000000..a49089008cb3 --- /dev/null +++ b/packages/1-framework/0-foundation/contract/src/storage-value-object-schema.ts @@ -0,0 +1,49 @@ +import { type } from 'arktype'; + +const ScalarFieldTypeSchema = type({ + '+': 'reject', + kind: "'scalar'", + codecId: 'string', + 'typeParams?': 'Record', +}); + +const ValueObjectFieldTypeSchema = type({ + '+': 'reject', + kind: "'valueObject'", + plane: "'storage'", + namespaceId: 'string', + entityKind: "'valueObject'", + entityName: 'string', +}); + +const UnionFieldTypeSchema = type({ + '+': 'reject', + kind: "'union'", + members: ScalarFieldTypeSchema.or(ValueObjectFieldTypeSchema).array(), +}); + +const StorageValueSetReferenceSchema = type({ + '+': 'reject', + plane: "'storage'", + namespaceId: 'string', + entityKind: "'valueSet'", + entityName: 'string', + 'spaceId?': 'string', +}); + +const StorageValueObjectFieldSchema = type({ + '+': 'reject', + nullable: 'boolean', + type: ScalarFieldTypeSchema.or(ValueObjectFieldTypeSchema).or(UnionFieldTypeSchema), + 'many?': type('false').or({ '+': 'reject', elementNullable: 'boolean' }), + 'dict?': 'true', + 'valueSet?': StorageValueSetReferenceSchema, +}); + +/** + * A value object, the storage entity under `storage.namespaces..entries.valueObject.` (ADR 309). The framework defines this entity kind, since domain fields refer to it; both families validate their entries against this schema. + */ +export const StorageValueObjectSchema = type({ + '+': 'reject', + fields: type({ '[string]': StorageValueObjectFieldSchema }), +}); diff --git a/packages/1-framework/0-foundation/contract/test/storage-value-object-schema.test.ts b/packages/1-framework/0-foundation/contract/test/storage-value-object-schema.test.ts new file mode 100644 index 000000000000..ce8fc9c58729 --- /dev/null +++ b/packages/1-framework/0-foundation/contract/test/storage-value-object-schema.test.ts @@ -0,0 +1,64 @@ +import { type } from 'arktype'; +import { describe, expect, it } from 'vitest'; +import { StorageValueObjectSchema } from '../src/storage-value-object-schema'; + +const text = { type: { kind: 'scalar', codecId: 'pg/text@1' }, nullable: false }; +const addressReference = { + kind: 'valueObject', + plane: 'storage', + namespaceId: 'public', + entityKind: 'valueObject', + entityName: 'Address', +}; + +describe('StorageValueObjectSchema', () => { + it('accepts scalar, value object and union members, lists, dictionaries and value sets', () => { + const valueObject = { + fields: { + street: text, + home: { type: addressReference, nullable: true, many: { elementNullable: false } }, + either: { + type: { kind: 'union', members: [text.type, addressReference] }, + nullable: false, + }, + tags: { ...text, dict: true }, + kind: { + ...text, + valueSet: { + plane: 'storage', + namespaceId: 'public', + entityKind: 'valueSet', + entityName: 'Kind', + }, + }, + }, + }; + expect(StorageValueObjectSchema(valueObject)).toEqual(valueObject); + }); + + it.each([ + ['a member without nullable', { street: { type: text.type } }], + ['a member with dict false', { street: { ...text, dict: false } }], + ['a member with an unknown key', { street: { ...text, column: 'street' } }], + [ + 'a member that names its value object the old way', + { home: { ...text, type: { kind: 'valueObject', name: 'Address' } } }, + ], + [ + 'a member value set in the domain plane', + { + kind: { + ...text, + valueSet: { + plane: 'domain', + namespaceId: 'public', + entityKind: 'enum', + entityName: 'Kind', + }, + }, + }, + ], + ])('refuses %s', (_case, fields) => { + expect(StorageValueObjectSchema({ fields })).toBeInstanceOf(type.errors); + }); +}); diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/src/contract-schema.ts b/packages/2-mongo-family/1-foundation/mongo-contract/src/contract-schema.ts index 726398b7f054..beb17d351d0b 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/src/contract-schema.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/src/contract-schema.ts @@ -1,4 +1,8 @@ -import { ContractExecutionSectionSchema, CrossReferenceSchema } from '@internal/contract/types'; +import { + ContractExecutionSectionSchema, + CrossReferenceSchema, + StorageValueObjectSchema, +} from '@internal/contract/types'; import { type Type, type } from 'arktype'; import type { MongoJsonObject, MongoJsonPrimitive, MongoJsonValue } from './contract-types'; @@ -65,33 +69,6 @@ const FieldSchema = RawFieldSchema.pipe((field) => ({ many: field.many ?? false, })); -const StorageValueSetRefSchema = type({ - plane: "'storage'", - namespaceId: 'string', - entityKind: "'valueSet'", - entityName: 'string', - 'spaceId?': 'string', -}); - -const StorageValueObjectFieldSchema = type({ - '+': 'reject', - type: FieldTypeSchema, - 'nullable?': 'boolean', - 'many?': type('false').or({ elementNullable: 'boolean' }), - 'dict?': 'boolean', - 'valueSet?': StorageValueSetRefSchema, -}); - -/** - * Storage value object entry under `storage.namespaces[id].entries.valueObject[name]` (ADR 309). Its fields have the shape a domain field has; a member typed by an enum refers to the storage value set, since storage never refers to the domain plane. - */ -export const StorageValueObjectSchema = type({ - '+': 'reject', - fields: type({ - '[string]': StorageValueObjectFieldSchema, - }), -}); - const RelationOnSchema = type({ '+': 'reject', localFields: 'string[]', diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/src/entity-kinds.ts b/packages/2-mongo-family/1-foundation/mongo-contract/src/entity-kinds.ts index 531bcb695df0..25271a8b7552 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/src/entity-kinds.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/src/entity-kinds.ts @@ -1,13 +1,10 @@ +import { StorageValueObjectSchema } from '@internal/contract/types'; import type { AnyEntityKindDescriptor, EntityKindDescriptor, } from '@internal/framework-components/ir'; import { structuredError } from '@internal/utils/structured-error'; -import { - StorageCollectionSchema, - StorageValueObjectSchema, - StorageValueSetSchema, -} from './contract-schema'; +import { StorageCollectionSchema, StorageValueSetSchema } from './contract-schema'; import { MongoCollection, type MongoCollectionInput } from './ir/mongo-collection'; import { MongoValueObject, type MongoValueObjectInput } from './ir/mongo-value-object'; import { MongoValueSet, type MongoValueSetInput } from './ir/mongo-value-set'; diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/src/ir/mongo-value-object.ts b/packages/2-mongo-family/1-foundation/mongo-contract/src/ir/mongo-value-object.ts index 9c90e02a9546..4a1c9a60113a 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/src/ir/mongo-value-object.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/src/ir/mongo-value-object.ts @@ -1,11 +1,9 @@ import type { ContractValueObjectField } from '@internal/contract/types'; import { freezeNode, IRNodeBase } from '@internal/framework-components/ir'; -/** Hydration / construction input shape for {@link MongoValueObject}; mirrors the stored JSON, where a field may leave out `nullable` and `many`. */ +/** Hydration / construction input shape for {@link MongoValueObject}; mirrors the stored JSON, where a field may leave out `many`. */ export interface MongoValueObjectInput { - readonly fields: Readonly< - Record & { readonly nullable?: boolean }> - >; + readonly fields: Readonly>; } /** @@ -27,7 +25,7 @@ export class MongoValueObject extends IRNodeBase { Object.fromEntries( Object.entries(input.fields).map(([name, field]) => [ name, - Object.freeze({ ...field, nullable: field.nullable ?? false, many: field.many ?? false }), + Object.freeze({ ...field, many: field.many ?? false }), ]), ), ); diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/test/field-schema.test.ts b/packages/2-mongo-family/1-foundation/mongo-contract/test/field-schema.test.ts index 5008abac622f..7980f6a35434 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/test/field-schema.test.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/test/field-schema.test.ts @@ -34,9 +34,9 @@ describe.each(['model', 'storage value object'] as const)('%s field schema', (ki const fieldType = { kind: 'scalar', codecId: 'mongo/string@1' }; it('accepts a field with no many property', () => { - expect(MongoContractSchema(contractWithField(kind, { type: fieldType }))).not.toBeInstanceOf( - type.errors, - ); + expect( + MongoContractSchema(contractWithField(kind, { type: fieldType, nullable: false })), + ).not.toBeInstanceOf(type.errors); }); it.each([false, { elementNullable: false }, { elementNullable: true }])( @@ -55,8 +55,8 @@ describe.each(['model', 'storage value object'] as const)('%s field schema', (ki { elementNullable: 'false' }, { elementNullable: 0 }, ])('rejects malformed many %j', (many) => { - expect(MongoContractSchema(contractWithField(kind, { type: fieldType, many }))).toBeInstanceOf( - type.errors, - ); + expect( + MongoContractSchema(contractWithField(kind, { type: fieldType, nullable: false, many })), + ).toBeInstanceOf(type.errors); }); }); diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/test/value-object-schema.test.ts b/packages/2-mongo-family/1-foundation/mongo-contract/test/value-object-schema.test.ts index 8b23f2c45c0f..9699b30bddf2 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/test/value-object-schema.test.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/test/value-object-schema.test.ts @@ -104,18 +104,34 @@ describe('the Mongo contract schema with value objects in storage', () => { expect(MongoContractSchema(contract)).toEqual(contract); }); - it('leaves frozen storage unchanged, and the value object fills in an absent nullable and many', () => { + it('leaves frozen storage unchanged, and the value object fills in an absent many', () => { const contract = contractWith( { type: valueObjectType('Address'), nullable: false, many: false }, - { Address: { fields: { street: { type: text.type } } } }, + { Address: { fields: { street: { type: text.type, nullable: false } } } }, ); deepFreeze(contract.storage); expect({ parsed: MongoContractSchema(contract), - valueObject: new MongoValueObject({ fields: { street: { type: text.type } } }).fields, + valueObject: new MongoValueObject({ + fields: { street: { type: text.type, nullable: false } }, + }).fields, }).toEqual({ parsed: contract, valueObject: { street: text } }); }); + it.each([ + ['without nullable', { type: text.type }], + ['with dict false', { ...text, dict: false }], + ])('refuses a value object member %s', (_case, street) => { + expect( + MongoContractSchema( + contractWith( + { type: valueObjectType('Address'), nullable: false }, + { Address: { fields: { street } } }, + ), + ), + ).toBeInstanceOf(type.errors); + }); + it('refuses a field type that names its value object the old way', () => { expect( MongoContractSchema( diff --git a/packages/2-sql-family/1-core/contract/src/entity-kinds.ts b/packages/2-sql-family/1-core/contract/src/entity-kinds.ts index a87a1a9654f4..d99386204592 100644 --- a/packages/2-sql-family/1-core/contract/src/entity-kinds.ts +++ b/packages/2-sql-family/1-core/contract/src/entity-kinds.ts @@ -1,3 +1,4 @@ +import { StorageValueObjectSchema } from '@internal/contract/types'; import type { AnyEntityKindDescriptor, EntityKindDescriptor, @@ -6,11 +7,7 @@ import { ifDefined } from '@internal/utils/defined'; import { contractError } from './contract-errors'; import { CheckConstraint } from './ir/check-constraint'; import { Index } from './ir/sql-index'; -import { - StorageTableSchema, - StorageValueObjectSchema, - StorageValueSetSchema, -} from './ir/storage-entry-schemas'; +import { StorageTableSchema, StorageValueSetSchema } from './ir/storage-entry-schemas'; import { StorageTable, type StorageTableInput } from './ir/storage-table'; import { StorageValueObject, type StorageValueObjectInput } from './ir/storage-value-object'; import { StorageValueSet, type StorageValueSetInput } from './ir/storage-value-set'; diff --git a/packages/2-sql-family/1-core/contract/src/ir/storage-entry-schemas.ts b/packages/2-sql-family/1-core/contract/src/ir/storage-entry-schemas.ts index be3fa5c0d677..3f1d9f823f62 100644 --- a/packages/2-sql-family/1-core/contract/src/ir/storage-entry-schemas.ts +++ b/packages/2-sql-family/1-core/contract/src/ir/storage-entry-schemas.ts @@ -86,23 +86,6 @@ export const ContractFieldTypeSchema = type('unknown').narrow((value, ctx) => ), ); -const StorageValueObjectFieldSchema = type({ - '+': 'reject', - nullable: 'boolean', - type: ContractFieldTypeSchema, - 'many?': type('false').or({ '+': 'reject', elementNullable: 'boolean' }), - 'dict?': 'true', - 'valueSet?': StorageValueSetRefSchema, -}); - -/** - * Storage value object entry under `storage.namespaces[id].entries.valueObject[name]` (ADR 309). Its fields have the shape a domain field has; a member typed by an enum refers to the storage value set, since storage never refers to the domain plane. - */ -export const StorageValueObjectSchema = type({ - '+': 'reject', - fields: type({ '[string]': StorageValueObjectFieldSchema }), -}); - const StorageColumnSchema = type({ '+': 'reject', dataType: DATA_TYPE_ID_PATTERN, diff --git a/packages/2-sql-family/2-authoring/contract-ts/schemas/data-contract-sql-v1.json b/packages/2-sql-family/2-authoring/contract-ts/schemas/data-contract-sql-v1.json index 2cc5a0f29166..e62e1866b988 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/schemas/data-contract-sql-v1.json +++ b/packages/2-sql-family/2-authoring/contract-ts/schemas/data-contract-sql-v1.json @@ -762,7 +762,117 @@ "nullable": { "type": "boolean" }, - "type": {}, + "type": { + "anyOf": [ + { + "type": "object", + "properties": { + "codecId": { + "type": "string" + }, + "kind": { + "const": "scalar" + }, + "typeParams": { + "type": "object", + "additionalProperties": {} + } + }, + "required": ["codecId", "kind"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "entityKind": { + "const": "valueObject" + }, + "entityName": { + "type": "string" + }, + "kind": { + "const": "valueObject" + }, + "namespaceId": { + "type": "string" + }, + "plane": { + "const": "storage" + } + }, + "required": [ + "entityKind", + "entityName", + "kind", + "namespaceId", + "plane" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "kind": { + "const": "union" + }, + "members": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "codecId": { + "type": "string" + }, + "kind": { + "const": "scalar" + }, + "typeParams": { + "type": "object", + "additionalProperties": {} + } + }, + "required": ["codecId", "kind"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "entityKind": { + "const": "valueObject" + }, + "entityName": { + "type": "string" + }, + "kind": { + "const": "valueObject" + }, + "namespaceId": { + "type": "string" + }, + "plane": { + "const": "storage" + } + }, + "required": [ + "entityKind", + "entityName", + "kind", + "namespaceId", + "plane" + ], + "additionalProperties": false + } + ] + } + } + }, + "required": ["kind", "members"], + "additionalProperties": false + } + ] + }, "dict": { "const": true }, @@ -802,7 +912,8 @@ "type": "string" } }, - "required": ["entityKind", "entityName", "namespaceId", "plane"] + "required": ["entityKind", "entityName", "namespaceId", "plane"], + "additionalProperties": false } }, "required": ["nullable", "type"], From d0e16fcbd7a762a415b22f4e384cb46cb3956928 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 17:59:21 +0200 Subject: [PATCH 071/104] TML-3537: SQL contract validation refuses a value object member that is a union or a dictionary, naming the member Signed-off-by: willbot Signed-off-by: Will Madden --- .../1-core/contract/src/entity-kinds.ts | 9 ++++++--- .../contract/src/ir/storage-entry-schemas.ts | 16 +++++++++++++++ .../test/storage-value-object.test.ts | 20 +++++++++++++++++++ .../test/psl-print/refusals-storage.test.ts | 14 ++++++------- 4 files changed, 49 insertions(+), 10 deletions(-) diff --git a/packages/2-sql-family/1-core/contract/src/entity-kinds.ts b/packages/2-sql-family/1-core/contract/src/entity-kinds.ts index d99386204592..d9e656d5d8a5 100644 --- a/packages/2-sql-family/1-core/contract/src/entity-kinds.ts +++ b/packages/2-sql-family/1-core/contract/src/entity-kinds.ts @@ -1,4 +1,3 @@ -import { StorageValueObjectSchema } from '@internal/contract/types'; import type { AnyEntityKindDescriptor, EntityKindDescriptor, @@ -7,7 +6,11 @@ import { ifDefined } from '@internal/utils/defined'; import { contractError } from './contract-errors'; import { CheckConstraint } from './ir/check-constraint'; import { Index } from './ir/sql-index'; -import { StorageTableSchema, StorageValueSetSchema } from './ir/storage-entry-schemas'; +import { + SqlStorageValueObjectSchema, + StorageTableSchema, + StorageValueSetSchema, +} from './ir/storage-entry-schemas'; import { StorageTable, type StorageTableInput } from './ir/storage-table'; import { StorageValueObject, type StorageValueObjectInput } from './ir/storage-value-object'; import { StorageValueSet, type StorageValueSetInput } from './ir/storage-value-set'; @@ -54,7 +57,7 @@ export const valueObjectEntityKind: EntityKindDescriptor< StorageValueObject > = { kind: 'valueObject', - schema: StorageValueObjectSchema, + schema: SqlStorageValueObjectSchema, construct: (input) => new StorageValueObject(input), }; diff --git a/packages/2-sql-family/1-core/contract/src/ir/storage-entry-schemas.ts b/packages/2-sql-family/1-core/contract/src/ir/storage-entry-schemas.ts index 3f1d9f823f62..c622272913ea 100644 --- a/packages/2-sql-family/1-core/contract/src/ir/storage-entry-schemas.ts +++ b/packages/2-sql-family/1-core/contract/src/ir/storage-entry-schemas.ts @@ -1,3 +1,4 @@ +import { StorageValueObjectSchema } from '@internal/contract/types'; import { DATA_TYPE_ID_PATTERN } from '@internal/framework-components/codec'; import { type Type, type } from 'arktype'; import type { ForeignKeyIndex, ForeignKeyInput, ReferentialAction } from './foreign-key'; @@ -86,6 +87,21 @@ export const ContractFieldTypeSchema = type('unknown').narrow((value, ctx) => ), ); +/** + * The framework's value object schema, narrowed to what SQL stores: no member is a union or a dictionary, since the derived check cannot enforce them. + */ +export const SqlStorageValueObjectSchema = StorageValueObjectSchema.narrow((valueObject, ctx) => { + for (const [name, member] of Object.entries(valueObject.fields)) { + const kind = member.type.kind === 'union' ? 'union' : member.dict ? 'dictionary' : undefined; + if (kind !== undefined) { + return ctx.reject({ + message: `value object member "${name}" is a ${kind}; SQL value objects hold no unions or dictionaries, since their check cannot enforce them`, + }); + } + } + return true; +}); + const StorageColumnSchema = type({ '+': 'reject', dataType: DATA_TYPE_ID_PATTERN, diff --git a/packages/2-sql-family/1-core/contract/test/storage-value-object.test.ts b/packages/2-sql-family/1-core/contract/test/storage-value-object.test.ts index b8bbd269ae0b..b016dcaf083e 100644 --- a/packages/2-sql-family/1-core/contract/test/storage-value-object.test.ts +++ b/packages/2-sql-family/1-core/contract/test/storage-value-object.test.ts @@ -186,6 +186,26 @@ describe('validateSqlContractFully with a value object in storage', () => { ).toThrow(/Contract structural validation failed/); }); + it.each([ + [ + 'a union', + { + nullable: false, + type: { kind: 'union', members: [text.type, valueObjectType('Address')] }, + }, + 'union', + ], + ['a dictionary', { ...text, dict: true }, 'dictionary'], + ])('refuses a member that is %s, naming the member', (_case, member, kind) => { + const contract = contractWith(valueObjectType('Shape'), { + Address: address, + Shape: { fields: { either: member } }, + }); + expect(() => validateSqlContractFully(contract)).toThrow( + `value object member "either" is a ${kind}; SQL value objects hold no unions or dictionaries`, + ); + }); + it('refuses value objects in the domain plane, and says to re-emit', () => { const contract = contractWith(valueObjectType('Address')); const old = { diff --git a/packages/3-targets/postgres/1-target/test/psl-print/refusals-storage.test.ts b/packages/3-targets/postgres/1-target/test/psl-print/refusals-storage.test.ts index 644a1e1dd16c..00ef9d973512 100644 --- a/packages/3-targets/postgres/1-target/test/psl-print/refusals-storage.test.ts +++ b/packages/3-targets/postgres/1-target/test/psl-print/refusals-storage.test.ts @@ -278,19 +278,19 @@ describe('value objects', () => { ).toThrow(refusal({ namespaceId: 'auth', names: ['Address'] })); }); - it('refuses a value-object member whose type is a union', () => { - expect( - withAddress({ + it('never reaches a value-object member that is a union, which deserializing refuses', () => { + expect(() => + addressContract({ many: false, nullable: false, type: { kind: 'union', members: [{ kind: 'scalar', codecId: 'pg/text@1' }] }, }), - ).toThrow(refusal({ coordinate: '"public".Address.street', kind: 'union' })); + ).toThrow('value object member "street" is a union'); }); - it('refuses a value-object member that is a dictionary', () => { - expect(withAddress({ ...TEXT_FIELD, dict: true })).toThrow( - refusal({ coordinate: '"public".Address.street' }), + it('never reaches a value-object member that is a dictionary, which deserializing refuses', () => { + expect(() => addressContract({ ...TEXT_FIELD, dict: true })).toThrow( + 'value object member "street" is a dictionary', ); }); From e544bbf42dbd0fe1bdc393407724896162c47a2f Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 18:08:14 +0200 Subject: [PATCH 072/104] TML-3537: ContractWithDomain carries the storage namespaces, replacing StatementContract, ContractPlanes and the local StorageNamespaces aliases Signed-off-by: willbot Signed-off-by: Will Madden --- .../contract/src/domain-envelope.ts | 5 ++++ .../contract/src/storage-value-objects.ts | 10 +++----- .../contract/src/validate-domain.ts | 2 -- .../framework-components/src/ir/storage.ts | 12 ++++----- .../test/migration-statements.test.ts | 1 + .../control-api/operations/migration-plan.ts | 10 ++++---- .../control-api/statements/plan-questions.ts | 25 ++++++++++--------- .../statements/resolve-statements.ts | 14 +++-------- .../statements/statement-fixtures.ts | 4 +-- .../emitter/src/domain-type-generation.ts | 12 +++------ .../5-query-builders/orm/src/compile.ts | 6 ++--- .../query-builder/src/result-shape.ts | 16 ++++++------ .../1-core/contract/src/check-expressions.ts | 15 ++++++----- .../contract-ts/src/build-contract.ts | 4 +-- .../contract-ts/src/lower-column.ts | 4 +-- .../9-facade/test/contract-from-json.test.ts | 2 +- 16 files changed, 66 insertions(+), 76 deletions(-) diff --git a/packages/1-framework/0-foundation/contract/src/domain-envelope.ts b/packages/1-framework/0-foundation/contract/src/domain-envelope.ts index ff904c070daf..21cda0420f77 100644 --- a/packages/1-framework/0-foundation/contract/src/domain-envelope.ts +++ b/packages/1-framework/0-foundation/contract/src/domain-envelope.ts @@ -1,4 +1,5 @@ import type { ContractEnum, ContractModelBase } from './domain-types'; +import type { StorageBase } from './types'; export { UNBOUND_DOMAIN_NAMESPACE_ID } from './default-namespace'; @@ -20,6 +21,10 @@ export interface ApplicationDomain { readonly namespaces: Readonly>; } +/** + * What reading a contract's domain fields needs: the domain, and the storage namespaces that hold the value objects its fields refer to (ADR 309). + */ export type ContractWithDomain = { readonly domain: ApplicationDomain; + readonly storage: Pick; }; diff --git a/packages/1-framework/0-foundation/contract/src/storage-value-objects.ts b/packages/1-framework/0-foundation/contract/src/storage-value-objects.ts index 3aa0b7d27500..ceef283e3cd5 100644 --- a/packages/1-framework/0-foundation/contract/src/storage-value-objects.ts +++ b/packages/1-framework/0-foundation/contract/src/storage-value-objects.ts @@ -1,14 +1,12 @@ import { blindCast } from '@internal/utils/casts'; import { contractError } from './contract-errors'; +import type { ContractWithDomain } from './domain-envelope'; import type { ContractValueObject } from './domain-types'; import type { EntityCoordinate } from './entity-coordinate'; import { isPlainRecord } from './is-plain-record'; -import type { StorageBase } from './types'; - -type StorageNamespaces = Pick; function valueObjectsOf( - storage: StorageNamespaces, + storage: ContractWithDomain['storage'], namespaceId: string, ): Readonly> { const slot = Object.hasOwn(storage.namespaces, namespaceId) @@ -24,7 +22,7 @@ function valueObjectsOf( /** The storage value object a reference names, or `undefined` when storage does not have it. */ export function valueObjectAt( - storage: StorageNamespaces, + storage: ContractWithDomain['storage'], reference: EntityCoordinate<'storage', 'valueObject'>, ): ContractValueObject | undefined { const valueObjects = valueObjectsOf(storage, reference.namespaceId); @@ -37,7 +35,7 @@ export function valueObjectAt( * The value objects of the storage's only namespace. Throws when storage does not declare exactly one namespace, since a bare value object name is then ambiguous. */ export function storageValueObjectsAtDefaultNamespace( - storage: StorageNamespaces, + storage: ContractWithDomain['storage'], ): Readonly> { const namespaceIds = Object.keys(storage.namespaces); const [namespaceId] = namespaceIds; diff --git a/packages/1-framework/0-foundation/contract/src/validate-domain.ts b/packages/1-framework/0-foundation/contract/src/validate-domain.ts index eca97d8e6279..c960c0a522b4 100644 --- a/packages/1-framework/0-foundation/contract/src/validate-domain.ts +++ b/packages/1-framework/0-foundation/contract/src/validate-domain.ts @@ -6,7 +6,6 @@ import type { ContractValueObject } from './domain-types'; import { isPlainRecord } from './is-plain-record'; import { asNamespaceId, type NamespaceId } from './namespace-id'; import { valueObjectAt } from './storage-value-objects'; -import type { StorageBase } from './types'; export interface DomainRelationShape { readonly to: CrossReference; @@ -26,7 +25,6 @@ export interface DomainModelShape { export interface DomainContractShape extends ContractWithDomain { readonly roots: Record; - readonly storage: Pick; } interface IndexedModel { diff --git a/packages/1-framework/1-core/framework-components/src/ir/storage.ts b/packages/1-framework/1-core/framework-components/src/ir/storage.ts index 1d17f8fb4e80..50f771d7b926 100644 --- a/packages/1-framework/1-core/framework-components/src/ir/storage.ts +++ b/packages/1-framework/1-core/framework-components/src/ir/storage.ts @@ -1,5 +1,5 @@ import { isPlainRecord } from '@internal/contract/is-plain-record'; -import type { EntityCoordinate, StorageBase } from '@internal/contract/types'; +import type { ContractWithDomain, EntityCoordinate } from '@internal/contract/types'; import { canonicalStringify } from '@internal/utils/canonical-stringify'; import { blindCast } from '@internal/utils/casts'; import type { IRNode } from './ir-node'; @@ -19,7 +19,7 @@ export { isPlainRecord }; * entity name in that map. No family-specific kind vocabulary is required. */ export function* elementCoordinates( - storage: Pick, + storage: ContractWithDomain['storage'], ): Generator { for (const [namespaceId, ns] of Object.entries(storage.namespaces)) { const entries = ns.entries; @@ -60,11 +60,11 @@ export function coordinateKey( * entity kind and entity name. With no earlier storage, every entity counts as added. */ export function changedElementCoordinates( - from: Pick | null, - to: Pick, + from: ContractWithDomain['storage'] | null, + to: ContractWithDomain['storage'], ): EntityCoordinate[] { const contentAt = ( - storage: Pick | null, + storage: ContractWithDomain['storage'] | null, coordinate: EntityCoordinate, ): string | undefined => { if (storage === null) return undefined; @@ -97,7 +97,7 @@ export function changedElementCoordinates( * and cannot verify the entity's shape. */ export function entityAt( - storage: Pick, + storage: ContractWithDomain['storage'], coord: Pick, ): T | undefined { const ns = storage.namespaces[coord.namespaceId]; diff --git a/packages/1-framework/1-core/framework-components/test/migration-statements.test.ts b/packages/1-framework/1-core/framework-components/test/migration-statements.test.ts index 54915a6d308f..c20a0d09f0c6 100644 --- a/packages/1-framework/1-core/framework-components/test/migration-statements.test.ts +++ b/packages/1-framework/1-core/framework-components/test/migration-statements.test.ts @@ -30,6 +30,7 @@ function contractWith(models: Readonly>): Cont ]), ), }, + storage: { namespaces: {} }, }; } diff --git a/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts b/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts index f502fd724c69..5ef7c918de06 100644 --- a/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts +++ b/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts @@ -4,7 +4,7 @@ import { readFile } from 'node:fs/promises'; import type { PrismaNextConfig } from '@internal/config/config-types'; -import type { Contract, EntityCoordinate } from '@internal/contract/types'; +import type { Contract, ContractWithDomain, EntityCoordinate } from '@internal/contract/types'; import { createControlStack, hasOperationPreview, @@ -66,7 +66,7 @@ import { reportAppliedStatements, reportConsentStatement, } from '../statements/report-applied-statements'; -import { resolveStatements, type StatementContract } from '../statements/resolve-statements'; +import { resolveStatements } from '../statements/resolve-statements'; import { isPlannedStatement, type StatementText } from '../statements/statement-text'; import { reportTemporaryValues, type TemporaryValueReport } from '../statements/temporary-values'; import type { ControlClient } from '../types'; @@ -147,7 +147,7 @@ function invocationOf(options: { } /** The origin of a plan from an empty database: no models, so no statement resolves. */ -const EMPTY_ORIGIN: StatementContract = { +const EMPTY_ORIGIN: ContractWithDomain = { domain: { namespaces: {} }, storage: { namespaces: {} }, }; @@ -318,9 +318,9 @@ async function answerPlannedDataLoss(input: { readonly delta: PlannerSuccess | undefined; readonly statements: readonly StatementText[]; readonly consents: readonly StatementText[]; - readonly origin: StatementContract; + readonly origin: ContractWithDomain; readonly originKnown: boolean; - readonly destination: StatementContract; + readonly destination: ContractWithDomain; readonly answer: AnswerPlanQuestions; readonly invocation: CliInvocation; readonly planDelta: ( diff --git a/packages/1-framework/3-tooling/cli/src/control-api/statements/plan-questions.ts b/packages/1-framework/3-tooling/cli/src/control-api/statements/plan-questions.ts index d0f35698c607..877739d5a353 100644 --- a/packages/1-framework/3-tooling/cli/src/control-api/statements/plan-questions.ts +++ b/packages/1-framework/3-tooling/cli/src/control-api/statements/plan-questions.ts @@ -1,3 +1,4 @@ +import type { ContractWithDomain } from '@internal/contract/types'; import type { CliStructuredError } from '@internal/errors/control'; import { type EnumValueCoordinate, @@ -24,7 +25,7 @@ import { type UnusedConsent, } from '../../utils/cli-errors'; import type { ConsentVerb } from './parse-consent'; -import { resolveStatements, type StatementContract } from './resolve-statements'; +import { resolveStatements } from './resolve-statements'; import { statementFlag } from './statement-flag'; import { ENTITY_WORDS, type StatementText } from './statement-text'; @@ -106,9 +107,9 @@ export interface PlannedQuestions { /** The contracts a statement names things in, and the statements the plan carries out so far. */ export interface StatementContracts { - readonly origin: StatementContract; + readonly origin: ContractWithDomain; /** The destination contract; needed only to name a field of a renamed model. */ - readonly destination?: StatementContract; + readonly destination?: ContractWithDomain; /** The rename, convert and backfill statements the plan carries out, in the order given. */ readonly statements: readonly StatementText[]; } @@ -322,7 +323,7 @@ function modelRenameAdvice(model: ModelCoordinate, contracts: StatementContracts */ function onlyNewMember( subject: EnumValueCoordinate, - contracts: StatementContracts & { readonly destination: StatementContract }, + contracts: StatementContracts & { readonly destination: ContractWithDomain }, ): string | undefined { const value = renamedEnumValue(subject, contracts) ?? subject; const originMembers = @@ -343,7 +344,7 @@ function onlyNewMember( function renameForm( subject: MigrationSubject, text: string, - contracts: StatementContracts & { readonly destination: StatementContract }, + contracts: StatementContracts & { readonly destination: ContractWithDomain }, ): string { if (subject.kind === 'field' || subject.kind === 'enumValue') { const parent = text.slice(0, text.lastIndexOf('.')); @@ -370,7 +371,7 @@ function renamedByPlan(subject: MigrationSubject, contracts: StatementContracts) /** Whether the destination still has the subject's model or field, under the name a rename gives it. */ function inDestination( subject: MigrationSubject, - contracts: StatementContracts & { readonly destination: StatementContract }, + contracts: StatementContracts & { readonly destination: ContractWithDomain }, ): boolean { if (subject.kind === 'storage') return false; if (subject.kind === 'enumValue') { @@ -389,7 +390,7 @@ function inDestination( /** Checks a statement typed at a question against the plan's other statements. */ function resolvesWith( statement: StatementText, - contracts: StatementContracts & { readonly destination: StatementContract }, + contracts: StatementContracts & { readonly destination: ContractWithDomain }, ): Result { const resolved = resolveStatements({ statements: [...contracts.statements, statement], @@ -402,7 +403,7 @@ function resolvesWith( } type DataLossContracts = StatementContracts & { - readonly destination: StatementContract; + readonly destination: ContractWithDomain; readonly originKnown: boolean; /** Whether the command writes a migration whose conversion the user fills in. */ readonly canConvert: boolean; @@ -435,7 +436,7 @@ export type KeepDataEditAdvice = ( */ function missingFieldModel( subject: MigrationSubject, - contracts: StatementContracts & { readonly destination: StatementContract }, + contracts: StatementContracts & { readonly destination: ContractWithDomain }, ): ModelCoordinate | undefined { if (subject.kind !== 'field') return undefined; const model = renamedModel(subject, contracts) ?? subject; @@ -670,7 +671,7 @@ function withNamespaceDeleteOnFirstQuestion( function acceptingNamespaceAnswers( question: PlanQuestion, removedNamespace: string | undefined, - contracts: StatementContracts & { readonly destination: StatementContract }, + contracts: StatementContracts & { readonly destination: ContractWithDomain }, ): PlanQuestion { if (removedNamespace === undefined) return question; const namespaceAnswer = (verb: PlanQuestionVerb, text: string): NamespaceAnswer | undefined => { @@ -725,9 +726,9 @@ export async function askPlanQuestions readonly statements: readonly StatementText[]; readonly preAnswers: readonly StatementText[]; readonly consentAll: { readonly [verb in ConsentVerb]: boolean }; - readonly origin: StatementContract; + readonly origin: ContractWithDomain; readonly originKnown: boolean; - readonly destination: StatementContract; + readonly destination: ContractWithDomain; readonly answer: AnswerPlanQuestions; readonly replan: (statements: readonly StatementText[]) => Promise>; /** The command's words for the edits that keep a subject's data. */ diff --git a/packages/1-framework/3-tooling/cli/src/control-api/statements/resolve-statements.ts b/packages/1-framework/3-tooling/cli/src/control-api/statements/resolve-statements.ts index e11aabfd6070..3f53b47ac911 100644 --- a/packages/1-framework/3-tooling/cli/src/control-api/statements/resolve-statements.ts +++ b/packages/1-framework/3-tooling/cli/src/control-api/statements/resolve-statements.ts @@ -7,7 +7,6 @@ import { type ContractFieldType, type ContractModelBase, type ContractWithDomain, - type StorageBase, valueObjectAt, } from '@internal/contract/types'; import type { CliStructuredError } from '@internal/errors/control'; @@ -39,17 +38,12 @@ import { import { statementFlag } from './statement-flag'; import { ENTITY_WORDS, type StatementText } from './statement-text'; -/** A contract statements resolve against: its domain models, and the value objects its storage holds. */ -export type StatementContract = ContractWithDomain & { - readonly storage: Pick; -}; - /** * The origin contract statements resolve against, or where the command looked * for it without finding it. */ export type StatementOrigin = - | { readonly kind: 'contract'; readonly contract: StatementContract } + | { readonly kind: 'contract'; readonly contract: ContractWithDomain } | { readonly kind: 'missing'; readonly hash: string | null; @@ -66,14 +60,14 @@ export interface ResolveStatementsInput { /** The statements as the user wrote them, in the order given. */ readonly statements: readonly StatementText[]; readonly origin: StatementOrigin; - readonly destination: StatementContract; + readonly destination: ContractWithDomain; } type Side = 'old' | 'new'; interface NamedContract { readonly name: 'origin' | 'destination'; - readonly contract: StatementContract; + readonly contract: ContractWithDomain; } interface FoundModel { @@ -197,7 +191,7 @@ function modelIn( return Object.hasOwn(namespace.models, name) ? namespace.models[name] : undefined; } -function hasValueObject(contract: StatementContract, namespaceId: string, name: string): boolean { +function hasValueObject(contract: ContractWithDomain, namespaceId: string, name: string): boolean { return ( valueObjectAt(contract.storage, { plane: 'storage', diff --git a/packages/1-framework/3-tooling/cli/test/control-api/statements/statement-fixtures.ts b/packages/1-framework/3-tooling/cli/test/control-api/statements/statement-fixtures.ts index fafd03e39188..7a421c267f5d 100644 --- a/packages/1-framework/3-tooling/cli/test/control-api/statements/statement-fixtures.ts +++ b/packages/1-framework/3-tooling/cli/test/control-api/statements/statement-fixtures.ts @@ -3,12 +3,12 @@ import type { ContractModelBase, ContractRelation, ContractValueObject, + ContractWithDomain, } from '@internal/contract/types'; import { crossRef } from '@internal/contract/types'; import type { CliStructuredError } from '@internal/errors/control'; import type { Result } from '@internal/utils/result'; import { expect } from 'vitest'; -import type { StatementContract } from '../../../src/control-api/statements/resolve-statements'; interface ModelSpec { readonly fields?: readonly string[]; @@ -90,7 +90,7 @@ function valueObjectOf(fields: readonly string[]): ContractValueObject { }; } -export function contractOf(namespaces: Record): StatementContract { +export function contractOf(namespaces: Record): ContractWithDomain { const entries = Object.entries(namespaces); return { domain: { diff --git a/packages/1-framework/3-tooling/emitter/src/domain-type-generation.ts b/packages/1-framework/3-tooling/emitter/src/domain-type-generation.ts index db768890e351..39586c09eee9 100644 --- a/packages/1-framework/3-tooling/emitter/src/domain-type-generation.ts +++ b/packages/1-framework/3-tooling/emitter/src/domain-type-generation.ts @@ -5,9 +5,9 @@ import type { ContractModelBase, ContractValueObject, ContractValueObjectField, + ContractWithDomain, CrossReference, JsonValue, - StorageBase, ValueObjectFieldType, } from '@internal/contract/types'; import { valueObjectAt } from '@internal/contract/types'; @@ -544,10 +544,6 @@ function renderOverrideTree(tree: OverrideTree): string { return `{ ${parts.join('; ')} }`; } -type ContractPlanes = Pick & { - readonly storage: Pick; -}; - /** * The values a model field's value set holds, with the field's codec. A reference to a domain enum is read from the contract's domain. Any other model field goes to the family's `resolveStorageValueSet`, which reads its storage. */ @@ -579,7 +575,7 @@ function resolveDomainValueSet( /** The values of the storage value set a value object field names, with the field's codec. */ function resolveValueObjectFieldValueSet( field: ContractValueObjectField, - storage: Pick, + storage: ContractWithDomain['storage'], ): ResolvedFieldValueSet | undefined { if (field.valueSet === undefined || field.type.kind !== 'scalar') return undefined; const valueSet = entityAt<{ readonly values: readonly JsonValue[] }>(storage, field.valueSet); @@ -589,7 +585,7 @@ function resolveValueObjectFieldValueSet( /** Each value object in storage, with the storage namespace that holds it. */ export function storageValueObjects( - storage: Pick, + storage: ContractWithDomain['storage'], ): ReadonlyArray { return [...elementCoordinates(storage)].flatMap(({ namespaceId, entityKind, entityName }) => { if (entityKind !== 'valueObject') return []; @@ -605,7 +601,7 @@ export function storageValueObjects( /** `FieldTypeOverrides`: per namespace, `models` (domain namespace) or `valueObjects` (storage namespace), owner and field, the type of each field its codec does not give. */ export function generateFieldTypeOverridesMap( - contract: ContractPlanes, + contract: ContractWithDomain, codecLookup: CodecLookup | undefined, resolveStorageValueSet: StorageValueSetResolver | undefined, ): string { diff --git a/packages/2-mongo-family/5-query-builders/orm/src/compile.ts b/packages/2-mongo-family/5-query-builders/orm/src/compile.ts index 4509f8f71d6c..d50cd1448f37 100644 --- a/packages/2-mongo-family/5-query-builders/orm/src/compile.ts +++ b/packages/2-mongo-family/5-query-builders/orm/src/compile.ts @@ -1,4 +1,4 @@ -import type { ContractModel, PlanMeta, StorageBase } from '@internal/contract/types'; +import type { ContractModel, ContractWithDomain, PlanMeta } from '@internal/contract/types'; import type { MongoModelStorage } from '@internal/mongo-contract'; import type { MongoFieldShape, @@ -29,7 +29,7 @@ function isToOne(include: MongoIncludeExpr): boolean { */ function includeShape( include: MongoIncludeExpr, - storage: Pick, + storage: ContractWithDomain['storage'], ): MongoFieldShape { const target = contractModelToMongoResultShape(include.targetModel, { storage }); const fields = target.kind === 'document' ? target.fields : {}; @@ -64,7 +64,7 @@ export function compileMongoQuery( state: MongoCollectionState, storageHash: string, model: ContractModel, - storage: Pick, + storage: ContractWithDomain['storage'], ): MongoQueryPlan { const stages: MongoPipelineStage[] = []; diff --git a/packages/2-mongo-family/5-query-builders/query-builder/src/result-shape.ts b/packages/2-mongo-family/5-query-builders/query-builder/src/result-shape.ts index 7b610f4a0fb5..8a4ff7094005 100644 --- a/packages/2-mongo-family/5-query-builders/query-builder/src/result-shape.ts +++ b/packages/2-mongo-family/5-query-builders/query-builder/src/result-shape.ts @@ -2,22 +2,20 @@ import { type ContractField, type ContractModel, type ContractValueObject, - type StorageBase, + type ContractWithDomain, valueObjectAt, } from '@internal/contract/types'; import { type MongoModelStorage, ownStoredNamesOf } from '@internal/mongo-contract'; import type { MongoFieldShape, MongoResultShape } from '@internal/mongo-query-ast/execution'; import { freezeMongoFieldShape, freezeMongoResultShape } from '@internal/mongo-query-ast/execution'; -type StorageNamespaces = Pick; - const UNKNOWN: MongoFieldShape = Object.freeze({ kind: 'unknown' as const }); -const NO_STORAGE: StorageNamespaces = { namespaces: {} }; +const NO_STORAGE: ContractWithDomain['storage'] = { namespaces: {} }; function valueObjectFields( valueObject: ContractValueObject, - storage: StorageNamespaces, + storage: ContractWithDomain['storage'], enclosing: ReadonlySet, ): Record { return Object.fromEntries( @@ -30,7 +28,7 @@ function valueObjectFields( function elementShape( field: ContractField, - storage: StorageNamespaces, + storage: ContractWithDomain['storage'], enclosing: ReadonlySet, ): MongoFieldShape { const { type } = field; @@ -50,7 +48,7 @@ function elementShape( function fieldShape( field: ContractField, - storage: StorageNamespaces, + storage: ContractWithDomain['storage'], enclosing: ReadonlySet, ): MongoFieldShape { if (field.dict === true) return UNKNOWN; @@ -67,7 +65,7 @@ function fieldShape( */ export function contractFieldToMongoFieldShape( field: ContractField, - storage: StorageNamespaces = NO_STORAGE, + storage: ContractWithDomain['storage'] = NO_STORAGE, ): MongoFieldShape { return freezeMongoFieldShape(fieldShape(field, storage, new Set())); } @@ -81,7 +79,7 @@ export function contractModelToMongoResultShape( readonly selection?: readonly string[]; /** The shape of each included relation, keyed by relation name. */ readonly includes?: Readonly>; - readonly storage?: StorageNamespaces; + readonly storage?: ContractWithDomain['storage']; }, ): MongoResultShape { const fields: Record = { ...options?.includes }; diff --git a/packages/2-sql-family/1-core/contract/src/check-expressions.ts b/packages/2-sql-family/1-core/contract/src/check-expressions.ts index fd51eb54f4e9..fe9c5d7b26be 100644 --- a/packages/2-sql-family/1-core/contract/src/check-expressions.ts +++ b/packages/2-sql-family/1-core/contract/src/check-expressions.ts @@ -1,8 +1,8 @@ import type { ContractValueObject, ContractValueObjectField, + ContractWithDomain, EntityCoordinate, - StorageBase, } from '@internal/contract/types'; import { valueObjectAt } from '@internal/contract/types'; import type { CheckKind } from '@internal/sql-schema-ir/naming'; @@ -54,21 +54,20 @@ export type CheckExpressionRenderer = ( input: CheckExpressionInput, ) => ReadonlyArray; -type StorageNamespaces = Pick; type ValueObjectReference = EntityCoordinate<'storage', 'valueObject'>; /** * The value object a reference names, with nested value objects inlined and enum members' values read from their storage value sets. */ export function resolveValueObject( - storage: StorageNamespaces, + storage: ContractWithDomain['storage'], reference: ValueObjectReference, ): ResolvedValueObject { return resolveEntered(storage, reference, [referenceKey(reference)]); } function resolveEntered( - storage: StorageNamespaces, + storage: ContractWithDomain['storage'], reference: ValueObjectReference, path: readonly string[], ): ResolvedValueObject { @@ -88,7 +87,7 @@ function resolveEntered( } function resolveFieldType( - storage: StorageNamespaces, + storage: ContractWithDomain['storage'], field: ContractValueObjectField, path: readonly string[], ): ResolvedValueObjectFieldType { @@ -113,7 +112,7 @@ function resolveFieldType( * Whether `from` refers, through its own fields or nested ones, to a value object on the path being resolved. Each value object on the path refers to `from`, so this holds exactly when `from` is in a group of value objects that contain each other which the path has already entered. */ function reachesAny( - storage: StorageNamespaces, + storage: ContractWithDomain['storage'], from: ValueObjectReference, targets: ReadonlySet, ): boolean { @@ -132,7 +131,7 @@ function reachesAny( } function requiredValueObject( - storage: StorageNamespaces, + storage: ContractWithDomain['storage'], reference: ValueObjectReference, ): ContractValueObject { const valueObject = valueObjectAt(storage, reference); @@ -144,7 +143,7 @@ function requiredValueObject( } function valueSetValues( - storage: StorageNamespaces, + storage: ContractWithDomain['storage'], reference: EntityCoordinate<'storage', 'valueSet'>, ): readonly (string | number)[] { const valueSet = Object.hasOwn(storage.namespaces, reference.namespaceId) diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts b/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts index 3373c751a69b..65dd1a897f5e 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts @@ -8,12 +8,12 @@ import { type ContractEnum, type ContractModel, type ContractValueObject, + type ContractWithDomain, type CrossReference, coreHash, crossRef, type ExecutionMutationDefault, effectiveControlPolicy, - type StorageBase, type StorageHashBase, } from '@internal/contract/types'; import { encodeEnumTypeMembers } from '@internal/contract-authoring'; @@ -185,7 +185,7 @@ function storageOf( defaultNamespaceId: string, valueObjects: Record | undefined, storageValueSetsByNs: Readonly>>, -): Pick { +): ContractWithDomain['storage'] { const namespaceIds = new Set([defaultNamespaceId, ...Object.keys(storageValueSetsByNs)]); return { namespaces: Object.fromEntries( diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/lower-column.ts b/packages/2-sql-family/2-authoring/contract-ts/src/lower-column.ts index 397ccabb1c65..9d4b88f89e83 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/lower-column.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/lower-column.ts @@ -1,4 +1,4 @@ -import type { StorageBase } from '@internal/contract/types'; +import type { ContractWithDomain } from '@internal/contract/types'; import type { CodecLookupWithDescriptors, TypeLookups } from '@internal/framework-components/codec'; import { dataTypeParams } from '@internal/framework-components/codec'; import { @@ -79,7 +79,7 @@ export interface ColumnLoweringContext { readonly qualifyColumnType: ColumnTypeQualifier | undefined; readonly renderCheckExpressions: CheckExpressionRenderer | undefined; /** The value objects and value sets storage will hold, which a value object column's check is derived from. */ - readonly storage: Pick; + readonly storage: ContractWithDomain['storage']; /** Receives the pack entity a column's type constructor refers to. */ readonly collectedColumnEntities: CollectedColumnEntities; } diff --git a/packages/3-targets/mongo/9-facade/test/contract-from-json.test.ts b/packages/3-targets/mongo/9-facade/test/contract-from-json.test.ts index bae3a9616f3d..bc7f12dfdd38 100644 --- a/packages/3-targets/mongo/9-facade/test/contract-from-json.test.ts +++ b/packages/3-targets/mongo/9-facade/test/contract-from-json.test.ts @@ -110,7 +110,7 @@ describe('validating a contract once', () => { JSON.parse( JSON.stringify( Object.values(contract.storage.namespaces).map( - (namespace) => namespace.entries.valueObject, + (namespace) => namespace.entries['valueObject'], ), ), ), From 1878d5b032c24b7379d2cf421e2a610113a2d597 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 18:13:55 +0200 Subject: [PATCH 073/104] TML-3537: one framework walker, valueObjectsWithCoordinates, lists storage value objects; ValueObjectFieldType is ValueObjectReference; the test-only default-namespace accessor is gone Signed-off-by: willbot Signed-off-by: Will Madden --- .../0-foundation/contract/src/domain-types.ts | 8 ++-- .../contract/src/exports/types.ts | 7 +-- .../src/storage-value-object-schema.ts | 6 +-- .../contract/src/storage-value-objects.ts | 28 +++++------ .../contract/src/validate-domain.ts | 34 ++++--------- .../test/storage-value-objects.test.ts | 48 +++++++++++-------- .../emitter/src/domain-type-generation.ts | 28 +++-------- .../emitter/src/generate-contract-dts.ts | 12 +++-- .../mongo-contract/src/contract-schema.ts | 6 +-- .../test/contract-builder.dsl.test.ts | 13 ++++- .../5-query-builders/orm/src/collection.ts | 4 +- .../orm/src/field-accessor.ts | 14 +++--- .../query-builder/src/resolve-path.ts | 8 ++-- .../1-core/contract/src/check-expressions.ts | 14 +++--- .../contract-ts/src/build-contract.ts | 4 +- .../1-target/src/core/psl-print/refusals.ts | 4 +- .../value-objects.integration.test.ts | 9 +++- .../extension/instructions.md | 25 ++++++++++ 18 files changed, 140 insertions(+), 132 deletions(-) diff --git a/packages/1-framework/0-foundation/contract/src/domain-types.ts b/packages/1-framework/0-foundation/contract/src/domain-types.ts index 9ee98546e9a7..2cfbc0b1c030 100644 --- a/packages/1-framework/0-foundation/contract/src/domain-types.ts +++ b/packages/1-framework/0-foundation/contract/src/domain-types.ts @@ -9,17 +9,17 @@ export type ScalarFieldType = { readonly typeParams?: Record; }; -/** A field that holds a value object: the coordinate of its storage entity (ADR 309). */ -export interface ValueObjectFieldType extends EntityCoordinate<'storage', 'valueObject'> { +/** The type of a field that refers to a value object: the coordinate of the value object in storage (ADR 309). Not to be confused with {@link ContractValueObjectField}, a field of a value object. */ +export interface ValueObjectReference extends EntityCoordinate<'storage', 'valueObject'> { readonly kind: 'valueObject'; } export type UnionFieldType = { readonly kind: 'union'; - readonly members: ReadonlyArray; + readonly members: ReadonlyArray; }; -export type ContractFieldType = ScalarFieldType | ValueObjectFieldType | UnionFieldType; +export type ContractFieldType = ScalarFieldType | ValueObjectReference | UnionFieldType; export type ContractField = { readonly nullable: boolean; diff --git a/packages/1-framework/0-foundation/contract/src/exports/types.ts b/packages/1-framework/0-foundation/contract/src/exports/types.ts index d53c6c2994de..18311ac9f4e6 100644 --- a/packages/1-framework/0-foundation/contract/src/exports/types.ts +++ b/packages/1-framework/0-foundation/contract/src/exports/types.ts @@ -39,7 +39,7 @@ export type { ReferenceRelationKeys, ScalarFieldType, UnionFieldType, - ValueObjectFieldType, + ValueObjectReference, } from '../domain-types'; export type { EntityCoordinate } from '../entity-coordinate'; export { ContractExecutionSectionSchema } from '../execution-section-schema'; @@ -59,10 +59,7 @@ export type { } from '../parameterized-output-types'; export { type ResolvedDomainModel, resolveDomainModel } from '../resolve-domain-model'; export { StorageValueObjectSchema } from '../storage-value-object-schema'; -export { - storageValueObjectsAtDefaultNamespace, - valueObjectAt, -} from '../storage-value-objects'; +export { valueObjectAt, valueObjectsWithCoordinates } from '../storage-value-objects'; export type { $, Brand, diff --git a/packages/1-framework/0-foundation/contract/src/storage-value-object-schema.ts b/packages/1-framework/0-foundation/contract/src/storage-value-object-schema.ts index a49089008cb3..6a84ad67c2fa 100644 --- a/packages/1-framework/0-foundation/contract/src/storage-value-object-schema.ts +++ b/packages/1-framework/0-foundation/contract/src/storage-value-object-schema.ts @@ -7,7 +7,7 @@ const ScalarFieldTypeSchema = type({ 'typeParams?': 'Record', }); -const ValueObjectFieldTypeSchema = type({ +const ValueObjectReferenceSchema = type({ '+': 'reject', kind: "'valueObject'", plane: "'storage'", @@ -19,7 +19,7 @@ const ValueObjectFieldTypeSchema = type({ const UnionFieldTypeSchema = type({ '+': 'reject', kind: "'union'", - members: ScalarFieldTypeSchema.or(ValueObjectFieldTypeSchema).array(), + members: ScalarFieldTypeSchema.or(ValueObjectReferenceSchema).array(), }); const StorageValueSetReferenceSchema = type({ @@ -34,7 +34,7 @@ const StorageValueSetReferenceSchema = type({ const StorageValueObjectFieldSchema = type({ '+': 'reject', nullable: 'boolean', - type: ScalarFieldTypeSchema.or(ValueObjectFieldTypeSchema).or(UnionFieldTypeSchema), + type: ScalarFieldTypeSchema.or(ValueObjectReferenceSchema).or(UnionFieldTypeSchema), 'many?': type('false').or({ '+': 'reject', elementNullable: 'boolean' }), 'dict?': 'true', 'valueSet?': StorageValueSetReferenceSchema, diff --git a/packages/1-framework/0-foundation/contract/src/storage-value-objects.ts b/packages/1-framework/0-foundation/contract/src/storage-value-objects.ts index ceef283e3cd5..8ec58a2d6c7e 100644 --- a/packages/1-framework/0-foundation/contract/src/storage-value-objects.ts +++ b/packages/1-framework/0-foundation/contract/src/storage-value-objects.ts @@ -1,5 +1,4 @@ import { blindCast } from '@internal/utils/casts'; -import { contractError } from './contract-errors'; import type { ContractWithDomain } from './domain-envelope'; import type { ContractValueObject } from './domain-types'; import type { EntityCoordinate } from './entity-coordinate'; @@ -31,20 +30,15 @@ export function valueObjectAt( : undefined; } -/** - * The value objects of the storage's only namespace. Throws when storage does not declare exactly one namespace, since a bare value object name is then ambiguous. - */ -export function storageValueObjectsAtDefaultNamespace( - storage: ContractWithDomain['storage'], -): Readonly> { - const namespaceIds = Object.keys(storage.namespaces); - const [namespaceId] = namespaceIds; - if (namespaceId === undefined || namespaceIds.length > 1) { - throw contractError( - 'CONTRACT.NAMESPACE_INVALID', - `bare-name resolution requires exactly one storage namespace, found ${namespaceIds.length} (${namespaceIds.join(', ')}); select a namespace explicitly`, - { meta: { reason: 'multiple-storage-namespaces', namespaceIds } }, - ); - } - return valueObjectsOf(storage, namespaceId); +/** Each value object in storage, in every namespace, with its coordinate. */ +export function valueObjectsWithCoordinates(storage: ContractWithDomain['storage']): ReadonlyArray<{ + readonly coordinate: EntityCoordinate<'storage', 'valueObject'>; + readonly valueObject: ContractValueObject; +}> { + return Object.keys(storage.namespaces).flatMap((namespaceId) => + Object.entries(valueObjectsOf(storage, namespaceId)).map(([entityName, valueObject]) => ({ + coordinate: { plane: 'storage', namespaceId, entityKind: 'valueObject', entityName }, + valueObject, + })), + ); } diff --git a/packages/1-framework/0-foundation/contract/src/validate-domain.ts b/packages/1-framework/0-foundation/contract/src/validate-domain.ts index c960c0a522b4..306d4fc389fc 100644 --- a/packages/1-framework/0-foundation/contract/src/validate-domain.ts +++ b/packages/1-framework/0-foundation/contract/src/validate-domain.ts @@ -2,10 +2,9 @@ import { blindCast } from '@internal/utils/casts'; import { ContractValidationError } from './contract-validation-error'; import type { CrossReference } from './cross-reference'; import type { ContractWithDomain } from './domain-envelope'; -import type { ContractValueObject } from './domain-types'; import { isPlainRecord } from './is-plain-record'; import { asNamespaceId, type NamespaceId } from './namespace-id'; -import { valueObjectAt } from './storage-value-objects'; +import { valueObjectAt, valueObjectsWithCoordinates } from './storage-value-objects'; export interface DomainRelationShape { readonly to: CrossReference; @@ -79,27 +78,6 @@ export function removedDomainKeyProblems(contract: unknown): readonly string[] { ); } -interface StorageValueObjectEntry { - readonly namespaceId: string; - readonly name: string; - readonly valueObject: ContractValueObject; -} - -function storageValueObjects(contract: DomainContractShape): readonly StorageValueObjectEntry[] { - return Object.entries(contract.storage.namespaces).flatMap(([namespaceId, namespace]) => { - const slot = namespace.entries['valueObject']; - if (!isPlainRecord(slot)) return []; - return Object.entries(slot).map(([name, valueObject]) => ({ - namespaceId, - name, - valueObject: blindCast< - ContractValueObject, - 'the family validators check each storage valueObject entry against the value object schema' - >(valueObject), - })); - }); -} - export function validateContractDomain(contract: DomainContractShape): void { const errors: string[] = [...removedDomainKeyProblems(contract)]; const modelIndex = indexDomainModels(contract); @@ -370,7 +348,10 @@ function validateValueObjectReferences(contract: DomainContractShape, errors: st } } } - for (const { namespaceId, name, valueObject } of storageValueObjects(contract)) { + for (const { + coordinate: { namespaceId, entityName: name }, + valueObject, + } of valueObjectsWithCoordinates(contract.storage)) { for (const [fieldName, field] of Object.entries(valueObject.fields)) { const location = `Value object "${namespaceId}:${name}" field "${fieldName}"`; checkType(fieldLike(field)?.type, location); @@ -430,7 +411,10 @@ function validateFieldModifiers( validateField(`Model "${namespaceId}:${modelName}" field "${fieldName}"`, field); } } - for (const { namespaceId, name, valueObject } of storageValueObjects(contract)) { + for (const { + coordinate: { namespaceId, entityName: name }, + valueObject, + } of valueObjectsWithCoordinates(contract.storage)) { for (const [fieldName, field] of Object.entries(valueObject.fields)) { validateField(`Value object "${namespaceId}:${name}" field "${fieldName}"`, field); } diff --git a/packages/1-framework/0-foundation/contract/test/storage-value-objects.test.ts b/packages/1-framework/0-foundation/contract/test/storage-value-objects.test.ts index da44a664497a..2c3455898575 100644 --- a/packages/1-framework/0-foundation/contract/test/storage-value-objects.test.ts +++ b/packages/1-framework/0-foundation/contract/test/storage-value-objects.test.ts @@ -1,6 +1,6 @@ import { blindCast } from '@internal/utils/casts'; import { describe, expect, it } from 'vitest'; -import { storageValueObjectsAtDefaultNamespace, valueObjectAt } from '../src/storage-value-objects'; +import { valueObjectAt, valueObjectsWithCoordinates } from '../src/storage-value-objects'; import type { StorageNamespace } from '../src/types'; const address = { @@ -66,29 +66,35 @@ describe('valueObjectAt with an unusual namespace', () => { }); }); -describe('storageValueObjectsAtDefaultNamespace', () => { - it('reads the value objects of the only storage namespace', () => { - expect(storageValueObjectsAtDefaultNamespace(storage)).toEqual({ Address: address }); - }); - - it('is empty when the namespace has no value objects', () => { +describe('valueObjectsWithCoordinates', () => { + it('lists each value object in every storage namespace with its coordinate', () => { expect( - storageValueObjectsAtDefaultNamespace({ - namespaces: { public: { id: 'public', entries: {} } }, - }), - ).toEqual({}); - }); - - it('refuses a storage with more than one namespace', () => { - expect(() => - storageValueObjectsAtDefaultNamespace({ + valueObjectsWithCoordinates({ namespaces: { - public: { id: 'public', entries: {} }, - billing: { id: 'billing', entries: {} }, + public: { id: 'public', entries: { valueObject: { Address: address }, table: {} } }, + billing: { id: 'billing', entries: { valueObject: { Address: address } } }, + bare: blindCast({ id: 'bare' }), }, }), - ).toThrow( - 'bare-name resolution requires exactly one storage namespace, found 2 (public, billing); select a namespace explicitly', - ); + ).toEqual([ + { + coordinate: { + plane: 'storage', + namespaceId: 'public', + entityKind: 'valueObject', + entityName: 'Address', + }, + valueObject: address, + }, + { + coordinate: { + plane: 'storage', + namespaceId: 'billing', + entityKind: 'valueObject', + entityName: 'Address', + }, + valueObject: address, + }, + ]); }); }); diff --git a/packages/1-framework/3-tooling/emitter/src/domain-type-generation.ts b/packages/1-framework/3-tooling/emitter/src/domain-type-generation.ts index 39586c09eee9..69077b54e49f 100644 --- a/packages/1-framework/3-tooling/emitter/src/domain-type-generation.ts +++ b/packages/1-framework/3-tooling/emitter/src/domain-type-generation.ts @@ -8,16 +8,16 @@ import type { ContractWithDomain, CrossReference, JsonValue, - ValueObjectFieldType, + ValueObjectReference, } from '@internal/contract/types'; -import { valueObjectAt } from '@internal/contract/types'; +import { valueObjectsWithCoordinates } from '@internal/contract/types'; import type { CodecLookup } from '@internal/framework-components/codec'; import { type ImportSpecifierResolver, keepInternalSpecifiers, type TypesImportSpec, } from '@internal/framework-components/emission'; -import { elementCoordinates, entityAt } from '@internal/framework-components/ir'; +import { entityAt } from '@internal/framework-components/ir'; import { type ImportRequirement, renderImports, tsStringLiteral } from '@internal/ts-render'; import { blindCast } from '@internal/utils/casts'; import { emitterError } from './emitter-errors'; @@ -83,7 +83,7 @@ export function generateRootsType(roots: Record | undefi return `{ ${entries} }`; } -function valueObjectFieldTypeText(type: ValueObjectFieldType): string { +function valueObjectFieldTypeText(type: ValueObjectReference): string { return `{ readonly kind: "valueObject"; readonly plane: "storage"; readonly namespaceId: ${serializeValue(type.namespaceId)}; readonly entityKind: "valueObject"; readonly entityName: ${serializeValue(type.entityName)} }`; } @@ -583,22 +583,6 @@ function resolveValueObjectFieldValueSet( return { encodedValues: valueSet.values, codecId: field.type.codecId }; } -/** Each value object in storage, with the storage namespace that holds it. */ -export function storageValueObjects( - storage: ContractWithDomain['storage'], -): ReadonlyArray { - return [...elementCoordinates(storage)].flatMap(({ namespaceId, entityKind, entityName }) => { - if (entityKind !== 'valueObject') return []; - const valueObject = valueObjectAt(storage, { - plane: 'storage', - namespaceId, - entityKind, - entityName, - }); - return valueObject === undefined ? [] : [[namespaceId, entityName, valueObject] as const]; - }); -} - /** `FieldTypeOverrides`: per namespace, `models` (domain namespace) or `valueObjects` (storage namespace), owner and field, the type of each field its codec does not give. */ export function generateFieldTypeOverridesMap( contract: ContractWithDomain, @@ -622,8 +606,8 @@ export function generateFieldTypeOverridesMap( ), ), ); - const valueObjectFields = storageValueObjects(contract.storage).flatMap( - ([nsId, valueObjectName, valueObject]) => + const valueObjectFields = valueObjectsWithCoordinates(contract.storage).flatMap( + ({ coordinate: { namespaceId: nsId, entityName: valueObjectName }, valueObject }) => Object.entries(valueObject.fields).map( ([fieldName, field]) => [ diff --git a/packages/1-framework/3-tooling/emitter/src/generate-contract-dts.ts b/packages/1-framework/3-tooling/emitter/src/generate-contract-dts.ts index 1b6a063f7086..79a200d96576 100644 --- a/packages/1-framework/3-tooling/emitter/src/generate-contract-dts.ts +++ b/packages/1-framework/3-tooling/emitter/src/generate-contract-dts.ts @@ -1,4 +1,9 @@ -import type { Contract, ContractEnum, ContractModelBase } from '@internal/contract/types'; +import { + type Contract, + type ContractEnum, + type ContractModelBase, + valueObjectsWithCoordinates, +} from '@internal/contract/types'; import type { CodecLookup } from '@internal/framework-components/codec'; import { type EmissionSpi, @@ -24,7 +29,6 @@ import { serializeExecutionType, serializeObjectKey, serializeValue, - storageValueObjects, } from './domain-type-generation'; import { emitterError } from './emitter-errors'; import { generateModelTypesBlock } from './model-types-emission'; @@ -111,7 +115,9 @@ export function generateContractDts( const rootsType = generateRootsType(contract.roots); const firstNamespaceOfValueObject = new Map(); - for (const [namespaceId, name] of storageValueObjects(contract.storage)) { + for (const { + coordinate: { namespaceId, entityName: name }, + } of valueObjectsWithCoordinates(contract.storage)) { if (!firstNamespaceOfValueObject.has(name)) firstNamespaceOfValueObject.set(name, namespaceId); } const valueObjectNamespaces = [...firstNamespaceOfValueObject]; diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/src/contract-schema.ts b/packages/2-mongo-family/1-foundation/mongo-contract/src/contract-schema.ts index beb17d351d0b..2098b78a10cc 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/src/contract-schema.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/src/contract-schema.ts @@ -15,7 +15,7 @@ const ScalarFieldTypeSchema = type({ 'typeParams?': 'Record', }); -const ValueObjectFieldTypeSchema = type({ +const ValueObjectReferenceSchema = type({ '+': 'reject', kind: "'valueObject'", plane: "'storage'", @@ -27,10 +27,10 @@ const ValueObjectFieldTypeSchema = type({ const UnionFieldTypeSchema = type({ '+': 'reject', kind: "'union'", - members: ScalarFieldTypeSchema.or(ValueObjectFieldTypeSchema).array(), + members: ScalarFieldTypeSchema.or(ValueObjectReferenceSchema).array(), }); -const FieldTypeSchema = ScalarFieldTypeSchema.or(ValueObjectFieldTypeSchema).or( +const FieldTypeSchema = ScalarFieldTypeSchema.or(ValueObjectReferenceSchema).or( UnionFieldTypeSchema, ); diff --git a/packages/2-mongo-family/2-authoring/contract-ts/test/contract-builder.dsl.test.ts b/packages/2-mongo-family/2-authoring/contract-ts/test/contract-builder.dsl.test.ts index 8aa9db6955a1..907a7ca42c75 100644 --- a/packages/2-mongo-family/2-authoring/contract-ts/test/contract-builder.dsl.test.ts +++ b/packages/2-mongo-family/2-authoring/contract-ts/test/contract-builder.dsl.test.ts @@ -1,7 +1,7 @@ import { crossRef, domainModelsAtDefaultNamespace, - storageValueObjectsAtDefaultNamespace, + valueObjectsWithCoordinates, } from '@internal/contract/types'; import { UNBOUND_NAMESPACE_ID } from '@internal/framework-components/ir'; import { describe, expect, it } from 'vitest'; @@ -369,7 +369,16 @@ describe('mongo contract builder', () => { }, }); expect( - JSON.parse(JSON.stringify(storageValueObjectsAtDefaultNamespace(contract.storage))), + JSON.parse( + JSON.stringify( + Object.fromEntries( + valueObjectsWithCoordinates(contract.storage).map(({ coordinate, valueObject }) => [ + coordinate.entityName, + valueObject, + ]), + ), + ), + ), ).toEqual({ Address: { fields: { diff --git a/packages/2-mongo-family/5-query-builders/orm/src/collection.ts b/packages/2-mongo-family/5-query-builders/orm/src/collection.ts index 2e00a9fa9e58..f5192dc02082 100644 --- a/packages/2-mongo-family/5-query-builders/orm/src/collection.ts +++ b/packages/2-mongo-family/5-query-builders/orm/src/collection.ts @@ -5,7 +5,7 @@ import { type ContractReferenceRelation, type ContractValueObject, type PlanMeta, - type ValueObjectFieldType, + type ValueObjectReference, valueObjectAt, } from '@internal/contract/types'; import { UNBOUND_NAMESPACE_ID } from '@internal/framework-components/ir'; @@ -1002,7 +1002,7 @@ class MongoCollectionImpl< return contractModelToMongoResultShape(model, { storage: this.#contract.storage }); } - #valueObjectAt(reference: ValueObjectFieldType): ContractValueObject | undefined { + #valueObjectAt(reference: ValueObjectReference): ContractValueObject | undefined { return valueObjectAt(this.#contract.storage, reference); } diff --git a/packages/2-mongo-family/5-query-builders/orm/src/field-accessor.ts b/packages/2-mongo-family/5-query-builders/orm/src/field-accessor.ts index 3ec309a1473a..45d03a8901ac 100644 --- a/packages/2-mongo-family/5-query-builders/orm/src/field-accessor.ts +++ b/packages/2-mongo-family/5-query-builders/orm/src/field-accessor.ts @@ -1,7 +1,7 @@ import type { ContractField, ContractValueObjectAt, - ValueObjectFieldType, + ValueObjectReference, } from '@internal/contract/types'; import type { AnyMongoTypeMaps, @@ -52,7 +52,7 @@ type ValueObjectFieldKeys< > = { [K in keyof MongoModelsMap[ModelName]['fields'] & string]: MongoModelsMap[ModelName]['fields'][K] extends { - readonly type: ValueObjectFieldType; + readonly type: ValueObjectReference; } ? K : never; @@ -93,7 +93,7 @@ export type FieldExpression> = pop(end: 1 | -1): FieldOperation; } & ([NumericOperand] extends [never] ? unknown : NumericOps); -type VOFields = +type VOFields = ContractValueObjectAt extends { readonly fields: infer F extends Record; } @@ -108,14 +108,14 @@ type VOScalarFieldKeys> = { type VOValueObjectFieldKeys> = { [K in keyof Fields & string]: Fields[K] extends { - readonly type: ValueObjectFieldType; + readonly type: ValueObjectReference; } ? K : never; }[keyof Fields & string]; /** A value object's storage coordinate as one string, so the value objects a path has entered form a union. */ -type ValueObjectKey = +type ValueObjectKey = `${Reference['namespaceId']}.${Reference['entityName']}`; /** The dot paths into a value object's fields. A path ends at a value object it has already entered, so a value object that contains itself gives finitely many paths. */ @@ -128,7 +128,7 @@ type VODotPaths< | { [K in VOScalarFieldKeys]: `${Prefix}${K}` }[VOScalarFieldKeys] | { [K in VOValueObjectFieldKeys]: Fields[K] extends { - readonly type: infer Reference extends ValueObjectFieldType; + readonly type: infer Reference extends ValueObjectReference; } ? ValueObjectKey extends Entered ? never @@ -149,7 +149,7 @@ export type DotPath< TContract, ModelName >]: MongoModelsMap[ModelName]['fields'][K] extends { - readonly type: infer Reference extends ValueObjectFieldType; + readonly type: infer Reference extends ValueObjectReference; } ? VODotPaths, `${K}.`, ValueObjectKey> : never; diff --git a/packages/2-mongo-family/5-query-builders/query-builder/src/resolve-path.ts b/packages/2-mongo-family/5-query-builders/query-builder/src/resolve-path.ts index a3124a51503f..c141dfaa3e51 100644 --- a/packages/2-mongo-family/5-query-builders/query-builder/src/resolve-path.ts +++ b/packages/2-mongo-family/5-query-builders/query-builder/src/resolve-path.ts @@ -1,4 +1,4 @@ -import type { ContractValueObjectAt, ValueObjectFieldType } from '@internal/contract/types'; +import type { ContractValueObjectAt, ValueObjectReference } from '@internal/contract/types'; import type { MongoContract, MongoModelsMap, OwnStoredNameOf } from '@internal/mongo-contract'; import type { DocField } from './types'; @@ -127,7 +127,7 @@ type TranslateField } ? FieldToLeaf : F extends { - readonly type: infer Reference extends ValueObjectFieldType; + readonly type: infer Reference extends ValueObjectReference; readonly nullable: infer Null extends boolean; } ? ValueObjectKey extends Entered @@ -142,7 +142,7 @@ type TranslateField : DocField; /** A value object's storage coordinate as one string, so the value objects a path has entered form a union. */ -type ValueObjectKey = +type ValueObjectKey = `${Reference['namespaceId']}.${Reference['entityName']}`; /** @@ -155,7 +155,7 @@ type ValueObjectKey = */ type VONestedShape< TContract extends MongoContract, - Reference extends ValueObjectFieldType, + Reference extends ValueObjectReference, Entered extends string, > = [ContractValueObjectAt] extends [ infer VO extends { readonly fields: Record }, diff --git a/packages/2-sql-family/1-core/contract/src/check-expressions.ts b/packages/2-sql-family/1-core/contract/src/check-expressions.ts index fe9c5d7b26be..2cc02e84ba20 100644 --- a/packages/2-sql-family/1-core/contract/src/check-expressions.ts +++ b/packages/2-sql-family/1-core/contract/src/check-expressions.ts @@ -54,21 +54,19 @@ export type CheckExpressionRenderer = ( input: CheckExpressionInput, ) => ReadonlyArray; -type ValueObjectReference = EntityCoordinate<'storage', 'valueObject'>; - /** * The value object a reference names, with nested value objects inlined and enum members' values read from their storage value sets. */ export function resolveValueObject( storage: ContractWithDomain['storage'], - reference: ValueObjectReference, + reference: EntityCoordinate<'storage', 'valueObject'>, ): ResolvedValueObject { return resolveEntered(storage, reference, [referenceKey(reference)]); } function resolveEntered( storage: ContractWithDomain['storage'], - reference: ValueObjectReference, + reference: EntityCoordinate<'storage', 'valueObject'>, path: readonly string[], ): ResolvedValueObject { const valueObject = requiredValueObject(storage, reference); @@ -113,11 +111,11 @@ function resolveFieldType( */ function reachesAny( storage: ContractWithDomain['storage'], - from: ValueObjectReference, + from: EntityCoordinate<'storage', 'valueObject'>, targets: ReadonlySet, ): boolean { const seen = new Set(); - const pending: ValueObjectReference[] = [from]; + const pending: EntityCoordinate<'storage', 'valueObject'>[] = [from]; for (let next = pending.pop(); next !== undefined; next = pending.pop()) { const key = referenceKey(next); if (targets.has(key)) return true; @@ -132,7 +130,7 @@ function reachesAny( function requiredValueObject( storage: ContractWithDomain['storage'], - reference: ValueObjectReference, + reference: EntityCoordinate<'storage', 'valueObject'>, ): ContractValueObject { const valueObject = valueObjectAt(storage, reference); invariant( @@ -158,6 +156,6 @@ function valueSetValues( ); } -function referenceKey(reference: ValueObjectReference): string { +function referenceKey(reference: EntityCoordinate<'storage', 'valueObject'>): string { return JSON.stringify([reference.namespaceId, reference.entityName]); } diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts b/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts index 65dd1a897f5e..0b5e00817a93 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts @@ -158,7 +158,7 @@ function enabledCapabilities( } /** The value objects of a definition as storage entities, all in the default namespace; `undefined` when it has none. */ -function storageValueObjects( +function valueObjectsFromDefinition( definition: ContractDefinition, defaultNamespaceId: string, ): Record | undefined { @@ -360,7 +360,7 @@ export function buildSqlContractFromDefinition( // producer's) and flush once, threshold-batched by code. const authoringWarnings: AuthoringWarning[] = [...(definition.warnings ?? [])]; const collectedColumnEntities: CollectedColumnEntities = {}; - const valueObjects = storageValueObjects(definition, defaultNamespaceId); + const valueObjects = valueObjectsFromDefinition(definition, defaultNamespaceId); const renderCheckExpressions = resolveCheckExpressionRenderer(definition.target); const tableLoweringContext: TableLoweringContext = { lookups, diff --git a/packages/3-targets/postgres/1-target/src/core/psl-print/refusals.ts b/packages/3-targets/postgres/1-target/src/core/psl-print/refusals.ts index 8ffd819061bb..16658c3abb4e 100644 --- a/packages/3-targets/postgres/1-target/src/core/psl-print/refusals.ts +++ b/packages/3-targets/postgres/1-target/src/core/psl-print/refusals.ts @@ -13,7 +13,7 @@ import type { ContractField, ExecutionMutationDefaultValue, ScalarFieldType, - ValueObjectFieldType, + ValueObjectReference, } from '@internal/contract/types'; import type { GeneratorPhase } from '@internal/framework-components/components'; import { UNBOUND_NAMESPACE_ID } from '@internal/framework-components/ir'; @@ -302,7 +302,7 @@ export function refuseFieldColumnMismatch(input: { export function refuseUnwritableFieldShape( field: ContractField, coordinate: string, -): asserts field is ContractField & { readonly type: ScalarFieldType | ValueObjectFieldType } { +): asserts field is ContractField & { readonly type: ScalarFieldType | ValueObjectReference } { if (field.type.kind === 'union') { throw unsupported( `field ${coordinate} has a ${field.type.kind} type, which cannot be written in Prisma 8 PSL.`, diff --git a/test/integration/test/value-objects/value-objects.integration.test.ts b/test/integration/test/value-objects/value-objects.integration.test.ts index 633139b2014b..738aea828bc6 100644 --- a/test/integration/test/value-objects/value-objects.integration.test.ts +++ b/test/integration/test/value-objects/value-objects.integration.test.ts @@ -3,8 +3,8 @@ import { type ContractField, type ContractValueObject, domainModelsAtDefaultNamespace, - storageValueObjectsAtDefaultNamespace, UNBOUND_DOMAIN_NAMESPACE_ID, + valueObjectsWithCoordinates, } from '@internal/contract/types'; import { MongoContractSerializer } from '@internal/family-mongo/ir'; import type { AuthoringTypeConstructorDescriptor } from '@internal/framework-components/authoring'; @@ -196,7 +196,12 @@ function modelsAtDefaultNamespace(contract: Contract) { } function valueObjectsAtDefaultNamespace(contract: Contract) { - return storageValueObjectsAtDefaultNamespace(contract.storage); + return Object.fromEntries( + valueObjectsWithCoordinates(contract.storage).map(({ coordinate, valueObject }) => [ + coordinate.entityName, + valueObject, + ]), + ); } function valueObjectRef(namespaceId: string, entityName: string) { diff --git a/upgrade-instructions/pending/value-objects-in-storage/extension/instructions.md b/upgrade-instructions/pending/value-objects-in-storage/extension/instructions.md index 2ebbb70a8b04..6b7ea22293ab 100644 --- a/upgrade-instructions/pending/value-objects-in-storage/extension/instructions.md +++ b/upgrade-instructions/pending/value-objects-in-storage/extension/instructions.md @@ -25,6 +25,13 @@ changes: matches: - '\bValueObjectTypesFromDomain\b' - '\bFieldTypesFromDomain\s*<' + - id: value-object-reference-type + summary: | + In `@prisma/orm-framework/contract/types`, `ValueObjectFieldType` is renamed `ValueObjectReference`: the type of a field that refers to a value object, which is now the value object's storage coordinate. `ContractValueObjectField` stays the type of a value object's own field. + detection: + glob: "**/*.{ts,mts,cts,tsx}" + matches: + - '\bValueObjectFieldType\b' --- # Value objects are storage entities @@ -74,3 +81,21 @@ type ValueObjects = ValueObjectTypesFromStorage Date: Fri, 23 Oct 2026 18:20:07 +0200 Subject: [PATCH 074/104] TML-3537: FieldTypesFromDomain requires the storage namespaces, the emitter always passes them, and the upgrade script adds them to every contract.d.ts; the extension guide keys value object overrides by storage namespace Signed-off-by: willbot Signed-off-by: Will Madden --- .../subsystems/2. Contract Emitter & Types.md | 6 +-- .../contract/src/field-value-types.ts | 4 +- .../test/field-value-types.types.test-d.ts | 13 ++--- .../emitter/src/domain-type-generation.ts | 10 ++-- .../emitter/src/generate-contract-dts.ts | 4 +- ...generate-contract-dts.type-imports.test.ts | 4 +- .../test/emitter-hook.generation.test.ts | 4 +- .../test/value-type-maps.display.test.ts | 2 +- .../contract-ts/src/contract-types.ts | 6 ++- .../emitter-hook.storage-column-types.test.ts | 2 +- .../value-objects-in-storage.test.ts | 24 +++++++++- .../app/instructions.md | 2 +- .../value-objects-in-storage.ts | 48 +++++++++++++------ .../extension/instructions.md | 12 ++++- 14 files changed, 91 insertions(+), 50 deletions(-) diff --git a/docs/architecture docs/subsystems/2. Contract Emitter & Types.md b/docs/architecture docs/subsystems/2. Contract Emitter & Types.md index e2a45b02e082..b68dd9dabd34 100644 --- a/docs/architecture docs/subsystems/2. Contract Emitter & Types.md +++ b/docs/architecture docs/subsystems/2. Contract Emitter & Types.md @@ -237,8 +237,8 @@ export type QueryOperationTypes = PgTargetQueryOps; export type AggregateTypes = { /* … per-aggregate codec and trait maps … */ }; type ParameterizedOutputTypeFunctions = PgParameterizedOutputTypes; type FieldTypeOverrides = { readonly public: { readonly models: { readonly User: { readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' } } } } }; -export type FieldOutputTypes = FieldTypesFromDomain; -export type FieldInputTypes = FieldTypesFromDomain; +export type FieldOutputTypes = FieldTypesFromDomain; +export type FieldInputTypes = FieldTypesFromDomain; type ColumnTypeOverrides = { /* … the same, keyed by namespace, table and column … */ }; export type StorageColumnTypes = ColumnTypesFromStorage; export type StorageColumnInputTypes = ColumnTypesFromStorage; @@ -269,7 +269,7 @@ export type Contract = ContractWithTypeMaps; export type Namespaces = Contract['storage']['namespaces']; ``` -The emitter writes the field type maps of every family, and the SQL family its storage column type maps, as applications of `FieldTypesFromDomain` (`@internal/contract/types`) and `ColumnTypesFromStorage` (`@internal/sql-contract/types`) to the emitted contract's own shape. A contract written with `defineContract`, in either family, applies the same generics to its built shape, so the two paths share one set of rules: an override first, then, on the output side, the pack's type for a field with type parameters, then the codec's output or input type, an object of a value object's own field types, or the union of a union field's members, then list and dictionary wrapping and nullability. Value objects are storage entities ([ADR 309](../adrs/ADR%20309%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md)): `contract.d.ts` writes them in the storage namespaces type, under `entries.valueObject`, and types a field that holds one by its storage coordinate. Each value object's `Output` and `Input` alias is an entry of `ValueObjectTypesFromStorage`, applied to `ContractBase['storage']['namespaces']`. `FieldTypesFromDomain` takes the storage namespaces as an optional sixth type parameter, where it finds the value object a field's coordinate names; the emitter passes it only when the contract has value objects, so a contract without them emits the same text as before. A SQL discriminator field is typed by the literal union of its value set, through `FieldTypeOverrides`. The emitter writes only what a type cannot compute: `FieldTypeOverrides` and `ColumnTypeOverrides` hold the literal union of a value set (each value rendered by its codec's `renderValueLiteral`; a codec that renders no literal, such as one that sets `keepsWrittenForm`, leaves the codec's type) and text a codec renders with `renderOutputType`. Each enum member's type comes from the same `renderValueLiteral`, or is the codec's output type when the codec renders no literal. `Models` members index `FieldOutputTypes`. +The emitter writes the field type maps of every family, and the SQL family its storage column type maps, as applications of `FieldTypesFromDomain` (`@internal/contract/types`) and `ColumnTypesFromStorage` (`@internal/sql-contract/types`) to the emitted contract's own shape. A contract written with `defineContract`, in either family, applies the same generics to its built shape, so the two paths share one set of rules: an override first, then, on the output side, the pack's type for a field with type parameters, then the codec's output or input type, an object of a value object's own field types, or the union of a union field's members, then list and dictionary wrapping and nullability. Value objects are storage entities ([ADR 309](../adrs/ADR%20309%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md)): `contract.d.ts` writes them in the storage namespaces type, under `entries.valueObject`, and types a field that holds one by its storage coordinate. Each value object's `Output` and `Input` alias is an entry of `ValueObjectTypesFromStorage`, applied to `ContractBase['storage']['namespaces']`. `FieldTypesFromDomain` reads both planes: it takes the storage namespaces as a required sixth type parameter, where it finds the value object a field's coordinate names, and the emitter always passes it. A SQL discriminator field is typed by the literal union of its value set, through `FieldTypeOverrides`. The emitter writes only what a type cannot compute: `FieldTypeOverrides` and `ColumnTypeOverrides` hold the literal union of a value set (each value rendered by its codec's `renderValueLiteral`; a codec that renders no literal, such as one that sets `keepsWrittenForm`, leaves the codec's type) and text a codec renders with `renderOutputType`. Each enum member's type comes from the same `renderValueLiteral`, or is the codec's output type when the codec renders no literal. `Models` members index `FieldOutputTypes`. Both planes are namespaced: storage entities live under `storage.namespaces..entries[entityKind][entityName]` (e.g. `entries['table']['user']`) and models under `domain.namespaces..models` (see [Data Contract § Two planes, namespaced](1.%20Data%20Contract.md#two-planes-namespaced) and [ADR 221](../adrs/ADR%20221%20-%20Contract%20IR%20two%20planes%20with%20uniform%20entity%20coordinate%20and%20pack-contributed%20entity%20kinds.md)). Consumers read a namespace's models directly — `Contract['domain']['namespaces']['public']['models']` (or `[keyof …['namespaces']]` for the sole-namespace case). See [ADR 223 — Target-owned default namespace](../adrs/ADR%20223%20-%20Target-owned%20default%20namespace.md) for the runtime/emitter fail-loud split. diff --git a/packages/1-framework/0-foundation/contract/src/field-value-types.ts b/packages/1-framework/0-foundation/contract/src/field-value-types.ts index 55f29588b364..1b8b5959c346 100644 --- a/packages/1-framework/0-foundation/contract/src/field-value-types.ts +++ b/packages/1-framework/0-foundation/contract/src/field-value-types.ts @@ -260,7 +260,7 @@ type FieldValueType< : never; /** - * `FieldOutputTypes` (`Side` `'output'`) or `FieldInputTypes` (`'input'`) of a contract whose domain namespaces are `DomainNamespaces` and whose storage namespaces are `StorageNamespaces`, computed from the contract's literal shape. A contract without value objects may leave `StorageNamespaces` out. Both an emitted `contract.d.ts` and a contract written with `defineContract` type their fields through it. + * `FieldOutputTypes` (`Side` `'output'`) or `FieldInputTypes` (`'input'`) of a contract whose domain namespaces are `DomainNamespaces` and whose storage namespaces are `StorageNamespaces`, computed from the contract's literal shape. It reads both planes: the domain for the fields, and storage for the value objects the fields refer to, so it always takes both. Both an emitted `contract.d.ts` and a contract written with `defineContract` type their fields through it. * * Each value is typed by, in order: * @@ -277,7 +277,7 @@ export type FieldTypesFromDomain< ParameterizedOutputs extends ParameterizedOutputTypesFunction, Overrides, Side extends ValueTypeSide, - StorageNamespaces = Record, + StorageNamespaces, > = { readonly [Ns in keyof DomainNamespaces]: NamespaceFieldTypes< Lookup, diff --git a/packages/1-framework/0-foundation/contract/test/field-value-types.types.test-d.ts b/packages/1-framework/0-foundation/contract/test/field-value-types.types.test-d.ts index c2d147b05c56..473028791f7e 100644 --- a/packages/1-framework/0-foundation/contract/test/field-value-types.types.test-d.ts +++ b/packages/1-framework/0-foundation/contract/test/field-value-types.types.test-d.ts @@ -265,16 +265,9 @@ describe('field types from a contract domain', () => { expectTypeOf().toEqualTypeOf<{ readonly elsewhere: bigint }>(); }); - test('a contract without value objects may leave the storage namespaces out', () => { - type WithoutStorage = FieldTypesFromDomain< - Domain, - CodecTypes, - CharOutputTypes, - Overrides, - 'output' - >['app']['User']; - expectTypeOf().toEqualTypeOf(); - expectTypeOf().toEqualTypeOf<'happy' | 'sad' | null>(); + test('the storage namespaces are required, so no value object field goes untyped', () => { + // @ts-expect-error FieldTypesFromDomain reads both planes and takes the storage namespaces + expectTypeOf>(); }); test('a union field is the union of its members, a scalar member taking its plain codec type', () => { diff --git a/packages/1-framework/3-tooling/emitter/src/domain-type-generation.ts b/packages/1-framework/3-tooling/emitter/src/domain-type-generation.ts index 69077b54e49f..3cf03dcb5444 100644 --- a/packages/1-framework/3-tooling/emitter/src/domain-type-generation.ts +++ b/packages/1-framework/3-tooling/emitter/src/domain-type-generation.ts @@ -378,13 +378,9 @@ export function parameterizedOutputTypeFunctionsAlias( return `type ${PARAMETERIZED_OUTPUT_TYPE_FUNCTIONS} = ${aliases.length > 0 ? aliases.join(' | ') : 'never'};`; } -/** `FieldOutputTypes` (`side` `'output'`) or `FieldInputTypes` (`'input'`): the shared generic applied to the emitted contract's domain, with `FieldTypeOverrides`, and to its storage namespaces when the contract has value objects. */ -export function fieldTypesMapExpression( - side: 'output' | 'input', - hasValueObjects: boolean, -): string { - const storageNamespaces = hasValueObjects ? ", ContractBase['storage']['namespaces']" : ''; - return `FieldTypesFromDomain`; +/** `FieldOutputTypes` (`side` `'output'`) or `FieldInputTypes` (`'input'`): the shared generic applied to the emitted contract's domain, with `FieldTypeOverrides`, and to its storage namespaces. */ +export function fieldTypesMapExpression(side: 'output' | 'input'): string { + return `FieldTypesFromDomain`; } export function generateContractFieldDescriptor(fieldName: string, field: ContractField): string { diff --git a/packages/1-framework/3-tooling/emitter/src/generate-contract-dts.ts b/packages/1-framework/3-tooling/emitter/src/generate-contract-dts.ts index 79a200d96576..f17a2ff04395 100644 --- a/packages/1-framework/3-tooling/emitter/src/generate-contract-dts.ts +++ b/packages/1-framework/3-tooling/emitter/src/generate-contract-dts.ts @@ -188,8 +188,8 @@ ${familyTypeAliases} ${parameterizedOutputTypesDeclaration} ${valueObjectTypeAliases} type FieldTypeOverrides = ${fieldTypeOverrides}; -export type FieldOutputTypes = ${fieldTypesMapExpression('output', hasValueObjects)}; -export type FieldInputTypes = ${fieldTypesMapExpression('input', hasValueObjects)}; +export type FieldOutputTypes = ${fieldTypesMapExpression('output')}; +export type FieldInputTypes = ${fieldTypesMapExpression('input')}; ${extraTypeExports ? `${extraTypeExports}\n` : ''} ${modelTypesBlock} diff --git a/packages/1-framework/3-tooling/emitter/test/generate-contract-dts.type-imports.test.ts b/packages/1-framework/3-tooling/emitter/test/generate-contract-dts.type-imports.test.ts index caecc6ec7d54..cc6bf1377229 100644 --- a/packages/1-framework/3-tooling/emitter/test/generate-contract-dts.type-imports.test.ts +++ b/packages/1-framework/3-tooling/emitter/test/generate-contract-dts.type-imports.test.ts @@ -136,11 +136,11 @@ describe('generateContractDts field type maps', () => { expect(dts).toMatch(/\n {2}FieldTypesFromDomain,\n/); }); - it('leaves the storage namespaces out of the shared generic for a contract without value objects', () => { + it('passes the storage namespaces to the shared generic for a contract without value objects too', () => { const dts = generateContractDts(createTestContract(), createMockSpi(), [CODEC_TYPES], HASHES); expect(dts).toContain( - "export type FieldOutputTypes = FieldTypesFromDomain;", + "export type FieldOutputTypes = FieldTypesFromDomain;", ); expect(dts).not.toContain('ValueObjectTypesFromStorage'); }); diff --git a/packages/2-mongo-family/3-tooling/emitter/test/emitter-hook.generation.test.ts b/packages/2-mongo-family/3-tooling/emitter/test/emitter-hook.generation.test.ts index a44e2a176fcc..6dbb17279c56 100644 --- a/packages/2-mongo-family/3-tooling/emitter/test/emitter-hook.generation.test.ts +++ b/packages/2-mongo-family/3-tooling/emitter/test/emitter-hook.generation.test.ts @@ -806,10 +806,10 @@ describe('mongoEmission.generateContractTypes', () => { }); const types = generateContractDts(contract, mongoEmission, [], testHashes); expect(types).toContain( - "export type FieldOutputTypes = FieldTypesFromDomain;", + "export type FieldOutputTypes = FieldTypesFromDomain;", ); expect(types).toContain( - "export type FieldInputTypes = FieldTypesFromDomain;", + "export type FieldInputTypes = FieldTypesFromDomain;", ); }); }); diff --git a/packages/2-sql-family/1-core/contract/test/value-type-maps.display.test.ts b/packages/2-sql-family/1-core/contract/test/value-type-maps.display.test.ts index 286203b1d074..ac547244a107 100644 --- a/packages/2-sql-family/1-core/contract/test/value-type-maps.display.test.ts +++ b/packages/2-sql-family/1-core/contract/test/value-type-maps.display.test.ts @@ -50,7 +50,7 @@ type Storage = { }; }; -type Fields = FieldTypesFromDomain; +type Fields = FieldTypesFromDomain; type Columns = ColumnTypesFromStorage; declare const fields: Fields; diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/contract-types.ts b/packages/2-sql-family/2-authoring/contract-ts/src/contract-types.ts index 8fe8d352cf92..30cead1907e6 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/contract-types.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/contract-types.ts @@ -1618,7 +1618,8 @@ export type SqlContractResult = ContractWithTypeMaps< CodecTypesFromDefinition, ParameterizedOutputTypesFromDefinition, FieldTypeOverrides, - 'output' + 'output', + BuiltStorage['namespaces'] > >; readonly fieldInputTypes: ResolvedTypeMap< @@ -1627,7 +1628,8 @@ export type SqlContractResult = ContractWithTypeMaps< CodecTypesFromDefinition, ParameterizedOutputTypesFromDefinition, FieldTypeOverrides, - 'input' + 'input', + BuiltStorage['namespaces'] > >; readonly storageColumnTypes: ResolvedTypeMap< diff --git a/packages/2-sql-family/3-tooling/emitter/test/emitter-hook.storage-column-types.test.ts b/packages/2-sql-family/3-tooling/emitter/test/emitter-hook.storage-column-types.test.ts index 6b89e81192fa..950db0a56187 100644 --- a/packages/2-sql-family/3-tooling/emitter/test/emitter-hook.storage-column-types.test.ts +++ b/packages/2-sql-family/3-tooling/emitter/test/emitter-hook.storage-column-types.test.ts @@ -816,7 +816,7 @@ describe('StorageColumnTypes', () => { const dts = generateContractDts(contract, sqlEmission, [], testHashes); expect(dts).toContain( - "export type FieldOutputTypes = FieldTypesFromDomain;", + "export type FieldOutputTypes = FieldTypesFromDomain;", ); expect(dts).toContain( 'readonly tags: { readonly nullable: false; readonly type: { readonly kind: "scalar"; readonly codecId: "pg/text@1" }; readonly many: { readonly elementNullable: false } }', diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/value-objects-in-storage.test.ts b/test/integration/test/upgrade-instructions/value-objects-in-storage/value-objects-in-storage.test.ts index d81f57a8cc72..2ba4533e0e15 100644 --- a/test/integration/test/upgrade-instructions/value-objects-in-storage/value-objects-in-storage.test.ts +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/value-objects-in-storage.test.ts @@ -1,4 +1,4 @@ -import { chmodSync, readFileSync } from 'node:fs'; +import { chmodSync, readFileSync, writeFileSync } from 'node:fs'; import { computeStorageHash } from '@internal/contract/hashing'; import { MongoContractSerializer } from '@internal/family-mongo/ir'; import { mongoContractCanonicalizationHooks } from '@internal/mongo-contract/canonicalization-hooks'; @@ -75,6 +75,28 @@ describe('a project whose contracts keep value objects in the domain plane', () }); }); +describe('a contract.d.ts whose contract the script leaves unchanged', () => { + it('gains the storage namespaces argument of FieldTypesFromDomain, which is now required', () => { + const root = copyFixture('after'); + const path = join( + root, + 'mongo/migrations/snapshots/95332b0bd6fbbfab4462d882c037a574c92e51897249c333ba681b12c4d603c9/contract.d.ts', + ); + writeFileSync( + path, + readFileSync(path, 'utf8').replaceAll(",\n ContractBase['storage']['namespaces']\n>", '\n>'), + ); + expect({ run: runScript(root), tree: readTree(root) }).toEqual({ + run: { + status: 0, + stdout: 'Rewrote 1 file and renamed 0 snapshot directories.\n', + stderr: '', + }, + tree: fixtureTree('after'), + }); + }); +}); + describe('the rewritten contracts', () => { const after = fixtureTree('after'); const contracts = Object.keys(after) diff --git a/upgrade-instructions/pending/value-objects-in-storage/app/instructions.md b/upgrade-instructions/pending/value-objects-in-storage/app/instructions.md index 027fbefe6529..9edaa96805a5 100644 --- a/upgrade-instructions/pending/value-objects-in-storage/app/instructions.md +++ b/upgrade-instructions/pending/value-objects-in-storage/app/instructions.md @@ -44,7 +44,7 @@ Storage now holds the value objects, so each contract it rewrites has a new stor When it finishes, it prints how many files it rewrote, how many snapshot directories it renamed, and each storage hash it replaced (` -> `). A project already in the new form is left unchanged, and the script says that nothing changed. It changes no file and exits 1 when a file it must read is not valid JSON, when a contract's stored hash does not match its content, when a field holds a value object the contract does not declare, when a member's enum has no storage value set, when a discriminator's value set name is already taken by an enum, when a renamed snapshot directory already exists with different content, or when it cannot read a directory under the root. If it stops on an error partway, for example on a full disk, run it again: it finishes the upgrade. -The script rewrites a snapshot's `contract.d.ts` for the moved value objects. It does not add the types of the discriminator value sets. Nothing reads them from a snapshot: a `migration.ts` that imports a snapshot's `contract.d.ts` compiles without them, and `prisma contract emit` writes them for the emitted contract. +The script rewrites a snapshot's `contract.d.ts` for the moved value objects. In every `contract.d.ts` it finds, including those of contracts without value objects, it also passes the storage namespaces to `FieldTypesFromDomain`, which now requires them. It does not add the types of the discriminator value sets. Nothing reads them from a snapshot: a `migration.ts` that imports a snapshot's `contract.d.ts` compiles without them, and `prisma contract emit` writes them for the emitted contract. Then: diff --git a/upgrade-instructions/pending/value-objects-in-storage/app/scripts/value-objects-in-storage/value-objects-in-storage.ts b/upgrade-instructions/pending/value-objects-in-storage/app/scripts/value-objects-in-storage/value-objects-in-storage.ts index f5924eb74f54..636f82459917 100644 --- a/upgrade-instructions/pending/value-objects-in-storage/app/scripts/value-objects-in-storage/value-objects-in-storage.ts +++ b/upgrade-instructions/pending/value-objects-in-storage/app/scripts/value-objects-in-storage/value-objects-in-storage.ts @@ -799,6 +799,12 @@ interface Edit { } const STORAGE_NAMESPACES = "ContractBase['storage']['namespaces']"; + +function dtsPathOf(contractPath: string, snapshot: boolean): string { + return snapshot + ? join(dirname(contractPath), 'contract.d.ts') + : `${contractPath.slice(0, -'.json'.length)}.d.ts`; +} const DTS_LINE_WIDTH = 100; /** @@ -868,15 +874,20 @@ function rewriteDts( /ValueObjectTypesFromDomain<(\s*)ContractBase\['domain'\]\['namespaces'\]/g, `ValueObjectTypesFromStorage<$1${STORAGE_NAMESPACES}`, ) - .replace(/\bValueObjectTypesFromDomain\b/g, 'ValueObjectTypesFromStorage') - .replace( - /(FieldTypesFromDomain<[^<>]*?(['"])(?:output|input)\2)(\s*)>/g, - (_match: string, head: string, _quote: string, space: string) => - space.includes('\n') - ? `${head},${lineEnding} ${STORAGE_NAMESPACES}${lineEnding}>` - : `${head}, ${STORAGE_NAMESPACES}>`, - ); - return replaceHashTokens(result, hashes); + .replace(/\bValueObjectTypesFromDomain\b/g, 'ValueObjectTypesFromStorage'); + return replaceHashTokens(withStorageNamespacesArgument(result), hashes); +} + +/** `FieldTypesFromDomain` with the storage namespaces as its sixth argument, which it now requires of every contract. */ +function withStorageNamespacesArgument(text: string): string { + const lineEnding = lineEndingOf(text); + return text.replace( + /(FieldTypesFromDomain<[^<>]*?(['"])(?:output|input)\2)(\s*)>/g, + (_match: string, head: string, _quote: string, space: string) => + space.includes('\n') + ? `${head},${lineEnding} ${STORAGE_NAMESPACES}${lineEnding}>` + : `${head}, ${STORAGE_NAMESPACES}>`, + ); } /** A value object field's coordinate type, on one line when it fits the emitter's line width, else one member per line. */ @@ -978,6 +989,7 @@ function main({ return 1; } const plans: ContractPlan[] = []; + const argumentWrites = new Map(); const stops: string[] = []; let contractsFound = 0; @@ -996,8 +1008,16 @@ function main({ if (family === undefined || !isRecord(contract['storage'])) continue; contractsFound++; const rewrite = rewriteContract(contract, family); - if (!rewrite.changed) continue; const snapshot = isSnapshotContract(path); + if (!rewrite.changed) { + const dtsPath = dtsPathOf(path, snapshot); + if (existsSync(dtsPath)) { + const dts = readFileSync(dtsPath, 'utf8'); + const next = withStorageNamespacesArgument(dts); + if (next !== dts) argumentWrites.set(dtsPath, next); + } + continue; + } const stored = contract['storage']['storageHash']; const oldHash = snapshot ? basename(dirname(path)) : typeof stored === 'string' ? stored : ''; const computed = computeStorageHash(family, contract); @@ -1038,9 +1058,7 @@ function main({ const referenceWrites = new Map(); const contractWrites = new Map(); for (const plan of plans) { - const dtsPath = plan.snapshot - ? join(dirname(plan.path), 'contract.d.ts') - : `${plan.path.slice(0, -'.json'.length)}.d.ts`; + const dtsPath = dtsPathOf(plan.path, plan.snapshot); const dtsHashes = new Map([...hashes, [plan.oldHash, plan.newHash]]); const dts = existsSync(dtsPath) ? rewriteDts(readFileSync(dtsPath, 'utf8'), plan.valueObjectNamespaces, dtsHashes) @@ -1126,6 +1144,7 @@ function main({ for (const [path, text] of snapshotRewrites) writeFile(path, text); for (const [path, text] of referenceWrites) writeFile(path, text); for (const [path, text] of contractWrites) writeFile(path, text); + for (const [path, text] of argumentWrites) writeFile(path, text); for (const dir of removals) removeDirectory(dir); } catch (error) { const message = error instanceof Error ? error.message : String(error); @@ -1134,7 +1153,8 @@ function main({ ); return 1; } - const rewrittenFiles = snapshotRewrites.length + referenceWrites.size + contractWrites.size; + const rewrittenFiles = + snapshotRewrites.length + referenceWrites.size + contractWrites.size + argumentWrites.size; const lines = summary(root, contractsFound, rewrittenFiles, removals.size, hashes); process.stdout.write(`${lines.join('\n')}\n`); return 0; diff --git a/upgrade-instructions/pending/value-objects-in-storage/extension/instructions.md b/upgrade-instructions/pending/value-objects-in-storage/extension/instructions.md index 6b7ea22293ab..3d095c2c17c9 100644 --- a/upgrade-instructions/pending/value-objects-in-storage/extension/instructions.md +++ b/upgrade-instructions/pending/value-objects-in-storage/extension/instructions.md @@ -19,7 +19,7 @@ changes: - '\bPostgresCheck(?:Kind|ExpressionInput|ExpressionCandidate)\b' - id: field-types-read-storage-value-objects summary: | - In `@prisma/orm-framework/contract/types`, `ValueObjectTypesFromDomain` is now `ValueObjectTypesFromStorage`, which takes the contract's storage namespaces. `FieldTypesFromDomain` takes the storage namespaces as a new sixth argument, `Contract['storage']['namespaces']`. It defaults to none, so code that still passes five arguments types no value object field: such a field gets no type. An override for a value object field is keyed by the value object's storage namespace, at `[storage namespace]['valueObjects'][valueObject][field]`. + In `@prisma/orm-framework/contract/types`, `ValueObjectTypesFromDomain` is now `ValueObjectTypesFromStorage`, which takes the contract's storage namespaces. `FieldTypesFromDomain` takes the storage namespaces as a new, required sixth argument, `Contract['storage']['namespaces']`, since it reads both planes. An override for a value object field is keyed by the value object's storage namespace, at `[storage namespace]['valueObjects'][valueObject][field]`, while a model field's override stays keyed by its domain namespace. detection: glob: "**/*.{ts,mts,cts,tsx}" matches: @@ -80,7 +80,15 @@ type Fields = FieldTypesFromDomain; ``` -Pass the sixth argument whenever the contract has value objects. A model field's override stays at `[namespace]['models'][model][field]`. A value object field's override moves to the namespace the value object is stored in: on SQL that is usually the model's namespace, and on MongoDB it is `__unbound__`. +Pass the sixth argument for every contract, with value objects or without. `FieldTypeOverrides` has two kinds of key at its top level. Under `models`, the key is the domain namespace of the model. Under `valueObjects`, the key is the storage namespace that holds the value object. These can differ. On SQL every value object is stored in the default storage namespace, such as `public` on Postgres, whatever the namespace of the model that holds it. On MongoDB it is `__unbound__`. + +```ts +// a model `User` in the PSL namespace `billing`, with an `Address` field, on Postgres +type FieldTypeOverrides = { + readonly billing: { readonly models: { readonly User: { readonly status: { readonly output: 'active' | 'closed' } } } }; + readonly public: { readonly valueObjects: { readonly Address: { readonly kind: { readonly output: 'home' | 'work' } } } }; +}; +``` ## `value-object-reference-type` From dd134148f26eaf613a8494fcf5e2248d253eee33 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 18:25:43 +0200 Subject: [PATCH 075/104] TML-3537: re-emit every contract.d.ts with the storage namespaces passed to FieldTypesFromDomain Signed-off-by: willbot Signed-off-by: Will Madden --- apps/telemetry-backend/src/prisma/contract.d.ts | 6 ++++-- examples/bundle-size/src/mongo/generated/contract.d.ts | 6 ++++-- examples/bundle-size/src/postgres/generated/contract.d.ts | 6 ++++-- examples/multi-extension-monorepo/app/src/contract.d.ts | 6 ++++-- .../contract.d.ts | 6 ++++-- .../packages/audit/src/contract.d.ts | 6 ++++-- .../contract.d.ts | 6 ++++-- .../packages/feature-flags/src/contract.d.ts | 6 ++++-- examples/paradedb-demo/src/prisma/contract.d.ts | 6 ++++-- examples/prisma-8-demo-mysql/src/prisma/contract.d.ts | 6 ++++-- examples/prisma-8-demo-sqlite/src/prisma/contract.d.ts | 6 ++++-- .../contract.d.ts | 6 ++++-- examples/prisma-8-postgis-demo/src/prisma/contract.d.ts | 6 ++++-- examples/prisma7-adoption/generated/prisma8/contract.d.ts | 6 ++++-- examples/react-router-demo/src/prisma/contract.d.ts | 6 ++++-- examples/supabase/src/contract.d.ts | 6 ++++-- .../sql-builder/test/fixtures/generated/contract.d.ts | 6 ++++-- .../contract.d.ts | 6 ++++-- packages/3-extensions/paradedb/src/contract.d.ts | 6 ++++-- .../contract.d.ts | 6 ++++-- packages/3-extensions/pgvector/src/contract.d.ts | 6 ++++-- .../contract.d.ts | 6 ++++-- packages/3-extensions/postgis/src/contract.d.ts | 6 ++++-- .../fixtures/collection-methods/generated/contract.d.ts | 6 ++++-- .../fixtures/fragment-namespace/generated/contract.d.ts | 6 ++++-- .../sql-orm-client/test/fixtures/generated/contract.d.ts | 6 ++++-- .../fixtures/junction-namespaces/generated/contract.d.ts | 6 ++++-- .../test/fixtures/on-update-only/generated/contract.d.ts | 6 ++++-- .../test/fixtures/polymorphism/generated/contract.d.ts | 6 ++++-- .../test/fixtures/scope-namespace/generated/contract.d.ts | 6 ++++-- packages/3-extensions/supabase/src/contract/contract.d.ts | 6 ++++-- .../supabase/test/fixtures/example-app/contract.d.ts | 6 ++++-- .../supabase/test/fixtures/no-policy/contract.d.ts | 6 ++++-- .../supabase/test/fixtures/renamed-policy/contract.d.ts | 6 ++++-- .../mysql/9-facade/test/fixtures/generated/contract.d.ts | 6 ++++-- .../postgres/9-facade/test/fixtures/generated/contract.d.ts | 6 ++++-- test/e2e/framework/test/fixtures/generated/contract.d.ts | 6 ++++-- .../framework/test/sqlite/fixtures/generated/contract.d.ts | 6 ++++-- .../fixtures/postgres/generated/contract.d.ts | 6 ++++-- .../contract-parity/fixtures/sqlite/generated/contract.d.ts | 6 ++++-- .../enum-members/_fixture-mongo/generated/contract.d.ts | 6 ++++-- .../_fixture-native-enums/generated/contract.d.ts | 6 ++++-- .../enum-members/_fixture-postgres/generated/contract.d.ts | 6 ++++-- .../enum-members/_fixture-sqlite/generated/contract.d.ts | 6 ++++-- .../test/enum-order-by/_fixture/generated/contract.d.ts | 6 ++++-- test/integration/test/fixtures/contract.d.ts | 6 ++++-- .../integration/test/mongo/fixtures/generated/contract.d.ts | 6 ++++-- .../_fixture-aliases/generated/contract.d.ts | 6 ++++-- .../_fixture-attributes/generated/contract.d.ts | 6 ++++-- .../mongo/temporal-presets/_fixture/generated/contract.d.ts | 6 ++++-- .../namespaced-accessors/fixtures/generated/contract.d.ts | 6 ++++-- .../fixtures/postgres/generated/contract.d.ts | 6 ++++-- .../planner-golden/fixtures/sqlite/generated/contract.d.ts | 6 ++++-- .../avg/_fixture/decimal/generated/contract.d.ts | 6 ++++-- .../avg/_fixture/numeric/generated/contract.d.ts | 6 ++++-- .../aggregation/count/_fixture/generated/contract.d.ts | 6 ++++-- .../group_by/_fixture/main/generated/contract.d.ts | 6 ++++-- .../_fixture/regression-21789/generated/contract.d.ts | 6 ++++-- .../group_by_having/_fixture/common/generated/contract.d.ts | 6 ++++-- .../_fixture/decimal/generated/contract.d.ts | 6 ++++-- .../_fixture/compound/generated/contract.d.ts | 6 ++++-- .../many_count_relation/_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/nested/generated/contract.d.ts | 6 ++++-- .../_fixture/self/generated/contract.d.ts | 6 ++++-- .../aggregation/max/_fixture/common/generated/contract.d.ts | 6 ++++-- .../max/_fixture/decimal/generated/contract.d.ts | 6 ++++-- .../aggregation/min/_fixture/common/generated/contract.d.ts | 6 ++++-- .../min/_fixture/decimal/generated/contract.d.ts | 6 ++++-- .../sum/_fixture/decimal/generated/contract.d.ts | 6 ++++-- .../sum/_fixture/numeric/generated/contract.d.ts | 6 ++++-- .../_fixture/base/generated/contract.d.ts | 6 ++++-- .../_fixture/nested/generated/contract.d.ts | 6 ++++-- .../data_types/bigint/_fixture/generated/contract.d.ts | 6 ++++-- .../data_types/bool/_fixture/generated/contract.d.ts | 6 ++++-- .../bytes/_fixture/relations/generated/contract.d.ts | 6 ++++-- .../bytes/_fixture/scalars/generated/contract.d.ts | 6 ++++-- .../data_types/datetime/_fixture/generated/contract.d.ts | 6 ++++-- .../data_types/decimal/_fixture/generated/contract.d.ts | 6 ++++-- .../enum_type/_fixture/postgres/generated/contract.d.ts | 6 ++++-- .../data_types/float/_fixture/generated/contract.d.ts | 6 ++++-- .../queries/data_types/int/_fixture/generated/contract.d.ts | 6 ++++-- .../data_types/json/_fixture/scalar/generated/contract.d.ts | 6 ++++-- .../native/postgres/_fixture/other/generated/contract.d.ts | 6 ++++-- .../native/postgres/_fixture/string/generated/contract.d.ts | 6 ++++-- .../data_types/string/_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/common/generated/contract.d.ts | 6 ++++-- .../_fixture/decimal/generated/contract.d.ts | 6 ++++-- .../through_relation/_fixture/enum/generated/contract.d.ts | 6 ++++-- .../through_relation/_fixture/json/generated/contract.d.ts | 6 ++++-- .../through_relation/_fixture/lists/generated/contract.d.ts | 6 ++++-- .../queries/distinct/_fixture/generated/contract.d.ts | 6 ++++-- .../filters/bigint_filter/_fixture/generated/contract.d.ts | 6 ++++-- .../filters/bytes_filter/_fixture/generated/contract.d.ts | 6 ++++-- .../filters/decimal_filter/_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/decimal-list/generated/contract.d.ts | 6 ++++-- .../field_reference/_fixture/enum/generated/contract.d.ts | 6 ++++-- .../field_reference/_fixture/list/generated/contract.d.ts | 6 ++++-- .../field_reference/_fixture/mixed/generated/contract.d.ts | 6 ++++-- .../bigint_filter/_fixture/generated/contract.d.ts | 6 ++++-- .../bytes_filter/_fixture/generated/contract.d.ts | 6 ++++-- .../datetime_filter/_fixture/generated/contract.d.ts | 6 ++++-- .../decimal_filter/_fixture/generated/contract.d.ts | 6 ++++-- .../failure/_fixture/common/generated/contract.d.ts | 6 ++++-- .../failure/_fixture/default/generated/contract.d.ts | 6 ++++-- .../failure/_fixture/list/generated/contract.d.ts | 6 ++++-- .../float_filter/_fixture/generated/contract.d.ts | 6 ++++-- .../having_filter/_fixture/generated/contract.d.ts | 6 ++++-- .../int_filter/_fixture/generated/contract.d.ts | 6 ++++-- .../json_filter/_fixture/generated/contract.d.ts | 6 ++++-- .../json_filter/_fixture/list/generated/contract.d.ts | 6 ++++-- .../_fixture/complex/generated/contract.d.ts | 6 ++++-- .../_fixture/one-to-many/generated/contract.d.ts | 6 ++++-- .../_fixture/one-to-one-list/generated/contract.d.ts | 6 ++++-- .../_fixture/one-to-one/generated/contract.d.ts | 6 ++++-- .../string_filter/_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/compound-one-to-many/generated/contract.d.ts | 6 ++++-- .../_fixture/many-to-many/generated/contract.d.ts | 6 ++++-- .../_fixture/one-to-many/generated/contract.d.ts | 6 ++++-- .../filters/filters/_fixture/generated/contract.d.ts | 6 ++++-- .../filters/json/_fixture/optional/generated/contract.d.ts | 6 ++++-- .../list_filters/_fixture/base/generated/contract.d.ts | 6 ++++-- .../list_filters/_fixture/decimal/generated/contract.d.ts | 6 ++++-- .../list_filters/_fixture/json/generated/contract.d.ts | 6 ++++-- .../many_relation/_fixture/25103/generated/contract.d.ts | 6 ++++-- .../_fixture/l2-to-one/generated/contract.d.ts | 6 ++++-- .../one2one_regression/_fixture/generated/contract.d.ts | 6 ++++-- .../one_relation/_fixture/21356/generated/contract.d.ts | 6 ++++-- .../one_relation/_fixture/21366/generated/contract.d.ts | 6 ++++-- .../create_many/_fixture/generated/contract.d.ts | 6 ++++-- .../batching-bigint/_fixture/generated/contract.d.ts | 6 ++++-- .../batching-bytes/_fixture/generated/contract.d.ts | 6 ++++-- .../functional/blog-update/_fixture/generated/contract.d.ts | 6 ++++-- .../bytes-upsert/_fixture/generated/contract.d.ts | 6 ++++-- .../chunking-query/_fixture/generated/contract.d.ts | 6 ++++-- .../create-default-date/_fixture/generated/contract.d.ts | 6 ++++-- .../decimal-list/_fixture/generated/contract.d.ts | 6 ++++-- .../decimal-precision/_fixture/generated/contract.d.ts | 6 ++++-- .../decimal-scalar/_fixture/generated/contract.d.ts | 6 ++++-- .../default-selection/_fixture/generated/contract.d.ts | 6 ++++-- .../functional/distinct/_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/sqlite/generated/contract.d.ts | 6 ++++-- .../functional/enum-array/_fixture/generated/contract.d.ts | 6 ++++-- .../functional/enums/_fixture/generated/contract.d.ts | 6 ++++-- .../extended-where/_fixture/generated/contract.d.ts | 6 ++++-- .../filter-count-relations/_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../handle-int-overflow/_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../issues-11974/_fixture/generated/contract.d.ts | 6 ++++-- .../issues-12378/_fixture/generated/contract.d.ts | 6 ++++-- .../issues-12557/_fixture/generated/contract.d.ts | 6 ++++-- .../issues-12572/_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../issues-14271/_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../issues-15044/_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../issues-23902/_fixture/generated/contract.d.ts | 6 ++++-- .../issues-25404/_fixture/generated/contract.d.ts | 6 ++++-- .../issues-27455-bytes-id/_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../functional/issues-4004/_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../functional/json-fields/_fixture/generated/contract.d.ts | 6 ++++-- .../large-floats/_fixture/generated/contract.d.ts | 6 ++++-- .../legacy-aggregate-raw/_fixture/generated/contract.d.ts | 6 ++++-- .../legacy-aggregations/_fixture/generated/contract.d.ts | 6 ++++-- .../functional/legacy-json/_fixture/generated/contract.d.ts | 6 ++++-- .../legacy-malformed-id/_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../methods-count/_fixture/generated/contract.d.ts | 6 ++++-- .../methods-createMany/_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../methods-upsert-simple/_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/different-names/generated/contract.d.ts | 6 ++++-- .../_fixture/identical-names/generated/contract.d.ts | 6 ++++-- .../multi-schema/_fixture/no-map/generated/contract.d.ts | 6 ++++-- .../multiple-types/_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/generated/contract.d.ts | 6 ++++-- .../_fixture/cascade-map/generated/contract.d.ts | 6 ++++-- .../_fixture/cascade-plain/generated/contract.d.ts | 6 ++++-- .../_fixture/noaction-map/generated/contract.d.ts | 6 ++++-- .../_fixture/noaction-plain/generated/contract.d.ts | 6 ++++-- .../_fixture/restrict-map/generated/contract.d.ts | 6 ++++-- .../_fixture/restrict-plain/generated/contract.d.ts | 6 ++++-- .../_fixture/setnull-map/generated/contract.d.ts | 6 ++++-- .../_fixture/setnull-plain/generated/contract.d.ts | 6 ++++-- .../_fixture/cascade-map/generated/contract.d.ts | 6 ++++-- .../_fixture/cascade-plain/generated/contract.d.ts | 6 ++++-- .../_fixture/noaction-map/generated/contract.d.ts | 6 ++++-- .../_fixture/noaction-plain/generated/contract.d.ts | 6 ++++-- .../_fixture/restrict-map/generated/contract.d.ts | 6 ++++-- .../_fixture/restrict-plain/generated/contract.d.ts | 6 ++++-- .../_fixture/setnull-map/generated/contract.d.ts | 6 ++++-- .../_fixture/setnull-plain/generated/contract.d.ts | 6 ++++-- .../_fixture/cascade-nomap/generated/contract.d.ts | 6 ++++-- .../_fixture/default-nomap/generated/contract.d.ts | 6 ++++-- .../_fixture/noaction-nomap/generated/contract.d.ts | 6 ++++-- .../_fixture/restrict-nomap/generated/contract.d.ts | 6 ++++-- .../fixtures/generated-no-pgvector/contract.d.ts | 6 ++++-- .../test/sql-builder/fixtures/generated/contract.d.ts | 6 ++++-- .../fixtures/collection-methods/generated/contract.d.ts | 6 ++++-- .../execution-defaulted-tags/generated/contract.d.ts | 6 ++++-- .../fixtures/fragment-namespace/generated/contract.d.ts | 6 ++++-- .../test/sql-orm-client/fixtures/generated/contract.d.ts | 6 ++++-- .../integer-representation-sqlite/generated/contract.d.ts | 6 ++++-- .../fixtures/integer-representation/generated/contract.d.ts | 6 ++++-- .../fixtures/junction-namespaces/generated/contract.d.ts | 6 ++++-- .../sql-orm-client/fixtures/mn-psl/generated/contract.d.ts | 6 ++++-- .../fixtures/non-identifier-names/generated/contract.d.ts | 6 ++++-- .../fixtures/on-update-only/generated/contract.d.ts | 6 ++++-- .../polymorphism-namespaces/generated/contract.d.ts | 6 ++++-- .../polymorphism-self-relation/generated/contract.d.ts | 6 ++++-- .../fixtures/polymorphism/generated/contract.d.ts | 6 ++++-- .../fixtures/scalar-lists/generated/contract.d.ts | 6 ++++-- .../fixtures/scope-namespace/generated/contract.d.ts | 6 ++++-- .../fixtures/self-relations/generated/contract.d.ts | 6 ++++-- .../_fixture-aliases/generated/contract.d.ts | 6 ++++-- .../_fixture-attributes/generated/contract.d.ts | 6 ++++-- .../temporal-defaults/_fixture-now/generated/contract.d.ts | 6 ++++-- .../_fixture-timestamp/generated/contract.d.ts | 6 ++++-- .../test/temporal-defaults/_fixture/generated/contract.d.ts | 6 ++++-- 257 files changed, 1028 insertions(+), 514 deletions(-) diff --git a/apps/telemetry-backend/src/prisma/contract.d.ts b/apps/telemetry-backend/src/prisma/contract.d.ts index 1693002477b3..c89a352d9809 100644 --- a/apps/telemetry-backend/src/prisma/contract.d.ts +++ b/apps/telemetry-backend/src/prisma/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/examples/bundle-size/src/mongo/generated/contract.d.ts b/examples/bundle-size/src/mongo/generated/contract.d.ts index a2e3425203a4..90239fc4f4e0 100644 --- a/examples/bundle-size/src/mongo/generated/contract.d.ts +++ b/examples/bundle-size/src/mongo/generated/contract.d.ts @@ -37,14 +37,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { diff --git a/examples/bundle-size/src/postgres/generated/contract.d.ts b/examples/bundle-size/src/postgres/generated/contract.d.ts index 2f7c4b4bdf2e..6612b4c98861 100644 --- a/examples/bundle-size/src/postgres/generated/contract.d.ts +++ b/examples/bundle-size/src/postgres/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/examples/multi-extension-monorepo/app/src/contract.d.ts b/examples/multi-extension-monorepo/app/src/contract.d.ts index dda7c5f9bb24..87fd9e1c74c5 100644 --- a/examples/multi-extension-monorepo/app/src/contract.d.ts +++ b/examples/multi-extension-monorepo/app/src/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/examples/multi-extension-monorepo/packages/audit/migrations/snapshots/d60ebdd877de6283fafbbe0dc9a4fa13c1197009a53ba95ed37ba04fa3640cb1/contract.d.ts b/examples/multi-extension-monorepo/packages/audit/migrations/snapshots/d60ebdd877de6283fafbbe0dc9a4fa13c1197009a53ba95ed37ba04fa3640cb1/contract.d.ts index 29773ce69d8d..69c08654b1b8 100644 --- a/examples/multi-extension-monorepo/packages/audit/migrations/snapshots/d60ebdd877de6283fafbbe0dc9a4fa13c1197009a53ba95ed37ba04fa3640cb1/contract.d.ts +++ b/examples/multi-extension-monorepo/packages/audit/migrations/snapshots/d60ebdd877de6283fafbbe0dc9a4fa13c1197009a53ba95ed37ba04fa3640cb1/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/examples/multi-extension-monorepo/packages/audit/src/contract.d.ts b/examples/multi-extension-monorepo/packages/audit/src/contract.d.ts index 29773ce69d8d..69c08654b1b8 100644 --- a/examples/multi-extension-monorepo/packages/audit/src/contract.d.ts +++ b/examples/multi-extension-monorepo/packages/audit/src/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/examples/multi-extension-monorepo/packages/feature-flags/migrations/snapshots/9ea058903cdc9636107c83c1a6bbb6c3f28d92d4515cff00c31c2b4e37bb5bd4/contract.d.ts b/examples/multi-extension-monorepo/packages/feature-flags/migrations/snapshots/9ea058903cdc9636107c83c1a6bbb6c3f28d92d4515cff00c31c2b4e37bb5bd4/contract.d.ts index d64cdbee6969..fa7f1de08e39 100644 --- a/examples/multi-extension-monorepo/packages/feature-flags/migrations/snapshots/9ea058903cdc9636107c83c1a6bbb6c3f28d92d4515cff00c31c2b4e37bb5bd4/contract.d.ts +++ b/examples/multi-extension-monorepo/packages/feature-flags/migrations/snapshots/9ea058903cdc9636107c83c1a6bbb6c3f28d92d4515cff00c31c2b4e37bb5bd4/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/examples/multi-extension-monorepo/packages/feature-flags/src/contract.d.ts b/examples/multi-extension-monorepo/packages/feature-flags/src/contract.d.ts index d64cdbee6969..fa7f1de08e39 100644 --- a/examples/multi-extension-monorepo/packages/feature-flags/src/contract.d.ts +++ b/examples/multi-extension-monorepo/packages/feature-flags/src/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/examples/paradedb-demo/src/prisma/contract.d.ts b/examples/paradedb-demo/src/prisma/contract.d.ts index e08135357e63..4bf232c93130 100644 --- a/examples/paradedb-demo/src/prisma/contract.d.ts +++ b/examples/paradedb-demo/src/prisma/contract.d.ts @@ -277,14 +277,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/examples/prisma-8-demo-mysql/src/prisma/contract.d.ts b/examples/prisma-8-demo-mysql/src/prisma/contract.d.ts index e7217b831946..ed95e8ace18a 100644 --- a/examples/prisma-8-demo-mysql/src/prisma/contract.d.ts +++ b/examples/prisma-8-demo-mysql/src/prisma/contract.d.ts @@ -49,14 +49,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = { readonly __unbound__: { diff --git a/examples/prisma-8-demo-sqlite/src/prisma/contract.d.ts b/examples/prisma-8-demo-sqlite/src/prisma/contract.d.ts index 438ea69fd955..f848cdae6967 100644 --- a/examples/prisma-8-demo-sqlite/src/prisma/contract.d.ts +++ b/examples/prisma-8-demo-sqlite/src/prisma/contract.d.ts @@ -140,14 +140,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/examples/prisma-8-postgis-demo/migrations/snapshots/28b5e5f98ce0e148f03e345518c9c53cb7a04a82e8e58de74a87c1b4ae08cf65/contract.d.ts b/examples/prisma-8-postgis-demo/migrations/snapshots/28b5e5f98ce0e148f03e345518c9c53cb7a04a82e8e58de74a87c1b4ae08cf65/contract.d.ts index 646e2f1caf86..5108faf8108f 100644 --- a/examples/prisma-8-postgis-demo/migrations/snapshots/28b5e5f98ce0e148f03e345518c9c53cb7a04a82e8e58de74a87c1b4ae08cf65/contract.d.ts +++ b/examples/prisma-8-postgis-demo/migrations/snapshots/28b5e5f98ce0e148f03e345518c9c53cb7a04a82e8e58de74a87c1b4ae08cf65/contract.d.ts @@ -279,14 +279,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/examples/prisma-8-postgis-demo/src/prisma/contract.d.ts b/examples/prisma-8-postgis-demo/src/prisma/contract.d.ts index 646e2f1caf86..5108faf8108f 100644 --- a/examples/prisma-8-postgis-demo/src/prisma/contract.d.ts +++ b/examples/prisma-8-postgis-demo/src/prisma/contract.d.ts @@ -279,14 +279,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/examples/prisma7-adoption/generated/prisma8/contract.d.ts b/examples/prisma7-adoption/generated/prisma8/contract.d.ts index 21eb3111821b..54ca7df0688b 100644 --- a/examples/prisma7-adoption/generated/prisma8/contract.d.ts +++ b/examples/prisma7-adoption/generated/prisma8/contract.d.ts @@ -284,14 +284,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = { readonly public: { diff --git a/examples/react-router-demo/src/prisma/contract.d.ts b/examples/react-router-demo/src/prisma/contract.d.ts index bf65f4c40259..e85a20c6c87f 100644 --- a/examples/react-router-demo/src/prisma/contract.d.ts +++ b/examples/react-router-demo/src/prisma/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/examples/supabase/src/contract.d.ts b/examples/supabase/src/contract.d.ts index 9e7dc8582f04..9605cb5bf7a2 100644 --- a/examples/supabase/src/contract.d.ts +++ b/examples/supabase/src/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/packages/2-sql-family/4-lanes/sql-builder/test/fixtures/generated/contract.d.ts b/packages/2-sql-family/4-lanes/sql-builder/test/fixtures/generated/contract.d.ts index 9e18c0f49137..d3d2ec5974d6 100644 --- a/packages/2-sql-family/4-lanes/sql-builder/test/fixtures/generated/contract.d.ts +++ b/packages/2-sql-family/4-lanes/sql-builder/test/fixtures/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/packages/3-extensions/paradedb/migrations/snapshots/0c0734babd6eeb868fee1f281ca96963022475611560e9f170f465daa35f8599/contract.d.ts b/packages/3-extensions/paradedb/migrations/snapshots/0c0734babd6eeb868fee1f281ca96963022475611560e9f170f465daa35f8599/contract.d.ts index f1f672b24a37..c4748b39956e 100644 --- a/packages/3-extensions/paradedb/migrations/snapshots/0c0734babd6eeb868fee1f281ca96963022475611560e9f170f465daa35f8599/contract.d.ts +++ b/packages/3-extensions/paradedb/migrations/snapshots/0c0734babd6eeb868fee1f281ca96963022475611560e9f170f465daa35f8599/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/packages/3-extensions/paradedb/src/contract.d.ts b/packages/3-extensions/paradedb/src/contract.d.ts index f1f672b24a37..c4748b39956e 100644 --- a/packages/3-extensions/paradedb/src/contract.d.ts +++ b/packages/3-extensions/paradedb/src/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/packages/3-extensions/pgvector/migrations/snapshots/4a96b488a4ce92b434e5f7d6607b6435c0955f0d36b0018077045787764240e6/contract.d.ts b/packages/3-extensions/pgvector/migrations/snapshots/4a96b488a4ce92b434e5f7d6607b6435c0955f0d36b0018077045787764240e6/contract.d.ts index 1b9b42d298c2..5d014b849ee5 100644 --- a/packages/3-extensions/pgvector/migrations/snapshots/4a96b488a4ce92b434e5f7d6607b6435c0955f0d36b0018077045787764240e6/contract.d.ts +++ b/packages/3-extensions/pgvector/migrations/snapshots/4a96b488a4ce92b434e5f7d6607b6435c0955f0d36b0018077045787764240e6/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/packages/3-extensions/pgvector/src/contract.d.ts b/packages/3-extensions/pgvector/src/contract.d.ts index 1b9b42d298c2..5d014b849ee5 100644 --- a/packages/3-extensions/pgvector/src/contract.d.ts +++ b/packages/3-extensions/pgvector/src/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/packages/3-extensions/postgis/migrations/snapshots/2db551a8a5619d0d97710f29130eaea0eccbdd5238f015ee9d18408d438e0f8a/contract.d.ts b/packages/3-extensions/postgis/migrations/snapshots/2db551a8a5619d0d97710f29130eaea0eccbdd5238f015ee9d18408d438e0f8a/contract.d.ts index 0dc46a2b86df..68057e10f7d4 100644 --- a/packages/3-extensions/postgis/migrations/snapshots/2db551a8a5619d0d97710f29130eaea0eccbdd5238f015ee9d18408d438e0f8a/contract.d.ts +++ b/packages/3-extensions/postgis/migrations/snapshots/2db551a8a5619d0d97710f29130eaea0eccbdd5238f015ee9d18408d438e0f8a/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/packages/3-extensions/postgis/src/contract.d.ts b/packages/3-extensions/postgis/src/contract.d.ts index 0dc46a2b86df..68057e10f7d4 100644 --- a/packages/3-extensions/postgis/src/contract.d.ts +++ b/packages/3-extensions/postgis/src/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/collection-methods/generated/contract.d.ts b/packages/3-extensions/sql-orm-client/test/fixtures/collection-methods/generated/contract.d.ts index f183b7057b60..8c339719f244 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/collection-methods/generated/contract.d.ts +++ b/packages/3-extensions/sql-orm-client/test/fixtures/collection-methods/generated/contract.d.ts @@ -288,14 +288,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = { readonly public: { diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/fragment-namespace/generated/contract.d.ts b/packages/3-extensions/sql-orm-client/test/fixtures/fragment-namespace/generated/contract.d.ts index 60a7270d5074..069381911c50 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/fragment-namespace/generated/contract.d.ts +++ b/packages/3-extensions/sql-orm-client/test/fixtures/fragment-namespace/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/generated/contract.d.ts b/packages/3-extensions/sql-orm-client/test/fixtures/generated/contract.d.ts index 1b61993abed0..50fe2a67136c 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/generated/contract.d.ts +++ b/packages/3-extensions/sql-orm-client/test/fixtures/generated/contract.d.ts @@ -296,14 +296,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/junction-namespaces/generated/contract.d.ts b/packages/3-extensions/sql-orm-client/test/fixtures/junction-namespaces/generated/contract.d.ts index 6485c07833b6..39b31348475c 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/junction-namespaces/generated/contract.d.ts +++ b/packages/3-extensions/sql-orm-client/test/fixtures/junction-namespaces/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/on-update-only/generated/contract.d.ts b/packages/3-extensions/sql-orm-client/test/fixtures/on-update-only/generated/contract.d.ts index 3791d6557c4e..88b672847ec3 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/on-update-only/generated/contract.d.ts +++ b/packages/3-extensions/sql-orm-client/test/fixtures/on-update-only/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/polymorphism/generated/contract.d.ts b/packages/3-extensions/sql-orm-client/test/fixtures/polymorphism/generated/contract.d.ts index 6be107903d2f..3db1e40b9a97 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/polymorphism/generated/contract.d.ts +++ b/packages/3-extensions/sql-orm-client/test/fixtures/polymorphism/generated/contract.d.ts @@ -292,14 +292,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = { readonly public: { diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/scope-namespace/generated/contract.d.ts b/packages/3-extensions/sql-orm-client/test/fixtures/scope-namespace/generated/contract.d.ts index de27d01bcaf3..dd6df2b631f4 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/scope-namespace/generated/contract.d.ts +++ b/packages/3-extensions/sql-orm-client/test/fixtures/scope-namespace/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/packages/3-extensions/supabase/src/contract/contract.d.ts b/packages/3-extensions/supabase/src/contract/contract.d.ts index a19410d9bfcc..d16f5f50c32b 100644 --- a/packages/3-extensions/supabase/src/contract/contract.d.ts +++ b/packages/3-extensions/supabase/src/contract/contract.d.ts @@ -363,14 +363,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = { readonly auth: { diff --git a/packages/3-extensions/supabase/test/fixtures/example-app/contract.d.ts b/packages/3-extensions/supabase/test/fixtures/example-app/contract.d.ts index 22fdc47cb8c8..ea32c18c3fa3 100644 --- a/packages/3-extensions/supabase/test/fixtures/example-app/contract.d.ts +++ b/packages/3-extensions/supabase/test/fixtures/example-app/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/packages/3-extensions/supabase/test/fixtures/no-policy/contract.d.ts b/packages/3-extensions/supabase/test/fixtures/no-policy/contract.d.ts index 530bfe26b5d5..80933d36559c 100644 --- a/packages/3-extensions/supabase/test/fixtures/no-policy/contract.d.ts +++ b/packages/3-extensions/supabase/test/fixtures/no-policy/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/packages/3-extensions/supabase/test/fixtures/renamed-policy/contract.d.ts b/packages/3-extensions/supabase/test/fixtures/renamed-policy/contract.d.ts index 88bb0ebe53aa..fd032f6f475d 100644 --- a/packages/3-extensions/supabase/test/fixtures/renamed-policy/contract.d.ts +++ b/packages/3-extensions/supabase/test/fixtures/renamed-policy/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/packages/3-targets/mysql/9-facade/test/fixtures/generated/contract.d.ts b/packages/3-targets/mysql/9-facade/test/fixtures/generated/contract.d.ts index a004281d15e7..43faf6d19958 100644 --- a/packages/3-targets/mysql/9-facade/test/fixtures/generated/contract.d.ts +++ b/packages/3-targets/mysql/9-facade/test/fixtures/generated/contract.d.ts @@ -49,14 +49,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = { readonly __unbound__: { diff --git a/packages/3-targets/postgres/9-facade/test/fixtures/generated/contract.d.ts b/packages/3-targets/postgres/9-facade/test/fixtures/generated/contract.d.ts index 9e18c0f49137..d3d2ec5974d6 100644 --- a/packages/3-targets/postgres/9-facade/test/fixtures/generated/contract.d.ts +++ b/packages/3-targets/postgres/9-facade/test/fixtures/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/e2e/framework/test/fixtures/generated/contract.d.ts b/test/e2e/framework/test/fixtures/generated/contract.d.ts index de2badee313e..9bf452fc00a1 100644 --- a/test/e2e/framework/test/fixtures/generated/contract.d.ts +++ b/test/e2e/framework/test/fixtures/generated/contract.d.ts @@ -290,14 +290,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = { readonly public: { diff --git a/test/e2e/framework/test/sqlite/fixtures/generated/contract.d.ts b/test/e2e/framework/test/sqlite/fixtures/generated/contract.d.ts index cd1b049a0829..e53d7794df3f 100644 --- a/test/e2e/framework/test/sqlite/fixtures/generated/contract.d.ts +++ b/test/e2e/framework/test/sqlite/fixtures/generated/contract.d.ts @@ -139,14 +139,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/contract-parity/fixtures/postgres/generated/contract.d.ts b/test/integration/test/contract-parity/fixtures/postgres/generated/contract.d.ts index 99c0c9cfa7ae..fdd9f50e7a46 100644 --- a/test/integration/test/contract-parity/fixtures/postgres/generated/contract.d.ts +++ b/test/integration/test/contract-parity/fixtures/postgres/generated/contract.d.ts @@ -288,14 +288,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = { readonly public: { diff --git a/test/integration/test/contract-parity/fixtures/sqlite/generated/contract.d.ts b/test/integration/test/contract-parity/fixtures/sqlite/generated/contract.d.ts index 0c7bcb3b1ddc..421ef15e0fc2 100644 --- a/test/integration/test/contract-parity/fixtures/sqlite/generated/contract.d.ts +++ b/test/integration/test/contract-parity/fixtures/sqlite/generated/contract.d.ts @@ -140,14 +140,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/enum-members/_fixture-mongo/generated/contract.d.ts b/test/integration/test/enum-members/_fixture-mongo/generated/contract.d.ts index eedea43c6bff..52afad942da3 100644 --- a/test/integration/test/enum-members/_fixture-mongo/generated/contract.d.ts +++ b/test/integration/test/enum-members/_fixture-mongo/generated/contract.d.ts @@ -47,14 +47,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { diff --git a/test/integration/test/enum-members/_fixture-native-enums/generated/contract.d.ts b/test/integration/test/enum-members/_fixture-native-enums/generated/contract.d.ts index 687d13d61338..1c4e44101eae 100644 --- a/test/integration/test/enum-members/_fixture-native-enums/generated/contract.d.ts +++ b/test/integration/test/enum-members/_fixture-native-enums/generated/contract.d.ts @@ -284,14 +284,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = { readonly public: { diff --git a/test/integration/test/enum-members/_fixture-postgres/generated/contract.d.ts b/test/integration/test/enum-members/_fixture-postgres/generated/contract.d.ts index 5112cec5fe8c..370cca4ee37e 100644 --- a/test/integration/test/enum-members/_fixture-postgres/generated/contract.d.ts +++ b/test/integration/test/enum-members/_fixture-postgres/generated/contract.d.ts @@ -298,14 +298,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = { readonly public: { diff --git a/test/integration/test/enum-members/_fixture-sqlite/generated/contract.d.ts b/test/integration/test/enum-members/_fixture-sqlite/generated/contract.d.ts index 3ee5e7202850..b1d42da822db 100644 --- a/test/integration/test/enum-members/_fixture-sqlite/generated/contract.d.ts +++ b/test/integration/test/enum-members/_fixture-sqlite/generated/contract.d.ts @@ -148,14 +148,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = { readonly __unbound__: { diff --git a/test/integration/test/enum-order-by/_fixture/generated/contract.d.ts b/test/integration/test/enum-order-by/_fixture/generated/contract.d.ts index 862f77b98a6f..905728a9ca17 100644 --- a/test/integration/test/enum-order-by/_fixture/generated/contract.d.ts +++ b/test/integration/test/enum-order-by/_fixture/generated/contract.d.ts @@ -283,14 +283,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = { readonly public: { diff --git a/test/integration/test/fixtures/contract.d.ts b/test/integration/test/fixtures/contract.d.ts index 46ed60f2042d..846bce4b813d 100644 --- a/test/integration/test/fixtures/contract.d.ts +++ b/test/integration/test/fixtures/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/mongo/fixtures/generated/contract.d.ts b/test/integration/test/mongo/fixtures/generated/contract.d.ts index 52bfe5641ab6..6d549884744f 100644 --- a/test/integration/test/mongo/fixtures/generated/contract.d.ts +++ b/test/integration/test/mongo/fixtures/generated/contract.d.ts @@ -37,14 +37,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { diff --git a/test/integration/test/mongo/temporal-presets/_fixture-aliases/generated/contract.d.ts b/test/integration/test/mongo/temporal-presets/_fixture-aliases/generated/contract.d.ts index 4df7ae8700a5..04128f128dcc 100644 --- a/test/integration/test/mongo/temporal-presets/_fixture-aliases/generated/contract.d.ts +++ b/test/integration/test/mongo/temporal-presets/_fixture-aliases/generated/contract.d.ts @@ -38,14 +38,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { diff --git a/test/integration/test/mongo/temporal-presets/_fixture-attributes/generated/contract.d.ts b/test/integration/test/mongo/temporal-presets/_fixture-attributes/generated/contract.d.ts index 4df7ae8700a5..04128f128dcc 100644 --- a/test/integration/test/mongo/temporal-presets/_fixture-attributes/generated/contract.d.ts +++ b/test/integration/test/mongo/temporal-presets/_fixture-attributes/generated/contract.d.ts @@ -38,14 +38,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { diff --git a/test/integration/test/mongo/temporal-presets/_fixture/generated/contract.d.ts b/test/integration/test/mongo/temporal-presets/_fixture/generated/contract.d.ts index 4df7ae8700a5..04128f128dcc 100644 --- a/test/integration/test/mongo/temporal-presets/_fixture/generated/contract.d.ts +++ b/test/integration/test/mongo/temporal-presets/_fixture/generated/contract.d.ts @@ -38,14 +38,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { diff --git a/test/integration/test/namespaced-accessors/fixtures/generated/contract.d.ts b/test/integration/test/namespaced-accessors/fixtures/generated/contract.d.ts index ec0388b64a24..55ce6cf017a0 100644 --- a/test/integration/test/namespaced-accessors/fixtures/generated/contract.d.ts +++ b/test/integration/test/namespaced-accessors/fixtures/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/planner-golden/fixtures/postgres/generated/contract.d.ts b/test/integration/test/planner-golden/fixtures/postgres/generated/contract.d.ts index c15b2fd78abc..fd195f8a52e0 100644 --- a/test/integration/test/planner-golden/fixtures/postgres/generated/contract.d.ts +++ b/test/integration/test/planner-golden/fixtures/postgres/generated/contract.d.ts @@ -296,14 +296,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = { readonly public: { diff --git a/test/integration/test/planner-golden/fixtures/sqlite/generated/contract.d.ts b/test/integration/test/planner-golden/fixtures/sqlite/generated/contract.d.ts index 40b801a43976..829ebcd2280a 100644 --- a/test/integration/test/planner-golden/fixtures/sqlite/generated/contract.d.ts +++ b/test/integration/test/planner-golden/fixtures/sqlite/generated/contract.d.ts @@ -139,14 +139,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/aggregation/avg/_fixture/decimal/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/avg/_fixture/decimal/generated/contract.d.ts index 8994aaa6d12f..6b20ace0c417 100644 --- a/test/integration/test/ports/engines/queries/aggregation/avg/_fixture/decimal/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/avg/_fixture/decimal/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/aggregation/avg/_fixture/numeric/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/avg/_fixture/numeric/generated/contract.d.ts index 1562915a08ad..4c6e6d081893 100644 --- a/test/integration/test/ports/engines/queries/aggregation/avg/_fixture/numeric/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/avg/_fixture/numeric/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/aggregation/count/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/count/_fixture/generated/contract.d.ts index d779689893ba..8f90e7df3cb0 100644 --- a/test/integration/test/ports/engines/queries/aggregation/count/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/count/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/aggregation/group_by/_fixture/main/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/group_by/_fixture/main/generated/contract.d.ts index be9c1f80474b..f922f7a1db19 100644 --- a/test/integration/test/ports/engines/queries/aggregation/group_by/_fixture/main/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/group_by/_fixture/main/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/aggregation/group_by/_fixture/regression-21789/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/group_by/_fixture/regression-21789/generated/contract.d.ts index a37efcc18d2a..dca33a93c965 100644 --- a/test/integration/test/ports/engines/queries/aggregation/group_by/_fixture/regression-21789/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/group_by/_fixture/regression-21789/generated/contract.d.ts @@ -286,14 +286,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = { readonly public: { diff --git a/test/integration/test/ports/engines/queries/aggregation/group_by_having/_fixture/common/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/group_by_having/_fixture/common/generated/contract.d.ts index c9f0642514a5..937191f74c12 100644 --- a/test/integration/test/ports/engines/queries/aggregation/group_by_having/_fixture/common/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/group_by_having/_fixture/common/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/aggregation/group_by_having/_fixture/decimal/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/group_by_having/_fixture/decimal/generated/contract.d.ts index 8de9bb063666..7093b6778356 100644 --- a/test/integration/test/ports/engines/queries/aggregation/group_by_having/_fixture/decimal/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/group_by_having/_fixture/decimal/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/compound/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/compound/generated/contract.d.ts index 675e073dc12f..b3a08c639661 100644 --- a/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/compound/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/compound/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/generated/contract.d.ts index a1ee1bf922e3..417e03404514 100644 --- a/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/nested/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/nested/generated/contract.d.ts index 937b190d5723..11f08d7d623f 100644 --- a/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/nested/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/nested/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/self/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/self/generated/contract.d.ts index a4e2d68496c9..d8d6b448ae17 100644 --- a/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/self/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/self/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/aggregation/max/_fixture/common/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/max/_fixture/common/generated/contract.d.ts index bab5163282d3..9d483e62c072 100644 --- a/test/integration/test/ports/engines/queries/aggregation/max/_fixture/common/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/max/_fixture/common/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/aggregation/max/_fixture/decimal/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/max/_fixture/decimal/generated/contract.d.ts index 8994aaa6d12f..6b20ace0c417 100644 --- a/test/integration/test/ports/engines/queries/aggregation/max/_fixture/decimal/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/max/_fixture/decimal/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/aggregation/min/_fixture/common/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/min/_fixture/common/generated/contract.d.ts index bab5163282d3..9d483e62c072 100644 --- a/test/integration/test/ports/engines/queries/aggregation/min/_fixture/common/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/min/_fixture/common/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/aggregation/min/_fixture/decimal/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/min/_fixture/decimal/generated/contract.d.ts index 8994aaa6d12f..6b20ace0c417 100644 --- a/test/integration/test/ports/engines/queries/aggregation/min/_fixture/decimal/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/min/_fixture/decimal/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/aggregation/sum/_fixture/decimal/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/sum/_fixture/decimal/generated/contract.d.ts index 8994aaa6d12f..6b20ace0c417 100644 --- a/test/integration/test/ports/engines/queries/aggregation/sum/_fixture/decimal/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/sum/_fixture/decimal/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/aggregation/sum/_fixture/numeric/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/sum/_fixture/numeric/generated/contract.d.ts index 1562915a08ad..4c6e6d081893 100644 --- a/test/integration/test/ports/engines/queries/aggregation/sum/_fixture/numeric/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/sum/_fixture/numeric/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/aggregation/uniq-count-relation/_fixture/base/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/uniq-count-relation/_fixture/base/generated/contract.d.ts index a1ee1bf922e3..417e03404514 100644 --- a/test/integration/test/ports/engines/queries/aggregation/uniq-count-relation/_fixture/base/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/uniq-count-relation/_fixture/base/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/aggregation/uniq-count-relation/_fixture/nested/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/uniq-count-relation/_fixture/nested/generated/contract.d.ts index 937b190d5723..11f08d7d623f 100644 --- a/test/integration/test/ports/engines/queries/aggregation/uniq-count-relation/_fixture/nested/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/uniq-count-relation/_fixture/nested/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/data_types/bigint/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/bigint/_fixture/generated/contract.d.ts index d779689893ba..8f90e7df3cb0 100644 --- a/test/integration/test/ports/engines/queries/data_types/bigint/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/bigint/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/data_types/bool/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/bool/_fixture/generated/contract.d.ts index d779689893ba..8f90e7df3cb0 100644 --- a/test/integration/test/ports/engines/queries/data_types/bool/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/bool/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/relations/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/relations/generated/contract.d.ts index 7ac8f1197bb5..2e5f7692c7ea 100644 --- a/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/relations/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/relations/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/scalars/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/scalars/generated/contract.d.ts index d779689893ba..8f90e7df3cb0 100644 --- a/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/scalars/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/scalars/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/data_types/datetime/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/datetime/_fixture/generated/contract.d.ts index d779689893ba..8f90e7df3cb0 100644 --- a/test/integration/test/ports/engines/queries/data_types/datetime/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/datetime/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/data_types/decimal/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/decimal/_fixture/generated/contract.d.ts index 109914a4d8de..18310808d924 100644 --- a/test/integration/test/ports/engines/queries/data_types/decimal/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/decimal/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/data_types/enum_type/_fixture/postgres/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/enum_type/_fixture/postgres/generated/contract.d.ts index 925ae8883ced..a24b2df3e4fe 100644 --- a/test/integration/test/ports/engines/queries/data_types/enum_type/_fixture/postgres/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/enum_type/_fixture/postgres/generated/contract.d.ts @@ -283,14 +283,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = { readonly public: { diff --git a/test/integration/test/ports/engines/queries/data_types/float/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/float/_fixture/generated/contract.d.ts index d779689893ba..8f90e7df3cb0 100644 --- a/test/integration/test/ports/engines/queries/data_types/float/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/float/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/data_types/int/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/int/_fixture/generated/contract.d.ts index d779689893ba..8f90e7df3cb0 100644 --- a/test/integration/test/ports/engines/queries/data_types/int/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/int/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/data_types/json/_fixture/scalar/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/json/_fixture/scalar/generated/contract.d.ts index 9ca099e5864f..fbca3bfcc1c9 100644 --- a/test/integration/test/ports/engines/queries/data_types/json/_fixture/scalar/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/json/_fixture/scalar/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/data_types/native/postgres/_fixture/other/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/native/postgres/_fixture/other/generated/contract.d.ts index 12d0b6e738cb..153da5dab1ce 100644 --- a/test/integration/test/ports/engines/queries/data_types/native/postgres/_fixture/other/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/native/postgres/_fixture/other/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/data_types/native/postgres/_fixture/string/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/native/postgres/_fixture/string/generated/contract.d.ts index dc0231874407..082110cc196f 100644 --- a/test/integration/test/ports/engines/queries/data_types/native/postgres/_fixture/string/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/native/postgres/_fixture/string/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/data_types/string/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/string/_fixture/generated/contract.d.ts index d779689893ba..8f90e7df3cb0 100644 --- a/test/integration/test/ports/engines/queries/data_types/string/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/string/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/common/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/common/generated/contract.d.ts index 72333e7efd56..96dcb5f5a086 100644 --- a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/common/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/common/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/decimal/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/decimal/generated/contract.d.ts index 3697c8568a72..7214addb8271 100644 --- a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/decimal/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/decimal/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/enum/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/enum/generated/contract.d.ts index f640da6873c7..cbc8b3419fdc 100644 --- a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/enum/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/enum/generated/contract.d.ts @@ -286,14 +286,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = { readonly public: { diff --git a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/json/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/json/generated/contract.d.ts index 2d4b5cac847b..30499eaea859 100644 --- a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/json/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/json/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/lists/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/lists/generated/contract.d.ts index 79206733120d..102aef3ab889 100644 --- a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/lists/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/lists/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/distinct/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/distinct/_fixture/generated/contract.d.ts index 9d1d668e98b8..503ea7ec4a4f 100644 --- a/test/integration/test/ports/engines/queries/distinct/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/distinct/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/bigint_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/bigint_filter/_fixture/generated/contract.d.ts index d779689893ba..8f90e7df3cb0 100644 --- a/test/integration/test/ports/engines/queries/filters/bigint_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/bigint_filter/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/bytes_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/bytes_filter/_fixture/generated/contract.d.ts index d779689893ba..8f90e7df3cb0 100644 --- a/test/integration/test/ports/engines/queries/filters/bytes_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/bytes_filter/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/decimal_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/decimal_filter/_fixture/generated/contract.d.ts index a5a1d92119a8..5adfa088b9f2 100644 --- a/test/integration/test/ports/engines/queries/filters/decimal_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/decimal_filter/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/decimal-list/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/decimal-list/generated/contract.d.ts index 5f9ac42d0928..0d37452fb9bf 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/decimal-list/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/decimal-list/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/enum/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/enum/generated/contract.d.ts index 4cb49f6c35a1..ed4854cf0ff4 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/enum/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/enum/generated/contract.d.ts @@ -284,14 +284,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = { readonly public: { diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/list/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/list/generated/contract.d.ts index b8f0cf309f14..15f1fe4be568 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/list/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/list/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/mixed/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/mixed/generated/contract.d.ts index dc39509895e6..322aeabed645 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/mixed/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/mixed/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/bigint_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/bigint_filter/_fixture/generated/contract.d.ts index dd5c1a42fe22..1eb2b5c5e515 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/bigint_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/bigint_filter/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/bytes_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/bytes_filter/_fixture/generated/contract.d.ts index dd5c1a42fe22..1eb2b5c5e515 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/bytes_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/bytes_filter/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/datetime_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/datetime_filter/_fixture/generated/contract.d.ts index dd5c1a42fe22..1eb2b5c5e515 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/datetime_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/datetime_filter/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/decimal_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/decimal_filter/_fixture/generated/contract.d.ts index 5ea92658568f..53a402b5ce0b 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/decimal_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/decimal_filter/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/common/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/common/generated/contract.d.ts index dd5c1a42fe22..1eb2b5c5e515 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/common/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/common/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/default/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/default/generated/contract.d.ts index ad2e01408b1b..3f3a2213b8b9 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/default/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/default/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/list/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/list/generated/contract.d.ts index 76b81d9bf55d..5dbd0eb66396 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/list/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/list/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/float_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/float_filter/_fixture/generated/contract.d.ts index dd5c1a42fe22..1eb2b5c5e515 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/float_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/float_filter/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/having_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/having_filter/_fixture/generated/contract.d.ts index dd5c1a42fe22..1eb2b5c5e515 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/having_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/having_filter/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/int_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/int_filter/_fixture/generated/contract.d.ts index dd5c1a42fe22..1eb2b5c5e515 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/int_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/int_filter/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/json_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/json_filter/_fixture/generated/contract.d.ts index 8420e39fad18..4607d170c0ad 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/json_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/json_filter/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/json_filter/_fixture/list/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/json_filter/_fixture/list/generated/contract.d.ts index 2cd1436d45e3..7a972765f7be 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/json_filter/_fixture/list/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/json_filter/_fixture/list/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/complex/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/complex/generated/contract.d.ts index d7ba715441ff..cce398fb9fba 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/complex/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/complex/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-many/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-many/generated/contract.d.ts index dd6801d2d2bc..e37c9fbb378a 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-many/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-many/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-one-list/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-one-list/generated/contract.d.ts index 2e61795a0eb8..5c47b4393c39 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-one-list/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-one-list/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-one/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-one/generated/contract.d.ts index 2b7e08b037af..99030ca45f06 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-one/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-one/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/string_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/string_filter/_fixture/generated/contract.d.ts index dd5c1a42fe22..1eb2b5c5e515 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/string_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/string_filter/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/compound-one-to-many/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/compound-one-to-many/generated/contract.d.ts index 96a694af843c..305dd5942c57 100644 --- a/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/compound-one-to-many/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/compound-one-to-many/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/many-to-many/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/many-to-many/generated/contract.d.ts index c13aef3e9026..0b0640f89a27 100644 --- a/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/many-to-many/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/many-to-many/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/one-to-many/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/one-to-many/generated/contract.d.ts index 43ea84575c3a..eba601a3a17c 100644 --- a/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/one-to-many/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/one-to-many/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/filters/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/filters/_fixture/generated/contract.d.ts index 64dd88ee1aa1..2b78e4ac05cc 100644 --- a/test/integration/test/ports/engines/queries/filters/filters/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/filters/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/json/_fixture/optional/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/json/_fixture/optional/generated/contract.d.ts index 9ca099e5864f..fbca3bfcc1c9 100644 --- a/test/integration/test/ports/engines/queries/filters/json/_fixture/optional/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/json/_fixture/optional/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/base/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/base/generated/contract.d.ts index f819841e29e8..4b8da6ddee47 100644 --- a/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/base/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/base/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/decimal/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/decimal/generated/contract.d.ts index 224b3fdb9677..511041691c75 100644 --- a/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/decimal/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/decimal/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/json/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/json/generated/contract.d.ts index c727f333a97c..68c90049d21f 100644 --- a/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/json/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/json/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/25103/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/25103/generated/contract.d.ts index ff1048a05f13..ef0b1314379a 100644 --- a/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/25103/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/25103/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/l2-to-one/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/l2-to-one/generated/contract.d.ts index c04857c65509..40cf942b8a19 100644 --- a/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/l2-to-one/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/l2-to-one/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/one2one_regression/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/one2one_regression/_fixture/generated/contract.d.ts index 0f9122cb735a..97d20d08f85e 100644 --- a/test/integration/test/ports/engines/queries/filters/one2one_regression/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/one2one_regression/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/one_relation/_fixture/21356/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/one_relation/_fixture/21356/generated/contract.d.ts index ec5c81cfdf69..71515159faef 100644 --- a/test/integration/test/ports/engines/queries/filters/one_relation/_fixture/21356/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/one_relation/_fixture/21356/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/queries/filters/one_relation/_fixture/21366/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/one_relation/_fixture/21366/generated/contract.d.ts index 55a8c61e1681..2cb1ae63d4f5 100644 --- a/test/integration/test/ports/engines/queries/filters/one_relation/_fixture/21366/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/one_relation/_fixture/21366/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/engines/writes/top_level_mutations/create_many/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/writes/top_level_mutations/create_many/_fixture/generated/contract.d.ts index dd36eb73b576..9c0b9b04ce37 100644 --- a/test/integration/test/ports/engines/writes/top_level_mutations/create_many/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/writes/top_level_mutations/create_many/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/batching-bigint/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/batching-bigint/_fixture/generated/contract.d.ts index 770751b56107..6c080fae78df 100644 --- a/test/integration/test/ports/prisma/functional/batching-bigint/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/batching-bigint/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/batching-bytes/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/batching-bytes/_fixture/generated/contract.d.ts index cfb63964e6e8..7b81d864e5e6 100644 --- a/test/integration/test/ports/prisma/functional/batching-bytes/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/batching-bytes/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/blog-update/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/blog-update/_fixture/generated/contract.d.ts index a86dc39af2b7..982537476703 100644 --- a/test/integration/test/ports/prisma/functional/blog-update/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/blog-update/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/bytes-upsert/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/bytes-upsert/_fixture/generated/contract.d.ts index 5fef861be65c..e5e3fd0ecbfd 100644 --- a/test/integration/test/ports/prisma/functional/bytes-upsert/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/bytes-upsert/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/chunking-query/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/chunking-query/_fixture/generated/contract.d.ts index 52358c0088cc..e822076d1319 100644 --- a/test/integration/test/ports/prisma/functional/chunking-query/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/chunking-query/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/create-default-date/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/create-default-date/_fixture/generated/contract.d.ts index eed03568903d..2265d3ebf58d 100644 --- a/test/integration/test/ports/prisma/functional/create-default-date/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/create-default-date/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/decimal-list/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/decimal-list/_fixture/generated/contract.d.ts index 3ebef0a4e66b..6a20ed32de99 100644 --- a/test/integration/test/ports/prisma/functional/decimal-list/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/decimal-list/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/decimal-precision/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/decimal-precision/_fixture/generated/contract.d.ts index 1ffebf031bfb..bb42da7fa456 100644 --- a/test/integration/test/ports/prisma/functional/decimal-precision/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/decimal-precision/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/decimal-scalar/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/decimal-scalar/_fixture/generated/contract.d.ts index e53cf605cc1f..d32d517e8768 100644 --- a/test/integration/test/ports/prisma/functional/decimal-scalar/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/decimal-scalar/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/default-selection/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/default-selection/_fixture/generated/contract.d.ts index 7010402d918d..fa25f00657f2 100644 --- a/test/integration/test/ports/prisma/functional/default-selection/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/default-selection/_fixture/generated/contract.d.ts @@ -284,14 +284,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = { readonly public: { diff --git a/test/integration/test/ports/prisma/functional/distinct/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/distinct/_fixture/generated/contract.d.ts index 3728b907f88d..8afcb2ad48ee 100644 --- a/test/integration/test/ports/prisma/functional/distinct/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/distinct/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/driver-adapters-error-forwarding/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/driver-adapters-error-forwarding/_fixture/generated/contract.d.ts index 91e1a7c96915..ecfa65dab8bc 100644 --- a/test/integration/test/ports/prisma/functional/driver-adapters-error-forwarding/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/driver-adapters-error-forwarding/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/driver-adapters-team-orm-687-bytes/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/driver-adapters-team-orm-687-bytes/_fixture/generated/contract.d.ts index 98e89753c3cb..f0a02be8fc1f 100644 --- a/test/integration/test/ports/prisma/functional/driver-adapters-team-orm-687-bytes/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/driver-adapters-team-orm-687-bytes/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/driver-adapters-validate-active-provider/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/driver-adapters-validate-active-provider/_fixture/generated/contract.d.ts index cf183a819998..75e1db43539f 100644 --- a/test/integration/test/ports/prisma/functional/driver-adapters-validate-active-provider/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/driver-adapters-validate-active-provider/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/driver-adapters-validate-active-provider/_fixture/sqlite/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/driver-adapters-validate-active-provider/_fixture/sqlite/generated/contract.d.ts index 029b1270e896..c21215dfb70f 100644 --- a/test/integration/test/ports/prisma/functional/driver-adapters-validate-active-provider/_fixture/sqlite/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/driver-adapters-validate-active-provider/_fixture/sqlite/generated/contract.d.ts @@ -139,14 +139,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/enum-array/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/enum-array/_fixture/generated/contract.d.ts index b4278316118c..9516a2d0306b 100644 --- a/test/integration/test/ports/prisma/functional/enum-array/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/enum-array/_fixture/generated/contract.d.ts @@ -287,14 +287,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = { readonly public: { diff --git a/test/integration/test/ports/prisma/functional/enums/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/enums/_fixture/generated/contract.d.ts index 9cfb6d68e177..0fb352c5df0c 100644 --- a/test/integration/test/ports/prisma/functional/enums/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/enums/_fixture/generated/contract.d.ts @@ -48,14 +48,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { diff --git a/test/integration/test/ports/prisma/functional/extended-where/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/extended-where/_fixture/generated/contract.d.ts index 504f5c359730..ef496fd45411 100644 --- a/test/integration/test/ports/prisma/functional/extended-where/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/extended-where/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/filter-count-relations/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/filter-count-relations/_fixture/generated/contract.d.ts index 63760b9b60a8..4a1948bb32ba 100644 --- a/test/integration/test/ports/prisma/functional/filter-count-relations/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/filter-count-relations/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/find-unique-or-throw-batching/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/find-unique-or-throw-batching/_fixture/generated/contract.d.ts index 22a069102323..95d3148cf795 100644 --- a/test/integration/test/ports/prisma/functional/find-unique-or-throw-batching/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/find-unique-or-throw-batching/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/handle-int-overflow/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/handle-int-overflow/_fixture/generated/contract.d.ts index b07f703aff98..17a78d07bd57 100644 --- a/test/integration/test/ports/prisma/functional/handle-int-overflow/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/handle-int-overflow/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/interactive-transactions/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/interactive-transactions/_fixture/generated/contract.d.ts index e7372428eb79..720c2be3278f 100644 --- a/test/integration/test/ports/prisma/functional/interactive-transactions/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/interactive-transactions/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/issues-11974/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-11974/_fixture/generated/contract.d.ts index 03943b16b968..74be01ae70a6 100644 --- a/test/integration/test/ports/prisma/functional/issues-11974/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-11974/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/issues-12378/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-12378/_fixture/generated/contract.d.ts index 649bde986b3a..07bc928c3055 100644 --- a/test/integration/test/ports/prisma/functional/issues-12378/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-12378/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/issues-12557/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-12557/_fixture/generated/contract.d.ts index f5c23bcff54d..fe37da612845 100644 --- a/test/integration/test/ports/prisma/functional/issues-12557/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-12557/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/issues-12572/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-12572/_fixture/generated/contract.d.ts index fe025e3580e6..afbe55c25158 100644 --- a/test/integration/test/ports/prisma/functional/issues-12572/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-12572/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/issues-13089-dollar-in-search/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-13089-dollar-in-search/_fixture/generated/contract.d.ts index 6c8cf008e42f..dfaf5075d7d1 100644 --- a/test/integration/test/ports/prisma/functional/issues-13089-dollar-in-search/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-13089-dollar-in-search/_fixture/generated/contract.d.ts @@ -37,14 +37,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { diff --git a/test/integration/test/ports/prisma/functional/issues-14271/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-14271/_fixture/generated/contract.d.ts index 3a99aac578ef..8539016476da 100644 --- a/test/integration/test/ports/prisma/functional/issues-14271/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-14271/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/issues-14954-date-batch/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-14954-date-batch/_fixture/generated/contract.d.ts index ac1e295b99ed..8845559baa89 100644 --- a/test/integration/test/ports/prisma/functional/issues-14954-date-batch/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-14954-date-batch/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/issues-15044/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-15044/_fixture/generated/contract.d.ts index e4190b54db32..2c31e881d648 100644 --- a/test/integration/test/ports/prisma/functional/issues-15044/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-15044/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/issues-16535-select-enum/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-16535-select-enum/_fixture/generated/contract.d.ts index c4b18fdfa269..7e459822c74a 100644 --- a/test/integration/test/ports/prisma/functional/issues-16535-select-enum/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-16535-select-enum/_fixture/generated/contract.d.ts @@ -284,14 +284,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = { readonly public: { diff --git a/test/integration/test/ports/prisma/functional/issues-17005-args-type-conflict/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-17005-args-type-conflict/_fixture/generated/contract.d.ts index 94f0c67f2d55..2f5a0c0d7fc4 100644 --- a/test/integration/test/ports/prisma/functional/issues-17005-args-type-conflict/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-17005-args-type-conflict/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/issues-17030-args-type-conflict/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-17030-args-type-conflict/_fixture/generated/contract.d.ts index bd5515fc7d82..55b2f5a83619 100644 --- a/test/integration/test/ports/prisma/functional/issues-17030-args-type-conflict/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-17030-args-type-conflict/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/issues-18970-invalid-date/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-18970-invalid-date/_fixture/generated/contract.d.ts index 4131acc89359..70d1617d59c7 100644 --- a/test/integration/test/ports/prisma/functional/issues-18970-invalid-date/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-18970-invalid-date/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/issues-20261-group-by-shortcut/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-20261-group-by-shortcut/_fixture/generated/contract.d.ts index c25a3f997fae..ca980aeea0ba 100644 --- a/test/integration/test/ports/prisma/functional/issues-20261-group-by-shortcut/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-20261-group-by-shortcut/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/issues-21352-id-does-not-exist/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-21352-id-does-not-exist/_fixture/generated/contract.d.ts index aa28e7bb7cae..27b5c35ccb66 100644 --- a/test/integration/test/ports/prisma/functional/issues-21352-id-does-not-exist/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-21352-id-does-not-exist/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/issues-21454-type-in-json/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-21454-type-in-json/_fixture/generated/contract.d.ts index 560f98a11db7..7bc058bd1593 100644 --- a/test/integration/test/ports/prisma/functional/issues-21454-type-in-json/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-21454-type-in-json/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/issues-21631-batching-in-transaction/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-21631-batching-in-transaction/_fixture/generated/contract.d.ts index fc513e332c0f..0b691f16f579 100644 --- a/test/integration/test/ports/prisma/functional/issues-21631-batching-in-transaction/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-21631-batching-in-transaction/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/issues-22098-column-does-not-exist/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-22098-column-does-not-exist/_fixture/generated/contract.d.ts index 0a822823c209..a0cb905c789a 100644 --- a/test/integration/test/ports/prisma/functional/issues-22098-column-does-not-exist/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-22098-column-does-not-exist/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/issues-22610-parallel-batch/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-22610-parallel-batch/_fixture/generated/contract.d.ts index 0e0060d58e2f..5bc54133d706 100644 --- a/test/integration/test/ports/prisma/functional/issues-22610-parallel-batch/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-22610-parallel-batch/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/issues-23201-non-ascii-comments/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-23201-non-ascii-comments/_fixture/generated/contract.d.ts index f7393ae7b813..0e9cf049a5fc 100644 --- a/test/integration/test/ports/prisma/functional/issues-23201-non-ascii-comments/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-23201-non-ascii-comments/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/issues-23902/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-23902/_fixture/generated/contract.d.ts index 8fa105a7a397..20318795d1e7 100644 --- a/test/integration/test/ports/prisma/functional/issues-23902/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-23902/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/issues-25404/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-25404/_fixture/generated/contract.d.ts index 239455d1bf48..90667d72ea16 100644 --- a/test/integration/test/ports/prisma/functional/issues-25404/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-25404/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/issues-27455-bytes-id/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-27455-bytes-id/_fixture/generated/contract.d.ts index cde28ce39cb7..937598b48f8c 100644 --- a/test/integration/test/ports/prisma/functional/issues-27455-bytes-id/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-27455-bytes-id/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/issues-27511-include-enum-array/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-27511-include-enum-array/_fixture/generated/contract.d.ts index 8875559edae1..820299233a76 100644 --- a/test/integration/test/ports/prisma/functional/issues-27511-include-enum-array/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-27511-include-enum-array/_fixture/generated/contract.d.ts @@ -286,14 +286,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = { readonly public: { diff --git a/test/integration/test/ports/prisma/functional/issues-28151-broken-nested-set/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-28151-broken-nested-set/_fixture/generated/contract.d.ts index c06d764c0b7a..84f54fcb025a 100644 --- a/test/integration/test/ports/prisma/functional/issues-28151-broken-nested-set/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-28151-broken-nested-set/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/issues-28192-pg-historical-dates/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-28192-pg-historical-dates/_fixture/generated/contract.d.ts index 26d713f0fe13..f521429a0070 100644 --- a/test/integration/test/ports/prisma/functional/issues-28192-pg-historical-dates/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-28192-pg-historical-dates/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/issues-28591-mapped-enums/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-28591-mapped-enums/_fixture/generated/contract.d.ts index 173ae2745864..a9f900d2b066 100644 --- a/test/integration/test/ports/prisma/functional/issues-28591-mapped-enums/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-28591-mapped-enums/_fixture/generated/contract.d.ts @@ -286,14 +286,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = { readonly public: { diff --git a/test/integration/test/ports/prisma/functional/issues-29010-bigint-precision-relation-joins/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-29010-bigint-precision-relation-joins/_fixture/generated/contract.d.ts index de1fe7bcf88e..cdfa38f469cf 100644 --- a/test/integration/test/ports/prisma/functional/issues-29010-bigint-precision-relation-joins/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-29010-bigint-precision-relation-joins/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/issues-29174-jsonb-parameter-regression/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-29174-jsonb-parameter-regression/_fixture/generated/contract.d.ts index 0dabff7ed5e1..0870f4af88e7 100644 --- a/test/integration/test/ports/prisma/functional/issues-29174-jsonb-parameter-regression/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-29174-jsonb-parameter-regression/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/issues-29176-cursor-parameter-regression/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-29176-cursor-parameter-regression/_fixture/generated/contract.d.ts index 8de0769ddc0c..1e9e62d97d49 100644 --- a/test/integration/test/ports/prisma/functional/issues-29176-cursor-parameter-regression/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-29176-cursor-parameter-regression/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/issues-29254-query-plan-cache-mutation/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-29254-query-plan-cache-mutation/_fixture/generated/contract.d.ts index 14a17a94691d..df27999c60d6 100644 --- a/test/integration/test/ports/prisma/functional/issues-29254-query-plan-cache-mutation/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-29254-query-plan-cache-mutation/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/issues-29267-uint8array-in-json/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-29267-uint8array-in-json/_fixture/generated/contract.d.ts index 23721cfbad6f..54971eeae5b0 100644 --- a/test/integration/test/ports/prisma/functional/issues-29267-uint8array-in-json/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-29267-uint8array-in-json/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/issues-29309-datetime-cursor/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-29309-datetime-cursor/_fixture/generated/contract.d.ts index 5591c4ea9896..44a2e45cf902 100644 --- a/test/integration/test/ports/prisma/functional/issues-29309-datetime-cursor/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-29309-datetime-cursor/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/issues-29331-query-plan-cache-bloat/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-29331-query-plan-cache-bloat/_fixture/generated/contract.d.ts index bc800c3225b1..28f7381bafb1 100644 --- a/test/integration/test/ports/prisma/functional/issues-29331-query-plan-cache-bloat/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-29331-query-plan-cache-bloat/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/issues-4004/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-4004/_fixture/generated/contract.d.ts index 3d8cd4540408..d85ccfc818ac 100644 --- a/test/integration/test/ports/prisma/functional/issues-4004/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-4004/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/issues-5952-decimal-batch/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-5952-decimal-batch/_fixture/generated/contract.d.ts index e21a42011513..da599488b40b 100644 --- a/test/integration/test/ports/prisma/functional/issues-5952-decimal-batch/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-5952-decimal-batch/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/issues-TML-1664-invalid-enum-value-error/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-TML-1664-invalid-enum-value-error/_fixture/generated/contract.d.ts index 8983b5a817c9..f5d8210c9ffe 100644 --- a/test/integration/test/ports/prisma/functional/issues-TML-1664-invalid-enum-value-error/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-TML-1664-invalid-enum-value-error/_fixture/generated/contract.d.ts @@ -286,14 +286,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = { readonly public: { diff --git a/test/integration/test/ports/prisma/functional/issues-TML-1664-unknown-enum-value-read-error/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-TML-1664-unknown-enum-value-read-error/_fixture/generated/contract.d.ts index a3eec68ef0dd..58282cf726ec 100644 --- a/test/integration/test/ports/prisma/functional/issues-TML-1664-unknown-enum-value-read-error/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-TML-1664-unknown-enum-value-read-error/_fixture/generated/contract.d.ts @@ -286,14 +286,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = { readonly public: { diff --git a/test/integration/test/ports/prisma/functional/issues-unmapped-driver-error-user-facing/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-unmapped-driver-error-user-facing/_fixture/generated/contract.d.ts index fae030a47022..5baeea5fa52d 100644 --- a/test/integration/test/ports/prisma/functional/issues-unmapped-driver-error-user-facing/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-unmapped-driver-error-user-facing/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/json-fields/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/json-fields/_fixture/generated/contract.d.ts index a309f5c28119..33490ab2f21e 100644 --- a/test/integration/test/ports/prisma/functional/json-fields/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/json-fields/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/large-floats/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/large-floats/_fixture/generated/contract.d.ts index 01ad29d7a5ca..81c5f3c3354f 100644 --- a/test/integration/test/ports/prisma/functional/large-floats/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/large-floats/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/legacy-aggregate-raw/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/legacy-aggregate-raw/_fixture/generated/contract.d.ts index 7f722c6e37f2..1498ba01dc58 100644 --- a/test/integration/test/ports/prisma/functional/legacy-aggregate-raw/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/legacy-aggregate-raw/_fixture/generated/contract.d.ts @@ -37,14 +37,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { diff --git a/test/integration/test/ports/prisma/functional/legacy-aggregations/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/legacy-aggregations/_fixture/generated/contract.d.ts index f43cf318729a..0cd272653b5d 100644 --- a/test/integration/test/ports/prisma/functional/legacy-aggregations/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/legacy-aggregations/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/legacy-json/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/legacy-json/_fixture/generated/contract.d.ts index 4cef25973193..f30f6a82b76f 100644 --- a/test/integration/test/ports/prisma/functional/legacy-json/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/legacy-json/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/legacy-malformed-id/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/legacy-malformed-id/_fixture/generated/contract.d.ts index 8382a0d2da93..d50d2699eee2 100644 --- a/test/integration/test/ports/prisma/functional/legacy-malformed-id/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/legacy-malformed-id/_fixture/generated/contract.d.ts @@ -37,14 +37,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; export namespace Models { diff --git a/test/integration/test/ports/prisma/functional/legacy-optional-relation-filters/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/legacy-optional-relation-filters/_fixture/generated/contract.d.ts index 083c5844f500..9813a41fd896 100644 --- a/test/integration/test/ports/prisma/functional/legacy-optional-relation-filters/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/legacy-optional-relation-filters/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/methods-count/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/methods-count/_fixture/generated/contract.d.ts index eea602596a1b..403b3840e1d0 100644 --- a/test/integration/test/ports/prisma/functional/methods-count/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/methods-count/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/methods-createMany/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/methods-createMany/_fixture/generated/contract.d.ts index 85828804fea4..e5a471e0f29d 100644 --- a/test/integration/test/ports/prisma/functional/methods-createMany/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/methods-createMany/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/methods-createManyAndReturn/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/methods-createManyAndReturn/_fixture/generated/contract.d.ts index 85828804fea4..e5a471e0f29d 100644 --- a/test/integration/test/ports/prisma/functional/methods-createManyAndReturn/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/methods-createManyAndReturn/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/methods-findFirstOrThrow/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/methods-findFirstOrThrow/_fixture/generated/contract.d.ts index 946ba7d0fc1a..76ca552d0b50 100644 --- a/test/integration/test/ports/prisma/functional/methods-findFirstOrThrow/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/methods-findFirstOrThrow/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/methods-findUniqueOrThrow/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/methods-findUniqueOrThrow/_fixture/generated/contract.d.ts index 9b8f16a9bf61..cd1b60707827 100644 --- a/test/integration/test/ports/prisma/functional/methods-findUniqueOrThrow/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/methods-findUniqueOrThrow/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/methods-updateManyAndReturn/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/methods-updateManyAndReturn/_fixture/generated/contract.d.ts index 85828804fea4..e5a471e0f29d 100644 --- a/test/integration/test/ports/prisma/functional/methods-updateManyAndReturn/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/methods-updateManyAndReturn/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/methods-upsert-native-atomic/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/methods-upsert-native-atomic/_fixture/generated/contract.d.ts index 0b95efc5d619..d042837663c2 100644 --- a/test/integration/test/ports/prisma/functional/methods-upsert-native-atomic/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/methods-upsert-native-atomic/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/methods-upsert-simple/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/methods-upsert-simple/_fixture/generated/contract.d.ts index a461637b958a..fa8995e6bcb4 100644 --- a/test/integration/test/ports/prisma/functional/methods-upsert-simple/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/methods-upsert-simple/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/mixed-string-uuid-datetime-list-inputs/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/mixed-string-uuid-datetime-list-inputs/_fixture/generated/contract.d.ts index 7995509d1b69..a09f057c2fb3 100644 --- a/test/integration/test/ports/prisma/functional/mixed-string-uuid-datetime-list-inputs/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/mixed-string-uuid-datetime-list-inputs/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/multi-schema/_fixture/different-names/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/multi-schema/_fixture/different-names/generated/contract.d.ts index 529ca795706f..dcc4b63c19cb 100644 --- a/test/integration/test/ports/prisma/functional/multi-schema/_fixture/different-names/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/multi-schema/_fixture/different-names/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/multi-schema/_fixture/identical-names/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/multi-schema/_fixture/identical-names/generated/contract.d.ts index b00907948a8a..b98e62c9f039 100644 --- a/test/integration/test/ports/prisma/functional/multi-schema/_fixture/identical-names/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/multi-schema/_fixture/identical-names/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/multi-schema/_fixture/no-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/multi-schema/_fixture/no-map/generated/contract.d.ts index 737a77ffb053..7a2b3d70d9d9 100644 --- a/test/integration/test/ports/prisma/functional/multi-schema/_fixture/no-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/multi-schema/_fixture/no-map/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/multiple-types/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/multiple-types/_fixture/generated/contract.d.ts index 47b5ad05b58b..ad40f103088d 100644 --- a/test/integration/test/ports/prisma/functional/multiple-types/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/multiple-types/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/optimistic-concurrency-control/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/optimistic-concurrency-control/_fixture/generated/contract.d.ts index 81ce1b51085d..c5f89310cec3 100644 --- a/test/integration/test/ports/prisma/functional/optimistic-concurrency-control/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/optimistic-concurrency-control/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/referential-actions-set-default-1to1/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/referential-actions-set-default-1to1/_fixture/generated/contract.d.ts index c4d505e3aa54..d967f8974df4 100644 --- a/test/integration/test/ports/prisma/functional/referential-actions-set-default-1to1/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/referential-actions-set-default-1to1/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/referential-actions-set-default-1ton/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/referential-actions-set-default-1ton/_fixture/generated/contract.d.ts index c4d505e3aa54..d967f8974df4 100644 --- a/test/integration/test/ports/prisma/functional/referential-actions-set-default-1ton/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/referential-actions-set-default-1ton/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/relation-mode-17255-mixed-actions/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-17255-mixed-actions/_fixture/generated/contract.d.ts index bfe81669f389..6810e7923b31 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-17255-mixed-actions/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-17255-mixed-actions/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/relation-mode-17255-same-actions/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-17255-same-actions/_fixture/generated/contract.d.ts index 96db3950538d..93c637c8a844 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-17255-same-actions/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-17255-same-actions/_fixture/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/cascade-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/cascade-map/generated/contract.d.ts index a7b938547524..a2155116dd18 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/cascade-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/cascade-map/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/cascade-plain/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/cascade-plain/generated/contract.d.ts index 33926a34618f..ea8bcb3c109c 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/cascade-plain/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/cascade-plain/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/noaction-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/noaction-map/generated/contract.d.ts index 4d01a2d0ac40..c0d9f814e49f 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/noaction-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/noaction-map/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/noaction-plain/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/noaction-plain/generated/contract.d.ts index 8078214bd704..655f1ac9d82c 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/noaction-plain/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/noaction-plain/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/restrict-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/restrict-map/generated/contract.d.ts index d603788e5b12..4c1251985e74 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/restrict-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/restrict-map/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/restrict-plain/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/restrict-plain/generated/contract.d.ts index 1e004b2a8236..e22b75945a1d 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/restrict-plain/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/restrict-plain/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/setnull-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/setnull-map/generated/contract.d.ts index 8eb10332aa70..b81b7e7cc071 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/setnull-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/setnull-map/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/setnull-plain/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/setnull-plain/generated/contract.d.ts index 3f66ec45d68e..77e60516fe0b 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/setnull-plain/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/setnull-plain/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/cascade-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/cascade-map/generated/contract.d.ts index 56486066af92..ee21399befbf 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/cascade-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/cascade-map/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/cascade-plain/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/cascade-plain/generated/contract.d.ts index 0b4c42e4018d..a4cb742c6623 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/cascade-plain/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/cascade-plain/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/noaction-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/noaction-map/generated/contract.d.ts index f140de78a362..711d77dfe6b6 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/noaction-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/noaction-map/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/noaction-plain/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/noaction-plain/generated/contract.d.ts index 90f67d019724..ab2f6eae2bec 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/noaction-plain/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/noaction-plain/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/restrict-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/restrict-map/generated/contract.d.ts index c28d4a0d01f3..8262df7b254f 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/restrict-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/restrict-map/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/restrict-plain/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/restrict-plain/generated/contract.d.ts index 158caecfe984..e3c2e5e85f03 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/restrict-plain/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/restrict-plain/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/setnull-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/setnull-map/generated/contract.d.ts index 02cb33812498..287b22f3be7b 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/setnull-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/setnull-map/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/setnull-plain/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/setnull-plain/generated/contract.d.ts index bb71aca35f2e..5a88552ed63a 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/setnull-plain/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/setnull-plain/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/cascade-nomap/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/cascade-nomap/generated/contract.d.ts index 07c364a8829d..af6de3acc92d 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/cascade-nomap/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/cascade-nomap/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/default-nomap/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/default-nomap/generated/contract.d.ts index 0995910a7079..4f46ac1db62d 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/default-nomap/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/default-nomap/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/noaction-nomap/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/noaction-nomap/generated/contract.d.ts index 0995910a7079..4f46ac1db62d 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/noaction-nomap/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/noaction-nomap/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/restrict-nomap/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/restrict-nomap/generated/contract.d.ts index a996181bdf4a..e4b52d02f3bb 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/restrict-nomap/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/restrict-nomap/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/sql-builder/fixtures/generated-no-pgvector/contract.d.ts b/test/integration/test/sql-builder/fixtures/generated-no-pgvector/contract.d.ts index 9e18c0f49137..d3d2ec5974d6 100644 --- a/test/integration/test/sql-builder/fixtures/generated-no-pgvector/contract.d.ts +++ b/test/integration/test/sql-builder/fixtures/generated-no-pgvector/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/sql-builder/fixtures/generated/contract.d.ts b/test/integration/test/sql-builder/fixtures/generated/contract.d.ts index 4a0e10055039..de353848fdc8 100644 --- a/test/integration/test/sql-builder/fixtures/generated/contract.d.ts +++ b/test/integration/test/sql-builder/fixtures/generated/contract.d.ts @@ -283,14 +283,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/sql-orm-client/fixtures/collection-methods/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/collection-methods/generated/contract.d.ts index f183b7057b60..8c339719f244 100644 --- a/test/integration/test/sql-orm-client/fixtures/collection-methods/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/collection-methods/generated/contract.d.ts @@ -288,14 +288,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = { readonly public: { diff --git a/test/integration/test/sql-orm-client/fixtures/execution-defaulted-tags/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/execution-defaulted-tags/generated/contract.d.ts index e72833127757..cc2aaf11a359 100644 --- a/test/integration/test/sql-orm-client/fixtures/execution-defaulted-tags/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/execution-defaulted-tags/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/sql-orm-client/fixtures/fragment-namespace/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/fragment-namespace/generated/contract.d.ts index 60a7270d5074..069381911c50 100644 --- a/test/integration/test/sql-orm-client/fixtures/fragment-namespace/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/fragment-namespace/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/sql-orm-client/fixtures/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/generated/contract.d.ts index c62eb322e559..9bfdbf381d6b 100644 --- a/test/integration/test/sql-orm-client/fixtures/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/generated/contract.d.ts @@ -283,14 +283,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/sql-orm-client/fixtures/integer-representation-sqlite/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/integer-representation-sqlite/generated/contract.d.ts index cd76cf0f6c18..ea074a87babd 100644 --- a/test/integration/test/sql-orm-client/fixtures/integer-representation-sqlite/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/integer-representation-sqlite/generated/contract.d.ts @@ -139,14 +139,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/sql-orm-client/fixtures/integer-representation/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/integer-representation/generated/contract.d.ts index 779d26e9b511..714f74a2d5ab 100644 --- a/test/integration/test/sql-orm-client/fixtures/integer-representation/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/integer-representation/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/sql-orm-client/fixtures/junction-namespaces/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/junction-namespaces/generated/contract.d.ts index 6485c07833b6..39b31348475c 100644 --- a/test/integration/test/sql-orm-client/fixtures/junction-namespaces/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/junction-namespaces/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/sql-orm-client/fixtures/mn-psl/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/mn-psl/generated/contract.d.ts index 9dbd281ece6b..f9c5eac71171 100644 --- a/test/integration/test/sql-orm-client/fixtures/mn-psl/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/mn-psl/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/sql-orm-client/fixtures/non-identifier-names/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/non-identifier-names/generated/contract.d.ts index 135f0746170c..53265ec213d5 100644 --- a/test/integration/test/sql-orm-client/fixtures/non-identifier-names/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/non-identifier-names/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/sql-orm-client/fixtures/on-update-only/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/on-update-only/generated/contract.d.ts index 3791d6557c4e..88b672847ec3 100644 --- a/test/integration/test/sql-orm-client/fixtures/on-update-only/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/on-update-only/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/sql-orm-client/fixtures/polymorphism-namespaces/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/polymorphism-namespaces/generated/contract.d.ts index b3950802dae0..ef7760ab7389 100644 --- a/test/integration/test/sql-orm-client/fixtures/polymorphism-namespaces/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/polymorphism-namespaces/generated/contract.d.ts @@ -296,14 +296,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = { readonly a: { diff --git a/test/integration/test/sql-orm-client/fixtures/polymorphism-self-relation/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/polymorphism-self-relation/generated/contract.d.ts index e8ff586c232f..b0bed32a24b2 100644 --- a/test/integration/test/sql-orm-client/fixtures/polymorphism-self-relation/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/polymorphism-self-relation/generated/contract.d.ts @@ -283,14 +283,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = { readonly public: { diff --git a/test/integration/test/sql-orm-client/fixtures/polymorphism/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/polymorphism/generated/contract.d.ts index 6be107903d2f..3db1e40b9a97 100644 --- a/test/integration/test/sql-orm-client/fixtures/polymorphism/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/polymorphism/generated/contract.d.ts @@ -292,14 +292,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = { readonly public: { diff --git a/test/integration/test/sql-orm-client/fixtures/scalar-lists/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/scalar-lists/generated/contract.d.ts index 2a90f5ff561b..05bc1ad5f7b4 100644 --- a/test/integration/test/sql-orm-client/fixtures/scalar-lists/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/scalar-lists/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/sql-orm-client/fixtures/scope-namespace/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/scope-namespace/generated/contract.d.ts index de27d01bcaf3..dd6df2b631f4 100644 --- a/test/integration/test/sql-orm-client/fixtures/scope-namespace/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/scope-namespace/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/sql-orm-client/fixtures/self-relations/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/self-relations/generated/contract.d.ts index 62ece5edd6b4..86c1f2b4cb17 100644 --- a/test/integration/test/sql-orm-client/fixtures/self-relations/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/self-relations/generated/contract.d.ts @@ -275,14 +275,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/temporal-defaults/_fixture-aliases/generated/contract.d.ts b/test/integration/test/temporal-defaults/_fixture-aliases/generated/contract.d.ts index bfdb469b98f4..49794a4a1845 100644 --- a/test/integration/test/temporal-defaults/_fixture-aliases/generated/contract.d.ts +++ b/test/integration/test/temporal-defaults/_fixture-aliases/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.d.ts b/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.d.ts index f4415402082c..45dc1e666356 100644 --- a/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.d.ts +++ b/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/temporal-defaults/_fixture-now/generated/contract.d.ts b/test/integration/test/temporal-defaults/_fixture-now/generated/contract.d.ts index f4415402082c..45dc1e666356 100644 --- a/test/integration/test/temporal-defaults/_fixture-now/generated/contract.d.ts +++ b/test/integration/test/temporal-defaults/_fixture-now/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/temporal-defaults/_fixture-timestamp/generated/contract.d.ts b/test/integration/test/temporal-defaults/_fixture-timestamp/generated/contract.d.ts index 4e9edd8725d0..bca36943d459 100644 --- a/test/integration/test/temporal-defaults/_fixture-timestamp/generated/contract.d.ts +++ b/test/integration/test/temporal-defaults/_fixture-timestamp/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< diff --git a/test/integration/test/temporal-defaults/_fixture/generated/contract.d.ts b/test/integration/test/temporal-defaults/_fixture/generated/contract.d.ts index bfdb469b98f4..49794a4a1845 100644 --- a/test/integration/test/temporal-defaults/_fixture/generated/contract.d.ts +++ b/test/integration/test/temporal-defaults/_fixture/generated/contract.d.ts @@ -276,14 +276,16 @@ export type FieldOutputTypes = FieldTypesFromDomain< CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'output' + 'output', + ContractBase['storage']['namespaces'] >; export type FieldInputTypes = FieldTypesFromDomain< ContractBase['domain']['namespaces'], CodecTypes, ParameterizedOutputTypeFunctions, FieldTypeOverrides, - 'input' + 'input', + ContractBase['storage']['namespaces'] >; type ColumnTypeOverrides = {}; export type StorageColumnTypes = ColumnTypesFromStorage< From b33fa79e5311634472540af4c2beaa129286f109 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 18:33:56 +0200 Subject: [PATCH 076/104] TML-3537: the check input types a value object column as ResolvedColumnValueObject, anyObject names what the check accepts, and the build and contract print share one derivation of the discriminator value set and the value object column Signed-off-by: willbot Signed-off-by: Will Madden --- .../1-core/contract/src/check-expressions.ts | 46 +++++++++++++-- .../contract/src/exports/check-expressions.ts | 3 + .../contract/test/check-expressions.test.ts | 36 ++++++++++- .../contract-ts/src/build-contract.ts | 14 +++-- .../contract-ts/src/lower-column.ts | 27 +++------ .../contract-ts/src/storage-description.ts | 1 + .../test/check-constraint.authoring.test.ts | 4 +- .../contract-builder.derived-checks.test.ts | 59 +++++++++---------- .../1-target/src/core/check-expressions.ts | 14 +++-- .../src/core/psl-infer/infer-model-blocks.ts | 4 +- .../core/psl-print/contract-model-index.ts | 7 ++- .../src/core/psl-print/model-attributes.ts | 6 +- .../src/core/psl-print/psl-contract.ts | 32 +++++----- .../check-expressions.integration.test.ts | 6 +- .../1-target/test/check-expressions.test.ts | 4 +- .../check-expressions.value-object.test.ts | 32 +++++----- .../inferred-psl/inferred-psl.check.test.ts | 2 +- ...erred-psl.list-element-nullability.test.ts | 2 +- .../alter-column-type.integration.test.ts | 2 +- .../check-lifecycle-e2e.integration.test.ts | 2 +- .../native-array-columns.integration.test.ts | 2 +- .../extension/instructions.md | 2 +- 22 files changed, 189 insertions(+), 118 deletions(-) diff --git a/packages/2-sql-family/1-core/contract/src/check-expressions.ts b/packages/2-sql-family/1-core/contract/src/check-expressions.ts index 2cc02e84ba20..c6d6bb2b26c0 100644 --- a/packages/2-sql-family/1-core/contract/src/check-expressions.ts +++ b/packages/2-sql-family/1-core/contract/src/check-expressions.ts @@ -21,7 +21,7 @@ export interface ResolvedValueObjectField { } /** - * `values` holds the stored values of the value set an enum member refers to. `openObject` stands where a value object that contains itself refers back into itself: any object is accepted there, as the MongoDB validator does. + * `values` holds the stored values of the value set an enum member refers to. `anyObject` stands where a value object that contains itself refers back into itself: any object is accepted there, as the MongoDB validator does. */ export type ResolvedValueObjectFieldType = | { @@ -30,7 +30,7 @@ export type ResolvedValueObjectFieldType = readonly values: readonly (string | number)[] | undefined; } | { readonly kind: 'valueObject'; readonly valueObject: ResolvedValueObject } - | { readonly kind: 'openObject' }; + | { readonly kind: 'anyObject' }; /** One column's shape, as the contract builder hands it to a target's check renderer. */ export interface CheckExpressionInput { @@ -39,8 +39,15 @@ export interface CheckExpressionInput { readonly many: false | { readonly elementNullable: boolean }; /** The values a column typed by a domain enum, or a discriminator column, may hold. */ readonly memberValues: readonly (string | number)[] | undefined; - /** The value object field a column holds: its nullability, whether it is a list, and the value object. */ - readonly valueObjectField: ResolvedValueObjectField | undefined; + /** The value object the column holds, or `undefined` when it holds none. */ + readonly valueObject: ResolvedColumnValueObject | undefined; +} + +/** The value object a column holds, with the column's nullability, and whether the column's JSON value is an array of them. */ +export interface ResolvedColumnValueObject { + readonly nullable: boolean; + readonly many: false | { readonly elementNullable: boolean }; + readonly valueObject: ResolvedValueObject; } /** A check the target wants written: what it enforces, the column it constrains, and the predicate body without the surrounding `CHECK (…)`. The family names it. */ @@ -54,6 +61,35 @@ export type CheckExpressionRenderer = ( input: CheckExpressionInput, ) => ReadonlyArray; +/** + * What the check of a column that holds a value object reads. The contract build and `contract print` both derive it here, so print recognises exactly the check the build writes. + */ +export function resolveColumnValueObject( + storage: ContractWithDomain['storage'], + column: { + readonly reference: EntityCoordinate<'storage', 'valueObject'>; + readonly nullable: boolean; + readonly many: false | { readonly elementNullable: boolean }; + }, +): ResolvedColumnValueObject { + return { + nullable: column.nullable, + many: column.many, + valueObject: resolveValueObject(storage, column.reference), + }; +} + +/** + * The value set a discriminator column names (ADR 309): keyed `.`, and holding the variants' values sorted, so the order variants are declared in does not change the storage hash. The contract build writes it and `contract print` compares a contract's value set with it. + */ +export function discriminatorValueSet( + baseModelName: string, + discriminatorField: string, + variantValues: readonly string[], +): { readonly name: string; readonly values: readonly string[] } { + return { name: `${baseModelName}.${discriminatorField}`, values: [...variantValues].sort() }; +} + /** * The value object a reference names, with nested value objects inlined and enum members' values read from their storage value sets. */ @@ -102,7 +138,7 @@ function resolveFieldType( }; } const key = referenceKey(type); - if (reachesAny(storage, type, new Set(path))) return { kind: 'openObject' }; + if (reachesAny(storage, type, new Set(path))) return { kind: 'anyObject' }; return { kind: 'valueObject', valueObject: resolveEntered(storage, type, [...path, key]) }; } diff --git a/packages/2-sql-family/1-core/contract/src/exports/check-expressions.ts b/packages/2-sql-family/1-core/contract/src/exports/check-expressions.ts index cbf707e89a4e..7b09c7de9980 100644 --- a/packages/2-sql-family/1-core/contract/src/exports/check-expressions.ts +++ b/packages/2-sql-family/1-core/contract/src/exports/check-expressions.ts @@ -2,8 +2,11 @@ export { type CheckExpressionCandidate, type CheckExpressionInput, type CheckExpressionRenderer, + discriminatorValueSet, + type ResolvedColumnValueObject, type ResolvedValueObject, type ResolvedValueObjectField, type ResolvedValueObjectFieldType, + resolveColumnValueObject, resolveValueObject, } from '../check-expressions'; diff --git a/packages/2-sql-family/1-core/contract/test/check-expressions.test.ts b/packages/2-sql-family/1-core/contract/test/check-expressions.test.ts index 5456b352bf6f..099a6b25ffe2 100644 --- a/packages/2-sql-family/1-core/contract/test/check-expressions.test.ts +++ b/packages/2-sql-family/1-core/contract/test/check-expressions.test.ts @@ -1,5 +1,9 @@ import { describe, expect, it } from 'vitest'; -import { resolveValueObject } from '../src/check-expressions'; +import { + discriminatorValueSet, + resolveColumnValueObject, + resolveValueObject, +} from '../src/check-expressions'; function valueObjectRef(name: string) { return { @@ -105,7 +109,7 @@ describe('resolveValueObject', () => { children: { nullable: false, many: { elementNullable: false }, - type: { kind: 'openObject' }, + type: { kind: 'anyObject' }, }, }, }); @@ -127,7 +131,7 @@ describe('resolveValueObject', () => { many: false, type: { kind: 'scalar', codecId: 'pg/text@1', values: undefined }, }, - entry: { nullable: false, many: false, type: { kind: 'openObject' } }, + entry: { nullable: false, many: false, type: { kind: 'anyObject' } }, meta: { nullable: false, many: false, @@ -152,3 +156,29 @@ describe('resolveValueObject', () => { expect(() => resolveValueObject(storageWith({}), valueObjectRef('Missing'))).toThrow(/Missing/); }); }); + +describe('resolveColumnValueObject', () => { + it('holds the column nullability and list shape with the resolved value object', () => { + const storage = storageWith({ valueObject: { Address: { fields: {} } } }); + expect( + resolveColumnValueObject(storage, { + reference: valueObjectRef('Address'), + nullable: true, + many: { elementNullable: false }, + }), + ).toEqual({ + nullable: true, + many: { elementNullable: false }, + valueObject: resolveValueObject(storage, valueObjectRef('Address')), + }); + }); +}); + +describe('discriminatorValueSet', () => { + it('is keyed by the base model and field, and holds the variant values sorted', () => { + expect(discriminatorValueSet('Task', 'type', ['feature', 'bug'])).toEqual({ + name: 'Task.type', + values: ['bug', 'feature'], + }); + }); +}); diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts b/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts index 0b5e00817a93..847e83a35971 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts @@ -29,7 +29,10 @@ import type { import { mergeCapabilityMatrices } from '@internal/framework-components/components'; import { UNBOUND_NAMESPACE_ID } from '@internal/framework-components/ir'; import { sqlContractCanonicalizationHooks } from '@internal/sql-contract/canonicalization-hooks'; -import type { CheckExpressionRenderer } from '@internal/sql-contract/check-expressions'; +import { + type CheckExpressionRenderer, + discriminatorValueSet, +} from '@internal/sql-contract/check-expressions'; import { sqlDataTypeOfCodec } from '@internal/sql-contract/data-type'; import { validateIndexTypes } from '@internal/sql-contract/index-type-validation'; import { type IndexTypeRegistry, indexTypeRegistryOf } from '@internal/sql-contract/index-types'; @@ -224,7 +227,11 @@ function writeDiscriminatorValueSets( const column = columnName === undefined ? undefined : table?.columns[columnName]; if (table === undefined || columnName === undefined || column === undefined) continue; if (column.valueSet !== undefined) continue; - const valueSetName = `${model.modelName}.${discriminator.field}`; + const { name: valueSetName, values } = discriminatorValueSet( + model.modelName, + discriminator.field, + Object.keys(variants), + ); const namespaceValueSets = storageValueSetsByNs[namespaceId] ?? {}; if (Object.hasOwn(namespaceValueSets, valueSetName)) { throw contractError( @@ -240,7 +247,6 @@ function writeDiscriminatorValueSets( }, ); } - const values = Object.keys(variants).sort(); namespaceValueSets[valueSetName] = { kind: 'valueSet', values }; storageValueSetsByNs[namespaceId] = namespaceValueSets; const render = checkRendererFor(table); @@ -254,7 +260,7 @@ function writeDiscriminatorValueSets( columnName, many: false, memberValues: values, - valueObjectField: undefined, + valueObject: undefined, }), ); const namespaceTables = tablesByNamespace[namespaceId] ?? {}; diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/lower-column.ts b/packages/2-sql-family/2-authoring/contract-ts/src/lower-column.ts index 9d4b88f89e83..a29614ced0de 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/lower-column.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/lower-column.ts @@ -4,7 +4,7 @@ import { dataTypeParams } from '@internal/framework-components/codec'; import { type CheckExpressionCandidate, type CheckExpressionRenderer, - resolveValueObject, + resolveColumnValueObject, } from '@internal/sql-contract/check-expressions'; import { type SqlDataType, @@ -25,7 +25,7 @@ import { contractError } from './contract-errors'; import { type ColumnSite, columnSiteMeta, columnSiteSubject } from './declaration-sites'; import { checkMemberValues, enumValueSetRefs } from './enum-members'; import { type CollectedColumnEntities, collectEntityFromColumn } from './pack-entities'; -import type { ColumnDescription, ColumnValueObject } from './storage-description'; +import type { ColumnDescription } from './storage-description'; import { type ColumnTypeQualifier, resolveColumnDescriptor } from './target-authoring-hooks'; /** @@ -165,25 +165,14 @@ export function lowerColumn( many, memberValues: enumTypeHandle !== undefined ? checkMemberValues(enumTypeHandle, codecLookup) : undefined, - valueObjectField: + valueObject: description.valueObject !== undefined - ? resolvedValueObjectField(description.valueObject, description.nullable, context) + ? resolveColumnValueObject(context.storage, { + reference: description.valueObject.reference, + nullable: description.nullable, + many: description.valueObject.many, + }) : undefined, }); return { column, derivedChecks: lowerRenderedChecks(placement.tableName, candidates) }; } - -function resolvedValueObjectField( - valueObject: ColumnValueObject, - nullable: boolean, - context: ColumnLoweringContext, -) { - return { - nullable, - many: valueObject.many, - type: { - kind: 'valueObject', - valueObject: resolveValueObject(context.storage, valueObject.reference), - }, - } as const; -} diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/storage-description.ts b/packages/2-sql-family/2-authoring/contract-ts/src/storage-description.ts index 3e5694db0183..5172ad78503c 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/storage-description.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/storage-description.ts @@ -27,6 +27,7 @@ export interface ColumnDescription { export interface ColumnValueObject { readonly reference: EntityCoordinate<'storage', 'valueObject'>; + /** Whether the column's JSON value is an array of value objects. The column's own `many` is a native array column, which a value object column never is. */ readonly many: false | { readonly elementNullable: boolean }; } diff --git a/packages/2-sql-family/2-authoring/contract-ts/test/check-constraint.authoring.test.ts b/packages/2-sql-family/2-authoring/contract-ts/test/check-constraint.authoring.test.ts index 8bf20a11a0e5..3f2aab16801b 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/test/check-constraint.authoring.test.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/test/check-constraint.authoring.test.ts @@ -307,7 +307,7 @@ describe('check emission — entity-ref-resolved value set (native enum shape)', columnName: 'role', many: false, memberValues: undefined, - valueObjectField: undefined, + valueObject: undefined, }); }); }); @@ -509,7 +509,7 @@ describe('check emission — guards', () => { columnName: 'special', many: many ? { elementNullable: false } : false, memberValues: [1.5, 'NaN'], - valueObjectField: undefined, + valueObject: undefined, }); }, ); diff --git a/packages/2-sql-family/2-authoring/contract-ts/test/contract-builder.derived-checks.test.ts b/packages/2-sql-family/2-authoring/contract-ts/test/contract-builder.derived-checks.test.ts index 220bc74fe3b6..08b0895050fd 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/test/contract-builder.derived-checks.test.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/test/contract-builder.derived-checks.test.ts @@ -22,7 +22,7 @@ function renderCheckExpressions(input: CheckExpressionInput) { expression: `"${input.columnName}" IN (${input.memberValues.join(', ')})`, }, ]), - ...(input.valueObjectField === undefined + ...(input.valueObject === undefined ? [] : [ { @@ -116,31 +116,28 @@ function checkNames(contract: ReturnType, tableName: string) { } const resolvedAddress = { - kind: 'valueObject', - valueObject: { - fields: { - street: { - nullable: false, - many: false, - type: { kind: 'scalar', codecId: 'pg/text@1', values: undefined }, - }, - kind: { - nullable: true, - many: false, - type: { kind: 'scalar', codecId: 'pg/text@1', values: ['home', 'work'] }, - }, - geo: { - nullable: false, - many: false, - type: { - kind: 'valueObject', - valueObject: { - fields: { - lat: { - nullable: false, - many: false, - type: { kind: 'scalar', codecId: 'pg/text@1', values: undefined }, - }, + fields: { + street: { + nullable: false, + many: false, + type: { kind: 'scalar', codecId: 'pg/text@1', values: undefined }, + }, + kind: { + nullable: true, + many: false, + type: { kind: 'scalar', codecId: 'pg/text@1', values: ['home', 'work'] }, + }, + geo: { + nullable: false, + many: false, + type: { + kind: 'valueObject', + valueObject: { + fields: { + lat: { + nullable: false, + many: false, + type: { kind: 'scalar', codecId: 'pg/text@1', values: undefined }, }, }, }, @@ -154,21 +151,21 @@ describe('the derived check of a column that holds a value object', () => { build([userModel()]); expect( renderedInputs - .filter((input) => input.valueObjectField !== undefined) - .map(({ columnName, many, valueObjectField }) => ({ columnName, many, valueObjectField })), + .filter((input) => input.valueObject !== undefined) + .map(({ columnName, many, valueObject }) => ({ columnName, many, valueObject })), ).toEqual([ { columnName: 'home', many: false, - valueObjectField: { nullable: true, many: false, type: resolvedAddress }, + valueObject: { nullable: true, many: false, valueObject: resolvedAddress }, }, { columnName: 'stops', many: false, - valueObjectField: { + valueObject: { nullable: false, many: { elementNullable: true }, - type: resolvedAddress, + valueObject: resolvedAddress, }, }, ]); diff --git a/packages/3-targets/postgres/1-target/src/core/check-expressions.ts b/packages/3-targets/postgres/1-target/src/core/check-expressions.ts index 43eb308d5809..55bfaad69c9c 100644 --- a/packages/3-targets/postgres/1-target/src/core/check-expressions.ts +++ b/packages/3-targets/postgres/1-target/src/core/check-expressions.ts @@ -1,6 +1,7 @@ import type { CheckExpressionCandidate, CheckExpressionInput, + ResolvedColumnValueObject, ResolvedValueObject, ResolvedValueObjectField, ResolvedValueObjectFieldType, @@ -62,11 +63,11 @@ export function postgresRenderCheckExpressions( }); } - if (input.valueObjectField !== undefined) { + if (input.valueObject !== undefined) { candidates.push({ kind: 'valueObject', columnName: input.columnName, - expression: valueObjectCheck(column, input.valueObjectField), + expression: valueObjectCheck(column, input.valueObject), }); } @@ -76,7 +77,12 @@ export function postgresRenderCheckExpressions( /** * One strict `jsonpath` predicate over the whole column value. Strict mode keeps a comparison from unwrapping an array, and `IS TRUE` makes a predicate that evaluates to unknown fail the check, as a CHECK would otherwise pass on NULL. Member values are read through `keyvalue()`, so an absent member is never an error. */ -function valueObjectCheck(column: string, field: ResolvedValueObjectField): string { +function valueObjectCheck(column: string, columnValueObject: ResolvedColumnValueObject): string { + const field: ResolvedValueObjectField = { + nullable: columnValueObject.nullable, + many: columnValueObject.many, + type: { kind: 'valueObject', valueObject: columnValueObject.valueObject }, + }; const predicate = fieldPredicate(field, '$'); invariant(predicate !== undefined, 'a column that holds a value object always has a predicate'); const match = (path: string) => @@ -109,7 +115,7 @@ function valuePredicate(type: ResolvedValueObjectFieldType, path: string): strin switch (type.kind) { case 'valueObject': return objectPredicate(type.valueObject, path); - case 'openObject': + case 'anyObject': return `${path}.type() == "object"`; case 'scalar': { if (type.values !== undefined) return valueSetPredicate(type.values, path); diff --git a/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-model-blocks.ts b/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-model-blocks.ts index 4b0e5bb54f17..b46916917ea1 100644 --- a/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-model-blocks.ts +++ b/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-model-blocks.ts @@ -211,7 +211,7 @@ function computeDerivedCheckNames(table: SqlTableIR): ReadonlySet { columnName: column.name, many: column.many === true ? { elementNullable: false } : false, memberValues: undefined, - valueObjectField: undefined, + valueObject: undefined, })) { const derivedName = formatWireName( composeCheckWirePrefix(table.name, column.name, candidate.kind), @@ -332,7 +332,7 @@ function enforcesElementNotNull( columnName: column.name, many: { elementNullable: false }, memberValues: undefined, - valueObjectField: undefined, + valueObject: undefined, }).some( (candidate) => candidate.kind === 'elementNotNull' && diff --git a/packages/3-targets/postgres/1-target/src/core/psl-print/contract-model-index.ts b/packages/3-targets/postgres/1-target/src/core/psl-print/contract-model-index.ts index c4169344fb17..cf8a811ecd11 100644 --- a/packages/3-targets/postgres/1-target/src/core/psl-print/contract-model-index.ts +++ b/packages/3-targets/postgres/1-target/src/core/psl-print/contract-model-index.ts @@ -2,6 +2,7 @@ import type { Contract, ContractModelBase, CrossReference } from '@internal/cont import { UNBOUND_NAMESPACE_ID } from '@internal/framework-components/ir'; import { UNBOUND_PSL_NAMESPACE_NAME } from '@internal/framework-components/psl-ast'; import { canonicalizeJson } from '@internal/framework-components/utils'; +import { discriminatorValueSet } from '@internal/sql-contract/check-expressions'; import type { ForeignKey, SqlModelStorage, @@ -151,10 +152,10 @@ export function discriminatorValueSetOf( ): { readonly name: string; readonly values: readonly string[] } | undefined { const { discriminator, variants } = entry.model; if (discriminator === undefined || variants === undefined) return undefined; - const name = `${entry.name}.${discriminator.field}`; + const derived = discriminatorValueSet(entry.name, discriminator.field, Object.keys(variants)); const columnName = entry.storage.fields[discriminator.field]?.column; const valueSet = columnName === undefined ? undefined : entry.table.columns[columnName]?.valueSet; - return valueSet?.entityName === name && valueSet.namespaceId === entry.namespaceId - ? { name, values: Object.keys(variants).sort() } + return valueSet?.entityName === derived.name && valueSet.namespaceId === entry.namespaceId + ? derived : undefined; } diff --git a/packages/3-targets/postgres/1-target/src/core/psl-print/model-attributes.ts b/packages/3-targets/postgres/1-target/src/core/psl-print/model-attributes.ts index 90216b6341c5..bac3e014b31e 100644 --- a/packages/3-targets/postgres/1-target/src/core/psl-print/model-attributes.ts +++ b/packages/3-targets/postgres/1-target/src/core/psl-print/model-attributes.ts @@ -1,5 +1,5 @@ import type { PslModelAttribute } from '@internal/framework-components/psl-ast'; -import type { ResolvedValueObjectField } from '@internal/sql-contract/check-expressions'; +import type { ResolvedColumnValueObject } from '@internal/sql-contract/check-expressions'; import { escapePslString } from '@internal/sql-contract/data-type-support'; import type { StorageTable } from '@internal/sql-contract/types'; import { pslModelMapName } from '@internal/sql-contract-psl/map-names'; @@ -48,7 +48,7 @@ export function derivedChecks(input: { /** The values of each value set a domain enum or a discriminator writes, by name. */ readonly memberValueSets: ReadonlyMap; /** The value object field each column that holds one stores, by column name. */ - readonly valueObjectFields: ReadonlyMap; + readonly columnValueObjects: ReadonlyMap; }): ReadonlyMap { const checks = new Map(); if (!input.managed) return checks; @@ -65,7 +65,7 @@ export function derivedChecks(input: { columnName, many: column.many, memberValues: memberValues?.length === setValues?.length ? memberValues : undefined, - valueObjectField: input.valueObjectFields.get(columnName), + valueObject: input.columnValueObjects.get(columnName), })) { const prefix = composeCheckWirePrefix(input.tableName, columnName, candidate.kind); checks.set(formatWireName(prefix, computeCheckContentHash(candidate.expression)), { diff --git a/packages/3-targets/postgres/1-target/src/core/psl-print/psl-contract.ts b/packages/3-targets/postgres/1-target/src/core/psl-print/psl-contract.ts index a2ef867ef318..a8fad6167316 100644 --- a/packages/3-targets/postgres/1-target/src/core/psl-print/psl-contract.ts +++ b/packages/3-targets/postgres/1-target/src/core/psl-print/psl-contract.ts @@ -19,8 +19,8 @@ import { UNSPECIFIED_PSL_NAMESPACE_ID, } from '@internal/framework-components/psl-ast'; import { - type ResolvedValueObjectField, - resolveValueObject, + type ResolvedColumnValueObject, + resolveColumnValueObject, } from '@internal/sql-contract/check-expressions'; import type { ForeignKey, SqlModelStorage, SqlStorage } from '@internal/sql-contract/types'; import { invariant } from '@internal/utils/assertions'; @@ -107,12 +107,12 @@ function nativeEnumsOf( return nativeEnums; } -/** The value object field each column of a table holds, over the fields of every model the table stores. Nullability comes from the column, as in the build: a variant that shares its base table has a nullable column. */ -function valueObjectFieldsOf( +/** The value object each column of a table holds, over the fields of every model the table stores. Nullability comes from the column, as in the build: a variant that shares its base table has a nullable column. */ +function columnValueObjectsOf( contract: Contract, entries: readonly ModelWithTable[], -): ReadonlyMap { - const fields = new Map(); +): ReadonlyMap { + const columnValueObjects = new Map(); for (const entry of entries) { for (const [fieldName, field] of Object.entries(entry.model.fields)) { const columnName = entry.storage.fields[fieldName]?.column; @@ -120,17 +120,17 @@ function valueObjectFieldsOf( if (field.type.kind !== 'valueObject' || columnName === undefined || column === undefined) { continue; } - fields.set(columnName, { - nullable: column.nullable, - many: field.many ?? false, - type: { - kind: 'valueObject', - valueObject: resolveValueObject(contract.storage, field.type), - }, - }); + columnValueObjects.set( + columnName, + resolveColumnValueObject(contract.storage, { + reference: field.type, + nullable: column.nullable, + many: field.many ?? false, + }), + ); } } - return fields; + return columnValueObjects; } /** The value sets the PSL source derives for a namespace from the enum blocks written for it and from its discriminators. */ @@ -220,7 +220,7 @@ function buildModel( ...all.domainEnumValues, [discriminatorValueSet.name, discriminatorValueSet.values], ]), - valueObjectFields: valueObjectFieldsOf(all.contract, [entry, ...singleTableVariants]), + columnValueObjects: columnValueObjectsOf(all.contract, [entry, ...singleTableVariants]), }); if (variant?.singleTable !== true) refuseUnderivedChecks(entry, checks); return { diff --git a/packages/3-targets/postgres/1-target/test/check-expressions.integration.test.ts b/packages/3-targets/postgres/1-target/test/check-expressions.integration.test.ts index 8c881c74d720..fa8b54d78ac1 100644 --- a/packages/3-targets/postgres/1-target/test/check-expressions.integration.test.ts +++ b/packages/3-targets/postgres/1-target/test/check-expressions.integration.test.ts @@ -16,7 +16,7 @@ describe('numeric membership checks in Postgres', () => { columnName: 'value', many: many ? { elementNullable: false } : false, memberValues, - valueObjectField: undefined, + valueObject: undefined, }); await client.query( `CREATE TABLE members (value ${storageType}${many ? '[]' : ''}, ${checks.map(({ expression }) => `CHECK (${expression})`).join(', ')})`, @@ -60,7 +60,7 @@ describe('list membership checks in Postgres', () => { columnName, many: { elementNullable: false }, memberValues: members, - valueObjectField: undefined, + valueObject: undefined, }), ); await client.query( @@ -129,7 +129,7 @@ describe('list membership checks in Postgres', () => { columnName: many ? 'list' : 'scalar', many: many ? { elementNullable: false } : false, memberValues: members, - valueObjectField: undefined, + valueObject: undefined, }), ); await client.query( diff --git a/packages/3-targets/postgres/1-target/test/check-expressions.test.ts b/packages/3-targets/postgres/1-target/test/check-expressions.test.ts index c509d17e783e..c60a4e528678 100644 --- a/packages/3-targets/postgres/1-target/test/check-expressions.test.ts +++ b/packages/3-targets/postgres/1-target/test/check-expressions.test.ts @@ -7,7 +7,7 @@ const base: CheckExpressionInput = { columnName: 'role', many: false, memberValues: undefined, - valueObjectField: undefined, + valueObject: undefined, }; describe('postgresRenderCheckExpressions', () => { @@ -155,7 +155,7 @@ describe('postgresRenderCheckExpressions', () => { columnName: 'sta"tus', many: false, memberValues: ["o'brien"], - valueObjectField: undefined, + valueObject: undefined, }), ).toEqual([ { kind: 'membership', columnName: 'sta"tus', expression: `"sta""tus" IN ('o''brien')` }, diff --git a/packages/3-targets/postgres/1-target/test/check-expressions.value-object.test.ts b/packages/3-targets/postgres/1-target/test/check-expressions.value-object.test.ts index d3fd54a7c709..e94de0cf8930 100644 --- a/packages/3-targets/postgres/1-target/test/check-expressions.value-object.test.ts +++ b/packages/3-targets/postgres/1-target/test/check-expressions.value-object.test.ts @@ -1,4 +1,5 @@ import type { + ResolvedColumnValueObject, ResolvedValueObject, ResolvedValueObjectField, ResolvedValueObjectFieldType, @@ -25,14 +26,15 @@ function holding(valueObject: ResolvedValueObject): ResolvedValueObjectFieldType } function render( - valueObjectField: ResolvedValueObjectField, + valueObject: ResolvedValueObject, + column: Partial> = {}, ): readonly { readonly kind: string; readonly expression: string }[] { return postgresRenderCheckExpressions({ tableName: 'user', columnName: 'address', many: false, memberValues: undefined, - valueObjectField, + valueObject: { nullable: false, many: false, ...column, valueObject }, }).map(({ kind, expression }) => ({ kind, expression })); } @@ -45,7 +47,7 @@ const onlyStreet = '!exists($.keyvalue() ? (@.key != "street"))'; describe('postgresRenderCheckExpressions for a column that holds a value object', () => { it('requires an object with each required member, of its JSON type, and no other key', () => { - expect(render(member(holding(flat)))).toEqual([ + expect(render(flat)).toEqual([ { kind: 'valueObject', expression: `jsonb_path_match("address", 'strict ${isObject} && ${hasStreet} && ${onlyStreet}') IS TRUE`, @@ -54,7 +56,7 @@ describe('postgresRenderCheckExpressions for a column that holds a value object' }); it('accepts SQL NULL and JSON null in a nullable column', () => { - expect(render(member(holding(flat), { nullable: true }))).toEqual([ + expect(render(flat, { nullable: true })).toEqual([ { kind: 'valueObject', expression: `"address" IS NULL OR jsonb_path_match("address", 'strict $.type() == "null" || (${isObject} && ${hasStreet} && ${onlyStreet})') IS TRUE`, @@ -64,7 +66,7 @@ describe('postgresRenderCheckExpressions for a column that holds a value object' it('lets a nullable member be absent or JSON null', () => { const zip = member(scalar('pg/int4@1'), { nullable: true }); - expect(render(member(holding({ fields: { zip } })))).toEqual([ + expect(render({ fields: { zip } })).toEqual([ { kind: 'valueObject', expression: `jsonb_path_match("address", 'strict ${isObject} && !exists($.keyvalue() ? (@.key == "zip" && !(@.value.type() == "null" || (@.value.type() == "number")))) && !exists($.keyvalue() ? (@.key != "zip"))') IS TRUE`, @@ -75,7 +77,7 @@ describe('postgresRenderCheckExpressions for a column that holds a value object' it('checks list members and their element nullability', () => { const tags = member(scalar('pg/text@1'), { many: { elementNullable: false } }); const flags = member(scalar('pg/bool@1'), { many: { elementNullable: true } }); - expect(render(member(holding({ fields: { tags, flags } })))).toEqual([ + expect(render({ fields: { tags, flags } })).toEqual([ { kind: 'valueObject', expression: `jsonb_path_match("address", 'strict ${isObject} && exists($.keyvalue() ? (@.key == "flags" && (@.value.type() == "array" && !exists(@.value[*] ? (!(@.type() == "null" || (@.type() == "boolean"))))))) && exists($.keyvalue() ? (@.key == "tags" && (@.value.type() == "array" && !exists(@.value[*] ? (!((@.type() == "string"))))))) && !exists($.keyvalue() ? (@.key != "flags" && @.key != "tags"))') IS TRUE`, @@ -85,7 +87,7 @@ describe('postgresRenderCheckExpressions for a column that holds a value object' it('restricts an enum member to its value set', () => { const kind = member(scalar('pg/text@1', ['home', "o'brien"])); - expect(render(member(holding({ fields: { kind } })))).toEqual([ + expect(render({ fields: { kind } })).toEqual([ { kind: 'valueObject', expression: `jsonb_path_match("address", 'strict ${isObject} && exists($.keyvalue() ? (@.key == "kind" && (@.value.type() == "string" && (@.value == "home" || @.value == "o''brien")))) && !exists($.keyvalue() ? (@.key != "kind"))') IS TRUE`, @@ -95,7 +97,7 @@ describe('postgresRenderCheckExpressions for a column that holds a value object' it('restricts a numeric enum member to its value set', () => { const rank = member(scalar('pg/int4@1', [1, -2])); - expect(render(member(holding({ fields: { rank } })))).toEqual([ + expect(render({ fields: { rank } })).toEqual([ { kind: 'valueObject', expression: `jsonb_path_match("address", 'strict ${isObject} && exists($.keyvalue() ? (@.key == "rank" && (@.value.type() == "number" && (@.value == 1 || @.value == -2)))) && !exists($.keyvalue() ? (@.key != "rank"))') IS TRUE`, @@ -108,7 +110,7 @@ describe('postgresRenderCheckExpressions for a column that holds a value object' const stops = member(holding(flat), { many: { elementNullable: false } }); const nestedAt = (path: string) => `${path}.type() == "object" && exists(${path}.keyvalue() ? (@.key == "street" && (@.value.type() == "string"))) && !exists(${path}.keyvalue() ? (@.key != "street"))`; - expect(render(member(holding({ fields: { geo, stops } })))).toEqual([ + expect(render({ fields: { geo, stops } })).toEqual([ { kind: 'valueObject', expression: `jsonb_path_match("address", 'strict ${isObject} && exists($.keyvalue() ? (@.key == "geo" && (${nestedAt('@.value')}))) && exists($.keyvalue() ? (@.key == "stops" && (@.value.type() == "array" && !exists(@.value[*] ? (!((${nestedAt('@')}))))))) && !exists($.keyvalue() ? (@.key != "geo" && @.key != "stops"))') IS TRUE`, @@ -117,8 +119,8 @@ describe('postgresRenderCheckExpressions for a column that holds a value object' }); it('accepts any object where a value object refers back into itself', () => { - const children = member({ kind: 'openObject' }, { many: { elementNullable: false } }); - expect(render(member(holding({ fields: { children } })))).toEqual([ + const children = member({ kind: 'anyObject' }, { many: { elementNullable: false } }); + expect(render({ fields: { children } })).toEqual([ { kind: 'valueObject', expression: `jsonb_path_match("address", 'strict ${isObject} && exists($.keyvalue() ? (@.key == "children" && (@.value.type() == "array" && !exists(@.value[*] ? (!((@.type() == "object"))))))) && !exists($.keyvalue() ? (@.key != "children"))') IS TRUE`, @@ -127,7 +129,7 @@ describe('postgresRenderCheckExpressions for a column that holds a value object' }); it('checks a list column as an array of the value object', () => { - expect(render(member(holding(flat), { many: { elementNullable: false } }))).toEqual([ + expect(render(flat, { many: { elementNullable: false } })).toEqual([ { kind: 'valueObject', expression: `jsonb_path_match("address", 'strict $.type() == "array" && !exists($[*] ? (!((@.type() == "object" && exists(@.keyvalue() ? (@.key == "street" && (@.value.type() == "string"))) && !exists(@.keyvalue() ? (@.key != "street"))))))') IS TRUE`, @@ -140,7 +142,7 @@ describe('postgresRenderCheckExpressions for a column that holds a value object' const data = member(scalar('pg/jsonb@1')); const notes = member(scalar('pg/jsonb@1'), { nullable: true }); const blobs = member(scalar('pg/bytea@1'), { many: { elementNullable: false } }); - expect(render(member(holding({ fields: { blobs, bytes, data, notes } })))).toEqual([ + expect(render({ fields: { blobs, bytes, data, notes } })).toEqual([ { kind: 'valueObject', expression: `jsonb_path_match("address", 'strict ${isObject} && exists($.keyvalue() ? (@.key == "blobs" && (@.value.type() == "array" && !exists(@.value[*] ? (@.type() == "null"))))) && exists($.keyvalue() ? (@.key == "bytes" && (@.value.type() != "null"))) && exists($.keyvalue() ? (@.key == "data")) && !exists($.keyvalue() ? (@.key != "blobs" && @.key != "bytes" && @.key != "data" && @.key != "notes"))') IS TRUE`, @@ -150,7 +152,7 @@ describe('postgresRenderCheckExpressions for a column that holds a value object' it('requires a decimal member to be a JSON string, as the ORM writes it', () => { const amount = member(scalar('pg/numeric@1')); - expect(render(member(holding({ fields: { amount } })))).toEqual([ + expect(render({ fields: { amount } })).toEqual([ { kind: 'valueObject', expression: `jsonb_path_match("address", 'strict ${isObject} && exists($.keyvalue() ? (@.key == "amount" && (@.value.type() == "string"))) && !exists($.keyvalue() ? (@.key != "amount"))') IS TRUE`, @@ -160,7 +162,7 @@ describe('postgresRenderCheckExpressions for a column that holds a value object' it('writes member names as jsonpath strings', () => { const quoted = member(scalar('pg/text@1')); - expect(render(member(holding({ fields: { 'say "hi"': quoted } })))).toEqual([ + expect(render({ fields: { 'say "hi"': quoted } })).toEqual([ { kind: 'valueObject', expression: `jsonb_path_match("address", 'strict ${isObject} && exists($.keyvalue() ? (@.key == "say \\"hi\\"" && (@.value.type() == "string"))) && !exists($.keyvalue() ? (@.key != "say \\"hi\\""))') IS TRUE`, diff --git a/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.check.test.ts b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.check.test.ts index e19f8dc73002..60d5c6cb1809 100644 --- a/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.check.test.ts +++ b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.check.test.ts @@ -21,7 +21,7 @@ const elementCandidate = postgresRenderCheckExpressions({ columnName: 'tags', many: { elementNullable: false }, memberValues: undefined, - valueObjectField: undefined, + valueObject: undefined, })[0]; const derivedPrefix = composeCheckWirePrefix('users', 'tags', 'elementNotNull'); diff --git a/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.list-element-nullability.test.ts b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.list-element-nullability.test.ts index cfd185587acb..470d859565bd 100644 --- a/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.list-element-nullability.test.ts +++ b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.list-element-nullability.test.ts @@ -19,7 +19,7 @@ const elementCandidate = postgresRenderCheckExpressions({ columnName: 'tags', many: { elementNullable: false }, memberValues: undefined, - valueObjectField: undefined, + valueObject: undefined, })[0]; const derivedPrefix = composeCheckWirePrefix('users', 'tags', 'elementNotNull'); diff --git a/packages/3-targets/postgres/2-adapter/test/migrations/alter-column-type.integration.test.ts b/packages/3-targets/postgres/2-adapter/test/migrations/alter-column-type.integration.test.ts index 7f570cfc9600..5a98d8ec1d32 100644 --- a/packages/3-targets/postgres/2-adapter/test/migrations/alter-column-type.integration.test.ts +++ b/packages/3-targets/postgres/2-adapter/test/migrations/alter-column-type.integration.test.ts @@ -51,7 +51,7 @@ function elementChecks(): CheckConstraint[] { columnName: 'tags', many: { elementNullable: false }, memberValues: undefined, - valueObjectField: undefined, + valueObject: undefined, }).map( (candidate) => new CheckConstraint({ diff --git a/packages/3-targets/postgres/2-adapter/test/migrations/check-lifecycle-e2e.integration.test.ts b/packages/3-targets/postgres/2-adapter/test/migrations/check-lifecycle-e2e.integration.test.ts index e4582d19fedf..6d3059e9b503 100644 --- a/packages/3-targets/postgres/2-adapter/test/migrations/check-lifecycle-e2e.integration.test.ts +++ b/packages/3-targets/postgres/2-adapter/test/migrations/check-lifecycle-e2e.integration.test.ts @@ -108,7 +108,7 @@ function checksForColumn( columnName, many: options.many, memberValues: options.memberValues, - valueObjectField: undefined, + valueObject: undefined, }).map( (candidate) => new CheckConstraint({ diff --git a/packages/3-targets/postgres/2-adapter/test/migrations/native-array-columns.integration.test.ts b/packages/3-targets/postgres/2-adapter/test/migrations/native-array-columns.integration.test.ts index be083f5cc76f..66cfab324832 100644 --- a/packages/3-targets/postgres/2-adapter/test/migrations/native-array-columns.integration.test.ts +++ b/packages/3-targets/postgres/2-adapter/test/migrations/native-array-columns.integration.test.ts @@ -43,7 +43,7 @@ function declaredArrayElementChecks(): CheckConstraint[] { columnName, many: { elementNullable: false }, memberValues: undefined, - valueObjectField: undefined, + valueObject: undefined, }).map( (candidate) => new CheckConstraint({ diff --git a/upgrade-instructions/pending/value-objects-in-storage/extension/instructions.md b/upgrade-instructions/pending/value-objects-in-storage/extension/instructions.md index 3d095c2c17c9..5a75bc10d52b 100644 --- a/upgrade-instructions/pending/value-objects-in-storage/extension/instructions.md +++ b/upgrade-instructions/pending/value-objects-in-storage/extension/instructions.md @@ -64,7 +64,7 @@ import type { PostgresCheckExpressionInput } from '@prisma/orm-postgres/target/t import type { CheckExpressionInput } from '@prisma/orm-postgres/family-contract/check-expressions'; ``` -`postgresRenderCheckExpressions` stays in `@prisma/orm-postgres/target/types`. Its input also takes `valueObjectField`, the value object a column holds, or `undefined`. +`postgresRenderCheckExpressions` stays in `@prisma/orm-postgres/target/types`. Its input also takes `valueObject`, a `ResolvedColumnValueObject` (the column's nullability, whether its JSON value is an array, and the value object), or `undefined`. ## `field-types-read-storage-value-objects` From bd01650ed3d878d0087483bf56fab7abdbba9254 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 18:38:33 +0200 Subject: [PATCH 077/104] TML-3537: the Mongo validator reads each value object at the storage coordinate its field names, so value objects of one name in two namespaces stay apart Signed-off-by: willbot Signed-off-by: Will Madden --- .../mongo-contract/src/derive-json-schema.ts | 30 ++++++-------- .../mongo-contract/src/recursive-groups.ts | 30 +++++++++----- .../test/collection-validator-helpers.ts | 32 ++++++++++++--- .../test/derive-collection-validators.test.ts | 41 +++++++++++++++++++ .../test/derive-json-schema-helpers.ts | 12 ++++-- .../test/recursive-groups.test.ts | 41 +++++++++++-------- .../contract-ts/src/build-contract.ts | 6 ++- .../9-facade/test/mongo.enum.e2e.test.ts | 2 +- 8 files changed, 140 insertions(+), 54 deletions(-) diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/src/derive-json-schema.ts b/packages/2-mongo-family/1-foundation/mongo-contract/src/derive-json-schema.ts index d24d3fea42d9..7da67036d24c 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/src/derive-json-schema.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/src/derive-json-schema.ts @@ -4,14 +4,16 @@ import type { ContractModel, ContractRelation, ContractValueObject, + ContractWithDomain, JsonValue, } from '@internal/contract/types'; +import { valueObjectAt } from '@internal/contract/types'; import type { TypeLookups } from '@internal/framework-components/codec'; import { assertDefined } from '@internal/utils/assertions'; import type { MongoModelStorage } from './contract-types'; import { MongoValidator } from './ir/mongo-validator'; import { bsonTypesOfCodec } from './mongo-data-type'; -import { recursiveGroupsOf, typeKey } from './recursive-groups'; +import { modelKey, recursiveGroupsOf, valueObjectKey } from './recursive-groups'; import { isEmbedRelation } from './relation-fields'; import { DOCUMENT_ID, @@ -46,7 +48,7 @@ interface SchemaSources extends ModelValidatorSources { const OPEN_OBJECT: Record = { bsonType: 'object' }; function sourcesFor(input: ModelValidatorSources, rootKey: string): SchemaSources { - const groups = recursiveGroupsOf(input.valueObjects, input.models); + const groups = recursiveGroupsOf(input.storage, input.models); const rootGroup = groups.get(rootKey); return { ...input, @@ -56,12 +58,10 @@ function sourcesFor(input: ModelValidatorSources, rootKey: string): SchemaSource } function objectSchemaOf( - kind: 'valueObject' | 'model', - name: string, + key: string, shape: StoredShape, sources: SchemaSources, ): Record { - const key = typeKey(kind, name); const group = sources.groups.get(key); if (group !== undefined && sources.enteredGroups.has(group)) return OPEN_OBJECT; return deriveObjectSchema(shape, entering(key, sources)); @@ -131,7 +131,7 @@ function fieldToBsonSchema( field: ContractField, sources: SchemaSources, ): Record | undefined { - const { lookups, valueObjects, valueSets } = sources; + const { lookups, storage, valueSets } = sources; if (field.type.kind === 'scalar') { const bsonTypes = bsonTypesOfCodec(field.type.codecId, lookups); assertDefined( @@ -166,14 +166,9 @@ function fieldToBsonSchema( } if (field.type.kind === 'valueObject') { - const vo = valueObjects[field.type.entityName]; + const vo = valueObjectAt(storage, field.type); if (!vo) return undefined; - const voSchema = objectSchemaOf( - 'valueObject', - field.type.entityName, - valueObjectShape(vo), - sources, - ); + const voSchema = objectSchemaOf(valueObjectKey(field.type), valueObjectShape(vo), sources); if (field.many) { return { bsonType: field.nullable ? ['null', 'array'] : 'array', @@ -199,7 +194,7 @@ function embedToBsonSchema( const embeddedName = relation.to.model; const embedded = sources.models[embeddedName]; assertDefined(embedded, `Embedded model "${embeddedName}" is missing`); - const schema = objectSchemaOf('model', embeddedName, modelShape(embedded), sources); + const schema = objectSchemaOf(modelKey(embeddedName), modelShape(embedded), sources); return relation.cardinality === '1:N' ? { bsonType: 'array', items: schema } : schema; } @@ -289,7 +284,7 @@ function polymorphicSchema( for (const variant of variants) { const { properties: variantProperties, required } = propertiesOf( variant, - entering(typeKey('model', variant.modelName), sources), + entering(modelKey(variant.modelName), sources), base, ); @@ -315,7 +310,8 @@ function polymorphicSchema( export interface ModelValidatorSources { readonly models: Record; readonly lookups: TypeLookups; - readonly valueObjects: Record; + /** The storage the value objects are read from, at the coordinate each field names. */ + readonly storage: ContractWithDomain['storage']; readonly valueSets: FieldValueSets; } @@ -327,7 +323,7 @@ export function deriveModelJsonSchema( model: ValidatorModel, sources: ModelValidatorSources, ): Record { - const schemaSources = sourcesFor(sources, typeKey('model', modelName)); + const schemaSources = sourcesFor(sources, modelKey(modelName)); if (!model.discriminator || !model.variants) { return deriveObjectSchema(modelShape(model), schemaSources, { isDocument: true }); } diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/src/recursive-groups.ts b/packages/2-mongo-family/1-foundation/mongo-contract/src/recursive-groups.ts index 20982bbd424b..a208da47885b 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/src/recursive-groups.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/src/recursive-groups.ts @@ -1,33 +1,43 @@ -import type { ContractField, ContractValueObject } from '@internal/contract/types'; +import { + type ContractField, + type ContractWithDomain, + type EntityCoordinate, + valueObjectsWithCoordinates, +} from '@internal/contract/types'; import type { ValidatorModel } from './derive-json-schema'; import { isEmbedRelation } from './relation-fields'; -export function typeKey(kind: 'valueObject' | 'model', name: string): string { - return `${kind}:${name}`; +export function modelKey(name: string): string { + return `model:${name}`; +} + +/** The key of a value object, by its storage coordinate, so value objects of one name in two namespaces stay apart. */ +export function valueObjectKey(reference: EntityCoordinate<'storage', 'valueObject'>): string { + return `valueObject:${JSON.stringify([reference.namespaceId, reference.entityName])}`; } function referencesOf(fields: Record): string[] { return Object.values(fields).flatMap((field) => - field.type.kind === 'valueObject' ? [typeKey('valueObject', field.type.entityName)] : [], + field.type.kind === 'valueObject' ? [valueObjectKey(field.type)] : [], ); } /** - * The recursive groups of value objects and embedded models: each set of types that can reach each other through value-object fields and embed relations (a strongly connected component of that graph), and each type that refers to itself. Maps the {@link typeKey} of every type in such a group to the group's number; a type in no recursive group is absent. + * The recursive groups of value objects and embedded models: each set of types that can reach each other through value-object fields and embed relations (a strongly connected component of that graph), and each type that refers to itself. Maps the {@link valueObjectKey} or {@link modelKey} of every type in such a group to the group's number; a type in no recursive group is absent. */ export function recursiveGroupsOf( - valueObjects: Record, + storage: ContractWithDomain['storage'], models: Record>, ): ReadonlyMap { const edges = new Map(); - for (const [name, valueObject] of Object.entries(valueObjects)) { - edges.set(typeKey('valueObject', name), referencesOf(valueObject.fields)); + for (const { coordinate, valueObject } of valueObjectsWithCoordinates(storage)) { + edges.set(valueObjectKey(coordinate), referencesOf(valueObject.fields)); } for (const [name, model] of Object.entries(models)) { const embeds = Object.values(model.relations).flatMap((relation) => - isEmbedRelation(relation) ? [typeKey('model', relation.to.model)] : [], + isEmbedRelation(relation) ? [modelKey(relation.to.model)] : [], ); - edges.set(typeKey('model', name), [...referencesOf(model.fields), ...embeds]); + edges.set(modelKey(name), [...referencesOf(model.fields), ...embeds]); } const groups = new Map(); diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/test/collection-validator-helpers.ts b/packages/2-mongo-family/1-foundation/mongo-contract/test/collection-validator-helpers.ts index b3a22d9f1643..07cb19554e88 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/test/collection-validator-helpers.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/test/collection-validator-helpers.ts @@ -1,4 +1,10 @@ -import type { ContractEmbedRelation, ContractField, ContractModel } from '@internal/contract/types'; +import type { + ContractEmbedRelation, + ContractField, + ContractModel, + ContractValueObject, + ContractWithDomain, +} from '@internal/contract/types'; import { type CrossReference, crossRef } from '@internal/contract/types'; import type { MongoModelStorage } from '../src/contract-types'; import { @@ -47,14 +53,30 @@ export function embedMany(to: string): ContractEmbedRelation { return { to: crossRef(to), cardinality: '1:N' }; } +/** Storage holding `valueObjects` in the unbound namespace, where the test helpers' value object fields point. */ +export function storageWithValueObjects( + valueObjects: Record, +): ContractWithDomain['storage'] { + return { + namespaces: { __unbound__: { id: '__unbound__', entries: { valueObject: valueObjects } } }, + }; +} + +/** Test inputs that may give value objects by name, as stored in the unbound namespace, in place of storage. */ +export type ValidatorTestSources = Partial< + Pick & { + readonly valueObjects: Record; + } +>; + /** The validators of `models`, with each model in `roots` stored in the collection that keys it. */ export function derive( input: { readonly roots: Record; } & Pick & - Partial>, + ValidatorTestSources, ) { - const { roots, models, ...sources } = input; + const { roots, models, valueObjects, ...sources } = input; const stored = { ...models }; for (const [collection, root] of Object.entries(roots)) { const model = stored[root.model]; @@ -64,7 +86,7 @@ export function derive( } return deriveCollectionValidators({ lookups: mongoTypeLookups, - valueObjects: {}, + storage: storageWithValueObjects(valueObjects ?? {}), valueSets: {}, controlPolicies: { collections: {}, defaultControlPolicy: undefined }, ...sources, @@ -87,7 +109,7 @@ function itemsValidator(validators: Record): MongoValida /** The validator of collection `items`, whose root model has `fields`. */ export function rootValidator( fields: Record, - sources: Partial> = {}, + sources: Pick = {}, ): MongoValidator { return itemsValidator( derive({ roots: { items: crossRef('Item') }, models: { Item: model(fields) }, ...sources }), diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/test/derive-collection-validators.test.ts b/packages/2-mongo-family/1-foundation/mongo-contract/test/derive-collection-validators.test.ts index e6538f4b147b..62dcc5cbb542 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/test/derive-collection-validators.test.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/test/derive-collection-validators.test.ts @@ -131,6 +131,47 @@ describe('deriveCollectionValidators', () => { }); }); + it('reads each value object from the storage namespace its field names', () => { + const closedObject = (field: string) => ({ + bsonType: 'object', + required: [field], + properties: { [field]: { bsonType: 'string' } }, + additionalProperties: false, + }); + const validators = derive({ + roots: { users: crossRef('User') }, + models: { + User: model({ + _id: objectId(), + home: voField('Address', false, 'home'), + work: voField('Address', false, 'work'), + }), + }, + storage: { + namespaces: { + home: { + id: 'home', + entries: { + valueObject: { Address: { fields: { city: scalarField('mongo/string@1') } } }, + }, + }, + work: { + id: 'work', + entries: { + valueObject: { Address: { fields: { desk: scalarField('mongo/string@1') } } }, + }, + }, + }, + }, + }); + + expect(jsonSchemas(validators)['users']?.['properties']).toEqual({ + _id: { bsonType: 'objectId' }, + home: closedObject('city'), + work: closedObject('desk'), + }); + }); + describe('embedded models', () => { const Address = model({ city: scalarField('mongo/string@1') }); diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/test/derive-json-schema-helpers.ts b/packages/2-mongo-family/1-foundation/mongo-contract/test/derive-json-schema-helpers.ts index 425b875a8303..a42e30b41114 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/test/derive-json-schema-helpers.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/test/derive-json-schema-helpers.ts @@ -63,18 +63,22 @@ export function memberEnumField(codecId: string, enumName: string): ContractValu }; } -function valueObjectType(entityName: string): ContractField['type'] { +function valueObjectType(entityName: string, namespaceId = '__unbound__'): ContractField['type'] { return { kind: 'valueObject', plane: 'storage', - namespaceId: '__unbound__', + namespaceId, entityKind: 'valueObject', entityName, }; } -export function voField(name: string, nullable = false): FieldWithoutValueSet { - return { type: valueObjectType(name), nullable, many: false }; +export function voField( + name: string, + nullable = false, + namespaceId = '__unbound__', +): FieldWithoutValueSet { + return { type: valueObjectType(name, namespaceId), nullable, many: false }; } export function voArrayField( diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/test/recursive-groups.test.ts b/packages/2-mongo-family/1-foundation/mongo-contract/test/recursive-groups.test.ts index 8a6d15a4b22c..e85a975748a1 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/test/recursive-groups.test.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/test/recursive-groups.test.ts @@ -1,14 +1,26 @@ import { describe, expect, it } from 'vitest'; -import { recursiveGroupsOf, typeKey } from '../src/recursive-groups'; -import { embedMany, embedOne, model, objectId } from './collection-validator-helpers'; +import { modelKey, recursiveGroupsOf, valueObjectKey } from '../src/recursive-groups'; +import { + embedMany, + embedOne, + model, + objectId, + storageWithValueObjects, +} from './collection-validator-helpers'; import { scalarField, voArrayField, voField } from './derive-json-schema-helpers'; -const vo = (name: string) => typeKey('valueObject', name); +const vo = (entityName: string) => + valueObjectKey({ + plane: 'storage', + namespaceId: '__unbound__', + entityKind: 'valueObject', + entityName, + }); describe('recursiveGroupsOf', () => { it('puts a value object that refers to itself in its own group', () => { const groups = recursiveGroupsOf( - { NavItem: { fields: { children: voArrayField('NavItem') } } }, + storageWithValueObjects({ NavItem: { fields: { children: voArrayField('NavItem') } } }), {}, ); expect(groups.has(vo('NavItem'))).toBe(true); @@ -16,12 +28,12 @@ describe('recursiveGroupsOf', () => { it('puts value objects that reach each other in one group, and leaves the others out', () => { const groups = recursiveGroupsOf( - { + storageWithValueObjects({ A: { fields: { b: voField('B'), icon: voField('Icon') } }, B: { fields: { a: voField('A') } }, C: { fields: { a: voField('A') } }, Icon: { fields: { name: scalarField('mongo/string@1') } }, - }, + }), {}, ); expect(groups.get(vo('A'))).toBeDefined(); @@ -30,15 +42,12 @@ describe('recursiveGroupsOf', () => { }); it('follows embed relations between models', () => { - const groups = recursiveGroupsOf( - {}, - { - Root: model({ _id: objectId() }, { relations: { a: embedOne('A') } }), - A: model({}, { relations: { b: embedMany('B') } }), - B: model({}, { relations: { a: embedOne('A') } }), - }, - ); - expect(groups.get(typeKey('model', 'A'))).toBe(groups.get(typeKey('model', 'B'))); - expect(groups.has(typeKey('model', 'Root'))).toBe(false); + const groups = recursiveGroupsOf(storageWithValueObjects({}), { + Root: model({ _id: objectId() }, { relations: { a: embedOne('A') } }), + A: model({}, { relations: { b: embedMany('B') } }), + B: model({}, { relations: { a: embedOne('A') } }), + }); + expect(groups.get(modelKey('A'))).toBe(groups.get(modelKey('B'))); + expect(groups.has(modelKey('Root'))).toBe(false); }); }); diff --git a/packages/2-mongo-family/2-authoring/contract-ts/src/build-contract.ts b/packages/2-mongo-family/2-authoring/contract-ts/src/build-contract.ts index 2defeab45ed2..163a7eb57185 100644 --- a/packages/2-mongo-family/2-authoring/contract-ts/src/build-contract.ts +++ b/packages/2-mongo-family/2-authoring/contract-ts/src/build-contract.ts @@ -121,7 +121,11 @@ export function buildMongoContractFromDefinition( ? deriveCollectionValidators({ models: builtModels, lookups, - valueObjects: builtValueObjects, + storage: { + namespaces: { + [namespaceId]: { id: namespaceId, entries: { valueObject: builtValueObjects } }, + }, + }, valueSets: storageValueSets, controlPolicies, }) diff --git a/packages/3-targets/mongo/9-facade/test/mongo.enum.e2e.test.ts b/packages/3-targets/mongo/9-facade/test/mongo.enum.e2e.test.ts index 7e2499fb3659..f422c1b028b3 100644 --- a/packages/3-targets/mongo/9-facade/test/mongo.enum.e2e.test.ts +++ b/packages/3-targets/mongo/9-facade/test/mongo.enum.e2e.test.ts @@ -80,7 +80,7 @@ const storageValueSets = blindCast< const ACCOUNT_VALIDATOR = deriveCollectionValidators({ models: ns?.models ?? {}, lookups: { codecLookup, dataTypeLookup: createDataTypeLookup(mongoDataTypes) }, - valueObjects: {}, + storage: { namespaces: {} }, valueSets: storageValueSets, controlPolicies: { collections: {}, defaultControlPolicy: undefined }, })['accounts']; From d1f9c28015df2ba72fef9e74eb8fcf7ed46ee4bb Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 18:54:04 +0200 Subject: [PATCH 078/104] TML-3537: each Postgres codec declares the JSON type the value object check accepts for it, and the check reads it through the stack's codec lookup instead of a table in the target Signed-off-by: willbot Signed-off-by: Will Madden --- ... discriminator column names a value set.md | 2 +- .../subsystems/1. Data Contract.md | 2 +- .../1-core/contract/src/check-expressions.ts | 3 + .../contract-ts/src/build-contract.ts | 33 +++-- .../contract-ts/src/lower-column.ts | 33 +++-- .../src/core/arktype-json-codec.ts | 1 + .../3-extensions/pgvector/src/core/codecs.ts | 1 + .../3-extensions/postgis/src/core/codecs.ts | 1 + .../1-target/src/core/check-expressions.ts | 72 +++++++--- .../1-target/src/core/codec-descriptor.ts | 20 ++- .../postgres/1-target/src/core/codecs.ts | 32 +++++ .../postgres/1-target/src/core/date-codecs.ts | 2 + .../src/core/interval-string-codec.ts | 1 + .../src/core/psl-infer/infer-model-blocks.ts | 37 +++-- .../src/core/psl-print/model-attributes.ts | 19 ++- .../src/core/psl-print/psl-contract.ts | 1 + .../1-target/src/core/temporal-codecs.ts | 5 + .../src/core/temporal-string-codecs.ts | 4 + .../src/core/value-object-json-types.ts | 76 ---------- .../1-target/src/exports/codec-descriptor.ts | 1 + .../check-expressions.integration.test.ts | 52 ++++--- .../1-target/test/check-expressions.test.ts | 136 +++++++++++------- .../check-expressions.value-object.test.ts | 20 +-- .../test/codec-value-object-json-type.test.ts | 62 ++++++++ .../test/postgres-codec-descriptor.test-d.ts | 6 + .../test/postgres-codec-descriptor.test.ts | 10 ++ .../inferred-psl/inferred-psl.check.test.ts | 18 ++- ...erred-psl.list-element-nullability.test.ts | 18 ++- .../1-target/test/psl-print/build-context.ts | 1 + .../test/lower-to-execute-request.test.ts | 2 + .../alter-column-type.integration.test.ts | 18 ++- .../check-lifecycle-e2e.integration.test.ts | 22 +-- .../native-array-columns.integration.test.ts | 19 +-- ...ostgres-codec-registry-composition.test.ts | 2 + .../test/sql-renderer.cast-policy.test.ts | 2 + .../test/value-round-trip.integration.test.ts | 2 + .../authoring/extension-text-type.test.ts | 1 + .../sql-orm-client/include-codecs.test.ts | 1 + .../extension/instructions.md | 28 ++++ 39 files changed, 505 insertions(+), 261 deletions(-) delete mode 100644 packages/3-targets/postgres/1-target/src/core/value-object-json-types.ts create mode 100644 packages/3-targets/postgres/1-target/test/codec-value-object-json-type.test.ts diff --git a/docs/architecture docs/adrs/ADR 309 - Value objects are storage entities, and a discriminator column names a value set.md b/docs/architecture docs/adrs/ADR 309 - Value objects are storage entities, and a discriminator column names a value set.md index 171a6e44abef..e8e22a67f4be 100644 --- a/docs/architecture docs/adrs/ADR 309 - Value objects are storage entities, and a discriminator column names a value set.md +++ b/docs/architecture docs/adrs/ADR 309 - Value objects are storage entities, and a discriminator column names a value set.md @@ -106,7 +106,7 @@ jsonb_path_match("address", 'strict $.type() == "object" && !exists($.keyvalue() ? (@.key != "street"))') IS TRUE ``` -The value must be an object, or for a list an array of objects. Each required member must be present; a nullable member may be absent or JSON `null`; no other key may be. A scalar member must have the JSON type its codec writes, an enum member must hold a value of its value set, and a nested value object is checked the same way, inline. A nullable column also accepts SQL `NULL` and JSON `null`. Members are read through `keyvalue()`, so an absent member is never an error, and strict mode keeps a comparison from unwrapping an array. A value object that contains itself is checked in full down to where it refers back into itself, and any object is accepted below, as the MongoDB validator does. A `Decimal` member must be a JSON string, as the SQL ORM writes it. A member whose codec has no known JSON type is checked to be present and, when it is required, not JSON `null`; a JSON member may hold any value. The check is opaque and content-named ([ADR 244](ADR%20244%20-%20Check%20constraints%20are%20opaque%20wire-named%20expressions.md)), so any change to the value object changes the check's name, and the diff drops the old check and adds the new one. +The value must be an object, or for a list an array of objects. Each required member must be present; a nullable member may be absent or JSON `null`; no other key may be. A scalar member must have the JSON type its codec writes, an enum member must hold a value of its value set, and a nested value object is checked the same way, inline. A nullable column also accepts SQL `NULL` and JSON `null`. Members are read through `keyvalue()`, so an absent member is never an error, and strict mode keeps a comparison from unwrapping an array. A value object that contains itself is checked in full down to where it refers back into itself, and any object is accepted below, as the MongoDB validator does. A `Decimal` member must be a JSON string, as the SQL ORM writes it. Each PostgreSQL codec declares, on its descriptor, what the check accepts for a member of it: the JSON type the SQL ORM writes for its value; any JSON value, for a JSON codec; or presence only. A member whose codec declares presence only is checked to be present and, when it is required, not JSON `null`; a JSON member may hold any value. The check is opaque and content-named ([ADR 244](ADR%20244%20-%20Check%20constraints%20are%20opaque%20wire-named%20expressions.md)), so any change to the value object changes the check's name, and the diff drops the old check and adds the new one. **Why every change is an operation.** The storage hash identifies a database state. Two different hashes are two different database states, so the migration between them does something to the database. Because the value object and the discriminator values are enforced, a change to either is a change to a check or a validator, which is an operation. A plan that changes the storage hash and produces no operation is therefore unexpected: it means the database cannot tell the two states apart. Such cases will still arise, so the system handles them rather than refusing: `migration plan` writes the package with no operations and says so, naming what changed, and `db migrate` applies it by moving the marker to the new hash. diff --git a/docs/architecture docs/subsystems/1. Data Contract.md b/docs/architecture docs/subsystems/1. Data Contract.md index 7c819a5b9756..e282725c14d8 100644 --- a/docs/architecture docs/subsystems/1. Data Contract.md +++ b/docs/architecture docs/subsystems/1. Data Contract.md @@ -321,7 +321,7 @@ A `pg/int4@1` enum `Int4Level` with members `Low = 1` and `High = 10`, on column A list column gets `<@` containment instead of `IN`. In the same fixture, `levels FloatMixed[]`, whose members are written `1.5` and `NaN` and stored as `1.5` and `"NaN"`, gets `array_remove("levels", NULL) <@ '{1.5,"NaN"}'`, plus the derived check `array_position("levels", NULL) IS NULL` because its elements are not nullable. -On a `managed` Postgres table two more columns derive checks ([ADR 309](../adrs/ADR%20309%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md)). A discriminator column with no enum gets a membership check, `
__check`, holding the values of its `.` value set. A column that holds a value object gets a check named `
__value_object`: one strict `jsonpath` predicate that the column value is an object (or, for a list, an array of objects) with each required member present, no other key, each scalar member of its JSON type, each enum member one of its value set's values, and each nested value object checked the same way. A nullable column accepts SQL `NULL` and JSON `null`; the ORM writes SQL `NULL` for an absent value. A `Decimal` member must be a JSON string, as the ORM writes it. A required member whose codec has no known JSON type must be present and not JSON `null`; a `Json` member may hold any value. Below the point where a value object refers back into itself, any object is accepted, as the MongoDB validator does. +On a `managed` Postgres table two more columns derive checks ([ADR 309](../adrs/ADR%20309%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md)). A discriminator column with no enum gets a membership check, `
__check`, holding the values of its `.` value set. A column that holds a value object gets a check named `
__value_object`: one strict `jsonpath` predicate that the column value is an object (or, for a list, an array of objects) with each required member present, no other key, each scalar member of its JSON type, each enum member one of its value set's values, and each nested value object checked the same way. A nullable column accepts SQL `NULL` and JSON `null`; the ORM writes SQL `NULL` for an absent value. A `Decimal` member must be a JSON string, as the ORM writes it. Each Postgres codec declares on its descriptor the JSON type a member of it has (`valueObjectJsonType`), or that it holds any JSON value, or that only its presence is checked. A required member whose codec declares presence only must be present and not JSON `null`; a `Json` member may hold any value. Below the point where a value object refers back into itself, any object is accepted, as the MongoDB validator does. `contract infer` produces no value objects and no polymorphism, so on a live table with these checks it prints each one as an authored `@@check` with its exact name in `map:`, as it does for an enum membership check. diff --git a/packages/2-sql-family/1-core/contract/src/check-expressions.ts b/packages/2-sql-family/1-core/contract/src/check-expressions.ts index c6d6bb2b26c0..5255db253b8e 100644 --- a/packages/2-sql-family/1-core/contract/src/check-expressions.ts +++ b/packages/2-sql-family/1-core/contract/src/check-expressions.ts @@ -5,6 +5,7 @@ import type { EntityCoordinate, } from '@internal/contract/types'; import { valueObjectAt } from '@internal/contract/types'; +import type { CodecLookupWithDescriptors } from '@internal/framework-components/codec'; import type { CheckKind } from '@internal/sql-schema-ir/naming'; import { invariant } from '@internal/utils/assertions'; import { isStorageValueSet } from './ir/storage-value-set'; @@ -57,8 +58,10 @@ export interface CheckExpressionCandidate { readonly expression: string; } +/** A target's check renderer. It reads what it needs of a value object member's codec, such as the JSON type the codec declares, through `codecLookup`. */ export type CheckExpressionRenderer = ( input: CheckExpressionInput, + codecLookup: Pick, ) => ReadonlyArray; /** diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts b/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts index 847e83a35971..c5b9921c47c3 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts @@ -214,6 +214,7 @@ function writeDiscriminatorValueSets( tablesByNamespace: Record>, storageValueSetsByNs: Record>, checkRendererFor: (table: StorageTableInput) => CheckExpressionRenderer | undefined, + codecLookup: CodecLookupWithDescriptors, ): void { const defaultNamespaceId = definition.target.defaultNamespaceId; for (const model of definition.models) { @@ -255,13 +256,16 @@ function writeDiscriminatorValueSets( ? [] : lowerRenderedChecks( model.tableName, - render({ - tableName: model.tableName, - columnName, - many: false, - memberValues: values, - valueObject: undefined, - }), + render( + { + tableName: model.tableName, + columnName, + many: false, + memberValues: values, + valueObject: undefined, + }, + codecLookup, + ), ); const namespaceTables = tablesByNamespace[namespaceId] ?? {}; namespaceTables[model.tableName] = { @@ -388,11 +392,16 @@ export function buildSqlContractFromDefinition( namespaceTables[table.tableName] = lowerTable(table, tableLoweringContext); tablesByNamespace[table.namespaceId] = namespaceTables; } - writeDiscriminatorValueSets(definition, tablesByNamespace, storageValueSetsByNs, (table) => - renderCheckExpressions !== undefined && - effectiveControlPolicy(table.control, definition.defaultControlPolicy) === 'managed' - ? renderCheckExpressions - : undefined, + writeDiscriminatorValueSets( + definition, + tablesByNamespace, + storageValueSetsByNs, + (table) => + renderCheckExpressions !== undefined && + effectiveControlPolicy(table.control, definition.defaultControlPolicy) === 'managed' + ? renderCheckExpressions + : undefined, + codecLookup, ); const executionDefaults: ExecutionMutationDefault[] = components.flatMap( diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/lower-column.ts b/packages/2-sql-family/2-authoring/contract-ts/src/lower-column.ts index a29614ced0de..4d1a0b4920a1 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/lower-column.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/lower-column.ts @@ -159,20 +159,23 @@ export function lowerColumn( return { column, derivedChecks: [] }; } const { enumTypeHandle } = description; - const candidates = render({ - tableName: placement.tableName, - columnName: description.columnName, - many, - memberValues: - enumTypeHandle !== undefined ? checkMemberValues(enumTypeHandle, codecLookup) : undefined, - valueObject: - description.valueObject !== undefined - ? resolveColumnValueObject(context.storage, { - reference: description.valueObject.reference, - nullable: description.nullable, - many: description.valueObject.many, - }) - : undefined, - }); + const candidates = render( + { + tableName: placement.tableName, + columnName: description.columnName, + many, + memberValues: + enumTypeHandle !== undefined ? checkMemberValues(enumTypeHandle, codecLookup) : undefined, + valueObject: + description.valueObject !== undefined + ? resolveColumnValueObject(context.storage, { + reference: description.valueObject.reference, + nullable: description.nullable, + many: description.valueObject.many, + }) + : undefined, + }, + codecLookup, + ); return { column, derivedChecks: lowerRenderedChecks(placement.tableName, candidates) }; } diff --git a/packages/3-extensions/arktype-json/src/core/arktype-json-codec.ts b/packages/3-extensions/arktype-json/src/core/arktype-json-codec.ts index e5d56647cbe5..638332886229 100644 --- a/packages/3-extensions/arktype-json/src/core/arktype-json-codec.ts +++ b/packages/3-extensions/arktype-json/src/core/arktype-json-codec.ts @@ -179,6 +179,7 @@ const arktypeJsonParamsSchema = type({ }) satisfies StandardSchemaV1; export class ArktypeJsonDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'anyJson' as const; /** The document's JSON text, which `fromWire` parses as it parses a row; a document that is a string would otherwise reach it unquoted. */ protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return CastExpr.as(expression, 'text'); diff --git a/packages/3-extensions/pgvector/src/core/codecs.ts b/packages/3-extensions/pgvector/src/core/codecs.ts index fc741e0d07b3..f75c813c985b 100644 --- a/packages/3-extensions/pgvector/src/core/codecs.ts +++ b/packages/3-extensions/pgvector/src/core/codecs.ts @@ -140,6 +140,7 @@ const vectorTextJsonProjection = (expression: ProjectionExpr): ProjectionExpr => CastExpr.as(expression, 'text'); export class PgVectorDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'presenceOnly' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return vectorTextJsonProjection(expression); } diff --git a/packages/3-extensions/postgis/src/core/codecs.ts b/packages/3-extensions/postgis/src/core/codecs.ts index 8b589977f132..8ea33885a1bf 100644 --- a/packages/3-extensions/postgis/src/core/codecs.ts +++ b/packages/3-extensions/postgis/src/core/codecs.ts @@ -115,6 +115,7 @@ export class PostgisGeometryCodec extends CodecImpl< } export class PostgisGeometryDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'presenceOnly' as const; /** The HEXEWKB text PostgreSQL prints for a geometry, which `fromWire` reads as it reads a row. A JSON constructor handed the geometry itself may write GeoJSON instead. */ protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return CastExpr.as(expression, 'text'); diff --git a/packages/3-targets/postgres/1-target/src/core/check-expressions.ts b/packages/3-targets/postgres/1-target/src/core/check-expressions.ts index 55bfaad69c9c..e7d66bb2e9a8 100644 --- a/packages/3-targets/postgres/1-target/src/core/check-expressions.ts +++ b/packages/3-targets/postgres/1-target/src/core/check-expressions.ts @@ -1,3 +1,4 @@ +import type { CodecLookupWithDescriptors } from '@internal/framework-components/codec'; import type { CheckExpressionCandidate, CheckExpressionInput, @@ -7,11 +8,8 @@ import type { ResolvedValueObjectFieldType, } from '@internal/sql-contract/check-expressions'; import { invariant } from '@internal/utils/assertions'; +import { isPostgresCodecDescriptor, type ValueObjectJsonType } from './codec-descriptor'; import { escapeLiteral, quoteIdentifier } from './sql-utils'; -import { - valueObjectJsonTypeOfCodec, - valueObjectMemberHoldsAnyJson, -} from './value-object-json-types'; /** * Renders the checks a Postgres column needs, as opaque predicate text. @@ -29,6 +27,7 @@ import { */ export function postgresRenderCheckExpressions( input: CheckExpressionInput, + codecLookup: Pick, ): readonly CheckExpressionCandidate[] { const candidates: CheckExpressionCandidate[] = []; const column = quoteIdentifier(input.columnName); @@ -67,7 +66,7 @@ export function postgresRenderCheckExpressions( candidates.push({ kind: 'valueObject', columnName: input.columnName, - expression: valueObjectCheck(column, input.valueObject), + expression: valueObjectCheck(column, input.valueObject, jsonTypeReader(codecLookup)), }); } @@ -77,13 +76,33 @@ export function postgresRenderCheckExpressions( /** * One strict `jsonpath` predicate over the whole column value. Strict mode keeps a comparison from unwrapping an array, and `IS TRUE` makes a predicate that evaluates to unknown fail the check, as a CHECK would otherwise pass on NULL. Member values are read through `keyvalue()`, so an absent member is never an error. */ -function valueObjectCheck(column: string, columnValueObject: ResolvedColumnValueObject): string { +type JsonTypeOfCodec = (codecId: string) => ValueObjectJsonType; + +/** The JSON type each codec declares for a value object member, read from the codec's descriptor. */ +function jsonTypeReader( + codecLookup: Pick, +): JsonTypeOfCodec { + return (codecId) => { + const descriptor = codecLookup.descriptorFor(codecId); + invariant( + descriptor !== undefined && isPostgresCodecDescriptor(descriptor), + `value object member codec "${codecId}" is not a PostgreSQL codec of the stack; the contract validators refuse a codec the stack does not register`, + ); + return descriptor.valueObjectJsonType; + }; +} + +function valueObjectCheck( + column: string, + columnValueObject: ResolvedColumnValueObject, + jsonTypeOf: JsonTypeOfCodec, +): string { const field: ResolvedValueObjectField = { nullable: columnValueObject.nullable, many: columnValueObject.many, type: { kind: 'valueObject', valueObject: columnValueObject.valueObject }, }; - const predicate = fieldPredicate(field, '$'); + const predicate = fieldPredicate(field, '$', jsonTypeOf); invariant(predicate !== undefined, 'a column that holds a value object always has a predicate'); const match = (path: string) => `jsonb_path_match(${column}, '${escapeLiteral(`strict ${path}`)}') IS TRUE`; @@ -93,9 +112,13 @@ function valueObjectCheck(column: string, columnValueObject: ResolvedColumnValue } /** What the value at `path` must satisfy, or `undefined` when any value is accepted. */ -function fieldPredicate(field: ResolvedValueObjectField, path: string): string | undefined { - if (field.many === false) return valuePredicate(field.type, path); - const element = valuePredicate(field.type, '@'); +function fieldPredicate( + field: ResolvedValueObjectField, + path: string, + jsonTypeOf: JsonTypeOfCodec, +): string | undefined { + if (field.many === false) return valuePredicate(field.type, path, jsonTypeOf); + const element = valuePredicate(field.type, '@', jsonTypeOf); const { elementNullable } = field.many; const invalidElement = element === undefined @@ -111,16 +134,22 @@ function fieldPredicate(field: ResolvedValueObjectField, path: string): string | : `${isArray} && !exists(${path}[*] ? (${invalidElement}))`; } -function valuePredicate(type: ResolvedValueObjectFieldType, path: string): string | undefined { +function valuePredicate( + type: ResolvedValueObjectFieldType, + path: string, + jsonTypeOf: JsonTypeOfCodec, +): string | undefined { switch (type.kind) { case 'valueObject': - return objectPredicate(type.valueObject, path); + return objectPredicate(type.valueObject, path, jsonTypeOf); case 'anyObject': return `${path}.type() == "object"`; case 'scalar': { if (type.values !== undefined) return valueSetPredicate(type.values, path); - const jsonType = valueObjectJsonTypeOfCodec(type.codecId); - return jsonType === undefined ? undefined : `${path}.type() == "${jsonType}"`; + const jsonType = jsonTypeOf(type.codecId); + return jsonType === 'anyJson' || jsonType === 'presenceOnly' + ? undefined + : `${path}.type() == "${jsonType}"`; } } } @@ -136,13 +165,21 @@ function valueSetPredicate(values: readonly (string | number)[], path: string): return `(${alternatives('string', strings)}) || (${alternatives('number', numbers)})`; } -function objectPredicate(valueObject: ResolvedValueObject, path: string): string { +function objectPredicate( + valueObject: ResolvedValueObject, + path: string, + jsonTypeOf: JsonTypeOfCodec, +): string { const names = Object.keys(valueObject.fields).sort(); const members = names.flatMap((name) => { const field = valueObject.fields[name]; invariant(field !== undefined, `value object member "${name}" is missing`); const isMember = `@.key == ${jsonPathLiteral(name)}`; - const predicate = requiredMemberPredicate(field, fieldPredicate(field, '@.value')); + const predicate = requiredMemberPredicate( + field, + fieldPredicate(field, '@.value', jsonTypeOf), + jsonTypeOf, + ); if (!field.nullable) { return [ predicate === undefined @@ -166,9 +203,10 @@ function objectPredicate(valueObject: ResolvedValueObject, path: string): string function requiredMemberPredicate( field: ResolvedValueObjectField, predicate: string | undefined, + jsonTypeOf: JsonTypeOfCodec, ): string | undefined { if (predicate !== undefined || field.nullable || field.type.kind !== 'scalar') return predicate; - return valueObjectMemberHoldsAnyJson(field.type.codecId) ? undefined : '@.value.type() != "null"'; + return jsonTypeOf(field.type.codecId) === 'anyJson' ? undefined : '@.value.type() != "null"'; } function jsonPathLiteral(value: string | number): string { diff --git a/packages/3-targets/postgres/1-target/src/core/codec-descriptor.ts b/packages/3-targets/postgres/1-target/src/core/codec-descriptor.ts index 8ea15cf81e3b..12c08560ab9f 100644 --- a/packages/3-targets/postgres/1-target/src/core/codec-descriptor.ts +++ b/packages/3-targets/postgres/1-target/src/core/codec-descriptor.ts @@ -39,8 +39,17 @@ const ARRAY_ELEMENT_ALIAS = 'array_element'; const ARRAY_VALUE_COLUMN = 'value'; const ARRAY_ORDINALITY_COLUMN = 'ordinality'; +const VALUE_OBJECT_JSON_TYPES = ['string', 'number', 'boolean', 'anyJson', 'presenceOnly'] as const; + +/** + * What the check of a value object column accepts for a member of a codec: the JSON type the SQL ORM writes for its value (`'string'`, `'number'` or `'boolean'`), any JSON value (`'anyJson'`, for a JSON codec), or any value at all, checked only for being present and, when required, not JSON `null` (`'presenceOnly'`, for a codec whose JSON form the check does not pin down). + */ +export type ValueObjectJsonType = (typeof VALUE_OBJECT_JSON_TYPES)[number]; + export interface AnyPostgresCodecDescriptor extends AnyCodecDescriptor { readonly descriptorKind: typeof POSTGRES_CODEC_DESCRIPTOR_KIND; + /** See {@link ValueObjectJsonType}. Every PostgreSQL codec declares it. */ + readonly valueObjectJsonType: ValueObjectJsonType; projectJson(expression: ProjectionExpr, ref: CodecRef): ProjectionExpr; } @@ -49,6 +58,7 @@ export abstract class PostgresCodecDescriptor

implements AnyPostgresCodecDescriptor { readonly descriptorKind = POSTGRES_CODEC_DESCRIPTOR_KIND; + abstract readonly valueObjectJsonType: ValueObjectJsonType; protected abstract jsonProjection(expression: ProjectionExpr, params: P): ProjectionExpr; @@ -123,6 +133,8 @@ export interface PostgresCodecOptions< readonly dataType: DataType; /** Renders the literal of the value the codec reads from a stored value of `dataType`. A family template renders no literal, because only the target knows the form its data type stores a value in, so the adapted codec renders with this and never with a renderer on the template. */ readonly renderValueLiteral?: (value: JsonValue, side: 'output' | 'input') => string | undefined; + /** See {@link ValueObjectJsonType}. */ + readonly valueObjectJsonType: ValueObjectJsonType; readonly jsonProjection: (expression: ProjectionExpr, params: P) => ProjectionExpr; readonly jsonArrayProjection?: (expression: ProjectionExpr, params: P) => ProjectionExpr; /** @@ -140,12 +152,13 @@ export type AdaptedPostgresCodecDescriptor keyof CodecDescriptorTemplate> > & Pick & - Pick; + Pick; class PostgresCodecDescriptorAdapter< D extends AnyCodecDescriptorTemplate, > extends PostgresCodecDescriptor> { override readonly dataType: DataTypeId; + override readonly valueObjectJsonType: ValueObjectJsonType; override readonly codecId: string; override readonly traits: readonly CodecTrait[]; override readonly paramsSchema: StandardSchemaV1> | undefined; @@ -166,6 +179,7 @@ class PostgresCodecDescriptorAdapter< ) { super(); this.dataType = options.dataType.id; + this.valueObjectJsonType = options.valueObjectJsonType; this.codecId = descriptor.codecId; this.traits = descriptor.traits; this.paramsSchema = blindCast< @@ -253,7 +267,9 @@ export function isPostgresCodecDescriptor(value: unknown): value is AnyPostgresC 'factory' in value && typeof value.factory === 'function' && 'projectJson' in value && - typeof value.projectJson === 'function' + typeof value.projectJson === 'function' && + 'valueObjectJsonType' in value && + VALUE_OBJECT_JSON_TYPES.some((jsonType) => jsonType === value.valueObjectJsonType) ); } diff --git a/packages/3-targets/postgres/1-target/src/core/codecs.ts b/packages/3-targets/postgres/1-target/src/core/codecs.ts index 19384ba30946..a4cd35447411 100644 --- a/packages/3-targets/postgres/1-target/src/core/codecs.ts +++ b/packages/3-targets/postgres/1-target/src/core/codecs.ts @@ -227,6 +227,7 @@ const printedTextJsonProjection = (expression: ProjectionExpr): ProjectionExpr = CastExpr.as(expression, 'text'); export const postgresSqlCharDescriptor = postgresCodec(sqlCharDescriptor, { + valueObjectJsonType: 'string', dataType: pgChar, renderValueLiteral: renderTsLiteral, jsonProjection: unpaddedCharJsonProjection, @@ -234,6 +235,7 @@ export const postgresSqlCharDescriptor = postgresCodec(sqlCharDescriptor, { }); export const postgresSqlVarcharDescriptor = postgresCodec(sqlVarcharDescriptor, { + valueObjectJsonType: 'string', dataType: pgVarchar, renderValueLiteral: renderTsLiteral, jsonProjection: identityJsonProjection, @@ -242,6 +244,7 @@ export const postgresSqlVarcharDescriptor = postgresCodec(sqlVarcharDescriptor, }); export const postgresSqlIntDescriptor = postgresCodec(sqlIntDescriptor, { + valueObjectJsonType: 'number', dataType: pgInt4, renderValueLiteral: renderTsLiteral, jsonProjection: identityJsonProjection, @@ -249,6 +252,7 @@ export const postgresSqlIntDescriptor = postgresCodec(sqlIntDescriptor, { }); export const postgresSqlFloatDescriptor = postgresCodec(sqlFloatDescriptor, { + valueObjectJsonType: 'number', dataType: pgFloat8, renderValueLiteral: floatNumberLiteral, jsonProjection: identityJsonProjection, @@ -256,6 +260,7 @@ export const postgresSqlFloatDescriptor = postgresCodec(sqlFloatDescriptor, { }); export const postgresSqlTextDescriptor = postgresCodec(sqlTextDescriptor, { + valueObjectJsonType: 'string', dataType: pgText, renderValueLiteral: renderTsLiteral, jsonProjection: identityJsonProjection, @@ -276,6 +281,7 @@ const outputFunctionJsonProjection = (expression: ProjectionExpr): ProjectionExp ); export const postgresSqlRawDescriptor = postgresCodec(sqlRawDescriptor, { + valueObjectJsonType: 'presenceOnly', dataType: sqlRawDataType, jsonProjection: outputFunctionJsonProjection, factory: (descriptor, dataType, params) => () => new SqlRawCodec(descriptor, dataType, params), @@ -302,6 +308,7 @@ export class PgTextCodec extends CodecImpl< } export class PgTextDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return expression; } @@ -381,6 +388,7 @@ export function isPgEnumParams(value: unknown): value is PgEnumParams { } export class PgEnumDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'presenceOnly' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return expression; } @@ -506,6 +514,7 @@ export class PgTextArrayCodec extends CodecImpl< } export class PgTextArrayDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'presenceOnly' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return printedTextJsonProjection(expression); } @@ -541,6 +550,7 @@ export class PgInt4Codec extends CodecImpl< } export class PgInt4Descriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'number' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return expression; } @@ -585,6 +595,7 @@ export class PgInt2Codec extends CodecImpl< } export class PgInt2Descriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'number' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return expression; } @@ -635,6 +646,7 @@ export class PgInt8Codec extends CodecImpl< } export class PgInt8Descriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'presenceOnly' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return decimalTextJsonProjection(expression); } @@ -687,6 +699,7 @@ export class PgInt8NumberCodec extends CodecImpl< } export class PgInt8NumberDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'number' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return decimalTextJsonProjection(expression); } @@ -731,6 +744,7 @@ export class PgFloat4Codec extends CodecImpl< } export class PgFloat4Descriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'number' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return expression; } @@ -775,6 +789,7 @@ export class PgFloat8Codec extends CodecImpl< } export class PgFloat8Descriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'number' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return expression; } @@ -819,6 +834,7 @@ export class PgBoolCodec extends CodecImpl< } export class PgBoolDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'boolean' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return expression; } @@ -863,6 +879,7 @@ export class PgNumericCodec extends CodecImpl< } export class PgNumericDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return decimalTextJsonProjection(expression); } @@ -912,6 +929,7 @@ export class PgUnboundedIntCodec extends CodecImpl< } export class PgUnboundedIntDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'presenceOnly' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return decimalTextJsonProjection(expression); } @@ -962,6 +980,7 @@ export class PgTimetzCodec extends CodecImpl< } export class PgTimetzDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'presenceOnly' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return expression; } @@ -1006,6 +1025,7 @@ export class PgBitCodec extends CodecImpl< } export class PgBitDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'presenceOnly' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return expression; } @@ -1050,6 +1070,7 @@ export class PgVarbitCodec extends CodecImpl< } export class PgVarbitDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'presenceOnly' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return expression; } @@ -1094,6 +1115,7 @@ export class PgByteaCodec extends CodecImpl< } export class PgByteaDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'presenceOnly' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return printedTextJsonProjection(expression); } @@ -1137,6 +1159,7 @@ export class PgUuidCodec extends CodecImpl< } export class PgUuidDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return expression; } @@ -1181,6 +1204,7 @@ export class PgInetCodec extends CodecImpl< } export class PgInetDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'presenceOnly' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return expression; } @@ -1241,6 +1265,7 @@ export class PgTsqueryCodec extends CodecImpl< * column, and no traits, because comparing or ordering queries means nothing to an application. */ export class PgTsqueryDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'presenceOnly' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return expression; } @@ -1300,6 +1325,7 @@ export class PgIntervalCodec extends CodecImpl< } export class PgIntervalDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'presenceOnly' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return printedTextJsonProjection(expression); } @@ -1342,6 +1368,7 @@ export class PgJsonCodec extends CodecImpl< } export class PgJsonDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'anyJson' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return printedTextJsonProjection(expression); } @@ -1385,6 +1412,7 @@ export class PgJsonbCodec extends CodecImpl< } export class PgJsonbDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'anyJson' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return printedTextJsonProjection(expression); } @@ -1426,6 +1454,7 @@ export class PgFloatCodec extends SqlFloatCodec { } export class PgCharDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return unpaddedCharJsonProjection(expression); } @@ -1449,6 +1478,7 @@ export const pgCharColumn = (params: LengthParams = {}) => pgCharColumn satisfies ColumnHelperFor; export class PgVarcharDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return expression; } @@ -1472,6 +1502,7 @@ export const pgVarcharColumn = (params: LengthParams = {}) => pgVarcharColumn satisfies ColumnHelperFor; export class PgIntDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'number' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return expression; } @@ -1495,6 +1526,7 @@ export const pgIntColumn = () => pgIntColumn satisfies ColumnHelperFor; export class PgFloatDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'number' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return expression; } diff --git a/packages/3-targets/postgres/1-target/src/core/date-codecs.ts b/packages/3-targets/postgres/1-target/src/core/date-codecs.ts index 8690b596b2dc..8ab374203bfd 100644 --- a/packages/3-targets/postgres/1-target/src/core/date-codecs.ts +++ b/packages/3-targets/postgres/1-target/src/core/date-codecs.ts @@ -134,6 +134,7 @@ export class PgTimestamptzDateCodec extends CodecImpl< } export class PgTimestamptzDateDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return CastExpr.as(expression, 'text'); } @@ -178,6 +179,7 @@ export class PgTimestampDateCodec extends CodecImpl< } export class PgTimestampDateDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return CastExpr.as(expression, 'text'); } diff --git a/packages/3-targets/postgres/1-target/src/core/interval-string-codec.ts b/packages/3-targets/postgres/1-target/src/core/interval-string-codec.ts index 56bd9c26df92..ff2dc2d082cb 100644 --- a/packages/3-targets/postgres/1-target/src/core/interval-string-codec.ts +++ b/packages/3-targets/postgres/1-target/src/core/interval-string-codec.ts @@ -70,6 +70,7 @@ export class PgIntervalStringCodec extends CodecImpl< } export class PgIntervalStringDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return CastExpr.as(expression, 'text'); } diff --git a/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-model-blocks.ts b/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-model-blocks.ts index b46916917ea1..75015f7d288d 100644 --- a/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-model-blocks.ts +++ b/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-model-blocks.ts @@ -197,6 +197,9 @@ export function buildModel( }; } +/** Infer derives no value object check, so the check renderer reads no codec. */ +const NO_CODECS = { descriptorFor: () => undefined }; + /** * The live check names that are derived: a live check's name matches `formatWireName(composeCheckWirePrefix(table, column, kind), computeCheckContentHash(expression))` for some column of the table and some candidate `postgresRenderCheckExpressions` would render for that column. Never by comparing expressions — the live body is a Postgres reprint, never the authored text. One set serves both the element nullability of list fields and `@@check` exclusion in `buildModel`, so the two decisions cannot drift apart. * @@ -206,13 +209,16 @@ function computeDerivedCheckNames(table: SqlTableIR): ReadonlySet { const liveCheckNames = new Set((table.checks ?? []).map((check) => check.name)); const derivedCheckNames = new Set(); for (const column of Object.values(table.columns)) { - for (const candidate of postgresRenderCheckExpressions({ - tableName: table.name, - columnName: column.name, - many: column.many === true ? { elementNullable: false } : false, - memberValues: undefined, - valueObject: undefined, - })) { + for (const candidate of postgresRenderCheckExpressions( + { + tableName: table.name, + columnName: column.name, + many: column.many === true ? { elementNullable: false } : false, + memberValues: undefined, + valueObject: undefined, + }, + NO_CODECS, + )) { const derivedName = formatWireName( composeCheckWirePrefix(table.name, column.name, candidate.kind), computeCheckContentHash(candidate.expression), @@ -327,13 +333,16 @@ function enforcesElementNotNull( column: SqlColumnIR, derivedCheckNames: ReadonlySet, ): boolean { - return postgresRenderCheckExpressions({ - tableName: table.name, - columnName: column.name, - many: { elementNullable: false }, - memberValues: undefined, - valueObject: undefined, - }).some( + return postgresRenderCheckExpressions( + { + tableName: table.name, + columnName: column.name, + many: { elementNullable: false }, + memberValues: undefined, + valueObject: undefined, + }, + NO_CODECS, + ).some( (candidate) => candidate.kind === 'elementNotNull' && derivedCheckNames.has( diff --git a/packages/3-targets/postgres/1-target/src/core/psl-print/model-attributes.ts b/packages/3-targets/postgres/1-target/src/core/psl-print/model-attributes.ts index bac3e014b31e..b04a6d938409 100644 --- a/packages/3-targets/postgres/1-target/src/core/psl-print/model-attributes.ts +++ b/packages/3-targets/postgres/1-target/src/core/psl-print/model-attributes.ts @@ -1,3 +1,4 @@ +import type { CodecLookupWithDescriptors } from '@internal/framework-components/codec'; import type { PslModelAttribute } from '@internal/framework-components/psl-ast'; import type { ResolvedColumnValueObject } from '@internal/sql-contract/check-expressions'; import { escapePslString } from '@internal/sql-contract/data-type-support'; @@ -49,6 +50,7 @@ export function derivedChecks(input: { readonly memberValueSets: ReadonlyMap; /** The value object field each column that holds one stores, by column name. */ readonly columnValueObjects: ReadonlyMap; + readonly codecLookup: Pick; }): ReadonlyMap { const checks = new Map(); if (!input.managed) return checks; @@ -60,13 +62,16 @@ export function derivedChecks(input: { const memberValues = setValues?.filter( (value): value is string | number => typeof value === 'string' || typeof value === 'number', ); - for (const candidate of postgresRenderCheckExpressions({ - tableName: input.tableName, - columnName, - many: column.many, - memberValues: memberValues?.length === setValues?.length ? memberValues : undefined, - valueObject: input.columnValueObjects.get(columnName), - })) { + for (const candidate of postgresRenderCheckExpressions( + { + tableName: input.tableName, + columnName, + many: column.many, + memberValues: memberValues?.length === setValues?.length ? memberValues : undefined, + valueObject: input.columnValueObjects.get(columnName), + }, + input.codecLookup, + )) { const prefix = composeCheckWirePrefix(input.tableName, columnName, candidate.kind); checks.set(formatWireName(prefix, computeCheckContentHash(candidate.expression)), { prefix, diff --git a/packages/3-targets/postgres/1-target/src/core/psl-print/psl-contract.ts b/packages/3-targets/postgres/1-target/src/core/psl-print/psl-contract.ts index a8fad6167316..9e4681042ac6 100644 --- a/packages/3-targets/postgres/1-target/src/core/psl-print/psl-contract.ts +++ b/packages/3-targets/postgres/1-target/src/core/psl-print/psl-contract.ts @@ -221,6 +221,7 @@ function buildModel( [discriminatorValueSet.name, discriminatorValueSet.values], ]), columnValueObjects: columnValueObjectsOf(all.contract, [entry, ...singleTableVariants]), + codecLookup: all.context.codecLookup, }); if (variant?.singleTable !== true) refuseUnderivedChecks(entry, checks); return { diff --git a/packages/3-targets/postgres/1-target/src/core/temporal-codecs.ts b/packages/3-targets/postgres/1-target/src/core/temporal-codecs.ts index dd5fba1d7b96..8b022d4044ba 100644 --- a/packages/3-targets/postgres/1-target/src/core/temporal-codecs.ts +++ b/packages/3-targets/postgres/1-target/src/core/temporal-codecs.ts @@ -64,6 +64,7 @@ export class PgDateTemporalCodec extends CodecImpl< } export class PgDateTemporalDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return CastExpr.as(expression, 'text'); } @@ -111,6 +112,7 @@ export class PgTimestampTemporalCodec extends CodecImpl< } export class PgTimestampTemporalDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return CastExpr.as(expression, 'text'); } @@ -167,6 +169,7 @@ export class PgTimestampInstantCodec extends CodecImpl< } export class PgTimestampInstantDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return CastExpr.as(expression, 'text'); } @@ -220,6 +223,7 @@ export class PgTimestamptzTemporalCodec extends CodecImpl< } export class PgTimestamptzTemporalDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return CastExpr.as(expression, 'text'); } @@ -267,6 +271,7 @@ export class PgTimeTemporalCodec extends CodecImpl< } export class PgTimeTemporalDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return CastExpr.as(expression, 'text'); } diff --git a/packages/3-targets/postgres/1-target/src/core/temporal-string-codecs.ts b/packages/3-targets/postgres/1-target/src/core/temporal-string-codecs.ts index 648d87e1b34b..c651fa684d25 100644 --- a/packages/3-targets/postgres/1-target/src/core/temporal-string-codecs.ts +++ b/packages/3-targets/postgres/1-target/src/core/temporal-string-codecs.ts @@ -50,6 +50,7 @@ export class PgDateStringCodec extends CodecImpl< } export class PgDateStringDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return CastExpr.as(expression, 'text'); } @@ -105,6 +106,7 @@ export class PgTimestampStringCodec extends CodecImpl< } export class PgTimestampStringDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return CastExpr.as(expression, 'text'); } @@ -149,6 +151,7 @@ export class PgTimestamptzStringCodec extends CodecImpl< } export class PgTimestamptzStringDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return CastExpr.as(expression, 'text'); } @@ -199,6 +202,7 @@ export class PgTimeStringCodec extends CodecImpl< } export class PgTimeStringDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return CastExpr.as(expression, 'text'); } diff --git a/packages/3-targets/postgres/1-target/src/core/value-object-json-types.ts b/packages/3-targets/postgres/1-target/src/core/value-object-json-types.ts deleted file mode 100644 index bd9945ec2999..000000000000 --- a/packages/3-targets/postgres/1-target/src/core/value-object-json-types.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { - PG_BOOL_CODEC_ID, - PG_CHAR_CODEC_ID, - PG_DATE_STRING_CODEC_ID, - PG_DATE_TEMPORAL_CODEC_ID, - PG_FLOAT_CODEC_ID, - PG_FLOAT4_CODEC_ID, - PG_FLOAT8_CODEC_ID, - PG_INT_CODEC_ID, - PG_INT2_CODEC_ID, - PG_INT4_CODEC_ID, - PG_INT8_NUMBER_CODEC_ID, - PG_JSON_CODEC_ID, - PG_JSONB_CODEC_ID, - PG_NUMERIC_CODEC_ID, - PG_TEXT_CODEC_ID, - PG_TIME_STRING_CODEC_ID, - PG_TIME_TEMPORAL_CODEC_ID, - PG_TIMESTAMP_STRING_CODEC_ID, - PG_TIMESTAMP_TEMPORAL_CODEC_ID, - PG_TIMESTAMPTZ_DATE_CODEC_ID, - PG_TIMESTAMPTZ_STRING_CODEC_ID, - PG_TIMESTAMPTZ_TEMPORAL_CODEC_ID, - PG_UUID_CODEC_ID, - PG_VARCHAR_CODEC_ID, - SQL_CHAR_CODEC_ID, - SQL_FLOAT_CODEC_ID, - SQL_INT_CODEC_ID, - SQL_TEXT_CODEC_ID, - SQL_VARCHAR_CODEC_ID, -} from './codec-ids'; - -type JsonType = 'string' | 'number' | 'boolean'; - -/** - * The JSON type a value object member of each codec has in the JSON the SQL ORM writes: the type its JavaScript value serializes to, or for `Decimal` the string the ORM writes. JSON members hold any value, and a member of a codec missing here is checked only for being present and, when required, not JSON null. - */ -const JSON_TYPE_OF_CODEC: Readonly> = { - [PG_TEXT_CODEC_ID]: 'string', - [PG_CHAR_CODEC_ID]: 'string', - [PG_VARCHAR_CODEC_ID]: 'string', - [SQL_TEXT_CODEC_ID]: 'string', - [SQL_CHAR_CODEC_ID]: 'string', - [SQL_VARCHAR_CODEC_ID]: 'string', - [PG_UUID_CODEC_ID]: 'string', - [PG_NUMERIC_CODEC_ID]: 'string', - [PG_DATE_TEMPORAL_CODEC_ID]: 'string', - [PG_TIMESTAMP_TEMPORAL_CODEC_ID]: 'string', - [PG_TIMESTAMPTZ_TEMPORAL_CODEC_ID]: 'string', - [PG_TIME_TEMPORAL_CODEC_ID]: 'string', - [PG_DATE_STRING_CODEC_ID]: 'string', - [PG_TIMESTAMP_STRING_CODEC_ID]: 'string', - [PG_TIMESTAMPTZ_STRING_CODEC_ID]: 'string', - [PG_TIMESTAMPTZ_DATE_CODEC_ID]: 'string', - [PG_TIME_STRING_CODEC_ID]: 'string', - [PG_INT_CODEC_ID]: 'number', - [PG_INT2_CODEC_ID]: 'number', - [PG_INT4_CODEC_ID]: 'number', - [PG_INT8_NUMBER_CODEC_ID]: 'number', - [PG_FLOAT_CODEC_ID]: 'number', - [PG_FLOAT4_CODEC_ID]: 'number', - [PG_FLOAT8_CODEC_ID]: 'number', - [SQL_INT_CODEC_ID]: 'number', - [SQL_FLOAT_CODEC_ID]: 'number', - [PG_BOOL_CODEC_ID]: 'boolean', -}; - -export function valueObjectJsonTypeOfCodec(codecId: string): JsonType | undefined { - return Object.hasOwn(JSON_TYPE_OF_CODEC, codecId) ? JSON_TYPE_OF_CODEC[codecId] : undefined; -} - -const JSON_CODECS: ReadonlySet = new Set([PG_JSON_CODEC_ID, PG_JSONB_CODEC_ID]); - -export function valueObjectMemberHoldsAnyJson(codecId: string): boolean { - return JSON_CODECS.has(codecId); -} diff --git a/packages/3-targets/postgres/1-target/src/exports/codec-descriptor.ts b/packages/3-targets/postgres/1-target/src/exports/codec-descriptor.ts index a775a45717a6..24bf3990b367 100644 --- a/packages/3-targets/postgres/1-target/src/exports/codec-descriptor.ts +++ b/packages/3-targets/postgres/1-target/src/exports/codec-descriptor.ts @@ -8,4 +8,5 @@ export { type PostgresCodecDescriptorRegistry, type PostgresCodecOptions, postgresCodec, + type ValueObjectJsonType, } from '../core/codec-descriptor'; diff --git a/packages/3-targets/postgres/1-target/test/check-expressions.integration.test.ts b/packages/3-targets/postgres/1-target/test/check-expressions.integration.test.ts index fa8b54d78ac1..0b5b7c9e7d7b 100644 --- a/packages/3-targets/postgres/1-target/test/check-expressions.integration.test.ts +++ b/packages/3-targets/postgres/1-target/test/check-expressions.integration.test.ts @@ -1,6 +1,7 @@ import { timeouts, withClient, withDevDatabase } from '@repo/test-utils'; import { describe, expect, it } from 'vitest'; import { postgresRenderCheckExpressions } from '../src/core/check-expressions'; +import { postgresCodecDescriptorRegistry } from '../src/core/registry'; describe('numeric membership checks in Postgres', () => { it.each(['smallint', 'integer', 'real', 'double precision', 'numeric'])( @@ -11,13 +12,16 @@ describe('numeric membership checks in Postgres', () => { const fractional = !['smallint', 'integer'].includes(storageType); const memberValues = fractional ? [-3, 1, 2.5] : [-3, 1, 2]; for (const many of [false, true]) { - const checks = postgresRenderCheckExpressions({ - tableName: 'members', - columnName: 'value', - many: many ? { elementNullable: false } : false, - memberValues, - valueObject: undefined, - }); + const checks = postgresRenderCheckExpressions( + { + tableName: 'members', + columnName: 'value', + many: many ? { elementNullable: false } : false, + memberValues, + valueObject: undefined, + }, + postgresCodecDescriptorRegistry, + ); await client.query( `CREATE TABLE members (value ${storageType}${many ? '[]' : ''}, ${checks.map(({ expression }) => `CHECK (${expression})`).join(', ')})`, ); @@ -55,13 +59,16 @@ describe('list membership checks in Postgres', () => { labels: { type: 'varchar(20)', members: ['say "hi"', 'back\\slash', "o'brien"] }, }; const checks = Object.entries(columns).flatMap(([columnName, { members }]) => - postgresRenderCheckExpressions({ - tableName: 'lists', - columnName, - many: { elementNullable: false }, - memberValues: members, - valueObject: undefined, - }), + postgresRenderCheckExpressions( + { + tableName: 'lists', + columnName, + many: { elementNullable: false }, + memberValues: members, + valueObject: undefined, + }, + postgresCodecDescriptorRegistry, + ), ); await client.query( `CREATE TABLE lists (id int, ${Object.entries(columns) @@ -124,13 +131,16 @@ describe('list membership checks in Postgres', () => { const members = ['a,b', '{brace}', '', 'NULL', ' lead']; const nearMisses = ['a', 'brace', ' ', 'Null', 'lead']; const checks = [false, true].flatMap((many) => - postgresRenderCheckExpressions({ - tableName: 'labels', - columnName: many ? 'list' : 'scalar', - many: many ? { elementNullable: false } : false, - memberValues: members, - valueObject: undefined, - }), + postgresRenderCheckExpressions( + { + tableName: 'labels', + columnName: many ? 'list' : 'scalar', + many: many ? { elementNullable: false } : false, + memberValues: members, + valueObject: undefined, + }, + postgresCodecDescriptorRegistry, + ), ); await client.query( `CREATE TABLE labels (id serial, scalar text, list text[], ${checks.map(({ expression }) => `CHECK (${expression})`).join(', ')})`, diff --git a/packages/3-targets/postgres/1-target/test/check-expressions.test.ts b/packages/3-targets/postgres/1-target/test/check-expressions.test.ts index c60a4e528678..08f5008bd088 100644 --- a/packages/3-targets/postgres/1-target/test/check-expressions.test.ts +++ b/packages/3-targets/postgres/1-target/test/check-expressions.test.ts @@ -1,6 +1,7 @@ import type { CheckExpressionInput } from '@internal/sql-contract/check-expressions'; import { describe, expect, it } from 'vitest'; import { postgresRenderCheckExpressions } from '../src/core/check-expressions'; +import { postgresCodecDescriptorRegistry } from '../src/core/registry'; const base: CheckExpressionInput = { tableName: 'User', @@ -12,24 +13,35 @@ const base: CheckExpressionInput = { describe('postgresRenderCheckExpressions', () => { it('renders a scalar domain enum as an IN membership predicate', () => { - expect(postgresRenderCheckExpressions({ ...base, memberValues: ['user', 'admin'] })).toEqual([ + expect( + postgresRenderCheckExpressions( + { ...base, memberValues: ['user', 'admin'] }, + postgresCodecDescriptorRegistry, + ), + ).toEqual([ { kind: 'membership', columnName: 'role', expression: `"role" IN ('user', 'admin')` }, ]); }); it('renders integer, fractional, and negative scalar members without quotes', () => { - expect(postgresRenderCheckExpressions({ ...base, memberValues: [1, 2.5, -3] })).toEqual([ - { kind: 'membership', columnName: 'role', expression: '"role" IN (1, 2.5, -3)' }, - ]); + expect( + postgresRenderCheckExpressions( + { ...base, memberValues: [1, 2.5, -3] }, + postgresCodecDescriptorRegistry, + ), + ).toEqual([{ kind: 'membership', columnName: 'role', expression: '"role" IN (1, 2.5, -3)' }]); }); it('compares numeric array members in the column type', () => { expect( - postgresRenderCheckExpressions({ - ...base, - many: { elementNullable: false }, - memberValues: [1, 2.5, -3], - }), + postgresRenderCheckExpressions( + { + ...base, + many: { elementNullable: false }, + memberValues: [1, 2.5, -3], + }, + postgresCodecDescriptorRegistry, + ), ).toEqual([ { kind: 'membership', @@ -49,7 +61,10 @@ describe('postgresRenderCheckExpressions', () => { (value) => { for (const many of [false, { elementNullable: false }] as const) { expect(() => - postgresRenderCheckExpressions({ ...base, many, memberValues: [1, value] }), + postgresRenderCheckExpressions( + { ...base, many, memberValues: [1, value] }, + postgresCodecDescriptorRegistry, + ), ).toThrow(/non-finite numeric member/); } }, @@ -57,12 +72,15 @@ describe('postgresRenderCheckExpressions', () => { it('renders array membership over null-stripped elements', () => { expect( - postgresRenderCheckExpressions({ - ...base, - columnName: 'roles', - many: { elementNullable: false }, - memberValues: ['user', 'admin'], - }), + postgresRenderCheckExpressions( + { + ...base, + columnName: 'roles', + many: { elementNullable: false }, + memberValues: ['user', 'admin'], + }, + postgresCodecDescriptorRegistry, + ), ).toEqual([ { kind: 'membership', @@ -79,11 +97,14 @@ describe('postgresRenderCheckExpressions', () => { it('renders element-non-null only for a list column with no member set', () => { expect( - postgresRenderCheckExpressions({ - ...base, - columnName: 'tags', - many: { elementNullable: false }, - }), + postgresRenderCheckExpressions( + { + ...base, + columnName: 'tags', + many: { elementNullable: false }, + }, + postgresCodecDescriptorRegistry, + ), ).toEqual([ { kind: 'elementNotNull', @@ -95,12 +116,15 @@ describe('postgresRenderCheckExpressions', () => { it('omits element-non-null for a nullable-element list while preserving membership', () => { expect( - postgresRenderCheckExpressions({ - ...base, - columnName: 'roles', - many: { elementNullable: true }, - memberValues: ['user', 'admin'], - }), + postgresRenderCheckExpressions( + { + ...base, + columnName: 'roles', + many: { elementNullable: true }, + memberValues: ['user', 'admin'], + }, + postgresCodecDescriptorRegistry, + ), ).toEqual([ { kind: 'membership', @@ -112,12 +136,15 @@ describe('postgresRenderCheckExpressions', () => { it('escapes array-literal and SQL quoting in list members', () => { expect( - postgresRenderCheckExpressions({ - ...base, - columnName: 'roles', - many: { elementNullable: true }, - memberValues: ['say "hi"', 'back\\slash', "o'brien", '', 'NULL'], - }), + postgresRenderCheckExpressions( + { + ...base, + columnName: 'roles', + many: { elementNullable: true }, + memberValues: ['say "hi"', 'back\\slash', "o'brien", '', 'NULL'], + }, + postgresCodecDescriptorRegistry, + ), ).toEqual([ { kind: 'membership', @@ -128,35 +155,44 @@ describe('postgresRenderCheckExpressions', () => { }); it('renders nothing for a plain scalar column', () => { - expect(postgresRenderCheckExpressions(base)).toEqual([]); + expect(postgresRenderCheckExpressions(base, postgresCodecDescriptorRegistry)).toEqual([]); }); it('throws on an empty member set for a scalar column', () => { - expect(() => postgresRenderCheckExpressions({ ...base, memberValues: [] })).toThrow( - /empty member set/, - ); + expect(() => + postgresRenderCheckExpressions( + { ...base, memberValues: [] }, + postgresCodecDescriptorRegistry, + ), + ).toThrow(/empty member set/); }); it('throws on an empty member set for an array column', () => { expect(() => - postgresRenderCheckExpressions({ - ...base, - columnName: 'roles', - many: { elementNullable: false }, - memberValues: [], - }), + postgresRenderCheckExpressions( + { + ...base, + columnName: 'roles', + many: { elementNullable: false }, + memberValues: [], + }, + postgresCodecDescriptorRegistry, + ), ).toThrow(/empty member set/); }); it('quotes identifiers and escapes literal quotes', () => { expect( - postgresRenderCheckExpressions({ - tableName: 'Order', - columnName: 'sta"tus', - many: false, - memberValues: ["o'brien"], - valueObject: undefined, - }), + postgresRenderCheckExpressions( + { + tableName: 'Order', + columnName: 'sta"tus', + many: false, + memberValues: ["o'brien"], + valueObject: undefined, + }, + postgresCodecDescriptorRegistry, + ), ).toEqual([ { kind: 'membership', columnName: 'sta"tus', expression: `"sta""tus" IN ('o''brien')` }, ]); diff --git a/packages/3-targets/postgres/1-target/test/check-expressions.value-object.test.ts b/packages/3-targets/postgres/1-target/test/check-expressions.value-object.test.ts index e94de0cf8930..a4ed0c62e2bb 100644 --- a/packages/3-targets/postgres/1-target/test/check-expressions.value-object.test.ts +++ b/packages/3-targets/postgres/1-target/test/check-expressions.value-object.test.ts @@ -6,6 +6,7 @@ import type { } from '@internal/sql-contract/check-expressions'; import { describe, expect, it } from 'vitest'; import { postgresRenderCheckExpressions } from '../src/core/check-expressions'; +import { postgresCodecDescriptorRegistry } from '../src/core/registry'; function scalar( codecId: string, @@ -29,13 +30,16 @@ function render( valueObject: ResolvedValueObject, column: Partial> = {}, ): readonly { readonly kind: string; readonly expression: string }[] { - return postgresRenderCheckExpressions({ - tableName: 'user', - columnName: 'address', - many: false, - memberValues: undefined, - valueObject: { nullable: false, many: false, ...column, valueObject }, - }).map(({ kind, expression }) => ({ kind, expression })); + return postgresRenderCheckExpressions( + { + tableName: 'user', + columnName: 'address', + many: false, + memberValues: undefined, + valueObject: { nullable: false, many: false, ...column, valueObject }, + }, + postgresCodecDescriptorRegistry, + ).map(({ kind, expression }) => ({ kind, expression })); } const street = member(scalar('pg/text@1')); @@ -137,7 +141,7 @@ describe('postgresRenderCheckExpressions for a column that holds a value object' ]); }); - it('refuses JSON null for a required member whose codec has no known JSON type, and checks only the presence of a JSON member', () => { + it('refuses JSON null for a required member whose codec declares presence only, and checks only the presence of a JSON member', () => { const bytes = member(scalar('pg/bytea@1')); const data = member(scalar('pg/jsonb@1')); const notes = member(scalar('pg/jsonb@1'), { nullable: true }); diff --git a/packages/3-targets/postgres/1-target/test/codec-value-object-json-type.test.ts b/packages/3-targets/postgres/1-target/test/codec-value-object-json-type.test.ts new file mode 100644 index 000000000000..c9438406e2ab --- /dev/null +++ b/packages/3-targets/postgres/1-target/test/codec-value-object-json-type.test.ts @@ -0,0 +1,62 @@ +import { describe, expect, it } from 'vitest'; +import { buildPostgresCodecDescriptorRegistry } from '../src/core/codec-descriptor'; +import { postgresCodecDescriptorRegistry } from '../src/core/registry'; + +const DECLARATIONS = ['string', 'number', 'boolean', 'anyJson', 'presenceOnly']; + +describe('the JSON type a codec declares for a value object member', () => { + it('is declared by every Postgres and SQL family codec', () => { + const undeclared = [...postgresCodecDescriptorRegistry.values()] + .filter((descriptor) => !DECLARATIONS.includes(descriptor.valueObjectJsonType)) + .map((descriptor) => descriptor.codecId); + expect(undeclared).toEqual([]); + }); + + it('is the type the SQL ORM writes, for the codecs that write one', () => { + expect( + Object.fromEntries( + [ + 'sql/text@1', + 'sql/int@1', + 'pg/numeric@1', + 'pg/bool@1', + 'pg/jsonb@1', + 'pg/int8@1', + 'pg/timestamp-date@1', + 'pg/timestamp-instant@1', + 'pg/interval-string@1', + ].map((codecId) => [ + codecId, + postgresCodecDescriptorRegistry.descriptorFor(codecId)?.valueObjectJsonType, + ]), + ), + ).toEqual({ + 'sql/text@1': 'string', + 'sql/int@1': 'number', + 'pg/numeric@1': 'string', + 'pg/bool@1': 'boolean', + 'pg/jsonb@1': 'anyJson', + 'pg/int8@1': 'presenceOnly', + 'pg/timestamp-date@1': 'string', + 'pg/timestamp-instant@1': 'string', + 'pg/interval-string@1': 'string', + }); + }); + + it('is required of a descriptor the registry accepts', () => { + const text = postgresCodecDescriptorRegistry.descriptorFor('pg/text@1'); + const { valueObjectJsonType: _declared, ...undeclared } = { + descriptorKind: text?.descriptorKind, + codecId: 'demo/undeclared@1', + traits: text?.traits, + paramsSchema: undefined, + isParameterized: false, + factory: text?.factory.bind(text), + projectJson: text?.projectJson.bind(text), + valueObjectJsonType: text?.valueObjectJsonType, + }; + expect(() => buildPostgresCodecDescriptorRegistry([undeclared])).toThrow( + expect.objectContaining({ code: 'RUNTIME.CODEC_DESCRIPTOR_INVALID' }), + ); + }); +}); diff --git a/packages/3-targets/postgres/1-target/test/postgres-codec-descriptor.test-d.ts b/packages/3-targets/postgres/1-target/test/postgres-codec-descriptor.test-d.ts index 32dd3b07dbc1..5bd9456051ac 100644 --- a/packages/3-targets/postgres/1-target/test/postgres-codec-descriptor.test-d.ts +++ b/packages/3-targets/postgres/1-target/test/postgres-codec-descriptor.test-d.ts @@ -83,6 +83,7 @@ class GenericVectorDescriptor extends CodecDescriptorImpl { } class DirectVectorDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'presenceOnly' as const; override readonly dataType = dataTypeId('demo/fixture'); override readonly codecId = 'demo/direct-vector@1' as const; override readonly traits = ['equality'] as const; @@ -105,6 +106,7 @@ class DirectVectorDescriptor extends PostgresCodecDescriptor { const genericDescriptor = new GenericVectorDescriptor(); const directDescriptor = new DirectVectorDescriptor(); const adaptedDescriptor = postgresCodec(genericDescriptor, { + valueObjectJsonType: 'presenceOnly', dataType: fixtureType, factory: (descriptor, type, params) => () => new VectorCodec(descriptor, type, params.length), jsonProjection(expression, params) { @@ -152,6 +154,7 @@ test('definePostgresCodecs rejects an unadapted generic descriptor', () => { test('postgresCodec requires explicit scalar projection behavior', () => { // @ts-expect-error -- jsonProjection is mandatory postgresCodec(genericDescriptor, { + valueObjectJsonType: 'presenceOnly', dataType: fixtureType, factory: (descriptor, type, params) => () => new VectorCodec(descriptor, type, params.length), }); @@ -174,11 +177,13 @@ class TextCodec extends CodecImpl<'demo/text@1', readonly ['equality'], string, test('a factory option builds a codec of the family codec it adapts', () => { postgresCodec(genericDescriptor, { + valueObjectJsonType: 'presenceOnly', dataType: fixtureType, jsonProjection: (expression) => expression, factory: (descriptor, type, params) => () => new VectorCodec(descriptor, type, params.length), }); postgresCodec(genericDescriptor, { + valueObjectJsonType: 'presenceOnly', dataType: fixtureType, jsonProjection: (expression) => expression, // @ts-expect-error -- the adapted descriptor's factory promises the family codec, so the option cannot build another @@ -188,6 +193,7 @@ test('a factory option builds a codec of the family codec it adapts', () => { // @ts-expect-error -- direct descriptors must implement scalar JSON projection class MissingJsonProjection extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'presenceOnly' as const; override readonly dataType = dataTypeId('demo/fixture'); override readonly codecId = 'demo/missing-json@1' as const; override readonly traits: readonly CodecTrait[] = []; diff --git a/packages/3-targets/postgres/1-target/test/postgres-codec-descriptor.test.ts b/packages/3-targets/postgres/1-target/test/postgres-codec-descriptor.test.ts index 054671a7fcc4..84fc8c7e05ec 100644 --- a/packages/3-targets/postgres/1-target/test/postgres-codec-descriptor.test.ts +++ b/packages/3-targets/postgres/1-target/test/postgres-codec-descriptor.test.ts @@ -130,6 +130,7 @@ class RenderingVectorDescriptor extends GenericVectorDescriptor { } class DirectVectorDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'presenceOnly' as const; override readonly dataType = dataTypeId('demo/fixture'); override readonly codecId = 'demo/direct-vector@1' as const; override readonly traits = ['equality'] as const; @@ -280,6 +281,7 @@ describe('postgresCodec', () => { it('refuses a family template that renders literals, naming the option to use instead', () => { expect(() => postgresCodec(new RenderingVectorDescriptor(), { + valueObjectJsonType: 'presenceOnly', dataType: fixtureVectorType, jsonProjection: (expression) => expression, factory: vectorCodecFactory, @@ -291,6 +293,7 @@ describe('postgresCodec', () => { it("renders literals with the renderer the target passes, never the template's", () => { const descriptor = postgresCodec(genericVectorDescriptor, { + valueObjectJsonType: 'presenceOnly', dataType: fixtureVectorType, renderValueLiteral: (value, side) => `${side}-from-target:${String(value)}`, jsonProjection: (expression) => expression, @@ -302,6 +305,7 @@ describe('postgresCodec', () => { it('preserves the wrapped descriptor contract and builds its codec with the factory option', () => { const descriptor = postgresCodec(genericVectorDescriptor, { + valueObjectJsonType: 'presenceOnly', dataType: fixtureVectorType, factory: vectorCodecFactory, jsonProjection: (expression, params) => @@ -329,11 +333,13 @@ describe('postgresCodec', () => { it('takes its parameter schema from the data type, not the template', () => { const descriptor = postgresCodec(genericVectorDescriptor, { + valueObjectJsonType: 'presenceOnly', dataType: fixtureVectorType, factory: vectorCodecFactory, jsonProjection: (expression) => expression, }); const withoutParams = postgresCodec(genericVectorDescriptor, { + valueObjectJsonType: 'presenceOnly', dataType: dataType('demo/plain', { read: (json) => json }), factory: vectorCodecFactory, jsonProjection: (expression) => expression, @@ -352,6 +358,7 @@ describe('postgresCodec', () => { } const adapt = (wrapped: GenericVectorDescriptor) => postgresCodec(wrapped, { + valueObjectJsonType: 'presenceOnly', dataType: fixtureVectorType, factory: vectorCodecFactory, jsonProjection: (expression) => expression, @@ -369,6 +376,7 @@ describe('postgresCodec', () => { it('accepts an array override only after typed parameter validation', () => { const overrideCalls: VectorParams[] = []; const descriptor = postgresCodec(genericVectorDescriptor, { + valueObjectJsonType: 'presenceOnly', dataType: fixtureVectorType, factory: vectorCodecFactory, jsonProjection: (expression) => expression, @@ -410,6 +418,7 @@ describe('Postgres codec descriptor registry', () => { isParameterized: descriptor.isParameterized, factory: descriptor.factory.bind(descriptor), projectJson: descriptor.projectJson.bind(descriptor), + valueObjectJsonType: descriptor.valueObjectJsonType, }; expect(structuralDescriptor).not.toBeInstanceOf(PostgresCodecDescriptor); @@ -431,6 +440,7 @@ describe('Postgres codec descriptor registry', () => { isParameterized: descriptor.isParameterized, factory: descriptor.factory.bind(descriptor), projectJson: descriptor.projectJson.bind(descriptor), + valueObjectJsonType: descriptor.valueObjectJsonType, }; expect(() => buildPostgresCodecDescriptorRegistry([genericVectorDescriptor])).toThrow( diff --git a/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.check.test.ts b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.check.test.ts index 60d5c6cb1809..2d2aa362a580 100644 --- a/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.check.test.ts +++ b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.check.test.ts @@ -14,15 +14,19 @@ import { import { SqlSchemaIR } from '@internal/sql-schema-ir/types'; import { describe, expect, it } from 'vitest'; import { postgresRenderCheckExpressions } from '../../../src/core/check-expressions'; +import { postgresCodecDescriptorRegistry } from '../../../src/core/registry'; import { printPslFromFlat } from '../fixtures'; -const elementCandidate = postgresRenderCheckExpressions({ - tableName: 'users', - columnName: 'tags', - many: { elementNullable: false }, - memberValues: undefined, - valueObject: undefined, -})[0]; +const elementCandidate = postgresRenderCheckExpressions( + { + tableName: 'users', + columnName: 'tags', + many: { elementNullable: false }, + memberValues: undefined, + valueObject: undefined, + }, + postgresCodecDescriptorRegistry, +)[0]; const derivedPrefix = composeCheckWirePrefix('users', 'tags', 'elementNotNull'); const derivedHash = computeCheckContentHash(elementCandidate?.expression ?? ''); diff --git a/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.list-element-nullability.test.ts b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.list-element-nullability.test.ts index 470d859565bd..23f012ddba83 100644 --- a/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.list-element-nullability.test.ts +++ b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.list-element-nullability.test.ts @@ -12,15 +12,19 @@ import { import { SqlSchemaIR } from '@internal/sql-schema-ir/types'; import { describe, expect, it } from 'vitest'; import { postgresRenderCheckExpressions } from '../../../src/core/check-expressions'; +import { postgresCodecDescriptorRegistry } from '../../../src/core/registry'; import { printPslFromFlat } from '../fixtures'; -const elementCandidate = postgresRenderCheckExpressions({ - tableName: 'users', - columnName: 'tags', - many: { elementNullable: false }, - memberValues: undefined, - valueObject: undefined, -})[0]; +const elementCandidate = postgresRenderCheckExpressions( + { + tableName: 'users', + columnName: 'tags', + many: { elementNullable: false }, + memberValues: undefined, + valueObject: undefined, + }, + postgresCodecDescriptorRegistry, +)[0]; const derivedPrefix = composeCheckWirePrefix('users', 'tags', 'elementNotNull'); const derivedHash = computeCheckContentHash(elementCandidate?.expression ?? ''); diff --git a/packages/3-targets/postgres/1-target/test/psl-print/build-context.ts b/packages/3-targets/postgres/1-target/test/psl-print/build-context.ts index afc78a406fda..08f23a6cce69 100644 --- a/packages/3-targets/postgres/1-target/test/psl-print/build-context.ts +++ b/packages/3-targets/postgres/1-target/test/psl-print/build-context.ts @@ -33,6 +33,7 @@ const citextTemplate: CodecDescriptorTemplate = { /** A codec the target does not own, as an extension would contribute it, representing text. */ export const extensionCodec: AnyPostgresCodecDescriptor = postgresCodec(citextTemplate, { + valueObjectJsonType: 'presenceOnly', dataType: pgText, jsonProjection: (expression) => expression, factory: () => () => { diff --git a/packages/3-targets/postgres/2-adapter/test/lower-to-execute-request.test.ts b/packages/3-targets/postgres/2-adapter/test/lower-to-execute-request.test.ts index 09e4ca9297cb..5f25b3b7c2fc 100644 --- a/packages/3-targets/postgres/2-adapter/test/lower-to-execute-request.test.ts +++ b/packages/3-targets/postgres/2-adapter/test/lower-to-execute-request.test.ts @@ -49,6 +49,7 @@ const transformingCodecDescriptor: AnyCodecDescriptor = { factory: () => () => transformingCodec, }; const transformingDescriptor = postgresCodec(transformingCodecDescriptor, { + valueObjectJsonType: 'presenceOnly', dataType: pgText, jsonProjection: (expression: ProjectionExpr) => expression, factory: () => () => transformingCodec, @@ -323,6 +324,7 @@ class ExtTransformDescriptor extends CodecDescriptorImpl { const extDescriptor = new ExtTransformDescriptor(); const extTransformDescriptor = postgresCodec(extDescriptor, { + valueObjectJsonType: 'presenceOnly', dataType: pgText, jsonProjection: (expression: ProjectionExpr) => expression, factory: () => extDescriptor.factory(), diff --git a/packages/3-targets/postgres/2-adapter/test/migrations/alter-column-type.integration.test.ts b/packages/3-targets/postgres/2-adapter/test/migrations/alter-column-type.integration.test.ts index 5a98d8ec1d32..639b69922fd4 100644 --- a/packages/3-targets/postgres/2-adapter/test/migrations/alter-column-type.integration.test.ts +++ b/packages/3-targets/postgres/2-adapter/test/migrations/alter-column-type.integration.test.ts @@ -8,6 +8,7 @@ import { import { UNBOUND_NAMESPACE_ID } from '@internal/framework-components/ir'; import { CheckConstraint, SqlStorage } from '@internal/sql-contract/types'; import { composeCheckWirePrefix, computeCheckContentHash } from '@internal/sql-schema-ir/naming'; +import { postgresCodecDescriptorRegistry } from '@internal/target-postgres/codecs'; import { postgresDataTypes } from '@internal/target-postgres/data-types'; import { AlterColumnTypeCall } from '@internal/target-postgres/op-factory-call'; import { createPostgresMigrationPlanner } from '@internal/target-postgres/planner'; @@ -46,13 +47,16 @@ const intList = { const textList = { ...intList, dataType: 'pg/text', codecId: 'pg/text@1' }; function elementChecks(): CheckConstraint[] { - return postgresRenderCheckExpressions({ - tableName: 'Item', - columnName: 'tags', - many: { elementNullable: false }, - memberValues: undefined, - valueObject: undefined, - }).map( + return postgresRenderCheckExpressions( + { + tableName: 'Item', + columnName: 'tags', + many: { elementNullable: false }, + memberValues: undefined, + valueObject: undefined, + }, + postgresCodecDescriptorRegistry, + ).map( (candidate) => new CheckConstraint({ naming: { diff --git a/packages/3-targets/postgres/2-adapter/test/migrations/check-lifecycle-e2e.integration.test.ts b/packages/3-targets/postgres/2-adapter/test/migrations/check-lifecycle-e2e.integration.test.ts index 6d3059e9b503..fb74b099c044 100644 --- a/packages/3-targets/postgres/2-adapter/test/migrations/check-lifecycle-e2e.integration.test.ts +++ b/packages/3-targets/postgres/2-adapter/test/migrations/check-lifecycle-e2e.integration.test.ts @@ -14,7 +14,10 @@ import { sql, } from '@internal/sql-contract-ts/contract-builder'; import { composeCheckWirePrefix, computeCheckContentHash } from '@internal/sql-schema-ir/naming'; -import { createPostgresBuiltinCodecLookup } from '@internal/target-postgres/codecs'; +import { + createPostgresBuiltinCodecLookup, + postgresCodecDescriptorRegistry, +} from '@internal/target-postgres/codecs'; import { postgresDataTypes } from '@internal/target-postgres/data-types'; import { PostgresDatabaseSchemaNode, @@ -103,13 +106,16 @@ function checksForColumn( readonly memberValues?: readonly string[]; }, ): CheckConstraint[] { - return postgresRenderCheckExpressions({ - tableName, - columnName, - many: options.many, - memberValues: options.memberValues, - valueObject: undefined, - }).map( + return postgresRenderCheckExpressions( + { + tableName, + columnName, + many: options.many, + memberValues: options.memberValues, + valueObject: undefined, + }, + postgresCodecDescriptorRegistry, + ).map( (candidate) => new CheckConstraint({ naming: { diff --git a/packages/3-targets/postgres/2-adapter/test/migrations/native-array-columns.integration.test.ts b/packages/3-targets/postgres/2-adapter/test/migrations/native-array-columns.integration.test.ts index 66cfab324832..06ff1dc2c36e 100644 --- a/packages/3-targets/postgres/2-adapter/test/migrations/native-array-columns.integration.test.ts +++ b/packages/3-targets/postgres/2-adapter/test/migrations/native-array-columns.integration.test.ts @@ -4,7 +4,7 @@ import { APP_SPACE_ID } from '@internal/framework-components/control'; import { UNBOUND_NAMESPACE_ID } from '@internal/framework-components/ir'; import { CheckConstraint, SqlStorage } from '@internal/sql-contract/types'; import { composeCheckWirePrefix, computeCheckContentHash } from '@internal/sql-schema-ir/naming'; - +import { postgresCodecDescriptorRegistry } from '@internal/target-postgres/codecs'; import { parsePostgresListText } from '@internal/target-postgres/control'; import { postgresCreateNamespace, @@ -38,13 +38,16 @@ const ARRAY_COLUMNS = ['tags', 'labels', 'scores', 'tagsWithDefault'] as const; */ function declaredArrayElementChecks(): CheckConstraint[] { return ARRAY_COLUMNS.flatMap((columnName) => - postgresRenderCheckExpressions({ - tableName: 'ArrayTest', - columnName, - many: { elementNullable: false }, - memberValues: undefined, - valueObject: undefined, - }).map( + postgresRenderCheckExpressions( + { + tableName: 'ArrayTest', + columnName, + many: { elementNullable: false }, + memberValues: undefined, + valueObject: undefined, + }, + postgresCodecDescriptorRegistry, + ).map( (candidate) => new CheckConstraint({ naming: { diff --git a/packages/3-targets/postgres/2-adapter/test/postgres-codec-registry-composition.test.ts b/packages/3-targets/postgres/2-adapter/test/postgres-codec-registry-composition.test.ts index 79f96088b1d2..7964219f5e43 100644 --- a/packages/3-targets/postgres/2-adapter/test/postgres-codec-registry-composition.test.ts +++ b/packages/3-targets/postgres/2-adapter/test/postgres-codec-registry-composition.test.ts @@ -131,6 +131,7 @@ function postgresDescriptor( const type = fixtureSqlDataType(codecId, typeText); const template = genericDescriptor(codecId, type); return postgresCodec(template, { + valueObjectJsonType: 'presenceOnly', dataType: type, jsonProjection(expression: ProjectionExpr): ProjectionExpr { onProjection?.(); @@ -163,6 +164,7 @@ function transformingPostgresDescriptor( }, }; return postgresCodec(descriptor, { + valueObjectJsonType: 'presenceOnly', dataType: type, jsonProjection: (expression: ProjectionExpr) => expression, factory: (_descriptor, _type, params) => descriptor.factory(params), diff --git a/packages/3-targets/postgres/2-adapter/test/sql-renderer.cast-policy.test.ts b/packages/3-targets/postgres/2-adapter/test/sql-renderer.cast-policy.test.ts index 285d010b0fdd..b4fb8784672b 100644 --- a/packages/3-targets/postgres/2-adapter/test/sql-renderer.cast-policy.test.ts +++ b/packages/3-targets/postgres/2-adapter/test/sql-renderer.cast-policy.test.ts @@ -77,6 +77,7 @@ const verbatimTemplate = genericDescriptor('demo/verbatim@1', verbatimDataType); const verbatimRegistry = buildPostgresCodecDescriptorRegistry([ postgresCodec(verbatimTemplate, { + valueObjectJsonType: 'presenceOnly', dataType: verbatimDataType, jsonProjection: (expression: ProjectionExpr) => expression, factory: (_descriptor, _type, params) => verbatimTemplate.factory(params), @@ -87,6 +88,7 @@ function descriptorFor(codecId: string, name: string): AnyPostgresCodecDescripto const type = fixtureDataType(name); const template = genericDescriptor(codecId, type); return postgresCodec(template, { + valueObjectJsonType: 'presenceOnly', dataType: type, jsonProjection: (expression: ProjectionExpr) => expression, factory: (_descriptor, _type, params) => template.factory(params), diff --git a/packages/3-targets/postgres/4-codec-testkit/test/value-round-trip.integration.test.ts b/packages/3-targets/postgres/4-codec-testkit/test/value-round-trip.integration.test.ts index c74e0e881217..8c6b39c94372 100644 --- a/packages/3-targets/postgres/4-codec-testkit/test/value-round-trip.integration.test.ts +++ b/packages/3-targets/postgres/4-codec-testkit/test/value-round-trip.integration.test.ts @@ -30,6 +30,7 @@ class MarkingTextCodec extends CodecImpl<'test/marking-text@1', readonly [], str } class MarkingTextDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'presenceOnly' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return expression; } @@ -59,6 +60,7 @@ class TextCodec extends CodecImpl<'test/upper-casing-text@1', readonly [], strin /** A text codec whose projection changes the text, so the projected value reads to another value than the row. */ class UpperCasingTextDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'presenceOnly' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return FunctionCallExpr.of('upper', [expression]); } diff --git a/test/integration/test/authoring/extension-text-type.test.ts b/test/integration/test/authoring/extension-text-type.test.ts index 9cecbde0d49e..4c1313e2ab2f 100644 --- a/test/integration/test/authoring/extension-text-type.test.ts +++ b/test/integration/test/authoring/extension-text-type.test.ts @@ -59,6 +59,7 @@ class CitextCodec extends CodecImpl> { + override readonly valueObjectJsonType = 'presenceOnly' as const; override readonly dataType = testtextCitext.id; override readonly codecId = CITEXT_CODEC_ID; override readonly traits = ['equality'] as const; diff --git a/test/integration/test/sql-orm-client/include-codecs.test.ts b/test/integration/test/sql-orm-client/include-codecs.test.ts index b43efbe1f7e7..2c8a8da40757 100644 --- a/test/integration/test/sql-orm-client/include-codecs.test.ts +++ b/test/integration/test/sql-orm-client/include-codecs.test.ts @@ -74,6 +74,7 @@ class IncludedTextDescriptor extends CodecDescriptorImpl { * assuming. This one stores and projects text unchanged. */ const includedTextDescriptor = postgresCodec(new IncludedTextDescriptor(), { + valueObjectJsonType: 'presenceOnly', dataType: pgText, jsonProjection: (expression) => expression, factory: (descriptor, dataType) => () => new IncludedTextCodec(descriptor, dataType), diff --git a/upgrade-instructions/pending/value-objects-in-storage/extension/instructions.md b/upgrade-instructions/pending/value-objects-in-storage/extension/instructions.md index 5a75bc10d52b..ecd429653164 100644 --- a/upgrade-instructions/pending/value-objects-in-storage/extension/instructions.md +++ b/upgrade-instructions/pending/value-objects-in-storage/extension/instructions.md @@ -32,6 +32,15 @@ changes: glob: "**/*.{ts,mts,cts,tsx}" matches: - '\bValueObjectFieldType\b' + - id: postgres-codec-value-object-json-type + summary: | + A PostgreSQL codec descriptor declares `valueObjectJsonType`: what the derived check of a value object column accepts for a member of the codec. It is `'string'`, `'number'` or `'boolean'` for the JSON type the SQL ORM writes for the codec's value, `'anyJson'` for a codec whose value is any JSON value, or `'presenceOnly'` when the check should only require the member to be present. A class that extends `PostgresCodecDescriptor` declares it as a property, and `postgresCodec(template, options)` takes it in its options. A registry refuses a descriptor without it, with `RUNTIME.CODEC_DESCRIPTOR_INVALID`. `postgresRenderCheckExpressions` takes the stack's codec lookup as a second argument. + detection: + glob: "**/*.{ts,mts,cts,tsx}" + matches: + - '\bextends\s+PostgresCodecDescriptor\b' + - '\bpostgresCodec\s*\(' + - '\bpostgresRenderCheckExpressions\s*\(' --- # Value objects are storage entities @@ -107,3 +116,22 @@ const reference: ValueObjectReference = { entityName: 'Address', }; ``` + +## `postgres-codec-value-object-json-type` + +```ts +// before +export class CitextDescriptor extends PostgresCodecDescriptor { + override readonly codecId = 'citext/text@1'; + // ... +} + +// after +export class CitextDescriptor extends PostgresCodecDescriptor { + override readonly valueObjectJsonType = 'string' as const; + override readonly codecId = 'citext/text@1'; + // ... +} +``` + +Choose `'presenceOnly'` for a codec whose value the SQL ORM does not write as one JSON string, number or boolean, such as a vector or a geometry. A call of `postgresRenderCheckExpressions(input)` becomes `postgresRenderCheckExpressions(input, codecLookup)`, where `codecLookup` is the stack's codec lookup. From e1bf6729800593dae575c4eb9325ed205864c3e2 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 18:55:09 +0200 Subject: [PATCH 079/104] TML-3537: the codec authoring guide and the error reference describe valueObjectJsonType Signed-off-by: willbot Signed-off-by: Will Madden --- docs/reference/codec-authoring-guide.md | 6 +++++- docs/reference/error-reference.md | 2 +- packages/2-sql-family/4-lanes/relational-core/README.md | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/reference/codec-authoring-guide.md b/docs/reference/codec-authoring-guide.md index f2337549b860..07d8e24c2c78 100644 --- a/docs/reference/codec-authoring-guide.md +++ b/docs/reference/codec-authoring-guide.md @@ -96,6 +96,7 @@ export class PgTextDescriptor extends PostgresCodecDescriptor { return expression; } override readonly dataType = pgText.id; + override readonly valueObjectJsonType = 'string' as const; override readonly codecId = 'pg/text@1' as const; override readonly traits = ['equality', 'order', 'textual'] as const; override readonly paramsSchema = undefined; @@ -148,6 +149,7 @@ export class PgVectorDescriptor extends PostgresCodecDescriptor { return CastExpr.as(expression, 'text'); } override readonly dataType = pgvectorVector.id; + override readonly valueObjectJsonType = 'presenceOnly' as const; override readonly codecId = 'pg/vector@1' as const; override readonly traits = ['equality'] as const; override readonly paramsSchema: StandardSchemaV1 = pgvectorVectorParams; @@ -234,6 +236,7 @@ export class ArktypeJsonDescriptor extends PostgresCodecDescriptor expression, factory: (descriptor, dataType) => () => new SqlIntCodec(descriptor, dataType), }); diff --git a/docs/reference/error-reference.md b/docs/reference/error-reference.md index 10bdf777d8e8..95c8905444a3 100644 --- a/docs/reference/error-reference.md +++ b/docs/reference/error-reference.md @@ -1204,7 +1204,7 @@ A codec projection used `CodecRef.many` against a SQLite codec descriptor: SQLit ### RUNTIME.CODEC_DESCRIPTOR_INVALID -A codec descriptor handed to a target codec-descriptor registry (Postgres, SQLite) is not a valid descriptor for that target: wrong discriminant, missing target descriptor methods, or a malformed params schema. Extend the target's descriptor class or adapt a generic descriptor with the target's wrapper (`postgresCodec()` / `sqliteCodec()`). Payload: `codecId`. +A codec descriptor handed to a target codec-descriptor registry (Postgres, SQLite) is not a valid descriptor for that target: wrong discriminant, missing target descriptor methods, a Postgres descriptor without `valueObjectJsonType`, or a malformed params schema. Extend the target's descriptor class or adapt a generic descriptor with the target's wrapper (`postgresCodec()` / `sqliteCodec()`). Payload: `codecId`. ### RUNTIME.CODEC_DESCRIPTOR_MISSING diff --git a/packages/2-sql-family/4-lanes/relational-core/README.md b/packages/2-sql-family/4-lanes/relational-core/README.md index a113dc0631f5..412a17d74281 100644 --- a/packages/2-sql-family/4-lanes/relational-core/README.md +++ b/packages/2-sql-family/4-lanes/relational-core/README.md @@ -120,6 +120,7 @@ class PgTextCodec extends CodecImpl<'pg/text@1', readonly ['equality'], string, class PgTextDescriptor extends PostgresCodecDescriptor { override readonly dataType = pgText.id; + override readonly valueObjectJsonType = 'string' as const; override readonly codecId = 'pg/text@1'; override readonly traits = ['equality'] as const; override readonly paramsSchema = undefined; From efd9ad7b5590f8d769a4c405dfe580ee0ff307c3 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 18:59:21 +0200 Subject: [PATCH 080/104] TML-3537: the value object check's kind is shape, and its constraint names end in _shape Signed-off-by: willbot Signed-off-by: Will Madden --- ... discriminator column names a value set.md | 2 +- .../subsystems/1. Data Contract.md | 2 +- docs/reference/error-reference.md | 2 +- .../1-core/schema-ir/src/naming.ts | 4 ++-- .../1-core/schema-ir/test/naming.test.ts | 8 +++---- .../contract-builder.derived-checks.test.ts | 10 ++++---- .../1-target/src/core/check-expressions.ts | 2 +- .../check-expressions.value-object.test.ts | 24 +++++++++---------- skills/prisma-8/references/contract.md | 2 +- .../value-object-checks.e2e.test.ts | 12 +++++----- .../value-object-checks.test.ts | 8 +++---- .../app/instructions.md | 2 +- .../extension/instructions.md | 2 +- 13 files changed, 40 insertions(+), 40 deletions(-) diff --git a/docs/architecture docs/adrs/ADR 309 - Value objects are storage entities, and a discriminator column names a value set.md b/docs/architecture docs/adrs/ADR 309 - Value objects are storage entities, and a discriminator column names a value set.md index e8e22a67f4be..3900cb5a2b06 100644 --- a/docs/architecture docs/adrs/ADR 309 - Value objects are storage entities, and a discriminator column names a value set.md +++ b/docs/architecture docs/adrs/ADR 309 - Value objects are storage entities, and a discriminator column names a value set.md @@ -98,7 +98,7 @@ Storage already holds stored facts the database does not enforce. SQLite derives "type": { "codecId": "pg/text@1", "dataType": "pg/text", "nullable": false, "valueSet": { "entityKind": "valueSet", "entityName": "Task.type", "namespaceId": "public", "plane": "storage" } } ``` -**Enforcement on Postgres.** On a managed table, a column that holds a value object gets a derived check, as an enum column does, named `

__value_object`. It is one strict `jsonpath` predicate, written so that an unknown result fails, rather than the key-set operators `?&` and `-`: +**Enforcement on Postgres.** On a managed table, a column that holds a value object gets a derived check, as an enum column does, named `
__shape`. Its check kind is `shape`: like `membership` and `elementNotNull`, the kind names what the check enforces, here the shape of the column's value. It is one strict `jsonpath` predicate, written so that an unknown result fails, rather than the key-set operators `?&` and `-`: ```sql jsonb_path_match("address", 'strict $.type() == "object" diff --git a/docs/architecture docs/subsystems/1. Data Contract.md b/docs/architecture docs/subsystems/1. Data Contract.md index e282725c14d8..b1d2e8c51ef1 100644 --- a/docs/architecture docs/subsystems/1. Data Contract.md +++ b/docs/architecture docs/subsystems/1. Data Contract.md @@ -321,7 +321,7 @@ A `pg/int4@1` enum `Int4Level` with members `Low = 1` and `High = 10`, on column A list column gets `<@` containment instead of `IN`. In the same fixture, `levels FloatMixed[]`, whose members are written `1.5` and `NaN` and stored as `1.5` and `"NaN"`, gets `array_remove("levels", NULL) <@ '{1.5,"NaN"}'`, plus the derived check `array_position("levels", NULL) IS NULL` because its elements are not nullable. -On a `managed` Postgres table two more columns derive checks ([ADR 309](../adrs/ADR%20309%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md)). A discriminator column with no enum gets a membership check, `
__check`, holding the values of its `.` value set. A column that holds a value object gets a check named `
__value_object`: one strict `jsonpath` predicate that the column value is an object (or, for a list, an array of objects) with each required member present, no other key, each scalar member of its JSON type, each enum member one of its value set's values, and each nested value object checked the same way. A nullable column accepts SQL `NULL` and JSON `null`; the ORM writes SQL `NULL` for an absent value. A `Decimal` member must be a JSON string, as the ORM writes it. Each Postgres codec declares on its descriptor the JSON type a member of it has (`valueObjectJsonType`), or that it holds any JSON value, or that only its presence is checked. A required member whose codec declares presence only must be present and not JSON `null`; a `Json` member may hold any value. Below the point where a value object refers back into itself, any object is accepted, as the MongoDB validator does. +On a `managed` Postgres table two more columns derive checks ([ADR 309](../adrs/ADR%20309%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md)). A discriminator column with no enum gets a membership check, `
__check`, holding the values of its `.` value set. A column that holds a value object gets a check named `
__shape`: one strict `jsonpath` predicate that the column value is an object (or, for a list, an array of objects) with each required member present, no other key, each scalar member of its JSON type, each enum member one of its value set's values, and each nested value object checked the same way. A nullable column accepts SQL `NULL` and JSON `null`; the ORM writes SQL `NULL` for an absent value. A `Decimal` member must be a JSON string, as the ORM writes it. Each Postgres codec declares on its descriptor the JSON type a member of it has (`valueObjectJsonType`), or that it holds any JSON value, or that only its presence is checked. A required member whose codec declares presence only must be present and not JSON `null`; a `Json` member may hold any value. Below the point where a value object refers back into itself, any object is accepted, as the MongoDB validator does. `contract infer` produces no value objects and no polymorphism, so on a live table with these checks it prints each one as an authored `@@check` with its exact name in `map:`, as it does for an enum membership check. diff --git a/docs/reference/error-reference.md b/docs/reference/error-reference.md index 95c8905444a3..08680a7afa50 100644 --- a/docs/reference/error-reference.md +++ b/docs/reference/error-reference.md @@ -271,7 +271,7 @@ A value handed to a data type's cast, or to an authoring entry that reads writte ### CONTRACT.CHECK_NAME_RESERVED -An authored `@@check` / `check()`'s `name:` prefix matches the shape a derived enforcement check would use for a column of the same table (`
__check`, `
__elem_not_null` or `
__value_object`), so it cannot be told apart from a derived check once a non-`managed` table strips those. The message and `collidingColumns` meta name the column(s) whose derived-check shape the prefix matches. Raised while building a SQL contract, once the table's real columns are in hand. The fix is to choose a different `name:`. Payload: `tableName`, `prefix`, `collidingColumns`. +An authored `@@check` / `check()`'s `name:` prefix matches the shape a derived enforcement check would use for a column of the same table (`
__check`, `
__elem_not_null` or `
__shape`), so it cannot be told apart from a derived check once a non-`managed` table strips those. The message and `collidingColumns` meta name the column(s) whose derived-check shape the prefix matches. Raised while building a SQL contract, once the table's real columns are in hand. The fix is to choose a different `name:`. Payload: `tableName`, `prefix`, `collidingColumns`. ### CONTRACT.CHECK_ON_STI_VARIANT diff --git a/packages/2-sql-family/1-core/schema-ir/src/naming.ts b/packages/2-sql-family/1-core/schema-ir/src/naming.ts index 5a05a0ff5518..9d92c0b643bd 100644 --- a/packages/2-sql-family/1-core/schema-ir/src/naming.ts +++ b/packages/2-sql-family/1-core/schema-ir/src/naming.ts @@ -158,13 +158,13 @@ export function computeCheckContentHash(expression: string): string { * The canonical spelling of this union — every layer that can depend on this * module imports it rather than re-spelling the literals. */ -export type CheckKind = 'membership' | 'elementNotNull' | 'valueObject'; +export type CheckKind = 'membership' | 'elementNotNull' | 'shape'; /** The trailing segment a check's wire-name prefix carries, per kind. */ const CHECK_KIND_SUFFIX = { membership: 'check', elementNotNull: 'elem_not_null', - valueObject: 'value_object', + shape: 'shape', } as const; /** diff --git a/packages/2-sql-family/1-core/schema-ir/test/naming.test.ts b/packages/2-sql-family/1-core/schema-ir/test/naming.test.ts index 56741d932bfc..6948b226144b 100644 --- a/packages/2-sql-family/1-core/schema-ir/test/naming.test.ts +++ b/packages/2-sql-family/1-core/schema-ir/test/naming.test.ts @@ -96,11 +96,11 @@ describe('composeCheckWirePrefix', () => { expect({ membership: composeCheckWirePrefix('User', 'role', 'membership'), elementNotNull: composeCheckWirePrefix('User', 'tags', 'elementNotNull'), - valueObject: composeCheckWirePrefix('User', 'address', 'valueObject'), + shape: composeCheckWirePrefix('User', 'address', 'shape'), }).toEqual({ membership: 'User_role_check', elementNotNull: 'User_tags_elem_not_null', - valueObject: 'User_address_value_object', + shape: 'User_address_shape', }); }); @@ -316,10 +316,10 @@ describe('derivedCheckPrefixes', () => { new Set([ composeCheckWirePrefix('User', 'role', 'membership'), composeCheckWirePrefix('User', 'role', 'elementNotNull'), - composeCheckWirePrefix('User', 'role', 'valueObject'), + composeCheckWirePrefix('User', 'role', 'shape'), composeCheckWirePrefix('User', 'tags', 'membership'), composeCheckWirePrefix('User', 'tags', 'elementNotNull'), - composeCheckWirePrefix('User', 'tags', 'valueObject'), + composeCheckWirePrefix('User', 'tags', 'shape'), ]), ); }); diff --git a/packages/2-sql-family/2-authoring/contract-ts/test/contract-builder.derived-checks.test.ts b/packages/2-sql-family/2-authoring/contract-ts/test/contract-builder.derived-checks.test.ts index 08b0895050fd..88942b27ded5 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/test/contract-builder.derived-checks.test.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/test/contract-builder.derived-checks.test.ts @@ -26,7 +26,7 @@ function renderCheckExpressions(input: CheckExpressionInput) { ? [] : [ { - kind: 'valueObject' as const, + kind: 'shape' as const, columnName: input.columnName, expression: `shape of "${input.columnName}"`, }, @@ -171,10 +171,10 @@ describe('the derived check of a column that holds a value object', () => { ]); }); - it('is named after the table, the column and the value object kind', () => { + it('is named after the table, the column and the shape kind', () => { expect(checkNames(build([userModel()]), 'user')).toEqual([ - composeCheckWirePrefix('user', 'home', 'valueObject'), - composeCheckWirePrefix('user', 'stops', 'valueObject'), + composeCheckWirePrefix('user', 'home', 'shape'), + composeCheckWirePrefix('user', 'stops', 'shape'), ]); }); @@ -189,7 +189,7 @@ describe('the derived check of a column that holds a value object', () => { { expression: 'true', map: undefined, - name: composeCheckWirePrefix('user', 'home', 'valueObject'), + name: composeCheckWirePrefix('user', 'home', 'shape'), }, ], }; diff --git a/packages/3-targets/postgres/1-target/src/core/check-expressions.ts b/packages/3-targets/postgres/1-target/src/core/check-expressions.ts index e7d66bb2e9a8..4ce9f37d5d2d 100644 --- a/packages/3-targets/postgres/1-target/src/core/check-expressions.ts +++ b/packages/3-targets/postgres/1-target/src/core/check-expressions.ts @@ -64,7 +64,7 @@ export function postgresRenderCheckExpressions( if (input.valueObject !== undefined) { candidates.push({ - kind: 'valueObject', + kind: 'shape', columnName: input.columnName, expression: valueObjectCheck(column, input.valueObject, jsonTypeReader(codecLookup)), }); diff --git a/packages/3-targets/postgres/1-target/test/check-expressions.value-object.test.ts b/packages/3-targets/postgres/1-target/test/check-expressions.value-object.test.ts index a4ed0c62e2bb..fc1263f02240 100644 --- a/packages/3-targets/postgres/1-target/test/check-expressions.value-object.test.ts +++ b/packages/3-targets/postgres/1-target/test/check-expressions.value-object.test.ts @@ -53,7 +53,7 @@ describe('postgresRenderCheckExpressions for a column that holds a value object' it('requires an object with each required member, of its JSON type, and no other key', () => { expect(render(flat)).toEqual([ { - kind: 'valueObject', + kind: 'shape', expression: `jsonb_path_match("address", 'strict ${isObject} && ${hasStreet} && ${onlyStreet}') IS TRUE`, }, ]); @@ -62,7 +62,7 @@ describe('postgresRenderCheckExpressions for a column that holds a value object' it('accepts SQL NULL and JSON null in a nullable column', () => { expect(render(flat, { nullable: true })).toEqual([ { - kind: 'valueObject', + kind: 'shape', expression: `"address" IS NULL OR jsonb_path_match("address", 'strict $.type() == "null" || (${isObject} && ${hasStreet} && ${onlyStreet})') IS TRUE`, }, ]); @@ -72,7 +72,7 @@ describe('postgresRenderCheckExpressions for a column that holds a value object' const zip = member(scalar('pg/int4@1'), { nullable: true }); expect(render({ fields: { zip } })).toEqual([ { - kind: 'valueObject', + kind: 'shape', expression: `jsonb_path_match("address", 'strict ${isObject} && !exists($.keyvalue() ? (@.key == "zip" && !(@.value.type() == "null" || (@.value.type() == "number")))) && !exists($.keyvalue() ? (@.key != "zip"))') IS TRUE`, }, ]); @@ -83,7 +83,7 @@ describe('postgresRenderCheckExpressions for a column that holds a value object' const flags = member(scalar('pg/bool@1'), { many: { elementNullable: true } }); expect(render({ fields: { tags, flags } })).toEqual([ { - kind: 'valueObject', + kind: 'shape', expression: `jsonb_path_match("address", 'strict ${isObject} && exists($.keyvalue() ? (@.key == "flags" && (@.value.type() == "array" && !exists(@.value[*] ? (!(@.type() == "null" || (@.type() == "boolean"))))))) && exists($.keyvalue() ? (@.key == "tags" && (@.value.type() == "array" && !exists(@.value[*] ? (!((@.type() == "string"))))))) && !exists($.keyvalue() ? (@.key != "flags" && @.key != "tags"))') IS TRUE`, }, ]); @@ -93,7 +93,7 @@ describe('postgresRenderCheckExpressions for a column that holds a value object' const kind = member(scalar('pg/text@1', ['home', "o'brien"])); expect(render({ fields: { kind } })).toEqual([ { - kind: 'valueObject', + kind: 'shape', expression: `jsonb_path_match("address", 'strict ${isObject} && exists($.keyvalue() ? (@.key == "kind" && (@.value.type() == "string" && (@.value == "home" || @.value == "o''brien")))) && !exists($.keyvalue() ? (@.key != "kind"))') IS TRUE`, }, ]); @@ -103,7 +103,7 @@ describe('postgresRenderCheckExpressions for a column that holds a value object' const rank = member(scalar('pg/int4@1', [1, -2])); expect(render({ fields: { rank } })).toEqual([ { - kind: 'valueObject', + kind: 'shape', expression: `jsonb_path_match("address", 'strict ${isObject} && exists($.keyvalue() ? (@.key == "rank" && (@.value.type() == "number" && (@.value == 1 || @.value == -2)))) && !exists($.keyvalue() ? (@.key != "rank"))') IS TRUE`, }, ]); @@ -116,7 +116,7 @@ describe('postgresRenderCheckExpressions for a column that holds a value object' `${path}.type() == "object" && exists(${path}.keyvalue() ? (@.key == "street" && (@.value.type() == "string"))) && !exists(${path}.keyvalue() ? (@.key != "street"))`; expect(render({ fields: { geo, stops } })).toEqual([ { - kind: 'valueObject', + kind: 'shape', expression: `jsonb_path_match("address", 'strict ${isObject} && exists($.keyvalue() ? (@.key == "geo" && (${nestedAt('@.value')}))) && exists($.keyvalue() ? (@.key == "stops" && (@.value.type() == "array" && !exists(@.value[*] ? (!((${nestedAt('@')}))))))) && !exists($.keyvalue() ? (@.key != "geo" && @.key != "stops"))') IS TRUE`, }, ]); @@ -126,7 +126,7 @@ describe('postgresRenderCheckExpressions for a column that holds a value object' const children = member({ kind: 'anyObject' }, { many: { elementNullable: false } }); expect(render({ fields: { children } })).toEqual([ { - kind: 'valueObject', + kind: 'shape', expression: `jsonb_path_match("address", 'strict ${isObject} && exists($.keyvalue() ? (@.key == "children" && (@.value.type() == "array" && !exists(@.value[*] ? (!((@.type() == "object"))))))) && !exists($.keyvalue() ? (@.key != "children"))') IS TRUE`, }, ]); @@ -135,7 +135,7 @@ describe('postgresRenderCheckExpressions for a column that holds a value object' it('checks a list column as an array of the value object', () => { expect(render(flat, { many: { elementNullable: false } })).toEqual([ { - kind: 'valueObject', + kind: 'shape', expression: `jsonb_path_match("address", 'strict $.type() == "array" && !exists($[*] ? (!((@.type() == "object" && exists(@.keyvalue() ? (@.key == "street" && (@.value.type() == "string"))) && !exists(@.keyvalue() ? (@.key != "street"))))))') IS TRUE`, }, ]); @@ -148,7 +148,7 @@ describe('postgresRenderCheckExpressions for a column that holds a value object' const blobs = member(scalar('pg/bytea@1'), { many: { elementNullable: false } }); expect(render({ fields: { blobs, bytes, data, notes } })).toEqual([ { - kind: 'valueObject', + kind: 'shape', expression: `jsonb_path_match("address", 'strict ${isObject} && exists($.keyvalue() ? (@.key == "blobs" && (@.value.type() == "array" && !exists(@.value[*] ? (@.type() == "null"))))) && exists($.keyvalue() ? (@.key == "bytes" && (@.value.type() != "null"))) && exists($.keyvalue() ? (@.key == "data")) && !exists($.keyvalue() ? (@.key != "blobs" && @.key != "bytes" && @.key != "data" && @.key != "notes"))') IS TRUE`, }, ]); @@ -158,7 +158,7 @@ describe('postgresRenderCheckExpressions for a column that holds a value object' const amount = member(scalar('pg/numeric@1')); expect(render({ fields: { amount } })).toEqual([ { - kind: 'valueObject', + kind: 'shape', expression: `jsonb_path_match("address", 'strict ${isObject} && exists($.keyvalue() ? (@.key == "amount" && (@.value.type() == "string"))) && !exists($.keyvalue() ? (@.key != "amount"))') IS TRUE`, }, ]); @@ -168,7 +168,7 @@ describe('postgresRenderCheckExpressions for a column that holds a value object' const quoted = member(scalar('pg/text@1')); expect(render({ fields: { 'say "hi"': quoted } })).toEqual([ { - kind: 'valueObject', + kind: 'shape', expression: `jsonb_path_match("address", 'strict ${isObject} && exists($.keyvalue() ? (@.key == "say \\"hi\\"" && (@.value.type() == "string"))) && !exists($.keyvalue() ? (@.key != "say \\"hi\\""))') IS TRUE`, }, ]); diff --git a/skills/prisma-8/references/contract.md b/skills/prisma-8/references/contract.md index 1d4439c6d9a5..5312cdaab31b 100644 --- a/skills/prisma-8/references/contract.md +++ b/skills/prisma-8/references/contract.md @@ -318,7 +318,7 @@ model User { Emitted `contract.json` carries the value object in storage, at `storage.namespaces..entries.valueObject.Address` with its field descriptors, and the domain field `address` refers to it by coordinate: `{ "kind": "valueObject", "plane": "storage", "namespaceId": "public", "entityKind": "valueObject", "entityName": "Address" }`. The `address` column lands as `codecId: "pg/jsonb@1"` / `dataType: "pg/jsonb"`. Because the value object is in storage, renaming, adding or removing one of its fields changes the storage hash, and `prisma migration plan` writes a migration for it. -On Postgres, a managed table gets a derived check on each value object column, `
__value_object_`: the value must be an object (an array of them for a list) with every required member, each of its JSON type, and no other key. A nullable column also accepts `NULL`. On SQL, a polymorphic base's discriminator column refers to a value set `.` holding the variants' values, and Postgres derives a check from it, as for an enum. +On Postgres, a managed table gets a derived check on each value object column, `
__shape_`: the value must be an object (an array of them for a list) with every required member, each of its JSON type, and no other key. A nullable column also accepts `NULL`. On SQL, a polymorphic base's discriminator column refers to a value set `.` holding the variants' values, and Postgres derives a check from it, as for an enum. Canonical worked example: `examples/prisma-8-demo/src/prisma/contract.prisma`. diff --git a/test/integration/test/cli-journeys/value-object-checks.e2e.test.ts b/test/integration/test/cli-journeys/value-object-checks.e2e.test.ts index 6309cc635f1e..61ed1f1b2d12 100644 --- a/test/integration/test/cli-journeys/value-object-checks.e2e.test.ts +++ b/test/integration/test/cli-journeys/value-object-checks.e2e.test.ts @@ -1,7 +1,7 @@ /** * Value objects and discriminators are storage entities, and Postgres enforces them with derived checks. * - * `User.address` holds an `Address` value object in a `jsonb` column. On a managed table the build derives a check on the column, `user_address_value_object_`, which accepts the documents the ORM writes and refuses any other shape. `Task` is a base model whose variants are stored in their own tables; its discriminator column `type` gets a check on the variants' values, `task_type_check_`. + * `User.address` holds an `Address` value object in a `jsonb` column. On a managed table the build derives a check on the column, `user_address_shape_`, which accepts the documents the ORM writes and refuses any other shape. `Task` is a base model whose variants are stored in their own tables; its discriminator column `type` gets a check on the variants' values, `task_type_check_`. * * Every journey runs twice: once through `migration plan` and `migrate`, once through `db update`. Each ends with `db verify --schema-only` clean and a further plan empty. * @@ -256,10 +256,10 @@ withTempDir(({ createTempDir }) => { 'utf-8', ), 'V1.02: the migration writes the check', - ).toMatch(/CONSTRAINT \\"user_address_value_object_[0-9a-f]{8}\\" CHECK/); + ).toMatch(/CONSTRAINT \\"user_address_shape_[0-9a-f]{8}\\" CHECK/); } expect(await checkNames(db.connectionString, 'user'), 'V1.02: the check').toEqual([ - expect.stringMatching(/^user_address_value_object_[0-9a-f]{8}$/), + expect.stringMatching(/^user_address_shape_[0-9a-f]{8}$/), ]); expect( @@ -314,7 +314,7 @@ withTempDir(({ createTempDir }) => { expect(await apply(ctx, via, 'V2.04'), 'V2.04: a member added').toEqual([ `Drop check constraint "${first}" on "user"`, expect.stringMatching( - /^Add check constraint "user_address_value_object_[0-9a-f]{8}" on "user"$/, + /^Add check constraint "user_address_shape_[0-9a-f]{8}" on "user"$/, ), ]); const [second] = await checkNames(db.connectionString, 'user'); @@ -329,7 +329,7 @@ withTempDir(({ createTempDir }) => { expect(await apply(ctx, via, 'V2.07'), 'V2.07: a member made nullable').toEqual([ `Drop check constraint "${second}" on "user"`, expect.stringMatching( - /^Add check constraint "user_address_value_object_[0-9a-f]{8}" on "user"$/, + /^Add check constraint "user_address_shape_[0-9a-f]{8}" on "user"$/, ), ]); await sql( @@ -377,7 +377,7 @@ withTempDir(({ createTempDir }) => { } expect(failed.exitCode, 'V3.04: the apply fails').not.toBe(0); expect(JSON.stringify(engineError(failed)), 'V3.04: the failure names the check').toMatch( - /user_address_value_object_[0-9a-f]{8}/, + /user_address_shape_[0-9a-f]{8}/, ); expect(await checkNames(db.connectionString, 'user'), 'V3.05: the old check stays').toEqual( diff --git a/test/integration/test/sql-orm-client/value-object-checks.test.ts b/test/integration/test/sql-orm-client/value-object-checks.test.ts index f9e7592b78be..75d0a7035a34 100644 --- a/test/integration/test/sql-orm-client/value-object-checks.test.ts +++ b/test/integration/test/sql-orm-client/value-object-checks.test.ts @@ -185,10 +185,10 @@ describe('derived checks of value object and discriminator columns on Postgres', live: live.rows.map((row) => row.conname), }).toEqual({ prefixes: [ - 'trip_detour_value_object', - 'trip_legs_value_object', - 'trip_menu_value_object', - 'trip_origin_value_object', + 'trip_detour_shape', + 'trip_legs_shape', + 'trip_menu_shape', + 'trip_origin_shape', 'shape_kind_check', ], live: declared.map((check) => check.name).sort(), diff --git a/upgrade-instructions/pending/value-objects-in-storage/app/instructions.md b/upgrade-instructions/pending/value-objects-in-storage/app/instructions.md index 9edaa96805a5..4d26aab9306e 100644 --- a/upgrade-instructions/pending/value-objects-in-storage/app/instructions.md +++ b/upgrade-instructions/pending/value-objects-in-storage/app/instructions.md @@ -76,7 +76,7 @@ type Outputs = ValueObjectTypesFromStorage__value_object_`. It accepts a JSON object with every required member, each of its JSON type, no other key, and for a list column an array of such objects; a nullable column also accepts SQL `NULL` and JSON `null`. For each discriminator column it derives `
__check_`, which accepts only the variants' values. A table whose control policy is not managed gets neither check. +For each column that holds a value object, the contract derives a check named `
__shape_`. It accepts a JSON object with every required member, each of its JSON type, no other key, and for a list column an array of such objects; a nullable column also accepts SQL `NULL` and JSON `null`. For each discriminator column it derives `
__check_`, which accepts only the variants' values. A table whose control policy is not managed gets neither check. After you re-emit the contract, `prisma migration plan` writes a migration that adds the checks. A project with no `db` ref names the origin, the last migration: `prisma migration plan --from `; without it the command refuses with `MIGRATION.PLAN_ORIGIN_UNKNOWN`, as it does for any plan in such a project. Its DDL preview prints each check's expression. Before you run `prisma db migrate`, find the rows that would fail by selecting the rows where the expression is not true, for example: diff --git a/upgrade-instructions/pending/value-objects-in-storage/extension/instructions.md b/upgrade-instructions/pending/value-objects-in-storage/extension/instructions.md index ecd429653164..7b4512eb8e20 100644 --- a/upgrade-instructions/pending/value-objects-in-storage/extension/instructions.md +++ b/upgrade-instructions/pending/value-objects-in-storage/extension/instructions.md @@ -12,7 +12,7 @@ changes: - '\bcompileMongoQuery\s*\(' - id: postgres-check-expression-types-move summary: | - `PostgresCheckKind`, `PostgresCheckExpressionInput` and `PostgresCheckExpressionCandidate` are removed from `@prisma/orm-postgres/target/types`. Use `CheckExpressionInput` and `CheckExpressionCandidate` from `@prisma/orm-postgres/family-contract/check-expressions` (also exported at the same path by `@prisma/orm-mysql` and `@prisma/orm-sqlite`). A candidate's `kind` is `'membership'`, `'elementNotNull'` or the new `'valueObject'`. + `PostgresCheckKind`, `PostgresCheckExpressionInput` and `PostgresCheckExpressionCandidate` are removed from `@prisma/orm-postgres/target/types`. Use `CheckExpressionInput` and `CheckExpressionCandidate` from `@prisma/orm-postgres/family-contract/check-expressions` (also exported at the same path by `@prisma/orm-mysql` and `@prisma/orm-sqlite`). A candidate's `kind` is `'membership'`, `'elementNotNull'` or the new `'shape'`, the check of a value object column's shape. detection: glob: "**/*.{ts,mts,cts,tsx}" matches: From f015cacf1ab612df1f8816c3de1b9ab6f75795e7 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 19:02:39 +0200 Subject: [PATCH 081/104] TML-3537: the truncated shape prefix in the naming test Signed-off-by: willbot Signed-off-by: Will Madden --- .../contract.d.ts | 1365 ---------------- .../contract.json | 1 - .../contract.d.ts | 1420 ---------------- .../contract.json | 1 - .../contract.d.ts | 1401 ---------------- .../contract.json | 1 - .../contract.d.ts | 1431 ---------------- .../contract.json | 1 - .../contract.d.ts | 1454 ----------------- .../contract.json | 1 - .../1-core/schema-ir/test/naming.test.ts | 2 +- 11 files changed, 1 insertion(+), 7077 deletions(-) delete mode 100644 examples/prisma-8-demo/migrations/snapshots/11d8695d1b8ea9c748834093ac4111ce68145827709363b7c748c2892be84146/contract.d.ts delete mode 100644 examples/prisma-8-demo/migrations/snapshots/11d8695d1b8ea9c748834093ac4111ce68145827709363b7c748c2892be84146/contract.json delete mode 100644 examples/prisma-8-demo/migrations/snapshots/1c819419d4357de21d9aa9404a0b1d3639e3bd33c3fe6176207c53b51365fa1f/contract.d.ts delete mode 100644 examples/prisma-8-demo/migrations/snapshots/1c819419d4357de21d9aa9404a0b1d3639e3bd33c3fe6176207c53b51365fa1f/contract.json delete mode 100644 examples/prisma-8-demo/migrations/snapshots/3c2324f6061012e9d50f869b3e6ae6aa41159acd559b1cc5459bc64e2ccb0f4e/contract.d.ts delete mode 100644 examples/prisma-8-demo/migrations/snapshots/3c2324f6061012e9d50f869b3e6ae6aa41159acd559b1cc5459bc64e2ccb0f4e/contract.json delete mode 100644 examples/prisma-8-demo/migrations/snapshots/9301c8a3b2ec83c89b4eb12568ccb0c9e916ba199d8743ab38ed468483d6921f/contract.d.ts delete mode 100644 examples/prisma-8-demo/migrations/snapshots/9301c8a3b2ec83c89b4eb12568ccb0c9e916ba199d8743ab38ed468483d6921f/contract.json delete mode 100644 examples/prisma-8-demo/migrations/snapshots/99a4393ee9f877db228baee1164325543fa018be780fd1253250d7d81539f38e/contract.d.ts delete mode 100644 examples/prisma-8-demo/migrations/snapshots/99a4393ee9f877db228baee1164325543fa018be780fd1253250d7d81539f38e/contract.json diff --git a/examples/prisma-8-demo/migrations/snapshots/11d8695d1b8ea9c748834093ac4111ce68145827709363b7c748c2892be84146/contract.d.ts b/examples/prisma-8-demo/migrations/snapshots/11d8695d1b8ea9c748834093ac4111ce68145827709363b7c748c2892be84146/contract.d.ts deleted file mode 100644 index d94608dfdbb9..000000000000 --- a/examples/prisma-8-demo/migrations/snapshots/11d8695d1b8ea9c748834093ac4111ce68145827709363b7c748c2892be84146/contract.d.ts +++ /dev/null @@ -1,1365 +0,0 @@ -// ⚠️ GENERATED FILE - DO NOT EDIT -// This file is automatically generated by 'prisma contract emit'. -// To regenerate, run: prisma contract emit -import type { - CodecTypes as PgVectorTypes, - ParameterizedOutputTypesForTypeParams as PgVectorParameterizedOutputTypes, -} from '@prisma/orm-extension-pgvector/codec-types'; -import type { QueryOperationTypes as PgVectorQueryOperationTypes } from '@prisma/orm-extension-pgvector/operation-types'; -import type { - CodecTypes as PgTypes, - ParameterizedOutputTypesForTypeParams as PgParameterizedOutputTypes, -} from '@prisma/orm-postgres/target/codec-types'; -import type { QueryOperationTypes as PgTargetQueryOps } from '@prisma/orm-postgres/target/operation-types'; - -import type { - ColumnTypesFromStorage, - ContractWithTypeMaps, - RelationKeys, - TypeMaps as TypeMapsType, -} from '@prisma/orm-postgres/family-contract/types'; -import type { - Contract as ContractType, - ExecutionHashBase, - FieldTypesFromDomain, - NamespaceId, - ProfileHashBase, - StorageHashBase, - ValueObjectTypesFromStorage, -} from '@prisma/orm-postgres/contract/types'; - -export type StorageHash = - StorageHashBase<'11d8695d1b8ea9c748834093ac4111ce68145827709363b7c748c2892be84146'>; -export type ExecutionHash = - ExecutionHashBase<'eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655'>; -export type ProfileHash = - ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; - -export type CodecTypes = PgTypes & PgVectorTypes; -export type LaneCodecTypes = CodecTypes; -export type QueryOperationTypes = PgTargetQueryOps & - PgVectorQueryOperationTypes; -export type AggregateTypes = { - readonly avg: { - readonly byCodec: { - readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/float4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/interval-string@1': { - readonly output: 'pg/interval-string@1'; - readonly nullable: true; - }; - readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - }; - }; - readonly avgDecimal: { - readonly byCodec: { - readonly 'pg/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - }; - }; - readonly count: { - readonly byCodec: {}; - readonly withoutInput: { readonly output: 'pg/int8number@1'; readonly nullable: false }; - readonly anyInput: { readonly output: 'pg/int8number@1'; readonly nullable: false }; - }; - readonly countBigInt: { - readonly byCodec: {}; - readonly withoutInput: { readonly output: 'pg/int8@1'; readonly nullable: false }; - readonly anyInput: { readonly output: 'pg/int8@1'; readonly nullable: false }; - }; - readonly max: { - readonly byCodec: { - readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true }; - readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true }; - readonly 'pg/date-temporal@1': { - readonly output: 'pg/date-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true }; - readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true }; - readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; - readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true }; - readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/interval-string@1': { - readonly output: 'pg/interval-string@1'; - readonly nullable: true; - }; - readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; - readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true }; - readonly 'pg/time-temporal@1': { - readonly output: 'pg/time-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-date@1': { - readonly output: 'pg/timestamp-date@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-instant@1': { - readonly output: 'pg/timestamp-instant@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-string@1': { - readonly output: 'pg/timestamp-string@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-temporal@1': { - readonly output: 'pg/timestamp-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-date@1': { - readonly output: 'pg/timestamptz-date@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-string@1': { - readonly output: 'pg/timestamptz-string@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-temporal@1': { - readonly output: 'pg/timestamptz-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { - readonly output: 'pg/unboundedint@1'; - readonly nullable: true; - }; - readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true }; - readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true }; - readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true }; - readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - }; - }; - readonly min: { - readonly byCodec: { - readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true }; - readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true }; - readonly 'pg/date-temporal@1': { - readonly output: 'pg/date-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true }; - readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true }; - readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; - readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true }; - readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/interval-string@1': { - readonly output: 'pg/interval-string@1'; - readonly nullable: true; - }; - readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; - readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true }; - readonly 'pg/time-temporal@1': { - readonly output: 'pg/time-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-date@1': { - readonly output: 'pg/timestamp-date@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-instant@1': { - readonly output: 'pg/timestamp-instant@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-string@1': { - readonly output: 'pg/timestamp-string@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-temporal@1': { - readonly output: 'pg/timestamp-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-date@1': { - readonly output: 'pg/timestamptz-date@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-string@1': { - readonly output: 'pg/timestamptz-string@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-temporal@1': { - readonly output: 'pg/timestamptz-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { - readonly output: 'pg/unboundedint@1'; - readonly nullable: true; - }; - readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true }; - readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true }; - readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true }; - readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - }; - }; - readonly stddev: { - readonly byCodec: { - readonly 'pg/float@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/float4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/float8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'sql/float@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - }; - }; - readonly sum: { - readonly byCodec: { - readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; - readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/interval-string@1': { - readonly output: 'pg/interval-string@1'; - readonly nullable: true; - }; - readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { - readonly output: 'pg/unboundedint@1'; - readonly nullable: true; - }; - readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - }; - }; - readonly sumBigInt: { - readonly byCodec: { - readonly 'pg/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { - readonly output: 'pg/unboundedint@1'; - readonly nullable: true; - }; - readonly 'sql/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - }; - }; -}; -type DefaultLiteralValue = CodecId extends keyof CodecTypes - ? Encoded extends CodecTypes[CodecId]['json'] - ? Encoded - : CodecTypes[CodecId]['json'] - : Encoded; -type ParameterizedOutputTypeFunctions = - PgParameterizedOutputTypes | PgVectorParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromStorage< - ContractBase['storage']['namespaces'], - CodecTypes, - ParameterizedOutputTypeFunctions, - FieldTypeOverrides, - 'output' ->; -type ValueObjectInputTypes = ValueObjectTypesFromStorage< - ContractBase['storage']['namespaces'], - CodecTypes, - ParameterizedOutputTypeFunctions, - FieldTypeOverrides, - 'input' ->; -export type AddressOutput = ValueObjectOutputTypes['public']['Address']; -export type AddressInput = ValueObjectInputTypes['public']['Address']; -type FieldTypeOverrides = { - readonly public: { - readonly models: { - readonly Post: { - readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 }; - }; - readonly Task: { - readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; - }; - readonly User: { - readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; - }; - }; - }; -}; -export type FieldOutputTypes = FieldTypesFromDomain< - ContractBase['domain']['namespaces'], - CodecTypes, - ParameterizedOutputTypeFunctions, - FieldTypeOverrides, - 'output', - ContractBase['storage']['namespaces'] ->; -export type FieldInputTypes = FieldTypesFromDomain< - ContractBase['domain']['namespaces'], - CodecTypes, - ParameterizedOutputTypeFunctions, - FieldTypeOverrides, - 'input', - ContractBase['storage']['namespaces'] ->; -type ColumnTypeOverrides = { - readonly public: { - readonly post: { readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 } }; - readonly task: { - readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; - }; - readonly user: { - readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; - }; - }; -}; -export type StorageColumnTypes = ColumnTypesFromStorage< - ContractBase['storage'], - CodecTypes, - ParameterizedOutputTypeFunctions, - ColumnTypeOverrides, - 'output' ->; -export type StorageColumnInputTypes = ColumnTypesFromStorage< - ContractBase['storage'], - CodecTypes, - ParameterizedOutputTypeFunctions, - ColumnTypeOverrides, - 'input' ->; - -export namespace Models { - export type public_Bug = { - createdAt: FieldOutputTypes['public']['Task']['createdAt']; - description: FieldOutputTypes['public']['Task']['description']; - id: FieldOutputTypes['public']['Task']['id']; - status: FieldOutputTypes['public']['Task']['status']; - title: FieldOutputTypes['public']['Task']['title']; - type: 'bug'; - userId: FieldOutputTypes['public']['Task']['userId']; - severity: FieldOutputTypes['public']['Bug']['severity']; - stepsToRepro: FieldOutputTypes['public']['Bug']['stepsToRepro']; - user: public_User; - readonly [RelationKeys]?: 'user'; - }; - export type public_Feature = { - createdAt: FieldOutputTypes['public']['Task']['createdAt']; - description: FieldOutputTypes['public']['Task']['description']; - id: FieldOutputTypes['public']['Task']['id']; - status: FieldOutputTypes['public']['Task']['status']; - title: FieldOutputTypes['public']['Task']['title']; - type: 'feature'; - userId: FieldOutputTypes['public']['Task']['userId']; - priority: FieldOutputTypes['public']['Feature']['priority']; - targetRelease: FieldOutputTypes['public']['Feature']['targetRelease']; - user: public_User; - readonly [RelationKeys]?: 'user'; - }; - export type public_Post = { - createdAt: FieldOutputTypes['public']['Post']['createdAt']; - embedding: FieldOutputTypes['public']['Post']['embedding']; - id: FieldOutputTypes['public']['Post']['id']; - priority: FieldOutputTypes['public']['Post']['priority']; - title: FieldOutputTypes['public']['Post']['title']; - userId: FieldOutputTypes['public']['Post']['userId']; - tags: public_Tag[]; - user: public_User; - readonly [RelationKeys]?: 'tags' | 'user'; - }; - export type public_PostTag = { - postId: FieldOutputTypes['public']['PostTag']['postId']; - tagId: FieldOutputTypes['public']['PostTag']['tagId']; - post: public_Post; - tag: public_Tag; - readonly [RelationKeys]?: 'post' | 'tag'; - }; - export type public_Tag = { - id: FieldOutputTypes['public']['Tag']['id']; - label: FieldOutputTypes['public']['Tag']['label']; - posts: public_Post[]; - readonly [RelationKeys]?: 'posts'; - }; - export type public_Task = { - createdAt: FieldOutputTypes['public']['Task']['createdAt']; - description: FieldOutputTypes['public']['Task']['description']; - id: FieldOutputTypes['public']['Task']['id']; - status: FieldOutputTypes['public']['Task']['status']; - title: FieldOutputTypes['public']['Task']['title']; - type: 'bug' | 'feature'; - userId: FieldOutputTypes['public']['Task']['userId']; - user: public_User; - readonly [RelationKeys]?: 'user'; - }; - export type public_User = { - address: FieldOutputTypes['public']['User']['address']; - createdAt: FieldOutputTypes['public']['User']['createdAt']; - displayName: FieldOutputTypes['public']['User']['displayName']; - email: FieldOutputTypes['public']['User']['email']; - id: FieldOutputTypes['public']['User']['id']; - kind: FieldOutputTypes['public']['User']['kind']; - posts: public_Post[]; - tasks: public_AnyTask[]; - readonly [RelationKeys]?: 'posts' | 'tasks'; - }; - export type public_AnyTask = public_Bug | public_Feature; -} - -export declare const models: { - public: { - Bug: Models.public_Bug; - Feature: Models.public_Feature; - Post: Models.public_Post; - PostTag: Models.public_PostTag; - Tag: Models.public_Tag; - Task: Models.public_Task; - User: Models.public_User; - AnyTask: Models.public_AnyTask; - }; -}; - -export type TypeMaps = TypeMapsType< - CodecTypes, - QueryOperationTypes, - FieldOutputTypes, - FieldInputTypes, - StorageColumnTypes, - StorageColumnInputTypes, - AggregateTypes ->; - -type ContractBase = Omit< - ContractType<{ - readonly namespaces: { - readonly public: { - readonly id: 'public'; - readonly kind: 'postgres-schema'; - readonly entries: { - readonly table: { - readonly bug: { - columns: { - readonly id: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly severity: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - readonly stepsToRepro: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: true; - readonly many: false; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly []; - foreignKeys: readonly [ - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'bug'; - readonly columns: readonly ['id']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'task'; - readonly columns: readonly ['id']; - }; - }, - ]; - }; - readonly feature: { - columns: { - readonly id: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly priority: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - readonly targetRelease: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: true; - readonly many: false; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly []; - foreignKeys: readonly [ - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'feature'; - readonly columns: readonly ['id']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'task'; - readonly columns: readonly ['id']; - }; - }, - ]; - }; - readonly post: { - columns: { - readonly createdAt: { - readonly dataType: 'pg/timestamptz'; - readonly codecId: 'pg/timestamptz-temporal@1'; - readonly nullable: false; - readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; - readonly many: false; - }; - readonly embedding: { - readonly dataType: 'pgvector/vector'; - readonly codecId: 'pg/vector@1'; - readonly nullable: true; - readonly typeParams: { readonly length: 1536 }; - readonly many: false; - }; - readonly id: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly priority: { - readonly dataType: 'pg/int4'; - readonly codecId: 'pg/int4@1'; - readonly nullable: false; - readonly default: { - readonly kind: 'literal'; - readonly value: DefaultLiteralValue<'pg/int4@1', 0>; - }; - readonly many: false; - }; - readonly title: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - readonly userId: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly [ - { - readonly name: 'post_userId_idx_a489d58a'; - readonly prefix: 'post_userId_idx'; - readonly columns: readonly ['userId']; - readonly unique: false; - }, - ]; - foreignKeys: readonly [ - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'post'; - readonly columns: readonly ['userId']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'user'; - readonly columns: readonly ['id']; - }; - }, - ]; - }; - readonly post_tag: { - columns: { - readonly postId: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly tagId: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - }; - primaryKey: { readonly columns: readonly ['postId', 'tagId'] }; - uniques: readonly []; - indexes: readonly [ - { - readonly name: 'post_tag_postId_idx_a7a72715'; - readonly prefix: 'post_tag_postId_idx'; - readonly columns: readonly ['postId']; - readonly unique: false; - }, - { - readonly name: 'post_tag_tagId_idx_86854244'; - readonly prefix: 'post_tag_tagId_idx'; - readonly columns: readonly ['tagId']; - readonly unique: false; - }, - ]; - foreignKeys: readonly [ - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'post_tag'; - readonly columns: readonly ['postId']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'post'; - readonly columns: readonly ['id']; - }; - }, - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'post_tag'; - readonly columns: readonly ['tagId']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'tag'; - readonly columns: readonly ['id']; - }; - }, - ]; - }; - readonly tag: { - columns: { - readonly id: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly label: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly [{ readonly columns: readonly ['label'] }]; - indexes: readonly []; - foreignKeys: readonly []; - }; - readonly task: { - columns: { - readonly createdAt: { - readonly dataType: 'pg/timestamptz'; - readonly codecId: 'pg/timestamptz-temporal@1'; - readonly nullable: false; - readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; - readonly many: false; - }; - readonly description: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: true; - readonly many: false; - }; - readonly id: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly status: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly default: { - readonly kind: 'literal'; - readonly value: DefaultLiteralValue<'pg/text@1', 'open'>; - }; - readonly many: false; - }; - readonly title: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - readonly type: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - readonly userId: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly [ - { - readonly name: 'task_userId_idx_a489d58a'; - readonly prefix: 'task_userId_idx'; - readonly columns: readonly ['userId']; - readonly unique: false; - }, - ]; - foreignKeys: readonly [ - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'task'; - readonly columns: readonly ['userId']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'user'; - readonly columns: readonly ['id']; - }; - }, - ]; - }; - readonly user: { - columns: { - readonly address: { - readonly dataType: 'pg/jsonb'; - readonly codecId: 'pg/jsonb@1'; - readonly nullable: true; - readonly many: false; - }; - readonly createdAt: { - readonly dataType: 'pg/timestamptz'; - readonly codecId: 'pg/timestamptz-temporal@1'; - readonly nullable: false; - readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; - readonly many: false; - }; - readonly displayName: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - readonly email: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - readonly id: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly kind: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly []; - foreignKeys: readonly []; - }; - }; - readonly valueSet: { - readonly Priority: { readonly kind: 'valueSet'; readonly values: readonly [0, 1, 2] }; - readonly 'Task.type': { - readonly kind: 'valueSet'; - readonly values: readonly ['bug', 'feature']; - }; - readonly user_type: { - readonly kind: 'valueSet'; - readonly values: readonly ['admin', 'user']; - }; - }; - readonly valueObject: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - }; - }; - }; - }; - }; - readonly storageHash: StorageHash; - }>, - 'roots' | 'domain' -> & { - readonly target: 'postgres'; - readonly targetFamily: 'sql'; - readonly roots: { - readonly post: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; - readonly post_tag: { readonly namespace: 'public' & NamespaceId; readonly model: 'PostTag' }; - readonly tag: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; - readonly task: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; - readonly user: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; - }; - readonly domain: { - readonly namespaces: { - readonly public: { - readonly models: { - readonly Bug: { - readonly fields: { - readonly severity: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly stepsToRepro: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - readonly relations: Record; - readonly storage: { - readonly table: 'bug'; - readonly namespaceId: 'public'; - readonly fields: { - readonly severity: { readonly column: 'severity' }; - readonly stepsToRepro: { readonly column: 'stepsToRepro' }; - }; - }; - readonly base: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; - }; - readonly Feature: { - readonly fields: { - readonly priority: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly targetRelease: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - readonly relations: Record; - readonly storage: { - readonly table: 'feature'; - readonly namespaceId: 'public'; - readonly fields: { - readonly priority: { readonly column: 'priority' }; - readonly targetRelease: { readonly column: 'targetRelease' }; - }; - }; - readonly base: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; - }; - readonly Post: { - readonly fields: { - readonly createdAt: { - readonly nullable: false; - readonly type: { - readonly kind: 'scalar'; - readonly codecId: 'pg/timestamptz-temporal@1'; - }; - }; - readonly embedding: { - readonly nullable: true; - readonly type: { - readonly kind: 'scalar'; - readonly codecId: 'pg/vector@1'; - readonly typeParams: { readonly length: 1536 }; - }; - }; - readonly id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly priority: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int4@1' }; - }; - readonly title: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - }; - readonly relations: { - readonly tags: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; - readonly cardinality: 'N:M'; - readonly on: { - readonly localFields: readonly ['id']; - readonly targetFields: readonly ['postId']; - }; - readonly through: { - readonly table: 'post_tag'; - readonly namespaceId: 'public'; - readonly parentColumns: readonly ['postId']; - readonly childColumns: readonly ['tagId']; - readonly targetColumns: readonly ['id']; - }; - }; - readonly user: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['userId']; - readonly targetFields: readonly ['id']; - }; - }; - }; - readonly storage: { - readonly table: 'post'; - readonly namespaceId: 'public'; - readonly fields: { - readonly createdAt: { readonly column: 'createdAt' }; - readonly embedding: { readonly column: 'embedding' }; - readonly id: { readonly column: 'id' }; - readonly priority: { readonly column: 'priority' }; - readonly title: { readonly column: 'title' }; - readonly userId: { readonly column: 'userId' }; - }; - }; - }; - readonly PostTag: { - readonly fields: { - readonly postId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly tagId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - }; - readonly relations: { - readonly post: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['postId']; - readonly targetFields: readonly ['id']; - }; - }; - readonly tag: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['tagId']; - readonly targetFields: readonly ['id']; - }; - }; - }; - readonly storage: { - readonly table: 'post_tag'; - readonly namespaceId: 'public'; - readonly fields: { - readonly postId: { readonly column: 'postId' }; - readonly tagId: { readonly column: 'tagId' }; - }; - }; - }; - readonly Tag: { - readonly fields: { - readonly id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly label: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - readonly relations: { - readonly posts: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; - readonly cardinality: 'N:M'; - readonly on: { - readonly localFields: readonly ['id']; - readonly targetFields: readonly ['tagId']; - }; - readonly through: { - readonly table: 'post_tag'; - readonly namespaceId: 'public'; - readonly parentColumns: readonly ['tagId']; - readonly childColumns: readonly ['postId']; - readonly targetColumns: readonly ['id']; - }; - }; - }; - readonly storage: { - readonly table: 'tag'; - readonly namespaceId: 'public'; - readonly fields: { - readonly id: { readonly column: 'id' }; - readonly label: { readonly column: 'label' }; - }; - }; - }; - readonly Task: { - readonly fields: { - readonly createdAt: { - readonly nullable: false; - readonly type: { - readonly kind: 'scalar'; - readonly codecId: 'pg/timestamptz-temporal@1'; - }; - }; - readonly description: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly status: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly title: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly type: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - }; - readonly relations: { - readonly user: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['userId']; - readonly targetFields: readonly ['id']; - }; - }; - }; - readonly storage: { - readonly table: 'task'; - readonly namespaceId: 'public'; - readonly fields: { - readonly createdAt: { readonly column: 'createdAt' }; - readonly description: { readonly column: 'description' }; - readonly id: { readonly column: 'id' }; - readonly status: { readonly column: 'status' }; - readonly title: { readonly column: 'title' }; - readonly type: { readonly column: 'type' }; - readonly userId: { readonly column: 'userId' }; - }; - }; - readonly discriminator: { readonly field: 'type' }; - readonly variants: { - readonly bug: { readonly namespace: 'public' & NamespaceId; readonly model: 'Bug' }; - readonly feature: { - readonly namespace: 'public' & NamespaceId; - readonly model: 'Feature'; - }; - }; - }; - readonly User: { - readonly fields: { - readonly address: { - readonly nullable: true; - readonly type: { - readonly kind: 'valueObject'; - readonly plane: 'storage'; - readonly namespaceId: 'public'; - readonly entityKind: 'valueObject'; - readonly entityName: 'Address'; - }; - }; - readonly createdAt: { - readonly nullable: false; - readonly type: { - readonly kind: 'scalar'; - readonly codecId: 'pg/timestamptz-temporal@1'; - }; - }; - readonly displayName: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly email: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly kind: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - readonly relations: { - readonly posts: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; - readonly cardinality: '1:N'; - readonly on: { - readonly localFields: readonly ['id']; - readonly targetFields: readonly ['userId']; - }; - }; - readonly tasks: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; - readonly cardinality: '1:N'; - readonly on: { - readonly localFields: readonly ['id']; - readonly targetFields: readonly ['userId']; - }; - }; - }; - readonly storage: { - readonly table: 'user'; - readonly namespaceId: 'public'; - readonly fields: { - readonly address: { readonly column: 'address' }; - readonly createdAt: { readonly column: 'createdAt' }; - readonly displayName: { readonly column: 'displayName' }; - readonly email: { readonly column: 'email' }; - readonly id: { readonly column: 'id' }; - readonly kind: { readonly column: 'kind' }; - }; - }; - }; - }; - readonly enum: { - readonly Priority: { - readonly codecId: 'pg/int4@1'; - readonly members: readonly [ - { readonly name: 'Low'; readonly value: 0 }, - { readonly name: 'High'; readonly value: 1 }, - { readonly name: 'Urgent'; readonly value: 2 }, - ]; - }; - readonly user_type: { - readonly codecId: 'pg/text@1'; - readonly members: readonly [ - { readonly name: 'admin'; readonly value: 'admin' }, - { readonly name: 'user'; readonly value: 'user' }, - ]; - }; - }; - readonly enumMemberTypes?: { - readonly Priority: readonly [ - { readonly name: 'Low'; readonly value: 0 }, - { readonly name: 'High'; readonly value: 1 }, - { readonly name: 'Urgent'; readonly value: 2 }, - ]; - readonly user_type: readonly [ - { readonly name: 'admin'; readonly value: 'admin' }, - { readonly name: 'user'; readonly value: 'user' }, - ]; - }; - }; - }; - }; - readonly capabilities: { - readonly postgres: { - readonly distinctOn: true; - readonly forKeyShare: true; - readonly forNoKeyUpdate: true; - readonly jsonAgg: true; - readonly lateral: true; - readonly limit: true; - readonly orderBy: true; - readonly 'pgvector.cosine': true; - readonly returning: true; - }; - readonly sql: { - readonly checkConstraint: true; - readonly defaultInInsert: true; - readonly enums: true; - readonly forShare: true; - readonly forUpdate: true; - readonly insertOnConflictSkip: true; - readonly insertOnConflictWithoutTarget: true; - readonly lateral: true; - readonly lockNowait: true; - readonly lockOf: true; - readonly lockSkipLocked: true; - readonly returning: true; - readonly scalarList: true; - }; - }; - readonly extensions: { - readonly 'demo/engagement-stats': { - readonly familyId: 'sql'; - readonly id: 'demo/engagement-stats'; - readonly kind: 'extension'; - readonly targetId: 'postgres'; - readonly types: { - readonly aggregateDescriptors: readonly [ - { - readonly input: { readonly kind: 'trait'; readonly trait: 'numeric' }; - readonly lower: unknown; - readonly nullable: true; - readonly operation: 'stddev'; - readonly output: { readonly codecId: 'pg/numeric@1'; readonly kind: 'codec' }; - }, - ]; - }; - readonly version: '0.0.1'; - }; - readonly pgvector: { - readonly capabilities: { readonly postgres: { readonly 'pgvector.cosine': true } }; - readonly familyId: 'sql'; - readonly id: 'pgvector'; - readonly kind: 'extension'; - readonly targetId: 'postgres'; - readonly types: { - readonly codecTypes: { - readonly import: { - readonly alias: 'PgVectorTypes'; - readonly named: 'CodecTypes'; - readonly package: '@internal/extension-pgvector/codec-types'; - }; - readonly parameterizedOutputTypes: { - readonly alias: 'PgVectorParameterizedOutputTypes'; - readonly named: 'ParameterizedOutputTypesForTypeParams'; - readonly package: '@internal/extension-pgvector/codec-types'; - }; - }; - readonly operationTypes: { - readonly import: { - readonly alias: 'PgVectorOperationTypes'; - readonly named: 'OperationTypes'; - readonly package: '@internal/extension-pgvector/operation-types'; - }; - }; - readonly queryOperationTypes: { - readonly import: { - readonly alias: 'PgVectorQueryOperationTypes'; - readonly named: 'QueryOperationTypes'; - readonly package: '@internal/extension-pgvector/operation-types'; - }; - }; - }; - readonly version: '0.0.1'; - }; - }; - readonly execution: { - readonly executionHash: ExecutionHash; - readonly mutations: { - readonly defaults: readonly [ - { - readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; - readonly ref: { - readonly entry: 'post'; - readonly field: 'id'; - readonly namespace: 'public'; - }; - }, - { - readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; - readonly ref: { - readonly entry: 'tag'; - readonly field: 'id'; - readonly namespace: 'public'; - }; - }, - { - readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; - readonly ref: { - readonly entry: 'task'; - readonly field: 'id'; - readonly namespace: 'public'; - }; - }, - { - readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; - readonly ref: { - readonly entry: 'user'; - readonly field: 'id'; - readonly namespace: 'public'; - }; - }, - ]; - }; - }; - readonly meta: {}; - readonly profileHash: ProfileHash; -}; - -export type Contract = ContractWithTypeMaps; - -export type Namespaces = Contract['storage']['namespaces']; diff --git a/examples/prisma-8-demo/migrations/snapshots/11d8695d1b8ea9c748834093ac4111ce68145827709363b7c748c2892be84146/contract.json b/examples/prisma-8-demo/migrations/snapshots/11d8695d1b8ea9c748834093ac4111ce68145827709363b7c748c2892be84146/contract.json deleted file mode 100644 index d1a142478ccd..000000000000 --- a/examples/prisma-8-demo/migrations/snapshots/11d8695d1b8ea9c748834093ac4111ce68145827709363b7c748c2892be84146/contract.json +++ /dev/null @@ -1 +0,0 @@ -{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{"postgres":{"distinctOn":true,"forKeyShare":true,"forNoKeyUpdate":true,"jsonAgg":true,"lateral":true,"limit":true,"orderBy":true,"pgvector.cosine":true,"returning":true},"sql":{"checkConstraint":true,"defaultInInsert":true,"enums":true,"forShare":true,"forUpdate":true,"insertOnConflictSkip":true,"insertOnConflictWithoutTarget":true,"lateral":true,"lockNowait":true,"lockOf":true,"lockSkipLocked":true,"returning":true,"scalarList":true}},"domain":{"namespaces":{"public":{"enum":{"Priority":{"codecId":"pg/int4@1","members":[{"name":"Low","value":0},{"name":"High","value":1},{"name":"Urgent","value":2}]},"user_type":{"codecId":"pg/text@1","members":[{"name":"admin","value":"admin"},{"name":"user","value":"user"}]}},"models":{"Bug":{"base":{"model":"Task","namespace":"public"},"fields":{"severity":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"stepsToRepro":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"severity":{"column":"severity"},"stepsToRepro":{"column":"stepsToRepro"}},"namespaceId":"public","table":"bug"}},"Feature":{"base":{"model":"Task","namespace":"public"},"fields":{"priority":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"targetRelease":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"priority":{"column":"priority"},"targetRelease":{"column":"targetRelease"}},"namespaceId":"public","table":"feature"}},"Post":{"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"embedding":{"nullable":true,"type":{"codecId":"pg/vector@1","kind":"scalar","typeParams":{"length":1536}}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"priority":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"Priority","namespaceId":"public","plane":"domain"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"tags":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["postId"]},"through":{"childColumns":["tagId"],"namespaceId":"public","parentColumns":["postId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Tag","namespace":"public"}},"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"embedding":{"column":"embedding"},"id":{"column":"id"},"priority":{"column":"priority"},"title":{"column":"title"},"userId":{"column":"userId"}},"namespaceId":"public","table":"post"}},"PostTag":{"fields":{"postId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"tagId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"post":{"cardinality":"N:1","nullable":false,"on":{"localFields":["postId"],"targetFields":["id"]},"to":{"model":"Post","namespace":"public"}},"tag":{"cardinality":"N:1","nullable":false,"on":{"localFields":["tagId"],"targetFields":["id"]},"to":{"model":"Tag","namespace":"public"}}},"storage":{"fields":{"postId":{"column":"postId"},"tagId":{"column":"tagId"}},"namespaceId":"public","table":"post_tag"}},"Tag":{"fields":{"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"label":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{"posts":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["tagId"]},"through":{"childColumns":["postId"],"namespaceId":"public","parentColumns":["tagId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Post","namespace":"public"}}},"storage":{"fields":{"id":{"column":"id"},"label":{"column":"label"}},"namespaceId":"public","table":"tag"}},"Task":{"discriminator":{"field":"type"},"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"description":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"status":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"type":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"description":{"column":"description"},"id":{"column":"id"},"status":{"column":"status"},"title":{"column":"title"},"type":{"column":"type"},"userId":{"column":"userId"}},"namespaceId":"public","table":"task"},"variants":{"bug":{"model":"Bug","namespace":"public"},"feature":{"model":"Feature","namespace":"public"}}},"User":{"fields":{"address":{"nullable":true,"type":{"entityKind":"valueObject","entityName":"Address","kind":"valueObject","namespaceId":"public","plane":"storage"}},"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"displayName":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"user_type","namespaceId":"public","plane":"domain"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Post","namespace":"public"}},"tasks":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Task","namespace":"public"}}},"storage":{"fields":{"address":{"column":"address"},"createdAt":{"column":"createdAt"},"displayName":{"column":"displayName"},"email":{"column":"email"},"id":{"column":"id"},"kind":{"column":"kind"}},"namespaceId":"public","table":"user"}}}}}},"execution":{"executionHash":"eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655","mutations":{"defaults":[{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"post","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"tag","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"task","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"user","field":"id","namespace":"public"}}]}},"extensions":{"demo/engagement-stats":{"familyId":"sql","id":"demo/engagement-stats","kind":"extension","targetId":"postgres","types":{"aggregateDescriptors":[{"input":{"kind":"trait","trait":"numeric"},"nullable":true,"operation":"stddev","output":{"codecId":"pg/numeric@1","kind":"codec"}}]},"version":"0.0.1"},"pgvector":{"capabilities":{"postgres":{"pgvector.cosine":true}},"familyId":"sql","id":"pgvector","kind":"extension","targetId":"postgres","types":{"codecTypes":{"import":{"alias":"PgVectorTypes","named":"CodecTypes","package":"@internal/extension-pgvector/codec-types"},"parameterizedOutputTypes":{"alias":"PgVectorParameterizedOutputTypes","named":"ParameterizedOutputTypesForTypeParams","package":"@internal/extension-pgvector/codec-types"}},"operationTypes":{"import":{"alias":"PgVectorOperationTypes","named":"OperationTypes","package":"@internal/extension-pgvector/operation-types"}},"queryOperationTypes":{"import":{"alias":"PgVectorQueryOperationTypes","named":"QueryOperationTypes","package":"@internal/extension-pgvector/operation-types"}}},"version":"0.0.1"}},"meta":{},"profileHash":"3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2","roots":{"post":{"model":"Post","namespace":"public"},"post_tag":{"model":"PostTag","namespace":"public"},"tag":{"model":"Tag","namespace":"public"},"task":{"model":"Task","namespace":"public"},"user":{"model":"User","namespace":"public"}},"schemaVersion":"1","storage":{"namespaces":{"public":{"entries":{"table":{"bug":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"severity":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"stepsToRepro":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"bug"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"feature":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"priority":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"targetRelease":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"feature"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"post":{"checks":[{"expression":"\"priority\" IN (0, 1, 2)","name":"post_priority_check_b236ace9","prefix":"post_priority_check"}],"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"embedding":{"codecId":"pg/vector@1","dataType":"pgvector/vector","nullable":true,"typeParams":{"length":1536}},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"priority":{"codecId":"pg/int4@1","dataType":"pg/int4","default":{"kind":"literal","value":0},"nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Priority","namespaceId":"public","plane":"storage"}},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"post_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"post"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"post_userId_idx_a489d58a","prefix":"post_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"post_tag":{"columns":{"postId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"tagId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"post_tag_postId_idx_a7a72715"},"source":{"columns":["postId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"post"}},{"index":{"name":"post_tag_tagId_idx_86854244"},"source":{"columns":["tagId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"tag"}}],"indexes":[{"columns":["postId"],"name":"post_tag_postId_idx_a7a72715","prefix":"post_tag_postId_idx","unique":false},{"columns":["tagId"],"name":"post_tag_tagId_idx_86854244","prefix":"post_tag_tagId_idx","unique":false}],"primaryKey":{"columns":["postId","tagId"]},"uniques":[]},"tag":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"label":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[{"columns":["label"]}]},"task":{"checks":[{"expression":"\"type\" IN ('bug', 'feature')","name":"task_type_check_cda3f5e7","prefix":"task_type_check"}],"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"description":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"status":{"codecId":"pg/text@1","dataType":"pg/text","default":{"kind":"literal","value":"open"},"nullable":false},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"type":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Task.type","namespaceId":"public","plane":"storage"}},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"task_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"task"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"task_userId_idx_a489d58a","prefix":"task_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"user":{"checks":[{"expression":"\"address\" IS NULL OR jsonb_path_match(\"address\", 'strict $.type() == \"null\" || ($.type() == \"object\" && exists($.keyvalue() ? (@.key == \"city\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"country\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"street\" && (@.value.type() == \"string\"))) && !exists($.keyvalue() ? (@.key == \"zip\" && !(@.value.type() == \"null\" || (@.value.type() == \"string\")))) && !exists($.keyvalue() ? (@.key != \"city\" && @.key != \"country\" && @.key != \"street\" && @.key != \"zip\")))') IS TRUE","name":"user_address_value_object_69432dc1","prefix":"user_address_value_object"},{"expression":"\"kind\" IN ('admin', 'user')","name":"user_kind_check_836d43ef","prefix":"user_kind_check"}],"columns":{"address":{"codecId":"pg/jsonb@1","dataType":"pg/jsonb","nullable":true},"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"displayName":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"email":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"kind":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"user_type","namespaceId":"public","plane":"storage"}}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]}},"valueObject":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}}}},"valueSet":{"Priority":{"kind":"valueSet","values":[0,1,2]},"Task.type":{"kind":"valueSet","values":["bug","feature"]},"user_type":{"kind":"valueSet","values":["admin","user"]}}},"id":"public","kind":"postgres-schema"}},"storageHash":"11d8695d1b8ea9c748834093ac4111ce68145827709363b7c748c2892be84146"},"target":"postgres","targetFamily":"sql"} diff --git a/examples/prisma-8-demo/migrations/snapshots/1c819419d4357de21d9aa9404a0b1d3639e3bd33c3fe6176207c53b51365fa1f/contract.d.ts b/examples/prisma-8-demo/migrations/snapshots/1c819419d4357de21d9aa9404a0b1d3639e3bd33c3fe6176207c53b51365fa1f/contract.d.ts deleted file mode 100644 index c9db5b9e6195..000000000000 --- a/examples/prisma-8-demo/migrations/snapshots/1c819419d4357de21d9aa9404a0b1d3639e3bd33c3fe6176207c53b51365fa1f/contract.d.ts +++ /dev/null @@ -1,1420 +0,0 @@ -// ⚠️ GENERATED FILE - DO NOT EDIT -// This file is automatically generated by 'prisma contract emit'. -// To regenerate, run: prisma contract emit -import type { - CodecTypes as PgVectorTypes, - ParameterizedOutputTypesForTypeParams as PgVectorParameterizedOutputTypes, -} from '@prisma/orm-extension-pgvector/codec-types'; -import type { QueryOperationTypes as PgVectorQueryOperationTypes } from '@prisma/orm-extension-pgvector/operation-types'; -import type { - CodecTypes as PgTypes, - ParameterizedOutputTypesForTypeParams as PgParameterizedOutputTypes, -} from '@prisma/orm-postgres/target/codec-types'; -import type { QueryOperationTypes as PgTargetQueryOps } from '@prisma/orm-postgres/target/operation-types'; - -import type { - ColumnTypesFromStorage, - ContractWithTypeMaps, - RelationKeys, - TypeMaps as TypeMapsType, -} from '@prisma/orm-postgres/family-contract/types'; -import type { - Contract as ContractType, - ExecutionHashBase, - FieldTypesFromDomain, - NamespaceId, - ProfileHashBase, - StorageHashBase, - ValueObjectTypesFromStorage, -} from '@prisma/orm-postgres/contract/types'; - -export type StorageHash = - StorageHashBase<'1c819419d4357de21d9aa9404a0b1d3639e3bd33c3fe6176207c53b51365fa1f'>; -export type ExecutionHash = - ExecutionHashBase<'eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655'>; -export type ProfileHash = - ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; - -export type CodecTypes = PgTypes & PgVectorTypes; -export type LaneCodecTypes = CodecTypes; -export type QueryOperationTypes = PgTargetQueryOps & - PgVectorQueryOperationTypes; -export type AggregateTypes = { - readonly avg: { - readonly byCodec: { - readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/float4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/interval-string@1': { - readonly output: 'pg/interval-string@1'; - readonly nullable: true; - }; - readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - }; - }; - readonly avgDecimal: { - readonly byCodec: { - readonly 'pg/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - }; - }; - readonly count: { - readonly byCodec: {}; - readonly withoutInput: { readonly output: 'pg/int8number@1'; readonly nullable: false }; - readonly anyInput: { readonly output: 'pg/int8number@1'; readonly nullable: false }; - }; - readonly countBigInt: { - readonly byCodec: {}; - readonly withoutInput: { readonly output: 'pg/int8@1'; readonly nullable: false }; - readonly anyInput: { readonly output: 'pg/int8@1'; readonly nullable: false }; - }; - readonly max: { - readonly byCodec: { - readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true }; - readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true }; - readonly 'pg/date-temporal@1': { - readonly output: 'pg/date-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true }; - readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true }; - readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; - readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true }; - readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/interval-string@1': { - readonly output: 'pg/interval-string@1'; - readonly nullable: true; - }; - readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; - readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true }; - readonly 'pg/time-temporal@1': { - readonly output: 'pg/time-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-date@1': { - readonly output: 'pg/timestamp-date@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-instant@1': { - readonly output: 'pg/timestamp-instant@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-string@1': { - readonly output: 'pg/timestamp-string@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-temporal@1': { - readonly output: 'pg/timestamp-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-date@1': { - readonly output: 'pg/timestamptz-date@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-string@1': { - readonly output: 'pg/timestamptz-string@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-temporal@1': { - readonly output: 'pg/timestamptz-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { - readonly output: 'pg/unboundedint@1'; - readonly nullable: true; - }; - readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true }; - readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true }; - readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true }; - readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - }; - }; - readonly min: { - readonly byCodec: { - readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true }; - readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true }; - readonly 'pg/date-temporal@1': { - readonly output: 'pg/date-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true }; - readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true }; - readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; - readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true }; - readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/interval-string@1': { - readonly output: 'pg/interval-string@1'; - readonly nullable: true; - }; - readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; - readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true }; - readonly 'pg/time-temporal@1': { - readonly output: 'pg/time-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-date@1': { - readonly output: 'pg/timestamp-date@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-instant@1': { - readonly output: 'pg/timestamp-instant@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-string@1': { - readonly output: 'pg/timestamp-string@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-temporal@1': { - readonly output: 'pg/timestamp-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-date@1': { - readonly output: 'pg/timestamptz-date@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-string@1': { - readonly output: 'pg/timestamptz-string@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-temporal@1': { - readonly output: 'pg/timestamptz-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { - readonly output: 'pg/unboundedint@1'; - readonly nullable: true; - }; - readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true }; - readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true }; - readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true }; - readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - }; - }; - readonly stddev: { - readonly byCodec: { - readonly 'pg/float@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/float4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/float8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'sql/float@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - }; - }; - readonly sum: { - readonly byCodec: { - readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; - readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/interval-string@1': { - readonly output: 'pg/interval-string@1'; - readonly nullable: true; - }; - readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { - readonly output: 'pg/unboundedint@1'; - readonly nullable: true; - }; - readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - }; - }; - readonly sumBigInt: { - readonly byCodec: { - readonly 'pg/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { - readonly output: 'pg/unboundedint@1'; - readonly nullable: true; - }; - readonly 'sql/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - }; - }; -}; -type DefaultLiteralValue = CodecId extends keyof CodecTypes - ? Encoded extends CodecTypes[CodecId]['json'] - ? Encoded - : CodecTypes[CodecId]['json'] - : Encoded; -type ParameterizedOutputTypeFunctions = - PgParameterizedOutputTypes | PgVectorParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromStorage< - ContractBase['storage']['namespaces'], - CodecTypes, - ParameterizedOutputTypeFunctions, - FieldTypeOverrides, - 'output' ->; -type ValueObjectInputTypes = ValueObjectTypesFromStorage< - ContractBase['storage']['namespaces'], - CodecTypes, - ParameterizedOutputTypeFunctions, - FieldTypeOverrides, - 'input' ->; -export type AddressOutput = ValueObjectOutputTypes['public']['Address']; -export type AddressInput = ValueObjectInputTypes['public']['Address']; -type FieldTypeOverrides = { - readonly public: { - readonly models: { - readonly Post: { - readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 }; - }; - readonly Task: { - readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; - }; - readonly User: { - readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; - }; - }; - }; -}; -export type FieldOutputTypes = FieldTypesFromDomain< - ContractBase['domain']['namespaces'], - CodecTypes, - ParameterizedOutputTypeFunctions, - FieldTypeOverrides, - 'output', - ContractBase['storage']['namespaces'] ->; -export type FieldInputTypes = FieldTypesFromDomain< - ContractBase['domain']['namespaces'], - CodecTypes, - ParameterizedOutputTypeFunctions, - FieldTypeOverrides, - 'input', - ContractBase['storage']['namespaces'] ->; -type ColumnTypeOverrides = { - readonly public: { - readonly post: { readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 } }; - readonly task: { - readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; - }; - readonly user: { - readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; - }; - }; -}; -export type StorageColumnTypes = ColumnTypesFromStorage< - ContractBase['storage'], - CodecTypes, - ParameterizedOutputTypeFunctions, - ColumnTypeOverrides, - 'output' ->; -export type StorageColumnInputTypes = ColumnTypesFromStorage< - ContractBase['storage'], - CodecTypes, - ParameterizedOutputTypeFunctions, - ColumnTypeOverrides, - 'input' ->; - -export namespace Models { - export type public_Bug = { - createdAt: FieldOutputTypes['public']['Task']['createdAt']; - description: FieldOutputTypes['public']['Task']['description']; - id: FieldOutputTypes['public']['Task']['id']; - status: FieldOutputTypes['public']['Task']['status']; - title: FieldOutputTypes['public']['Task']['title']; - type: 'bug'; - userId: FieldOutputTypes['public']['Task']['userId']; - severity: FieldOutputTypes['public']['Bug']['severity']; - stepsToRepro: FieldOutputTypes['public']['Bug']['stepsToRepro']; - user: public_User; - readonly [RelationKeys]?: 'user'; - }; - export type public_Feature = { - createdAt: FieldOutputTypes['public']['Task']['createdAt']; - description: FieldOutputTypes['public']['Task']['description']; - id: FieldOutputTypes['public']['Task']['id']; - status: FieldOutputTypes['public']['Task']['status']; - title: FieldOutputTypes['public']['Task']['title']; - type: 'feature'; - userId: FieldOutputTypes['public']['Task']['userId']; - priority: FieldOutputTypes['public']['Feature']['priority']; - targetRelease: FieldOutputTypes['public']['Feature']['targetRelease']; - user: public_User; - readonly [RelationKeys]?: 'user'; - }; - export type public_Post = { - createdAt: FieldOutputTypes['public']['Post']['createdAt']; - embedding: FieldOutputTypes['public']['Post']['embedding']; - expiresAt: FieldOutputTypes['public']['Post']['expiresAt']; - id: FieldOutputTypes['public']['Post']['id']; - impressionCount: FieldOutputTypes['public']['Post']['impressionCount']; - priority: FieldOutputTypes['public']['Post']['priority']; - reachScore: FieldOutputTypes['public']['Post']['reachScore']; - title: FieldOutputTypes['public']['Post']['title']; - userId: FieldOutputTypes['public']['Post']['userId']; - viewCount: FieldOutputTypes['public']['Post']['viewCount']; - tags: public_Tag[]; - user: public_User; - readonly [RelationKeys]?: 'tags' | 'user'; - }; - export type public_PostTag = { - postId: FieldOutputTypes['public']['PostTag']['postId']; - tagId: FieldOutputTypes['public']['PostTag']['tagId']; - post: public_Post; - tag: public_Tag; - readonly [RelationKeys]?: 'post' | 'tag'; - }; - export type public_Tag = { - id: FieldOutputTypes['public']['Tag']['id']; - label: FieldOutputTypes['public']['Tag']['label']; - posts: public_Post[]; - readonly [RelationKeys]?: 'posts'; - }; - export type public_Task = { - createdAt: FieldOutputTypes['public']['Task']['createdAt']; - description: FieldOutputTypes['public']['Task']['description']; - id: FieldOutputTypes['public']['Task']['id']; - status: FieldOutputTypes['public']['Task']['status']; - title: FieldOutputTypes['public']['Task']['title']; - type: 'bug' | 'feature'; - userId: FieldOutputTypes['public']['Task']['userId']; - user: public_User; - readonly [RelationKeys]?: 'user'; - }; - export type public_User = { - address: FieldOutputTypes['public']['User']['address']; - createdAt: FieldOutputTypes['public']['User']['createdAt']; - displayName: FieldOutputTypes['public']['User']['displayName']; - email: FieldOutputTypes['public']['User']['email']; - id: FieldOutputTypes['public']['User']['id']; - kind: FieldOutputTypes['public']['User']['kind']; - posts: public_Post[]; - tasks: public_AnyTask[]; - readonly [RelationKeys]?: 'posts' | 'tasks'; - }; - export type public_AnyTask = public_Bug | public_Feature; -} - -export declare const models: { - public: { - Bug: Models.public_Bug; - Feature: Models.public_Feature; - Post: Models.public_Post; - PostTag: Models.public_PostTag; - Tag: Models.public_Tag; - Task: Models.public_Task; - User: Models.public_User; - AnyTask: Models.public_AnyTask; - }; -}; - -export type TypeMaps = TypeMapsType< - CodecTypes, - QueryOperationTypes, - FieldOutputTypes, - FieldInputTypes, - StorageColumnTypes, - StorageColumnInputTypes, - AggregateTypes ->; - -type ContractBase = Omit< - ContractType<{ - readonly namespaces: { - readonly public: { - readonly id: 'public'; - readonly kind: 'postgres-schema'; - readonly entries: { - readonly table: { - readonly bug: { - columns: { - readonly id: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly severity: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - readonly stepsToRepro: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: true; - readonly many: false; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly []; - foreignKeys: readonly [ - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'bug'; - readonly columns: readonly ['id']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'task'; - readonly columns: readonly ['id']; - }; - }, - ]; - }; - readonly feature: { - columns: { - readonly id: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly priority: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - readonly targetRelease: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: true; - readonly many: false; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly []; - foreignKeys: readonly [ - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'feature'; - readonly columns: readonly ['id']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'task'; - readonly columns: readonly ['id']; - }; - }, - ]; - }; - readonly post: { - columns: { - readonly createdAt: { - readonly dataType: 'pg/timestamptz'; - readonly codecId: 'pg/timestamptz-temporal@1'; - readonly nullable: false; - readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; - readonly many: false; - }; - readonly embedding: { - readonly dataType: 'pgvector/vector'; - readonly codecId: 'pg/vector@1'; - readonly nullable: true; - readonly typeParams: { readonly length: 1536 }; - readonly many: false; - }; - readonly expiresAt: { - readonly dataType: 'pg/timestamptz'; - readonly codecId: 'pg/timestamptz-temporal@1'; - readonly nullable: false; - readonly default: { - readonly kind: 'function'; - readonly expression: "(now() + '7 days'::interval)"; - }; - readonly many: false; - }; - readonly id: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly impressionCount: { - readonly dataType: 'pg/int8'; - readonly codecId: 'pg/int8@1'; - readonly nullable: true; - readonly many: false; - }; - readonly priority: { - readonly dataType: 'pg/int4'; - readonly codecId: 'pg/int4@1'; - readonly nullable: false; - readonly default: { - readonly kind: 'literal'; - readonly value: DefaultLiteralValue<'pg/int4@1', 0>; - }; - readonly many: false; - }; - readonly reachScore: { - readonly dataType: 'pg/numeric'; - readonly codecId: 'pg/unboundedint@1'; - readonly nullable: true; - readonly many: false; - }; - readonly title: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - readonly userId: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly viewCount: { - readonly dataType: 'pg/int8'; - readonly codecId: 'pg/int8number@1'; - readonly nullable: true; - readonly many: false; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly [ - { - readonly name: 'post_userId_idx_a489d58a'; - readonly prefix: 'post_userId_idx'; - readonly columns: readonly ['userId']; - readonly unique: false; - }, - ]; - foreignKeys: readonly [ - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'post'; - readonly columns: readonly ['userId']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'user'; - readonly columns: readonly ['id']; - }; - }, - ]; - }; - readonly post_tag: { - columns: { - readonly postId: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly tagId: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - }; - primaryKey: { readonly columns: readonly ['postId', 'tagId'] }; - uniques: readonly []; - indexes: readonly [ - { - readonly name: 'post_tag_postId_idx_a7a72715'; - readonly prefix: 'post_tag_postId_idx'; - readonly columns: readonly ['postId']; - readonly unique: false; - }, - { - readonly name: 'post_tag_tagId_idx_86854244'; - readonly prefix: 'post_tag_tagId_idx'; - readonly columns: readonly ['tagId']; - readonly unique: false; - }, - ]; - foreignKeys: readonly [ - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'post_tag'; - readonly columns: readonly ['postId']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'post'; - readonly columns: readonly ['id']; - }; - }, - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'post_tag'; - readonly columns: readonly ['tagId']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'tag'; - readonly columns: readonly ['id']; - }; - }, - ]; - }; - readonly tag: { - columns: { - readonly id: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly label: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly [{ readonly columns: readonly ['label'] }]; - indexes: readonly []; - foreignKeys: readonly []; - }; - readonly task: { - columns: { - readonly createdAt: { - readonly dataType: 'pg/timestamptz'; - readonly codecId: 'pg/timestamptz-temporal@1'; - readonly nullable: false; - readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; - readonly many: false; - }; - readonly description: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: true; - readonly many: false; - }; - readonly id: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly status: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly default: { - readonly kind: 'literal'; - readonly value: DefaultLiteralValue<'pg/text@1', 'open'>; - }; - readonly many: false; - }; - readonly title: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - readonly type: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - readonly userId: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly [ - { - readonly name: 'task_userId_idx_a489d58a'; - readonly prefix: 'task_userId_idx'; - readonly columns: readonly ['userId']; - readonly unique: false; - }, - ]; - foreignKeys: readonly [ - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'task'; - readonly columns: readonly ['userId']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'user'; - readonly columns: readonly ['id']; - }; - }, - ]; - }; - readonly user: { - columns: { - readonly address: { - readonly dataType: 'pg/jsonb'; - readonly codecId: 'pg/jsonb@1'; - readonly nullable: true; - readonly many: false; - }; - readonly createdAt: { - readonly dataType: 'pg/timestamptz'; - readonly codecId: 'pg/timestamptz-temporal@1'; - readonly nullable: false; - readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; - readonly many: false; - }; - readonly displayName: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - readonly email: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - readonly id: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly kind: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly []; - foreignKeys: readonly []; - }; - }; - readonly valueSet: { - readonly Priority: { readonly kind: 'valueSet'; readonly values: readonly [0, 1, 2] }; - readonly 'Task.type': { - readonly kind: 'valueSet'; - readonly values: readonly ['bug', 'feature']; - }; - readonly user_type: { - readonly kind: 'valueSet'; - readonly values: readonly ['admin', 'user']; - }; - }; - readonly valueObject: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - }; - }; - }; - }; - }; - readonly storageHash: StorageHash; - }>, - 'roots' | 'domain' -> & { - readonly target: 'postgres'; - readonly targetFamily: 'sql'; - readonly roots: { - readonly post: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; - readonly post_tag: { readonly namespace: 'public' & NamespaceId; readonly model: 'PostTag' }; - readonly tag: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; - readonly task: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; - readonly user: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; - }; - readonly domain: { - readonly namespaces: { - readonly public: { - readonly models: { - readonly Bug: { - readonly fields: { - readonly severity: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly stepsToRepro: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - readonly relations: Record; - readonly storage: { - readonly table: 'bug'; - readonly namespaceId: 'public'; - readonly fields: { - readonly severity: { readonly column: 'severity' }; - readonly stepsToRepro: { readonly column: 'stepsToRepro' }; - }; - }; - readonly base: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; - }; - readonly Feature: { - readonly fields: { - readonly priority: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly targetRelease: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - readonly relations: Record; - readonly storage: { - readonly table: 'feature'; - readonly namespaceId: 'public'; - readonly fields: { - readonly priority: { readonly column: 'priority' }; - readonly targetRelease: { readonly column: 'targetRelease' }; - }; - }; - readonly base: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; - }; - readonly Post: { - readonly fields: { - readonly createdAt: { - readonly nullable: false; - readonly type: { - readonly kind: 'scalar'; - readonly codecId: 'pg/timestamptz-temporal@1'; - }; - }; - readonly embedding: { - readonly nullable: true; - readonly type: { - readonly kind: 'scalar'; - readonly codecId: 'pg/vector@1'; - readonly typeParams: { readonly length: 1536 }; - }; - }; - readonly expiresAt: { - readonly nullable: false; - readonly type: { - readonly kind: 'scalar'; - readonly codecId: 'pg/timestamptz-temporal@1'; - }; - }; - readonly id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly impressionCount: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int8@1' }; - }; - readonly priority: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int4@1' }; - }; - readonly reachScore: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/unboundedint@1' }; - }; - readonly title: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly viewCount: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int8number@1' }; - }; - }; - readonly relations: { - readonly tags: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; - readonly cardinality: 'N:M'; - readonly on: { - readonly localFields: readonly ['id']; - readonly targetFields: readonly ['postId']; - }; - readonly through: { - readonly table: 'post_tag'; - readonly namespaceId: 'public'; - readonly parentColumns: readonly ['postId']; - readonly childColumns: readonly ['tagId']; - readonly targetColumns: readonly ['id']; - }; - }; - readonly user: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['userId']; - readonly targetFields: readonly ['id']; - }; - }; - }; - readonly storage: { - readonly table: 'post'; - readonly namespaceId: 'public'; - readonly fields: { - readonly createdAt: { readonly column: 'createdAt' }; - readonly embedding: { readonly column: 'embedding' }; - readonly expiresAt: { readonly column: 'expiresAt' }; - readonly id: { readonly column: 'id' }; - readonly impressionCount: { readonly column: 'impressionCount' }; - readonly priority: { readonly column: 'priority' }; - readonly reachScore: { readonly column: 'reachScore' }; - readonly title: { readonly column: 'title' }; - readonly userId: { readonly column: 'userId' }; - readonly viewCount: { readonly column: 'viewCount' }; - }; - }; - }; - readonly PostTag: { - readonly fields: { - readonly postId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly tagId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - }; - readonly relations: { - readonly post: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['postId']; - readonly targetFields: readonly ['id']; - }; - }; - readonly tag: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['tagId']; - readonly targetFields: readonly ['id']; - }; - }; - }; - readonly storage: { - readonly table: 'post_tag'; - readonly namespaceId: 'public'; - readonly fields: { - readonly postId: { readonly column: 'postId' }; - readonly tagId: { readonly column: 'tagId' }; - }; - }; - }; - readonly Tag: { - readonly fields: { - readonly id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly label: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - readonly relations: { - readonly posts: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; - readonly cardinality: 'N:M'; - readonly on: { - readonly localFields: readonly ['id']; - readonly targetFields: readonly ['tagId']; - }; - readonly through: { - readonly table: 'post_tag'; - readonly namespaceId: 'public'; - readonly parentColumns: readonly ['tagId']; - readonly childColumns: readonly ['postId']; - readonly targetColumns: readonly ['id']; - }; - }; - }; - readonly storage: { - readonly table: 'tag'; - readonly namespaceId: 'public'; - readonly fields: { - readonly id: { readonly column: 'id' }; - readonly label: { readonly column: 'label' }; - }; - }; - }; - readonly Task: { - readonly fields: { - readonly createdAt: { - readonly nullable: false; - readonly type: { - readonly kind: 'scalar'; - readonly codecId: 'pg/timestamptz-temporal@1'; - }; - }; - readonly description: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly status: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly title: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly type: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - }; - readonly relations: { - readonly user: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['userId']; - readonly targetFields: readonly ['id']; - }; - }; - }; - readonly storage: { - readonly table: 'task'; - readonly namespaceId: 'public'; - readonly fields: { - readonly createdAt: { readonly column: 'createdAt' }; - readonly description: { readonly column: 'description' }; - readonly id: { readonly column: 'id' }; - readonly status: { readonly column: 'status' }; - readonly title: { readonly column: 'title' }; - readonly type: { readonly column: 'type' }; - readonly userId: { readonly column: 'userId' }; - }; - }; - readonly discriminator: { readonly field: 'type' }; - readonly variants: { - readonly bug: { readonly namespace: 'public' & NamespaceId; readonly model: 'Bug' }; - readonly feature: { - readonly namespace: 'public' & NamespaceId; - readonly model: 'Feature'; - }; - }; - }; - readonly User: { - readonly fields: { - readonly address: { - readonly nullable: true; - readonly type: { - readonly kind: 'valueObject'; - readonly plane: 'storage'; - readonly namespaceId: 'public'; - readonly entityKind: 'valueObject'; - readonly entityName: 'Address'; - }; - }; - readonly createdAt: { - readonly nullable: false; - readonly type: { - readonly kind: 'scalar'; - readonly codecId: 'pg/timestamptz-temporal@1'; - }; - }; - readonly displayName: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly email: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly kind: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - readonly relations: { - readonly posts: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; - readonly cardinality: '1:N'; - readonly on: { - readonly localFields: readonly ['id']; - readonly targetFields: readonly ['userId']; - }; - }; - readonly tasks: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; - readonly cardinality: '1:N'; - readonly on: { - readonly localFields: readonly ['id']; - readonly targetFields: readonly ['userId']; - }; - }; - }; - readonly storage: { - readonly table: 'user'; - readonly namespaceId: 'public'; - readonly fields: { - readonly address: { readonly column: 'address' }; - readonly createdAt: { readonly column: 'createdAt' }; - readonly displayName: { readonly column: 'displayName' }; - readonly email: { readonly column: 'email' }; - readonly id: { readonly column: 'id' }; - readonly kind: { readonly column: 'kind' }; - }; - }; - }; - }; - readonly enum: { - readonly Priority: { - readonly codecId: 'pg/int4@1'; - readonly members: readonly [ - { readonly name: 'Low'; readonly value: 0 }, - { readonly name: 'High'; readonly value: 1 }, - { readonly name: 'Urgent'; readonly value: 2 }, - ]; - }; - readonly user_type: { - readonly codecId: 'pg/text@1'; - readonly members: readonly [ - { readonly name: 'admin'; readonly value: 'admin' }, - { readonly name: 'user'; readonly value: 'user' }, - ]; - }; - }; - readonly enumMemberTypes?: { - readonly Priority: readonly [ - { readonly name: 'Low'; readonly value: 0 }, - { readonly name: 'High'; readonly value: 1 }, - { readonly name: 'Urgent'; readonly value: 2 }, - ]; - readonly user_type: readonly [ - { readonly name: 'admin'; readonly value: 'admin' }, - { readonly name: 'user'; readonly value: 'user' }, - ]; - }; - }; - }; - }; - readonly capabilities: { - readonly postgres: { - readonly distinctOn: true; - readonly forKeyShare: true; - readonly forNoKeyUpdate: true; - readonly jsonAgg: true; - readonly lateral: true; - readonly limit: true; - readonly orderBy: true; - readonly 'pgvector.cosine': true; - readonly returning: true; - }; - readonly sql: { - readonly checkConstraint: true; - readonly defaultInInsert: true; - readonly enums: true; - readonly forShare: true; - readonly forUpdate: true; - readonly insertOnConflictSkip: true; - readonly insertOnConflictWithoutTarget: true; - readonly lateral: true; - readonly lockNowait: true; - readonly lockOf: true; - readonly lockSkipLocked: true; - readonly returning: true; - readonly scalarList: true; - }; - }; - readonly extensions: { - readonly 'demo/engagement-stats': { - readonly familyId: 'sql'; - readonly id: 'demo/engagement-stats'; - readonly kind: 'extension'; - readonly targetId: 'postgres'; - readonly types: { - readonly aggregateDescriptors: readonly [ - { - readonly input: { readonly kind: 'trait'; readonly trait: 'numeric' }; - readonly lower: unknown; - readonly nullable: true; - readonly operation: 'stddev'; - readonly output: { readonly codecId: 'pg/numeric@1'; readonly kind: 'codec' }; - }, - ]; - }; - readonly version: '0.0.1'; - }; - readonly pgvector: { - readonly capabilities: { readonly postgres: { readonly 'pgvector.cosine': true } }; - readonly familyId: 'sql'; - readonly id: 'pgvector'; - readonly kind: 'extension'; - readonly targetId: 'postgres'; - readonly types: { - readonly codecTypes: { - readonly import: { - readonly alias: 'PgVectorTypes'; - readonly named: 'CodecTypes'; - readonly package: '@internal/extension-pgvector/codec-types'; - }; - readonly parameterizedOutputTypes: { - readonly alias: 'PgVectorParameterizedOutputTypes'; - readonly named: 'ParameterizedOutputTypesForTypeParams'; - readonly package: '@internal/extension-pgvector/codec-types'; - }; - }; - readonly operationTypes: { - readonly import: { - readonly alias: 'PgVectorOperationTypes'; - readonly named: 'OperationTypes'; - readonly package: '@internal/extension-pgvector/operation-types'; - }; - }; - readonly queryOperationTypes: { - readonly import: { - readonly alias: 'PgVectorQueryOperationTypes'; - readonly named: 'QueryOperationTypes'; - readonly package: '@internal/extension-pgvector/operation-types'; - }; - }; - }; - readonly version: '0.0.1'; - }; - }; - readonly execution: { - readonly executionHash: ExecutionHash; - readonly mutations: { - readonly defaults: readonly [ - { - readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; - readonly ref: { - readonly entry: 'post'; - readonly field: 'id'; - readonly namespace: 'public'; - }; - }, - { - readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; - readonly ref: { - readonly entry: 'tag'; - readonly field: 'id'; - readonly namespace: 'public'; - }; - }, - { - readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; - readonly ref: { - readonly entry: 'task'; - readonly field: 'id'; - readonly namespace: 'public'; - }; - }, - { - readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; - readonly ref: { - readonly entry: 'user'; - readonly field: 'id'; - readonly namespace: 'public'; - }; - }, - ]; - }; - }; - readonly meta: {}; - readonly profileHash: ProfileHash; -}; - -export type Contract = ContractWithTypeMaps; - -export type Namespaces = Contract['storage']['namespaces']; diff --git a/examples/prisma-8-demo/migrations/snapshots/1c819419d4357de21d9aa9404a0b1d3639e3bd33c3fe6176207c53b51365fa1f/contract.json b/examples/prisma-8-demo/migrations/snapshots/1c819419d4357de21d9aa9404a0b1d3639e3bd33c3fe6176207c53b51365fa1f/contract.json deleted file mode 100644 index 2ec8794c0430..000000000000 --- a/examples/prisma-8-demo/migrations/snapshots/1c819419d4357de21d9aa9404a0b1d3639e3bd33c3fe6176207c53b51365fa1f/contract.json +++ /dev/null @@ -1 +0,0 @@ -{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{"postgres":{"distinctOn":true,"forKeyShare":true,"forNoKeyUpdate":true,"jsonAgg":true,"lateral":true,"limit":true,"orderBy":true,"pgvector.cosine":true,"returning":true},"sql":{"checkConstraint":true,"defaultInInsert":true,"enums":true,"forShare":true,"forUpdate":true,"insertOnConflictSkip":true,"insertOnConflictWithoutTarget":true,"lateral":true,"lockNowait":true,"lockOf":true,"lockSkipLocked":true,"returning":true,"scalarList":true}},"domain":{"namespaces":{"public":{"enum":{"Priority":{"codecId":"pg/int4@1","members":[{"name":"Low","value":0},{"name":"High","value":1},{"name":"Urgent","value":2}]},"user_type":{"codecId":"pg/text@1","members":[{"name":"admin","value":"admin"},{"name":"user","value":"user"}]}},"models":{"Bug":{"base":{"model":"Task","namespace":"public"},"fields":{"severity":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"stepsToRepro":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"severity":{"column":"severity"},"stepsToRepro":{"column":"stepsToRepro"}},"namespaceId":"public","table":"bug"}},"Feature":{"base":{"model":"Task","namespace":"public"},"fields":{"priority":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"targetRelease":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"priority":{"column":"priority"},"targetRelease":{"column":"targetRelease"}},"namespaceId":"public","table":"feature"}},"Post":{"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"embedding":{"nullable":true,"type":{"codecId":"pg/vector@1","kind":"scalar","typeParams":{"length":1536}}},"expiresAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"impressionCount":{"nullable":true,"type":{"codecId":"pg/int8@1","kind":"scalar"}},"priority":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"Priority","namespaceId":"public","plane":"domain"}},"reachScore":{"nullable":true,"type":{"codecId":"pg/unboundedint@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"viewCount":{"nullable":true,"type":{"codecId":"pg/int8number@1","kind":"scalar"}}},"relations":{"tags":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["postId"]},"through":{"childColumns":["tagId"],"namespaceId":"public","parentColumns":["postId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Tag","namespace":"public"}},"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"embedding":{"column":"embedding"},"expiresAt":{"column":"expiresAt"},"id":{"column":"id"},"impressionCount":{"column":"impressionCount"},"priority":{"column":"priority"},"reachScore":{"column":"reachScore"},"title":{"column":"title"},"userId":{"column":"userId"},"viewCount":{"column":"viewCount"}},"namespaceId":"public","table":"post"}},"PostTag":{"fields":{"postId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"tagId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"post":{"cardinality":"N:1","nullable":false,"on":{"localFields":["postId"],"targetFields":["id"]},"to":{"model":"Post","namespace":"public"}},"tag":{"cardinality":"N:1","nullable":false,"on":{"localFields":["tagId"],"targetFields":["id"]},"to":{"model":"Tag","namespace":"public"}}},"storage":{"fields":{"postId":{"column":"postId"},"tagId":{"column":"tagId"}},"namespaceId":"public","table":"post_tag"}},"Tag":{"fields":{"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"label":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{"posts":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["tagId"]},"through":{"childColumns":["postId"],"namespaceId":"public","parentColumns":["tagId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Post","namespace":"public"}}},"storage":{"fields":{"id":{"column":"id"},"label":{"column":"label"}},"namespaceId":"public","table":"tag"}},"Task":{"discriminator":{"field":"type"},"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"description":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"status":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"type":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"description":{"column":"description"},"id":{"column":"id"},"status":{"column":"status"},"title":{"column":"title"},"type":{"column":"type"},"userId":{"column":"userId"}},"namespaceId":"public","table":"task"},"variants":{"bug":{"model":"Bug","namespace":"public"},"feature":{"model":"Feature","namespace":"public"}}},"User":{"fields":{"address":{"nullable":true,"type":{"entityKind":"valueObject","entityName":"Address","kind":"valueObject","namespaceId":"public","plane":"storage"}},"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"displayName":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"user_type","namespaceId":"public","plane":"domain"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Post","namespace":"public"}},"tasks":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Task","namespace":"public"}}},"storage":{"fields":{"address":{"column":"address"},"createdAt":{"column":"createdAt"},"displayName":{"column":"displayName"},"email":{"column":"email"},"id":{"column":"id"},"kind":{"column":"kind"}},"namespaceId":"public","table":"user"}}}}}},"execution":{"executionHash":"eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655","mutations":{"defaults":[{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"post","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"tag","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"task","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"user","field":"id","namespace":"public"}}]}},"extensions":{"demo/engagement-stats":{"familyId":"sql","id":"demo/engagement-stats","kind":"extension","targetId":"postgres","types":{"aggregateDescriptors":[{"input":{"kind":"trait","trait":"numeric"},"nullable":true,"operation":"stddev","output":{"codecId":"pg/numeric@1","kind":"codec"}}]},"version":"0.0.1"},"pgvector":{"capabilities":{"postgres":{"pgvector.cosine":true}},"familyId":"sql","id":"pgvector","kind":"extension","targetId":"postgres","types":{"codecTypes":{"import":{"alias":"PgVectorTypes","named":"CodecTypes","package":"@internal/extension-pgvector/codec-types"},"parameterizedOutputTypes":{"alias":"PgVectorParameterizedOutputTypes","named":"ParameterizedOutputTypesForTypeParams","package":"@internal/extension-pgvector/codec-types"}},"operationTypes":{"import":{"alias":"PgVectorOperationTypes","named":"OperationTypes","package":"@internal/extension-pgvector/operation-types"}},"queryOperationTypes":{"import":{"alias":"PgVectorQueryOperationTypes","named":"QueryOperationTypes","package":"@internal/extension-pgvector/operation-types"}}},"version":"0.0.1"}},"meta":{},"profileHash":"3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2","roots":{"post":{"model":"Post","namespace":"public"},"post_tag":{"model":"PostTag","namespace":"public"},"tag":{"model":"Tag","namespace":"public"},"task":{"model":"Task","namespace":"public"},"user":{"model":"User","namespace":"public"}},"schemaVersion":"1","storage":{"namespaces":{"public":{"entries":{"table":{"bug":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"severity":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"stepsToRepro":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"bug"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"feature":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"priority":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"targetRelease":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"feature"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"post":{"checks":[{"expression":"\"priority\" IN (0, 1, 2)","name":"post_priority_check_b236ace9","prefix":"post_priority_check"}],"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"embedding":{"codecId":"pg/vector@1","dataType":"pgvector/vector","nullable":true,"typeParams":{"length":1536}},"expiresAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"(now() + '7 days'::interval)","kind":"function"},"nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"impressionCount":{"codecId":"pg/int8@1","dataType":"pg/int8","nullable":true},"priority":{"codecId":"pg/int4@1","dataType":"pg/int4","default":{"kind":"literal","value":0},"nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Priority","namespaceId":"public","plane":"storage"}},"reachScore":{"codecId":"pg/unboundedint@1","dataType":"pg/numeric","nullable":true},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"viewCount":{"codecId":"pg/int8number@1","dataType":"pg/int8","nullable":true}},"foreignKeys":[{"index":{"name":"post_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"post"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"post_userId_idx_a489d58a","prefix":"post_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"post_tag":{"columns":{"postId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"tagId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"post_tag_postId_idx_a7a72715"},"source":{"columns":["postId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"post"}},{"index":{"name":"post_tag_tagId_idx_86854244"},"source":{"columns":["tagId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"tag"}}],"indexes":[{"columns":["postId"],"name":"post_tag_postId_idx_a7a72715","prefix":"post_tag_postId_idx","unique":false},{"columns":["tagId"],"name":"post_tag_tagId_idx_86854244","prefix":"post_tag_tagId_idx","unique":false}],"primaryKey":{"columns":["postId","tagId"]},"uniques":[]},"tag":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"label":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[{"columns":["label"]}]},"task":{"checks":[{"expression":"\"type\" IN ('bug', 'feature')","name":"task_type_check_cda3f5e7","prefix":"task_type_check"}],"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"description":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"status":{"codecId":"pg/text@1","dataType":"pg/text","default":{"kind":"literal","value":"open"},"nullable":false},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"type":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Task.type","namespaceId":"public","plane":"storage"}},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"task_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"task"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"task_userId_idx_a489d58a","prefix":"task_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"user":{"checks":[{"expression":"\"address\" IS NULL OR jsonb_path_match(\"address\", 'strict $.type() == \"null\" || ($.type() == \"object\" && exists($.keyvalue() ? (@.key == \"city\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"country\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"street\" && (@.value.type() == \"string\"))) && !exists($.keyvalue() ? (@.key == \"zip\" && !(@.value.type() == \"null\" || (@.value.type() == \"string\")))) && !exists($.keyvalue() ? (@.key != \"city\" && @.key != \"country\" && @.key != \"street\" && @.key != \"zip\")))') IS TRUE","name":"user_address_value_object_69432dc1","prefix":"user_address_value_object"},{"expression":"\"kind\" IN ('admin', 'user')","name":"user_kind_check_836d43ef","prefix":"user_kind_check"}],"columns":{"address":{"codecId":"pg/jsonb@1","dataType":"pg/jsonb","nullable":true},"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"displayName":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"email":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"kind":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"user_type","namespaceId":"public","plane":"storage"}}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]}},"valueObject":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}}}},"valueSet":{"Priority":{"kind":"valueSet","values":[0,1,2]},"Task.type":{"kind":"valueSet","values":["bug","feature"]},"user_type":{"kind":"valueSet","values":["admin","user"]}}},"id":"public","kind":"postgres-schema"}},"storageHash":"1c819419d4357de21d9aa9404a0b1d3639e3bd33c3fe6176207c53b51365fa1f"},"target":"postgres","targetFamily":"sql"} diff --git a/examples/prisma-8-demo/migrations/snapshots/3c2324f6061012e9d50f869b3e6ae6aa41159acd559b1cc5459bc64e2ccb0f4e/contract.d.ts b/examples/prisma-8-demo/migrations/snapshots/3c2324f6061012e9d50f869b3e6ae6aa41159acd559b1cc5459bc64e2ccb0f4e/contract.d.ts deleted file mode 100644 index 1e6e1ee7b5b2..000000000000 --- a/examples/prisma-8-demo/migrations/snapshots/3c2324f6061012e9d50f869b3e6ae6aa41159acd559b1cc5459bc64e2ccb0f4e/contract.d.ts +++ /dev/null @@ -1,1401 +0,0 @@ -// ⚠️ GENERATED FILE - DO NOT EDIT -// This file is automatically generated by 'prisma contract emit'. -// To regenerate, run: prisma contract emit -import type { - CodecTypes as PgVectorTypes, - ParameterizedOutputTypesForTypeParams as PgVectorParameterizedOutputTypes, -} from '@prisma/orm-extension-pgvector/codec-types'; -import type { QueryOperationTypes as PgVectorQueryOperationTypes } from '@prisma/orm-extension-pgvector/operation-types'; -import type { - CodecTypes as PgTypes, - ParameterizedOutputTypesForTypeParams as PgParameterizedOutputTypes, -} from '@prisma/orm-postgres/target/codec-types'; -import type { QueryOperationTypes as PgTargetQueryOps } from '@prisma/orm-postgres/target/operation-types'; - -import type { - ColumnTypesFromStorage, - ContractWithTypeMaps, - RelationKeys, - TypeMaps as TypeMapsType, -} from '@prisma/orm-postgres/family-contract/types'; -import type { - Contract as ContractType, - ExecutionHashBase, - FieldTypesFromDomain, - NamespaceId, - ProfileHashBase, - StorageHashBase, - ValueObjectTypesFromStorage, -} from '@prisma/orm-postgres/contract/types'; - -export type StorageHash = - StorageHashBase<'3c2324f6061012e9d50f869b3e6ae6aa41159acd559b1cc5459bc64e2ccb0f4e'>; -export type ExecutionHash = - ExecutionHashBase<'eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655'>; -export type ProfileHash = - ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; - -export type CodecTypes = PgTypes & PgVectorTypes; -export type LaneCodecTypes = CodecTypes; -export type QueryOperationTypes = PgTargetQueryOps & - PgVectorQueryOperationTypes; -export type AggregateTypes = { - readonly avg: { - readonly byCodec: { - readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/float4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/interval-string@1': { - readonly output: 'pg/interval-string@1'; - readonly nullable: true; - }; - readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - }; - }; - readonly avgDecimal: { - readonly byCodec: { - readonly 'pg/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - }; - }; - readonly count: { - readonly byCodec: {}; - readonly withoutInput: { readonly output: 'pg/int8number@1'; readonly nullable: false }; - readonly anyInput: { readonly output: 'pg/int8number@1'; readonly nullable: false }; - }; - readonly countBigInt: { - readonly byCodec: {}; - readonly withoutInput: { readonly output: 'pg/int8@1'; readonly nullable: false }; - readonly anyInput: { readonly output: 'pg/int8@1'; readonly nullable: false }; - }; - readonly max: { - readonly byCodec: { - readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true }; - readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true }; - readonly 'pg/date-temporal@1': { - readonly output: 'pg/date-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true }; - readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true }; - readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; - readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true }; - readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/interval-string@1': { - readonly output: 'pg/interval-string@1'; - readonly nullable: true; - }; - readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; - readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true }; - readonly 'pg/time-temporal@1': { - readonly output: 'pg/time-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-date@1': { - readonly output: 'pg/timestamp-date@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-instant@1': { - readonly output: 'pg/timestamp-instant@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-string@1': { - readonly output: 'pg/timestamp-string@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-temporal@1': { - readonly output: 'pg/timestamp-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-date@1': { - readonly output: 'pg/timestamptz-date@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-string@1': { - readonly output: 'pg/timestamptz-string@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-temporal@1': { - readonly output: 'pg/timestamptz-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { - readonly output: 'pg/unboundedint@1'; - readonly nullable: true; - }; - readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true }; - readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true }; - readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true }; - readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - }; - }; - readonly min: { - readonly byCodec: { - readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true }; - readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true }; - readonly 'pg/date-temporal@1': { - readonly output: 'pg/date-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true }; - readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true }; - readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; - readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true }; - readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/interval-string@1': { - readonly output: 'pg/interval-string@1'; - readonly nullable: true; - }; - readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; - readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true }; - readonly 'pg/time-temporal@1': { - readonly output: 'pg/time-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-date@1': { - readonly output: 'pg/timestamp-date@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-instant@1': { - readonly output: 'pg/timestamp-instant@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-string@1': { - readonly output: 'pg/timestamp-string@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-temporal@1': { - readonly output: 'pg/timestamp-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-date@1': { - readonly output: 'pg/timestamptz-date@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-string@1': { - readonly output: 'pg/timestamptz-string@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-temporal@1': { - readonly output: 'pg/timestamptz-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { - readonly output: 'pg/unboundedint@1'; - readonly nullable: true; - }; - readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true }; - readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true }; - readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true }; - readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - }; - }; - readonly stddev: { - readonly byCodec: { - readonly 'pg/float@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/float4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/float8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'sql/float@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - }; - }; - readonly sum: { - readonly byCodec: { - readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; - readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/interval-string@1': { - readonly output: 'pg/interval-string@1'; - readonly nullable: true; - }; - readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { - readonly output: 'pg/unboundedint@1'; - readonly nullable: true; - }; - readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - }; - }; - readonly sumBigInt: { - readonly byCodec: { - readonly 'pg/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { - readonly output: 'pg/unboundedint@1'; - readonly nullable: true; - }; - readonly 'sql/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - }; - }; -}; -type DefaultLiteralValue = CodecId extends keyof CodecTypes - ? Encoded extends CodecTypes[CodecId]['json'] - ? Encoded - : CodecTypes[CodecId]['json'] - : Encoded; -type ParameterizedOutputTypeFunctions = - PgParameterizedOutputTypes | PgVectorParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromStorage< - ContractBase['storage']['namespaces'], - CodecTypes, - ParameterizedOutputTypeFunctions, - FieldTypeOverrides, - 'output' ->; -type ValueObjectInputTypes = ValueObjectTypesFromStorage< - ContractBase['storage']['namespaces'], - CodecTypes, - ParameterizedOutputTypeFunctions, - FieldTypeOverrides, - 'input' ->; -export type AddressOutput = ValueObjectOutputTypes['public']['Address']; -export type AddressInput = ValueObjectInputTypes['public']['Address']; -type FieldTypeOverrides = { - readonly public: { - readonly models: { - readonly Post: { - readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 }; - }; - readonly Task: { - readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; - }; - readonly User: { - readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; - }; - }; - }; -}; -export type FieldOutputTypes = FieldTypesFromDomain< - ContractBase['domain']['namespaces'], - CodecTypes, - ParameterizedOutputTypeFunctions, - FieldTypeOverrides, - 'output', - ContractBase['storage']['namespaces'] ->; -export type FieldInputTypes = FieldTypesFromDomain< - ContractBase['domain']['namespaces'], - CodecTypes, - ParameterizedOutputTypeFunctions, - FieldTypeOverrides, - 'input', - ContractBase['storage']['namespaces'] ->; -type ColumnTypeOverrides = { - readonly public: { - readonly post: { readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 } }; - readonly task: { - readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; - }; - readonly user: { - readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; - }; - }; -}; -export type StorageColumnTypes = ColumnTypesFromStorage< - ContractBase['storage'], - CodecTypes, - ParameterizedOutputTypeFunctions, - ColumnTypeOverrides, - 'output' ->; -export type StorageColumnInputTypes = ColumnTypesFromStorage< - ContractBase['storage'], - CodecTypes, - ParameterizedOutputTypeFunctions, - ColumnTypeOverrides, - 'input' ->; - -export namespace Models { - export type public_Bug = { - createdAt: FieldOutputTypes['public']['Task']['createdAt']; - description: FieldOutputTypes['public']['Task']['description']; - id: FieldOutputTypes['public']['Task']['id']; - status: FieldOutputTypes['public']['Task']['status']; - title: FieldOutputTypes['public']['Task']['title']; - type: 'bug'; - userId: FieldOutputTypes['public']['Task']['userId']; - severity: FieldOutputTypes['public']['Bug']['severity']; - stepsToRepro: FieldOutputTypes['public']['Bug']['stepsToRepro']; - user: public_User; - readonly [RelationKeys]?: 'user'; - }; - export type public_Feature = { - createdAt: FieldOutputTypes['public']['Task']['createdAt']; - description: FieldOutputTypes['public']['Task']['description']; - id: FieldOutputTypes['public']['Task']['id']; - status: FieldOutputTypes['public']['Task']['status']; - title: FieldOutputTypes['public']['Task']['title']; - type: 'feature'; - userId: FieldOutputTypes['public']['Task']['userId']; - priority: FieldOutputTypes['public']['Feature']['priority']; - targetRelease: FieldOutputTypes['public']['Feature']['targetRelease']; - user: public_User; - readonly [RelationKeys]?: 'user'; - }; - export type public_Post = { - createdAt: FieldOutputTypes['public']['Post']['createdAt']; - embedding: FieldOutputTypes['public']['Post']['embedding']; - id: FieldOutputTypes['public']['Post']['id']; - impressionCount: FieldOutputTypes['public']['Post']['impressionCount']; - priority: FieldOutputTypes['public']['Post']['priority']; - reachScore: FieldOutputTypes['public']['Post']['reachScore']; - title: FieldOutputTypes['public']['Post']['title']; - userId: FieldOutputTypes['public']['Post']['userId']; - viewCount: FieldOutputTypes['public']['Post']['viewCount']; - tags: public_Tag[]; - user: public_User; - readonly [RelationKeys]?: 'tags' | 'user'; - }; - export type public_PostTag = { - postId: FieldOutputTypes['public']['PostTag']['postId']; - tagId: FieldOutputTypes['public']['PostTag']['tagId']; - post: public_Post; - tag: public_Tag; - readonly [RelationKeys]?: 'post' | 'tag'; - }; - export type public_Tag = { - id: FieldOutputTypes['public']['Tag']['id']; - label: FieldOutputTypes['public']['Tag']['label']; - posts: public_Post[]; - readonly [RelationKeys]?: 'posts'; - }; - export type public_Task = { - createdAt: FieldOutputTypes['public']['Task']['createdAt']; - description: FieldOutputTypes['public']['Task']['description']; - id: FieldOutputTypes['public']['Task']['id']; - status: FieldOutputTypes['public']['Task']['status']; - title: FieldOutputTypes['public']['Task']['title']; - type: 'bug' | 'feature'; - userId: FieldOutputTypes['public']['Task']['userId']; - user: public_User; - readonly [RelationKeys]?: 'user'; - }; - export type public_User = { - address: FieldOutputTypes['public']['User']['address']; - createdAt: FieldOutputTypes['public']['User']['createdAt']; - displayName: FieldOutputTypes['public']['User']['displayName']; - email: FieldOutputTypes['public']['User']['email']; - id: FieldOutputTypes['public']['User']['id']; - kind: FieldOutputTypes['public']['User']['kind']; - posts: public_Post[]; - tasks: public_AnyTask[]; - readonly [RelationKeys]?: 'posts' | 'tasks'; - }; - export type public_AnyTask = public_Bug | public_Feature; -} - -export declare const models: { - public: { - Bug: Models.public_Bug; - Feature: Models.public_Feature; - Post: Models.public_Post; - PostTag: Models.public_PostTag; - Tag: Models.public_Tag; - Task: Models.public_Task; - User: Models.public_User; - AnyTask: Models.public_AnyTask; - }; -}; - -export type TypeMaps = TypeMapsType< - CodecTypes, - QueryOperationTypes, - FieldOutputTypes, - FieldInputTypes, - StorageColumnTypes, - StorageColumnInputTypes, - AggregateTypes ->; - -type ContractBase = Omit< - ContractType<{ - readonly namespaces: { - readonly public: { - readonly id: 'public'; - readonly kind: 'postgres-schema'; - readonly entries: { - readonly table: { - readonly bug: { - columns: { - readonly id: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly severity: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - readonly stepsToRepro: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: true; - readonly many: false; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly []; - foreignKeys: readonly [ - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'bug'; - readonly columns: readonly ['id']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'task'; - readonly columns: readonly ['id']; - }; - }, - ]; - }; - readonly feature: { - columns: { - readonly id: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly priority: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - readonly targetRelease: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: true; - readonly many: false; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly []; - foreignKeys: readonly [ - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'feature'; - readonly columns: readonly ['id']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'task'; - readonly columns: readonly ['id']; - }; - }, - ]; - }; - readonly post: { - columns: { - readonly createdAt: { - readonly dataType: 'pg/timestamptz'; - readonly codecId: 'pg/timestamptz-temporal@1'; - readonly nullable: false; - readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; - readonly many: false; - }; - readonly embedding: { - readonly dataType: 'pgvector/vector'; - readonly codecId: 'pg/vector@1'; - readonly nullable: true; - readonly typeParams: { readonly length: 1536 }; - readonly many: false; - }; - readonly id: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly impressionCount: { - readonly dataType: 'pg/int8'; - readonly codecId: 'pg/int8@1'; - readonly nullable: true; - readonly many: false; - }; - readonly priority: { - readonly dataType: 'pg/int4'; - readonly codecId: 'pg/int4@1'; - readonly nullable: false; - readonly default: { - readonly kind: 'literal'; - readonly value: DefaultLiteralValue<'pg/int4@1', 0>; - }; - readonly many: false; - }; - readonly reachScore: { - readonly dataType: 'pg/numeric'; - readonly codecId: 'pg/unboundedint@1'; - readonly nullable: true; - readonly many: false; - }; - readonly title: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - readonly userId: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly viewCount: { - readonly dataType: 'pg/int8'; - readonly codecId: 'pg/int8number@1'; - readonly nullable: true; - readonly many: false; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly [ - { - readonly name: 'post_userId_idx_a489d58a'; - readonly prefix: 'post_userId_idx'; - readonly columns: readonly ['userId']; - readonly unique: false; - }, - ]; - foreignKeys: readonly [ - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'post'; - readonly columns: readonly ['userId']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'user'; - readonly columns: readonly ['id']; - }; - }, - ]; - }; - readonly post_tag: { - columns: { - readonly postId: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly tagId: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - }; - primaryKey: { readonly columns: readonly ['postId', 'tagId'] }; - uniques: readonly []; - indexes: readonly [ - { - readonly name: 'post_tag_postId_idx_a7a72715'; - readonly prefix: 'post_tag_postId_idx'; - readonly columns: readonly ['postId']; - readonly unique: false; - }, - { - readonly name: 'post_tag_tagId_idx_86854244'; - readonly prefix: 'post_tag_tagId_idx'; - readonly columns: readonly ['tagId']; - readonly unique: false; - }, - ]; - foreignKeys: readonly [ - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'post_tag'; - readonly columns: readonly ['postId']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'post'; - readonly columns: readonly ['id']; - }; - }, - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'post_tag'; - readonly columns: readonly ['tagId']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'tag'; - readonly columns: readonly ['id']; - }; - }, - ]; - }; - readonly tag: { - columns: { - readonly id: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly label: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly [{ readonly columns: readonly ['label'] }]; - indexes: readonly []; - foreignKeys: readonly []; - }; - readonly task: { - columns: { - readonly createdAt: { - readonly dataType: 'pg/timestamptz'; - readonly codecId: 'pg/timestamptz-temporal@1'; - readonly nullable: false; - readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; - readonly many: false; - }; - readonly description: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: true; - readonly many: false; - }; - readonly id: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly status: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly default: { - readonly kind: 'literal'; - readonly value: DefaultLiteralValue<'pg/text@1', 'open'>; - }; - readonly many: false; - }; - readonly title: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - readonly type: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - readonly userId: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly [ - { - readonly name: 'task_userId_idx_a489d58a'; - readonly prefix: 'task_userId_idx'; - readonly columns: readonly ['userId']; - readonly unique: false; - }, - ]; - foreignKeys: readonly [ - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'task'; - readonly columns: readonly ['userId']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'user'; - readonly columns: readonly ['id']; - }; - }, - ]; - }; - readonly user: { - columns: { - readonly address: { - readonly dataType: 'pg/jsonb'; - readonly codecId: 'pg/jsonb@1'; - readonly nullable: true; - readonly many: false; - }; - readonly createdAt: { - readonly dataType: 'pg/timestamptz'; - readonly codecId: 'pg/timestamptz-temporal@1'; - readonly nullable: false; - readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; - readonly many: false; - }; - readonly displayName: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - readonly email: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - readonly id: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly kind: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly []; - foreignKeys: readonly []; - }; - }; - readonly valueSet: { - readonly Priority: { readonly kind: 'valueSet'; readonly values: readonly [0, 1, 2] }; - readonly 'Task.type': { - readonly kind: 'valueSet'; - readonly values: readonly ['bug', 'feature']; - }; - readonly user_type: { - readonly kind: 'valueSet'; - readonly values: readonly ['admin', 'user']; - }; - }; - readonly valueObject: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - }; - }; - }; - }; - }; - readonly storageHash: StorageHash; - }>, - 'roots' | 'domain' -> & { - readonly target: 'postgres'; - readonly targetFamily: 'sql'; - readonly roots: { - readonly post: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; - readonly post_tag: { readonly namespace: 'public' & NamespaceId; readonly model: 'PostTag' }; - readonly tag: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; - readonly task: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; - readonly user: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; - }; - readonly domain: { - readonly namespaces: { - readonly public: { - readonly models: { - readonly Bug: { - readonly fields: { - readonly severity: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly stepsToRepro: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - readonly relations: Record; - readonly storage: { - readonly table: 'bug'; - readonly namespaceId: 'public'; - readonly fields: { - readonly severity: { readonly column: 'severity' }; - readonly stepsToRepro: { readonly column: 'stepsToRepro' }; - }; - }; - readonly base: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; - }; - readonly Feature: { - readonly fields: { - readonly priority: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly targetRelease: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - readonly relations: Record; - readonly storage: { - readonly table: 'feature'; - readonly namespaceId: 'public'; - readonly fields: { - readonly priority: { readonly column: 'priority' }; - readonly targetRelease: { readonly column: 'targetRelease' }; - }; - }; - readonly base: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; - }; - readonly Post: { - readonly fields: { - readonly createdAt: { - readonly nullable: false; - readonly type: { - readonly kind: 'scalar'; - readonly codecId: 'pg/timestamptz-temporal@1'; - }; - }; - readonly embedding: { - readonly nullable: true; - readonly type: { - readonly kind: 'scalar'; - readonly codecId: 'pg/vector@1'; - readonly typeParams: { readonly length: 1536 }; - }; - }; - readonly id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly impressionCount: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int8@1' }; - }; - readonly priority: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int4@1' }; - }; - readonly reachScore: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/unboundedint@1' }; - }; - readonly title: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly viewCount: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int8number@1' }; - }; - }; - readonly relations: { - readonly tags: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; - readonly cardinality: 'N:M'; - readonly on: { - readonly localFields: readonly ['id']; - readonly targetFields: readonly ['postId']; - }; - readonly through: { - readonly table: 'post_tag'; - readonly namespaceId: 'public'; - readonly parentColumns: readonly ['postId']; - readonly childColumns: readonly ['tagId']; - readonly targetColumns: readonly ['id']; - }; - }; - readonly user: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['userId']; - readonly targetFields: readonly ['id']; - }; - }; - }; - readonly storage: { - readonly table: 'post'; - readonly namespaceId: 'public'; - readonly fields: { - readonly createdAt: { readonly column: 'createdAt' }; - readonly embedding: { readonly column: 'embedding' }; - readonly id: { readonly column: 'id' }; - readonly impressionCount: { readonly column: 'impressionCount' }; - readonly priority: { readonly column: 'priority' }; - readonly reachScore: { readonly column: 'reachScore' }; - readonly title: { readonly column: 'title' }; - readonly userId: { readonly column: 'userId' }; - readonly viewCount: { readonly column: 'viewCount' }; - }; - }; - }; - readonly PostTag: { - readonly fields: { - readonly postId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly tagId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - }; - readonly relations: { - readonly post: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['postId']; - readonly targetFields: readonly ['id']; - }; - }; - readonly tag: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['tagId']; - readonly targetFields: readonly ['id']; - }; - }; - }; - readonly storage: { - readonly table: 'post_tag'; - readonly namespaceId: 'public'; - readonly fields: { - readonly postId: { readonly column: 'postId' }; - readonly tagId: { readonly column: 'tagId' }; - }; - }; - }; - readonly Tag: { - readonly fields: { - readonly id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly label: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - readonly relations: { - readonly posts: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; - readonly cardinality: 'N:M'; - readonly on: { - readonly localFields: readonly ['id']; - readonly targetFields: readonly ['tagId']; - }; - readonly through: { - readonly table: 'post_tag'; - readonly namespaceId: 'public'; - readonly parentColumns: readonly ['tagId']; - readonly childColumns: readonly ['postId']; - readonly targetColumns: readonly ['id']; - }; - }; - }; - readonly storage: { - readonly table: 'tag'; - readonly namespaceId: 'public'; - readonly fields: { - readonly id: { readonly column: 'id' }; - readonly label: { readonly column: 'label' }; - }; - }; - }; - readonly Task: { - readonly fields: { - readonly createdAt: { - readonly nullable: false; - readonly type: { - readonly kind: 'scalar'; - readonly codecId: 'pg/timestamptz-temporal@1'; - }; - }; - readonly description: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly status: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly title: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly type: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - }; - readonly relations: { - readonly user: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['userId']; - readonly targetFields: readonly ['id']; - }; - }; - }; - readonly storage: { - readonly table: 'task'; - readonly namespaceId: 'public'; - readonly fields: { - readonly createdAt: { readonly column: 'createdAt' }; - readonly description: { readonly column: 'description' }; - readonly id: { readonly column: 'id' }; - readonly status: { readonly column: 'status' }; - readonly title: { readonly column: 'title' }; - readonly type: { readonly column: 'type' }; - readonly userId: { readonly column: 'userId' }; - }; - }; - readonly discriminator: { readonly field: 'type' }; - readonly variants: { - readonly bug: { readonly namespace: 'public' & NamespaceId; readonly model: 'Bug' }; - readonly feature: { - readonly namespace: 'public' & NamespaceId; - readonly model: 'Feature'; - }; - }; - }; - readonly User: { - readonly fields: { - readonly address: { - readonly nullable: true; - readonly type: { - readonly kind: 'valueObject'; - readonly plane: 'storage'; - readonly namespaceId: 'public'; - readonly entityKind: 'valueObject'; - readonly entityName: 'Address'; - }; - }; - readonly createdAt: { - readonly nullable: false; - readonly type: { - readonly kind: 'scalar'; - readonly codecId: 'pg/timestamptz-temporal@1'; - }; - }; - readonly displayName: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly email: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly kind: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - readonly relations: { - readonly posts: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; - readonly cardinality: '1:N'; - readonly on: { - readonly localFields: readonly ['id']; - readonly targetFields: readonly ['userId']; - }; - }; - readonly tasks: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; - readonly cardinality: '1:N'; - readonly on: { - readonly localFields: readonly ['id']; - readonly targetFields: readonly ['userId']; - }; - }; - }; - readonly storage: { - readonly table: 'user'; - readonly namespaceId: 'public'; - readonly fields: { - readonly address: { readonly column: 'address' }; - readonly createdAt: { readonly column: 'createdAt' }; - readonly displayName: { readonly column: 'displayName' }; - readonly email: { readonly column: 'email' }; - readonly id: { readonly column: 'id' }; - readonly kind: { readonly column: 'kind' }; - }; - }; - }; - }; - readonly enum: { - readonly Priority: { - readonly codecId: 'pg/int4@1'; - readonly members: readonly [ - { readonly name: 'Low'; readonly value: 0 }, - { readonly name: 'High'; readonly value: 1 }, - { readonly name: 'Urgent'; readonly value: 2 }, - ]; - }; - readonly user_type: { - readonly codecId: 'pg/text@1'; - readonly members: readonly [ - { readonly name: 'admin'; readonly value: 'admin' }, - { readonly name: 'user'; readonly value: 'user' }, - ]; - }; - }; - readonly enumMemberTypes?: { - readonly Priority: readonly [ - { readonly name: 'Low'; readonly value: 0 }, - { readonly name: 'High'; readonly value: 1 }, - { readonly name: 'Urgent'; readonly value: 2 }, - ]; - readonly user_type: readonly [ - { readonly name: 'admin'; readonly value: 'admin' }, - { readonly name: 'user'; readonly value: 'user' }, - ]; - }; - }; - }; - }; - readonly capabilities: { - readonly postgres: { - readonly distinctOn: true; - readonly forKeyShare: true; - readonly forNoKeyUpdate: true; - readonly jsonAgg: true; - readonly lateral: true; - readonly limit: true; - readonly orderBy: true; - readonly 'pgvector.cosine': true; - readonly returning: true; - }; - readonly sql: { - readonly checkConstraint: true; - readonly defaultInInsert: true; - readonly enums: true; - readonly forShare: true; - readonly forUpdate: true; - readonly insertOnConflictSkip: true; - readonly insertOnConflictWithoutTarget: true; - readonly lateral: true; - readonly lockNowait: true; - readonly lockOf: true; - readonly lockSkipLocked: true; - readonly returning: true; - readonly scalarList: true; - }; - }; - readonly extensions: { - readonly 'demo/engagement-stats': { - readonly familyId: 'sql'; - readonly id: 'demo/engagement-stats'; - readonly kind: 'extension'; - readonly targetId: 'postgres'; - readonly types: { - readonly aggregateDescriptors: readonly [ - { - readonly input: { readonly kind: 'trait'; readonly trait: 'numeric' }; - readonly lower: unknown; - readonly nullable: true; - readonly operation: 'stddev'; - readonly output: { readonly codecId: 'pg/numeric@1'; readonly kind: 'codec' }; - }, - ]; - }; - readonly version: '0.0.1'; - }; - readonly pgvector: { - readonly capabilities: { readonly postgres: { readonly 'pgvector.cosine': true } }; - readonly familyId: 'sql'; - readonly id: 'pgvector'; - readonly kind: 'extension'; - readonly targetId: 'postgres'; - readonly types: { - readonly codecTypes: { - readonly import: { - readonly alias: 'PgVectorTypes'; - readonly named: 'CodecTypes'; - readonly package: '@internal/extension-pgvector/codec-types'; - }; - readonly parameterizedOutputTypes: { - readonly alias: 'PgVectorParameterizedOutputTypes'; - readonly named: 'ParameterizedOutputTypesForTypeParams'; - readonly package: '@internal/extension-pgvector/codec-types'; - }; - }; - readonly operationTypes: { - readonly import: { - readonly alias: 'PgVectorOperationTypes'; - readonly named: 'OperationTypes'; - readonly package: '@internal/extension-pgvector/operation-types'; - }; - }; - readonly queryOperationTypes: { - readonly import: { - readonly alias: 'PgVectorQueryOperationTypes'; - readonly named: 'QueryOperationTypes'; - readonly package: '@internal/extension-pgvector/operation-types'; - }; - }; - }; - readonly version: '0.0.1'; - }; - }; - readonly execution: { - readonly executionHash: ExecutionHash; - readonly mutations: { - readonly defaults: readonly [ - { - readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; - readonly ref: { - readonly entry: 'post'; - readonly field: 'id'; - readonly namespace: 'public'; - }; - }, - { - readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; - readonly ref: { - readonly entry: 'tag'; - readonly field: 'id'; - readonly namespace: 'public'; - }; - }, - { - readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; - readonly ref: { - readonly entry: 'task'; - readonly field: 'id'; - readonly namespace: 'public'; - }; - }, - { - readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; - readonly ref: { - readonly entry: 'user'; - readonly field: 'id'; - readonly namespace: 'public'; - }; - }, - ]; - }; - }; - readonly meta: {}; - readonly profileHash: ProfileHash; -}; - -export type Contract = ContractWithTypeMaps; - -export type Namespaces = Contract['storage']['namespaces']; diff --git a/examples/prisma-8-demo/migrations/snapshots/3c2324f6061012e9d50f869b3e6ae6aa41159acd559b1cc5459bc64e2ccb0f4e/contract.json b/examples/prisma-8-demo/migrations/snapshots/3c2324f6061012e9d50f869b3e6ae6aa41159acd559b1cc5459bc64e2ccb0f4e/contract.json deleted file mode 100644 index 4854522c8b67..000000000000 --- a/examples/prisma-8-demo/migrations/snapshots/3c2324f6061012e9d50f869b3e6ae6aa41159acd559b1cc5459bc64e2ccb0f4e/contract.json +++ /dev/null @@ -1 +0,0 @@ -{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{"postgres":{"distinctOn":true,"forKeyShare":true,"forNoKeyUpdate":true,"jsonAgg":true,"lateral":true,"limit":true,"orderBy":true,"pgvector.cosine":true,"returning":true},"sql":{"checkConstraint":true,"defaultInInsert":true,"enums":true,"forShare":true,"forUpdate":true,"insertOnConflictSkip":true,"insertOnConflictWithoutTarget":true,"lateral":true,"lockNowait":true,"lockOf":true,"lockSkipLocked":true,"returning":true,"scalarList":true}},"domain":{"namespaces":{"public":{"enum":{"Priority":{"codecId":"pg/int4@1","members":[{"name":"Low","value":0},{"name":"High","value":1},{"name":"Urgent","value":2}]},"user_type":{"codecId":"pg/text@1","members":[{"name":"admin","value":"admin"},{"name":"user","value":"user"}]}},"models":{"Bug":{"base":{"model":"Task","namespace":"public"},"fields":{"severity":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"stepsToRepro":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"severity":{"column":"severity"},"stepsToRepro":{"column":"stepsToRepro"}},"namespaceId":"public","table":"bug"}},"Feature":{"base":{"model":"Task","namespace":"public"},"fields":{"priority":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"targetRelease":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"priority":{"column":"priority"},"targetRelease":{"column":"targetRelease"}},"namespaceId":"public","table":"feature"}},"Post":{"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"embedding":{"nullable":true,"type":{"codecId":"pg/vector@1","kind":"scalar","typeParams":{"length":1536}}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"impressionCount":{"nullable":true,"type":{"codecId":"pg/int8@1","kind":"scalar"}},"priority":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"Priority","namespaceId":"public","plane":"domain"}},"reachScore":{"nullable":true,"type":{"codecId":"pg/unboundedint@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"viewCount":{"nullable":true,"type":{"codecId":"pg/int8number@1","kind":"scalar"}}},"relations":{"tags":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["postId"]},"through":{"childColumns":["tagId"],"namespaceId":"public","parentColumns":["postId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Tag","namespace":"public"}},"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"embedding":{"column":"embedding"},"id":{"column":"id"},"impressionCount":{"column":"impressionCount"},"priority":{"column":"priority"},"reachScore":{"column":"reachScore"},"title":{"column":"title"},"userId":{"column":"userId"},"viewCount":{"column":"viewCount"}},"namespaceId":"public","table":"post"}},"PostTag":{"fields":{"postId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"tagId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"post":{"cardinality":"N:1","nullable":false,"on":{"localFields":["postId"],"targetFields":["id"]},"to":{"model":"Post","namespace":"public"}},"tag":{"cardinality":"N:1","nullable":false,"on":{"localFields":["tagId"],"targetFields":["id"]},"to":{"model":"Tag","namespace":"public"}}},"storage":{"fields":{"postId":{"column":"postId"},"tagId":{"column":"tagId"}},"namespaceId":"public","table":"post_tag"}},"Tag":{"fields":{"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"label":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{"posts":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["tagId"]},"through":{"childColumns":["postId"],"namespaceId":"public","parentColumns":["tagId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Post","namespace":"public"}}},"storage":{"fields":{"id":{"column":"id"},"label":{"column":"label"}},"namespaceId":"public","table":"tag"}},"Task":{"discriminator":{"field":"type"},"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"description":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"status":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"type":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"description":{"column":"description"},"id":{"column":"id"},"status":{"column":"status"},"title":{"column":"title"},"type":{"column":"type"},"userId":{"column":"userId"}},"namespaceId":"public","table":"task"},"variants":{"bug":{"model":"Bug","namespace":"public"},"feature":{"model":"Feature","namespace":"public"}}},"User":{"fields":{"address":{"nullable":true,"type":{"entityKind":"valueObject","entityName":"Address","kind":"valueObject","namespaceId":"public","plane":"storage"}},"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"displayName":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"user_type","namespaceId":"public","plane":"domain"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Post","namespace":"public"}},"tasks":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Task","namespace":"public"}}},"storage":{"fields":{"address":{"column":"address"},"createdAt":{"column":"createdAt"},"displayName":{"column":"displayName"},"email":{"column":"email"},"id":{"column":"id"},"kind":{"column":"kind"}},"namespaceId":"public","table":"user"}}}}}},"execution":{"executionHash":"eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655","mutations":{"defaults":[{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"post","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"tag","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"task","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"user","field":"id","namespace":"public"}}]}},"extensions":{"demo/engagement-stats":{"familyId":"sql","id":"demo/engagement-stats","kind":"extension","targetId":"postgres","types":{"aggregateDescriptors":[{"input":{"kind":"trait","trait":"numeric"},"nullable":true,"operation":"stddev","output":{"codecId":"pg/numeric@1","kind":"codec"}}]},"version":"0.0.1"},"pgvector":{"capabilities":{"postgres":{"pgvector.cosine":true}},"familyId":"sql","id":"pgvector","kind":"extension","targetId":"postgres","types":{"codecTypes":{"import":{"alias":"PgVectorTypes","named":"CodecTypes","package":"@internal/extension-pgvector/codec-types"},"parameterizedOutputTypes":{"alias":"PgVectorParameterizedOutputTypes","named":"ParameterizedOutputTypesForTypeParams","package":"@internal/extension-pgvector/codec-types"}},"operationTypes":{"import":{"alias":"PgVectorOperationTypes","named":"OperationTypes","package":"@internal/extension-pgvector/operation-types"}},"queryOperationTypes":{"import":{"alias":"PgVectorQueryOperationTypes","named":"QueryOperationTypes","package":"@internal/extension-pgvector/operation-types"}}},"version":"0.0.1"}},"meta":{},"profileHash":"3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2","roots":{"post":{"model":"Post","namespace":"public"},"post_tag":{"model":"PostTag","namespace":"public"},"tag":{"model":"Tag","namespace":"public"},"task":{"model":"Task","namespace":"public"},"user":{"model":"User","namespace":"public"}},"schemaVersion":"1","storage":{"namespaces":{"public":{"entries":{"table":{"bug":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"severity":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"stepsToRepro":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"bug"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"feature":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"priority":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"targetRelease":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"feature"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"post":{"checks":[{"expression":"\"priority\" IN (0, 1, 2)","name":"post_priority_check_b236ace9","prefix":"post_priority_check"}],"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"embedding":{"codecId":"pg/vector@1","dataType":"pgvector/vector","nullable":true,"typeParams":{"length":1536}},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"impressionCount":{"codecId":"pg/int8@1","dataType":"pg/int8","nullable":true},"priority":{"codecId":"pg/int4@1","dataType":"pg/int4","default":{"kind":"literal","value":0},"nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Priority","namespaceId":"public","plane":"storage"}},"reachScore":{"codecId":"pg/unboundedint@1","dataType":"pg/numeric","nullable":true},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"viewCount":{"codecId":"pg/int8number@1","dataType":"pg/int8","nullable":true}},"foreignKeys":[{"index":{"name":"post_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"post"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"post_userId_idx_a489d58a","prefix":"post_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"post_tag":{"columns":{"postId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"tagId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"post_tag_postId_idx_a7a72715"},"source":{"columns":["postId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"post"}},{"index":{"name":"post_tag_tagId_idx_86854244"},"source":{"columns":["tagId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"tag"}}],"indexes":[{"columns":["postId"],"name":"post_tag_postId_idx_a7a72715","prefix":"post_tag_postId_idx","unique":false},{"columns":["tagId"],"name":"post_tag_tagId_idx_86854244","prefix":"post_tag_tagId_idx","unique":false}],"primaryKey":{"columns":["postId","tagId"]},"uniques":[]},"tag":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"label":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[{"columns":["label"]}]},"task":{"checks":[{"expression":"\"type\" IN ('bug', 'feature')","name":"task_type_check_cda3f5e7","prefix":"task_type_check"}],"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"description":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"status":{"codecId":"pg/text@1","dataType":"pg/text","default":{"kind":"literal","value":"open"},"nullable":false},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"type":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Task.type","namespaceId":"public","plane":"storage"}},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"task_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"task"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"task_userId_idx_a489d58a","prefix":"task_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"user":{"checks":[{"expression":"\"address\" IS NULL OR jsonb_path_match(\"address\", 'strict $.type() == \"null\" || ($.type() == \"object\" && exists($.keyvalue() ? (@.key == \"city\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"country\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"street\" && (@.value.type() == \"string\"))) && !exists($.keyvalue() ? (@.key == \"zip\" && !(@.value.type() == \"null\" || (@.value.type() == \"string\")))) && !exists($.keyvalue() ? (@.key != \"city\" && @.key != \"country\" && @.key != \"street\" && @.key != \"zip\")))') IS TRUE","name":"user_address_value_object_69432dc1","prefix":"user_address_value_object"},{"expression":"\"kind\" IN ('admin', 'user')","name":"user_kind_check_836d43ef","prefix":"user_kind_check"}],"columns":{"address":{"codecId":"pg/jsonb@1","dataType":"pg/jsonb","nullable":true},"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"displayName":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"email":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"kind":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"user_type","namespaceId":"public","plane":"storage"}}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]}},"valueObject":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}}}},"valueSet":{"Priority":{"kind":"valueSet","values":[0,1,2]},"Task.type":{"kind":"valueSet","values":["bug","feature"]},"user_type":{"kind":"valueSet","values":["admin","user"]}}},"id":"public","kind":"postgres-schema"}},"storageHash":"3c2324f6061012e9d50f869b3e6ae6aa41159acd559b1cc5459bc64e2ccb0f4e"},"target":"postgres","targetFamily":"sql"} diff --git a/examples/prisma-8-demo/migrations/snapshots/9301c8a3b2ec83c89b4eb12568ccb0c9e916ba199d8743ab38ed468483d6921f/contract.d.ts b/examples/prisma-8-demo/migrations/snapshots/9301c8a3b2ec83c89b4eb12568ccb0c9e916ba199d8743ab38ed468483d6921f/contract.d.ts deleted file mode 100644 index 9b6a112360f7..000000000000 --- a/examples/prisma-8-demo/migrations/snapshots/9301c8a3b2ec83c89b4eb12568ccb0c9e916ba199d8743ab38ed468483d6921f/contract.d.ts +++ /dev/null @@ -1,1431 +0,0 @@ -// ⚠️ GENERATED FILE - DO NOT EDIT -// This file is automatically generated by 'prisma contract emit'. -// To regenerate, run: prisma contract emit -import type { - CodecTypes as PgVectorTypes, - ParameterizedOutputTypesForTypeParams as PgVectorParameterizedOutputTypes, -} from '@prisma/orm-extension-pgvector/codec-types'; -import type { QueryOperationTypes as PgVectorQueryOperationTypes } from '@prisma/orm-extension-pgvector/operation-types'; -import type { - CodecTypes as PgTypes, - ParameterizedOutputTypesForTypeParams as PgParameterizedOutputTypes, -} from '@prisma/orm-postgres/target/codec-types'; -import type { QueryOperationTypes as PgTargetQueryOps } from '@prisma/orm-postgres/target/operation-types'; - -import type { - ColumnTypesFromStorage, - ContractWithTypeMaps, - RelationKeys, - TypeMaps as TypeMapsType, -} from '@prisma/orm-postgres/family-contract/types'; -import type { - Contract as ContractType, - ExecutionHashBase, - FieldTypesFromDomain, - NamespaceId, - ProfileHashBase, - StorageHashBase, - ValueObjectTypesFromStorage, -} from '@prisma/orm-postgres/contract/types'; - -export type StorageHash = - StorageHashBase<'9301c8a3b2ec83c89b4eb12568ccb0c9e916ba199d8743ab38ed468483d6921f'>; -export type ExecutionHash = - ExecutionHashBase<'eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655'>; -export type ProfileHash = - ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; - -export type CodecTypes = PgTypes & PgVectorTypes; -export type LaneCodecTypes = CodecTypes; -export type QueryOperationTypes = PgTargetQueryOps & - PgVectorQueryOperationTypes; -export type AggregateTypes = { - readonly avg: { - readonly byCodec: { - readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/float4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/interval-string@1': { - readonly output: 'pg/interval-string@1'; - readonly nullable: true; - }; - readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - }; - }; - readonly avgDecimal: { - readonly byCodec: { - readonly 'pg/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - }; - }; - readonly count: { - readonly byCodec: {}; - readonly withoutInput: { readonly output: 'pg/int8number@1'; readonly nullable: false }; - readonly anyInput: { readonly output: 'pg/int8number@1'; readonly nullable: false }; - }; - readonly countBigInt: { - readonly byCodec: {}; - readonly withoutInput: { readonly output: 'pg/int8@1'; readonly nullable: false }; - readonly anyInput: { readonly output: 'pg/int8@1'; readonly nullable: false }; - }; - readonly max: { - readonly byCodec: { - readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true }; - readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true }; - readonly 'pg/date-temporal@1': { - readonly output: 'pg/date-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true }; - readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true }; - readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; - readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true }; - readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/interval-string@1': { - readonly output: 'pg/interval-string@1'; - readonly nullable: true; - }; - readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; - readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true }; - readonly 'pg/time-temporal@1': { - readonly output: 'pg/time-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-date@1': { - readonly output: 'pg/timestamp-date@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-instant@1': { - readonly output: 'pg/timestamp-instant@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-string@1': { - readonly output: 'pg/timestamp-string@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-temporal@1': { - readonly output: 'pg/timestamp-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-date@1': { - readonly output: 'pg/timestamptz-date@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-string@1': { - readonly output: 'pg/timestamptz-string@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-temporal@1': { - readonly output: 'pg/timestamptz-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { - readonly output: 'pg/unboundedint@1'; - readonly nullable: true; - }; - readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true }; - readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true }; - readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true }; - readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - }; - }; - readonly min: { - readonly byCodec: { - readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true }; - readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true }; - readonly 'pg/date-temporal@1': { - readonly output: 'pg/date-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true }; - readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true }; - readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; - readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true }; - readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/interval-string@1': { - readonly output: 'pg/interval-string@1'; - readonly nullable: true; - }; - readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; - readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true }; - readonly 'pg/time-temporal@1': { - readonly output: 'pg/time-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-date@1': { - readonly output: 'pg/timestamp-date@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-instant@1': { - readonly output: 'pg/timestamp-instant@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-string@1': { - readonly output: 'pg/timestamp-string@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-temporal@1': { - readonly output: 'pg/timestamp-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-date@1': { - readonly output: 'pg/timestamptz-date@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-string@1': { - readonly output: 'pg/timestamptz-string@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-temporal@1': { - readonly output: 'pg/timestamptz-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { - readonly output: 'pg/unboundedint@1'; - readonly nullable: true; - }; - readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true }; - readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true }; - readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true }; - readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - }; - }; - readonly stddev: { - readonly byCodec: { - readonly 'pg/float@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/float4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/float8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'sql/float@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - }; - }; - readonly sum: { - readonly byCodec: { - readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; - readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/interval-string@1': { - readonly output: 'pg/interval-string@1'; - readonly nullable: true; - }; - readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { - readonly output: 'pg/unboundedint@1'; - readonly nullable: true; - }; - readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - }; - }; - readonly sumBigInt: { - readonly byCodec: { - readonly 'pg/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { - readonly output: 'pg/unboundedint@1'; - readonly nullable: true; - }; - readonly 'sql/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - }; - }; -}; -type DefaultLiteralValue = CodecId extends keyof CodecTypes - ? Encoded extends CodecTypes[CodecId]['json'] - ? Encoded - : CodecTypes[CodecId]['json'] - : Encoded; -type ParameterizedOutputTypeFunctions = - PgParameterizedOutputTypes | PgVectorParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromStorage< - ContractBase['storage']['namespaces'], - CodecTypes, - ParameterizedOutputTypeFunctions, - FieldTypeOverrides, - 'output' ->; -type ValueObjectInputTypes = ValueObjectTypesFromStorage< - ContractBase['storage']['namespaces'], - CodecTypes, - ParameterizedOutputTypeFunctions, - FieldTypeOverrides, - 'input' ->; -export type AddressOutput = ValueObjectOutputTypes['public']['Address']; -export type AddressInput = ValueObjectInputTypes['public']['Address']; -type FieldTypeOverrides = { - readonly public: { - readonly models: { - readonly Post: { - readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 }; - }; - readonly Task: { - readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; - }; - readonly User: { - readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; - }; - }; - }; -}; -export type FieldOutputTypes = FieldTypesFromDomain< - ContractBase['domain']['namespaces'], - CodecTypes, - ParameterizedOutputTypeFunctions, - FieldTypeOverrides, - 'output', - ContractBase['storage']['namespaces'] ->; -export type FieldInputTypes = FieldTypesFromDomain< - ContractBase['domain']['namespaces'], - CodecTypes, - ParameterizedOutputTypeFunctions, - FieldTypeOverrides, - 'input', - ContractBase['storage']['namespaces'] ->; -type ColumnTypeOverrides = { - readonly public: { - readonly post: { readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 } }; - readonly task: { - readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; - }; - readonly user: { - readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; - }; - }; -}; -export type StorageColumnTypes = ColumnTypesFromStorage< - ContractBase['storage'], - CodecTypes, - ParameterizedOutputTypeFunctions, - ColumnTypeOverrides, - 'output' ->; -export type StorageColumnInputTypes = ColumnTypesFromStorage< - ContractBase['storage'], - CodecTypes, - ParameterizedOutputTypeFunctions, - ColumnTypeOverrides, - 'input' ->; - -export namespace Models { - export type public_Bug = { - createdAt: FieldOutputTypes['public']['Task']['createdAt']; - description: FieldOutputTypes['public']['Task']['description']; - id: FieldOutputTypes['public']['Task']['id']; - status: FieldOutputTypes['public']['Task']['status']; - title: FieldOutputTypes['public']['Task']['title']; - type: 'bug'; - userId: FieldOutputTypes['public']['Task']['userId']; - severity: FieldOutputTypes['public']['Bug']['severity']; - stepsToRepro: FieldOutputTypes['public']['Bug']['stepsToRepro']; - user: public_User; - readonly [RelationKeys]?: 'user'; - }; - export type public_Feature = { - createdAt: FieldOutputTypes['public']['Task']['createdAt']; - description: FieldOutputTypes['public']['Task']['description']; - id: FieldOutputTypes['public']['Task']['id']; - status: FieldOutputTypes['public']['Task']['status']; - title: FieldOutputTypes['public']['Task']['title']; - type: 'feature'; - userId: FieldOutputTypes['public']['Task']['userId']; - priority: FieldOutputTypes['public']['Feature']['priority']; - targetRelease: FieldOutputTypes['public']['Feature']['targetRelease']; - user: public_User; - readonly [RelationKeys]?: 'user'; - }; - export type public_Post = { - createdAt: FieldOutputTypes['public']['Post']['createdAt']; - embedding: FieldOutputTypes['public']['Post']['embedding']; - expiresAt: FieldOutputTypes['public']['Post']['expiresAt']; - id: FieldOutputTypes['public']['Post']['id']; - impressionCount: FieldOutputTypes['public']['Post']['impressionCount']; - priority: FieldOutputTypes['public']['Post']['priority']; - reachScore: FieldOutputTypes['public']['Post']['reachScore']; - title: FieldOutputTypes['public']['Post']['title']; - userId: FieldOutputTypes['public']['Post']['userId']; - viewCount: FieldOutputTypes['public']['Post']['viewCount']; - tags: public_Tag[]; - user: public_User; - readonly [RelationKeys]?: 'tags' | 'user'; - }; - export type public_PostTag = { - postId: FieldOutputTypes['public']['PostTag']['postId']; - tagId: FieldOutputTypes['public']['PostTag']['tagId']; - post: public_Post; - tag: public_Tag; - readonly [RelationKeys]?: 'post' | 'tag'; - }; - export type public_Tag = { - id: FieldOutputTypes['public']['Tag']['id']; - label: FieldOutputTypes['public']['Tag']['label']; - posts: public_Post[]; - readonly [RelationKeys]?: 'posts'; - }; - export type public_Task = { - createdAt: FieldOutputTypes['public']['Task']['createdAt']; - description: FieldOutputTypes['public']['Task']['description']; - id: FieldOutputTypes['public']['Task']['id']; - status: FieldOutputTypes['public']['Task']['status']; - title: FieldOutputTypes['public']['Task']['title']; - type: 'bug' | 'feature'; - userId: FieldOutputTypes['public']['Task']['userId']; - user: public_User; - readonly [RelationKeys]?: 'user'; - }; - export type public_User = { - address: FieldOutputTypes['public']['User']['address']; - createdAt: FieldOutputTypes['public']['User']['createdAt']; - displayName: FieldOutputTypes['public']['User']['displayName']; - email: FieldOutputTypes['public']['User']['email']; - id: FieldOutputTypes['public']['User']['id']; - kind: FieldOutputTypes['public']['User']['kind']; - posts: public_Post[]; - tasks: public_AnyTask[]; - readonly [RelationKeys]?: 'posts' | 'tasks'; - }; - export type public_AnyTask = public_Bug | public_Feature; -} - -export declare const models: { - public: { - Bug: Models.public_Bug; - Feature: Models.public_Feature; - Post: Models.public_Post; - PostTag: Models.public_PostTag; - Tag: Models.public_Tag; - Task: Models.public_Task; - User: Models.public_User; - AnyTask: Models.public_AnyTask; - }; -}; - -export type TypeMaps = TypeMapsType< - CodecTypes, - QueryOperationTypes, - FieldOutputTypes, - FieldInputTypes, - StorageColumnTypes, - StorageColumnInputTypes, - AggregateTypes ->; - -type ContractBase = Omit< - ContractType<{ - readonly namespaces: { - readonly public: { - readonly id: 'public'; - readonly kind: 'postgres-schema'; - readonly entries: { - readonly table: { - readonly bug: { - columns: { - readonly id: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly severity: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - readonly stepsToRepro: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: true; - readonly many: false; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly []; - foreignKeys: readonly [ - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'bug'; - readonly columns: readonly ['id']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'task'; - readonly columns: readonly ['id']; - }; - }, - ]; - }; - readonly feature: { - columns: { - readonly id: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly priority: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - readonly targetRelease: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: true; - readonly many: false; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly []; - foreignKeys: readonly [ - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'feature'; - readonly columns: readonly ['id']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'task'; - readonly columns: readonly ['id']; - }; - }, - ]; - }; - readonly post: { - columns: { - readonly createdAt: { - readonly dataType: 'pg/timestamptz'; - readonly codecId: 'pg/timestamptz-temporal@1'; - readonly nullable: false; - readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; - readonly many: false; - }; - readonly embedding: { - readonly dataType: 'pgvector/vector'; - readonly codecId: 'pg/vector@1'; - readonly nullable: true; - readonly typeParams: { readonly length: 1536 }; - readonly many: false; - }; - readonly expiresAt: { - readonly dataType: 'pg/timestamptz'; - readonly codecId: 'pg/timestamptz-temporal@1'; - readonly nullable: false; - readonly default: { - readonly kind: 'function'; - readonly expression: "(now() + '7 days'::interval)"; - }; - readonly many: false; - }; - readonly id: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly impressionCount: { - readonly dataType: 'pg/int8'; - readonly codecId: 'pg/int8@1'; - readonly nullable: true; - readonly many: false; - }; - readonly priority: { - readonly dataType: 'pg/int4'; - readonly codecId: 'pg/int4@1'; - readonly nullable: false; - readonly default: { - readonly kind: 'literal'; - readonly value: DefaultLiteralValue<'pg/int4@1', 0>; - }; - readonly many: false; - }; - readonly reachScore: { - readonly dataType: 'pg/numeric'; - readonly codecId: 'pg/unboundedint@1'; - readonly nullable: true; - readonly many: false; - }; - readonly title: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - readonly userId: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly viewCount: { - readonly dataType: 'pg/int8'; - readonly codecId: 'pg/int8number@1'; - readonly nullable: true; - readonly many: false; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly [ - { - readonly name: 'post_title_search_e0dd1131'; - readonly prefix: 'post_title_search'; - readonly columns: readonly ['title']; - readonly unique: false; - readonly type: 'fullText'; - readonly options: { - readonly language: 'english'; - readonly weightGroups: readonly [readonly ['title']]; - }; - }, - { - readonly name: 'post_userId_idx_a489d58a'; - readonly prefix: 'post_userId_idx'; - readonly columns: readonly ['userId']; - readonly unique: false; - }, - ]; - foreignKeys: readonly [ - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'post'; - readonly columns: readonly ['userId']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'user'; - readonly columns: readonly ['id']; - }; - }, - ]; - }; - readonly post_tag: { - columns: { - readonly postId: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly tagId: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - }; - primaryKey: { readonly columns: readonly ['postId', 'tagId'] }; - uniques: readonly []; - indexes: readonly [ - { - readonly name: 'post_tag_postId_idx_a7a72715'; - readonly prefix: 'post_tag_postId_idx'; - readonly columns: readonly ['postId']; - readonly unique: false; - }, - { - readonly name: 'post_tag_tagId_idx_86854244'; - readonly prefix: 'post_tag_tagId_idx'; - readonly columns: readonly ['tagId']; - readonly unique: false; - }, - ]; - foreignKeys: readonly [ - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'post_tag'; - readonly columns: readonly ['postId']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'post'; - readonly columns: readonly ['id']; - }; - }, - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'post_tag'; - readonly columns: readonly ['tagId']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'tag'; - readonly columns: readonly ['id']; - }; - }, - ]; - }; - readonly tag: { - columns: { - readonly id: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly label: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly [{ readonly columns: readonly ['label'] }]; - indexes: readonly []; - foreignKeys: readonly []; - }; - readonly task: { - columns: { - readonly createdAt: { - readonly dataType: 'pg/timestamptz'; - readonly codecId: 'pg/timestamptz-temporal@1'; - readonly nullable: false; - readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; - readonly many: false; - }; - readonly description: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: true; - readonly many: false; - }; - readonly id: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly status: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly default: { - readonly kind: 'literal'; - readonly value: DefaultLiteralValue<'pg/text@1', 'open'>; - }; - readonly many: false; - }; - readonly title: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - readonly type: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - readonly userId: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly [ - { - readonly name: 'task_userId_idx_a489d58a'; - readonly prefix: 'task_userId_idx'; - readonly columns: readonly ['userId']; - readonly unique: false; - }, - ]; - foreignKeys: readonly [ - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'task'; - readonly columns: readonly ['userId']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'user'; - readonly columns: readonly ['id']; - }; - }, - ]; - }; - readonly user: { - columns: { - readonly address: { - readonly dataType: 'pg/jsonb'; - readonly codecId: 'pg/jsonb@1'; - readonly nullable: true; - readonly many: false; - }; - readonly createdAt: { - readonly dataType: 'pg/timestamptz'; - readonly codecId: 'pg/timestamptz-temporal@1'; - readonly nullable: false; - readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; - readonly many: false; - }; - readonly displayName: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - readonly email: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - readonly id: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly kind: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly []; - foreignKeys: readonly []; - }; - }; - readonly valueSet: { - readonly Priority: { readonly kind: 'valueSet'; readonly values: readonly [0, 1, 2] }; - readonly 'Task.type': { - readonly kind: 'valueSet'; - readonly values: readonly ['bug', 'feature']; - }; - readonly user_type: { - readonly kind: 'valueSet'; - readonly values: readonly ['admin', 'user']; - }; - }; - readonly valueObject: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - }; - }; - }; - }; - }; - readonly storageHash: StorageHash; - }>, - 'roots' | 'domain' -> & { - readonly target: 'postgres'; - readonly targetFamily: 'sql'; - readonly roots: { - readonly post: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; - readonly post_tag: { readonly namespace: 'public' & NamespaceId; readonly model: 'PostTag' }; - readonly tag: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; - readonly task: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; - readonly user: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; - }; - readonly domain: { - readonly namespaces: { - readonly public: { - readonly models: { - readonly Bug: { - readonly fields: { - readonly severity: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly stepsToRepro: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - readonly relations: Record; - readonly storage: { - readonly table: 'bug'; - readonly namespaceId: 'public'; - readonly fields: { - readonly severity: { readonly column: 'severity' }; - readonly stepsToRepro: { readonly column: 'stepsToRepro' }; - }; - }; - readonly base: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; - }; - readonly Feature: { - readonly fields: { - readonly priority: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly targetRelease: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - readonly relations: Record; - readonly storage: { - readonly table: 'feature'; - readonly namespaceId: 'public'; - readonly fields: { - readonly priority: { readonly column: 'priority' }; - readonly targetRelease: { readonly column: 'targetRelease' }; - }; - }; - readonly base: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; - }; - readonly Post: { - readonly fields: { - readonly createdAt: { - readonly nullable: false; - readonly type: { - readonly kind: 'scalar'; - readonly codecId: 'pg/timestamptz-temporal@1'; - }; - }; - readonly embedding: { - readonly nullable: true; - readonly type: { - readonly kind: 'scalar'; - readonly codecId: 'pg/vector@1'; - readonly typeParams: { readonly length: 1536 }; - }; - }; - readonly expiresAt: { - readonly nullable: false; - readonly type: { - readonly kind: 'scalar'; - readonly codecId: 'pg/timestamptz-temporal@1'; - }; - }; - readonly id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly impressionCount: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int8@1' }; - }; - readonly priority: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int4@1' }; - }; - readonly reachScore: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/unboundedint@1' }; - }; - readonly title: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly viewCount: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int8number@1' }; - }; - }; - readonly relations: { - readonly tags: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; - readonly cardinality: 'N:M'; - readonly on: { - readonly localFields: readonly ['id']; - readonly targetFields: readonly ['postId']; - }; - readonly through: { - readonly table: 'post_tag'; - readonly namespaceId: 'public'; - readonly parentColumns: readonly ['postId']; - readonly childColumns: readonly ['tagId']; - readonly targetColumns: readonly ['id']; - }; - }; - readonly user: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['userId']; - readonly targetFields: readonly ['id']; - }; - }; - }; - readonly storage: { - readonly table: 'post'; - readonly namespaceId: 'public'; - readonly fields: { - readonly createdAt: { readonly column: 'createdAt' }; - readonly embedding: { readonly column: 'embedding' }; - readonly expiresAt: { readonly column: 'expiresAt' }; - readonly id: { readonly column: 'id' }; - readonly impressionCount: { readonly column: 'impressionCount' }; - readonly priority: { readonly column: 'priority' }; - readonly reachScore: { readonly column: 'reachScore' }; - readonly title: { readonly column: 'title' }; - readonly userId: { readonly column: 'userId' }; - readonly viewCount: { readonly column: 'viewCount' }; - }; - }; - }; - readonly PostTag: { - readonly fields: { - readonly postId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly tagId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - }; - readonly relations: { - readonly post: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['postId']; - readonly targetFields: readonly ['id']; - }; - }; - readonly tag: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['tagId']; - readonly targetFields: readonly ['id']; - }; - }; - }; - readonly storage: { - readonly table: 'post_tag'; - readonly namespaceId: 'public'; - readonly fields: { - readonly postId: { readonly column: 'postId' }; - readonly tagId: { readonly column: 'tagId' }; - }; - }; - }; - readonly Tag: { - readonly fields: { - readonly id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly label: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - readonly relations: { - readonly posts: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; - readonly cardinality: 'N:M'; - readonly on: { - readonly localFields: readonly ['id']; - readonly targetFields: readonly ['tagId']; - }; - readonly through: { - readonly table: 'post_tag'; - readonly namespaceId: 'public'; - readonly parentColumns: readonly ['tagId']; - readonly childColumns: readonly ['postId']; - readonly targetColumns: readonly ['id']; - }; - }; - }; - readonly storage: { - readonly table: 'tag'; - readonly namespaceId: 'public'; - readonly fields: { - readonly id: { readonly column: 'id' }; - readonly label: { readonly column: 'label' }; - }; - }; - }; - readonly Task: { - readonly fields: { - readonly createdAt: { - readonly nullable: false; - readonly type: { - readonly kind: 'scalar'; - readonly codecId: 'pg/timestamptz-temporal@1'; - }; - }; - readonly description: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly status: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly title: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly type: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - }; - readonly relations: { - readonly user: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['userId']; - readonly targetFields: readonly ['id']; - }; - }; - }; - readonly storage: { - readonly table: 'task'; - readonly namespaceId: 'public'; - readonly fields: { - readonly createdAt: { readonly column: 'createdAt' }; - readonly description: { readonly column: 'description' }; - readonly id: { readonly column: 'id' }; - readonly status: { readonly column: 'status' }; - readonly title: { readonly column: 'title' }; - readonly type: { readonly column: 'type' }; - readonly userId: { readonly column: 'userId' }; - }; - }; - readonly discriminator: { readonly field: 'type' }; - readonly variants: { - readonly bug: { readonly namespace: 'public' & NamespaceId; readonly model: 'Bug' }; - readonly feature: { - readonly namespace: 'public' & NamespaceId; - readonly model: 'Feature'; - }; - }; - }; - readonly User: { - readonly fields: { - readonly address: { - readonly nullable: true; - readonly type: { - readonly kind: 'valueObject'; - readonly plane: 'storage'; - readonly namespaceId: 'public'; - readonly entityKind: 'valueObject'; - readonly entityName: 'Address'; - }; - }; - readonly createdAt: { - readonly nullable: false; - readonly type: { - readonly kind: 'scalar'; - readonly codecId: 'pg/timestamptz-temporal@1'; - }; - }; - readonly displayName: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly email: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly kind: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - readonly relations: { - readonly posts: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; - readonly cardinality: '1:N'; - readonly on: { - readonly localFields: readonly ['id']; - readonly targetFields: readonly ['userId']; - }; - }; - readonly tasks: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; - readonly cardinality: '1:N'; - readonly on: { - readonly localFields: readonly ['id']; - readonly targetFields: readonly ['userId']; - }; - }; - }; - readonly storage: { - readonly table: 'user'; - readonly namespaceId: 'public'; - readonly fields: { - readonly address: { readonly column: 'address' }; - readonly createdAt: { readonly column: 'createdAt' }; - readonly displayName: { readonly column: 'displayName' }; - readonly email: { readonly column: 'email' }; - readonly id: { readonly column: 'id' }; - readonly kind: { readonly column: 'kind' }; - }; - }; - }; - }; - readonly enum: { - readonly Priority: { - readonly codecId: 'pg/int4@1'; - readonly members: readonly [ - { readonly name: 'Low'; readonly value: 0 }, - { readonly name: 'High'; readonly value: 1 }, - { readonly name: 'Urgent'; readonly value: 2 }, - ]; - }; - readonly user_type: { - readonly codecId: 'pg/text@1'; - readonly members: readonly [ - { readonly name: 'admin'; readonly value: 'admin' }, - { readonly name: 'user'; readonly value: 'user' }, - ]; - }; - }; - readonly enumMemberTypes?: { - readonly Priority: readonly [ - { readonly name: 'Low'; readonly value: 0 }, - { readonly name: 'High'; readonly value: 1 }, - { readonly name: 'Urgent'; readonly value: 2 }, - ]; - readonly user_type: readonly [ - { readonly name: 'admin'; readonly value: 'admin' }, - { readonly name: 'user'; readonly value: 'user' }, - ]; - }; - }; - }; - }; - readonly capabilities: { - readonly postgres: { - readonly distinctOn: true; - readonly forKeyShare: true; - readonly forNoKeyUpdate: true; - readonly jsonAgg: true; - readonly lateral: true; - readonly limit: true; - readonly orderBy: true; - readonly 'pgvector.cosine': true; - readonly returning: true; - }; - readonly sql: { - readonly checkConstraint: true; - readonly defaultInInsert: true; - readonly enums: true; - readonly forShare: true; - readonly forUpdate: true; - readonly insertOnConflictSkip: true; - readonly insertOnConflictWithoutTarget: true; - readonly lateral: true; - readonly lockNowait: true; - readonly lockOf: true; - readonly lockSkipLocked: true; - readonly returning: true; - readonly scalarList: true; - }; - }; - readonly extensions: { - readonly 'demo/engagement-stats': { - readonly familyId: 'sql'; - readonly id: 'demo/engagement-stats'; - readonly kind: 'extension'; - readonly targetId: 'postgres'; - readonly types: { - readonly aggregateDescriptors: readonly [ - { - readonly input: { readonly kind: 'trait'; readonly trait: 'numeric' }; - readonly lower: unknown; - readonly nullable: true; - readonly operation: 'stddev'; - readonly output: { readonly codecId: 'pg/numeric@1'; readonly kind: 'codec' }; - }, - ]; - }; - readonly version: '0.0.1'; - }; - readonly pgvector: { - readonly capabilities: { readonly postgres: { readonly 'pgvector.cosine': true } }; - readonly familyId: 'sql'; - readonly id: 'pgvector'; - readonly kind: 'extension'; - readonly targetId: 'postgres'; - readonly types: { - readonly codecTypes: { - readonly import: { - readonly alias: 'PgVectorTypes'; - readonly named: 'CodecTypes'; - readonly package: '@internal/extension-pgvector/codec-types'; - }; - readonly parameterizedOutputTypes: { - readonly alias: 'PgVectorParameterizedOutputTypes'; - readonly named: 'ParameterizedOutputTypesForTypeParams'; - readonly package: '@internal/extension-pgvector/codec-types'; - }; - }; - readonly operationTypes: { - readonly import: { - readonly alias: 'PgVectorOperationTypes'; - readonly named: 'OperationTypes'; - readonly package: '@internal/extension-pgvector/operation-types'; - }; - }; - readonly queryOperationTypes: { - readonly import: { - readonly alias: 'PgVectorQueryOperationTypes'; - readonly named: 'QueryOperationTypes'; - readonly package: '@internal/extension-pgvector/operation-types'; - }; - }; - }; - readonly version: '0.0.1'; - }; - }; - readonly execution: { - readonly executionHash: ExecutionHash; - readonly mutations: { - readonly defaults: readonly [ - { - readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; - readonly ref: { - readonly entry: 'post'; - readonly field: 'id'; - readonly namespace: 'public'; - }; - }, - { - readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; - readonly ref: { - readonly entry: 'tag'; - readonly field: 'id'; - readonly namespace: 'public'; - }; - }, - { - readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; - readonly ref: { - readonly entry: 'task'; - readonly field: 'id'; - readonly namespace: 'public'; - }; - }, - { - readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; - readonly ref: { - readonly entry: 'user'; - readonly field: 'id'; - readonly namespace: 'public'; - }; - }, - ]; - }; - }; - readonly meta: {}; - readonly profileHash: ProfileHash; -}; - -export type Contract = ContractWithTypeMaps; - -export type Namespaces = Contract['storage']['namespaces']; diff --git a/examples/prisma-8-demo/migrations/snapshots/9301c8a3b2ec83c89b4eb12568ccb0c9e916ba199d8743ab38ed468483d6921f/contract.json b/examples/prisma-8-demo/migrations/snapshots/9301c8a3b2ec83c89b4eb12568ccb0c9e916ba199d8743ab38ed468483d6921f/contract.json deleted file mode 100644 index 24cfc59c7e09..000000000000 --- a/examples/prisma-8-demo/migrations/snapshots/9301c8a3b2ec83c89b4eb12568ccb0c9e916ba199d8743ab38ed468483d6921f/contract.json +++ /dev/null @@ -1 +0,0 @@ -{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{"postgres":{"distinctOn":true,"forKeyShare":true,"forNoKeyUpdate":true,"jsonAgg":true,"lateral":true,"limit":true,"orderBy":true,"pgvector.cosine":true,"returning":true},"sql":{"checkConstraint":true,"defaultInInsert":true,"enums":true,"forShare":true,"forUpdate":true,"insertOnConflictSkip":true,"insertOnConflictWithoutTarget":true,"lateral":true,"lockNowait":true,"lockOf":true,"lockSkipLocked":true,"returning":true,"scalarList":true}},"domain":{"namespaces":{"public":{"enum":{"Priority":{"codecId":"pg/int4@1","members":[{"name":"Low","value":0},{"name":"High","value":1},{"name":"Urgent","value":2}]},"user_type":{"codecId":"pg/text@1","members":[{"name":"admin","value":"admin"},{"name":"user","value":"user"}]}},"models":{"Bug":{"base":{"model":"Task","namespace":"public"},"fields":{"severity":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"stepsToRepro":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"severity":{"column":"severity"},"stepsToRepro":{"column":"stepsToRepro"}},"namespaceId":"public","table":"bug"}},"Feature":{"base":{"model":"Task","namespace":"public"},"fields":{"priority":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"targetRelease":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"priority":{"column":"priority"},"targetRelease":{"column":"targetRelease"}},"namespaceId":"public","table":"feature"}},"Post":{"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"embedding":{"nullable":true,"type":{"codecId":"pg/vector@1","kind":"scalar","typeParams":{"length":1536}}},"expiresAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"impressionCount":{"nullable":true,"type":{"codecId":"pg/int8@1","kind":"scalar"}},"priority":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"Priority","namespaceId":"public","plane":"domain"}},"reachScore":{"nullable":true,"type":{"codecId":"pg/unboundedint@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"viewCount":{"nullable":true,"type":{"codecId":"pg/int8number@1","kind":"scalar"}}},"relations":{"tags":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["postId"]},"through":{"childColumns":["tagId"],"namespaceId":"public","parentColumns":["postId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Tag","namespace":"public"}},"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"embedding":{"column":"embedding"},"expiresAt":{"column":"expiresAt"},"id":{"column":"id"},"impressionCount":{"column":"impressionCount"},"priority":{"column":"priority"},"reachScore":{"column":"reachScore"},"title":{"column":"title"},"userId":{"column":"userId"},"viewCount":{"column":"viewCount"}},"namespaceId":"public","table":"post"}},"PostTag":{"fields":{"postId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"tagId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"post":{"cardinality":"N:1","nullable":false,"on":{"localFields":["postId"],"targetFields":["id"]},"to":{"model":"Post","namespace":"public"}},"tag":{"cardinality":"N:1","nullable":false,"on":{"localFields":["tagId"],"targetFields":["id"]},"to":{"model":"Tag","namespace":"public"}}},"storage":{"fields":{"postId":{"column":"postId"},"tagId":{"column":"tagId"}},"namespaceId":"public","table":"post_tag"}},"Tag":{"fields":{"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"label":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{"posts":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["tagId"]},"through":{"childColumns":["postId"],"namespaceId":"public","parentColumns":["tagId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Post","namespace":"public"}}},"storage":{"fields":{"id":{"column":"id"},"label":{"column":"label"}},"namespaceId":"public","table":"tag"}},"Task":{"discriminator":{"field":"type"},"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"description":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"status":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"type":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"description":{"column":"description"},"id":{"column":"id"},"status":{"column":"status"},"title":{"column":"title"},"type":{"column":"type"},"userId":{"column":"userId"}},"namespaceId":"public","table":"task"},"variants":{"bug":{"model":"Bug","namespace":"public"},"feature":{"model":"Feature","namespace":"public"}}},"User":{"fields":{"address":{"nullable":true,"type":{"entityKind":"valueObject","entityName":"Address","kind":"valueObject","namespaceId":"public","plane":"storage"}},"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"displayName":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"user_type","namespaceId":"public","plane":"domain"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Post","namespace":"public"}},"tasks":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Task","namespace":"public"}}},"storage":{"fields":{"address":{"column":"address"},"createdAt":{"column":"createdAt"},"displayName":{"column":"displayName"},"email":{"column":"email"},"id":{"column":"id"},"kind":{"column":"kind"}},"namespaceId":"public","table":"user"}}}}}},"execution":{"executionHash":"eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655","mutations":{"defaults":[{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"post","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"tag","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"task","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"user","field":"id","namespace":"public"}}]}},"extensions":{"demo/engagement-stats":{"familyId":"sql","id":"demo/engagement-stats","kind":"extension","targetId":"postgres","types":{"aggregateDescriptors":[{"input":{"kind":"trait","trait":"numeric"},"nullable":true,"operation":"stddev","output":{"codecId":"pg/numeric@1","kind":"codec"}}]},"version":"0.0.1"},"pgvector":{"capabilities":{"postgres":{"pgvector.cosine":true}},"familyId":"sql","id":"pgvector","kind":"extension","targetId":"postgres","types":{"codecTypes":{"import":{"alias":"PgVectorTypes","named":"CodecTypes","package":"@internal/extension-pgvector/codec-types"},"parameterizedOutputTypes":{"alias":"PgVectorParameterizedOutputTypes","named":"ParameterizedOutputTypesForTypeParams","package":"@internal/extension-pgvector/codec-types"}},"operationTypes":{"import":{"alias":"PgVectorOperationTypes","named":"OperationTypes","package":"@internal/extension-pgvector/operation-types"}},"queryOperationTypes":{"import":{"alias":"PgVectorQueryOperationTypes","named":"QueryOperationTypes","package":"@internal/extension-pgvector/operation-types"}}},"version":"0.0.1"}},"meta":{},"profileHash":"3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2","roots":{"post":{"model":"Post","namespace":"public"},"post_tag":{"model":"PostTag","namespace":"public"},"tag":{"model":"Tag","namespace":"public"},"task":{"model":"Task","namespace":"public"},"user":{"model":"User","namespace":"public"}},"schemaVersion":"1","storage":{"namespaces":{"public":{"entries":{"table":{"bug":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"severity":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"stepsToRepro":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"bug"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"feature":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"priority":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"targetRelease":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"feature"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"post":{"checks":[{"expression":"\"priority\" IN (0, 1, 2)","name":"post_priority_check_b236ace9","prefix":"post_priority_check"}],"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"embedding":{"codecId":"pg/vector@1","dataType":"pgvector/vector","nullable":true,"typeParams":{"length":1536}},"expiresAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"(now() + '7 days'::interval)","kind":"function"},"nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"impressionCount":{"codecId":"pg/int8@1","dataType":"pg/int8","nullable":true},"priority":{"codecId":"pg/int4@1","dataType":"pg/int4","default":{"kind":"literal","value":0},"nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Priority","namespaceId":"public","plane":"storage"}},"reachScore":{"codecId":"pg/unboundedint@1","dataType":"pg/numeric","nullable":true},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"viewCount":{"codecId":"pg/int8number@1","dataType":"pg/int8","nullable":true}},"foreignKeys":[{"index":{"name":"post_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"post"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["title"],"name":"post_title_search_e0dd1131","options":{"language":"english","weightGroups":[["title"]]},"prefix":"post_title_search","type":"fullText","unique":false},{"columns":["userId"],"name":"post_userId_idx_a489d58a","prefix":"post_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"post_tag":{"columns":{"postId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"tagId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"post_tag_postId_idx_a7a72715"},"source":{"columns":["postId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"post"}},{"index":{"name":"post_tag_tagId_idx_86854244"},"source":{"columns":["tagId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"tag"}}],"indexes":[{"columns":["postId"],"name":"post_tag_postId_idx_a7a72715","prefix":"post_tag_postId_idx","unique":false},{"columns":["tagId"],"name":"post_tag_tagId_idx_86854244","prefix":"post_tag_tagId_idx","unique":false}],"primaryKey":{"columns":["postId","tagId"]},"uniques":[]},"tag":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"label":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[{"columns":["label"]}]},"task":{"checks":[{"expression":"\"type\" IN ('bug', 'feature')","name":"task_type_check_cda3f5e7","prefix":"task_type_check"}],"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"description":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"status":{"codecId":"pg/text@1","dataType":"pg/text","default":{"kind":"literal","value":"open"},"nullable":false},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"type":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Task.type","namespaceId":"public","plane":"storage"}},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"task_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"task"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"task_userId_idx_a489d58a","prefix":"task_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"user":{"checks":[{"expression":"\"address\" IS NULL OR jsonb_path_match(\"address\", 'strict $.type() == \"null\" || ($.type() == \"object\" && exists($.keyvalue() ? (@.key == \"city\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"country\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"street\" && (@.value.type() == \"string\"))) && !exists($.keyvalue() ? (@.key == \"zip\" && !(@.value.type() == \"null\" || (@.value.type() == \"string\")))) && !exists($.keyvalue() ? (@.key != \"city\" && @.key != \"country\" && @.key != \"street\" && @.key != \"zip\")))') IS TRUE","name":"user_address_value_object_69432dc1","prefix":"user_address_value_object"},{"expression":"\"kind\" IN ('admin', 'user')","name":"user_kind_check_836d43ef","prefix":"user_kind_check"}],"columns":{"address":{"codecId":"pg/jsonb@1","dataType":"pg/jsonb","nullable":true},"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"displayName":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"email":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"kind":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"user_type","namespaceId":"public","plane":"storage"}}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]}},"valueObject":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}}}},"valueSet":{"Priority":{"kind":"valueSet","values":[0,1,2]},"Task.type":{"kind":"valueSet","values":["bug","feature"]},"user_type":{"kind":"valueSet","values":["admin","user"]}}},"id":"public","kind":"postgres-schema"}},"storageHash":"9301c8a3b2ec83c89b4eb12568ccb0c9e916ba199d8743ab38ed468483d6921f"},"target":"postgres","targetFamily":"sql"} diff --git a/examples/prisma-8-demo/migrations/snapshots/99a4393ee9f877db228baee1164325543fa018be780fd1253250d7d81539f38e/contract.d.ts b/examples/prisma-8-demo/migrations/snapshots/99a4393ee9f877db228baee1164325543fa018be780fd1253250d7d81539f38e/contract.d.ts deleted file mode 100644 index 229fda1ada33..000000000000 --- a/examples/prisma-8-demo/migrations/snapshots/99a4393ee9f877db228baee1164325543fa018be780fd1253250d7d81539f38e/contract.d.ts +++ /dev/null @@ -1,1454 +0,0 @@ -// ⚠️ GENERATED FILE - DO NOT EDIT -// This file is automatically generated by 'prisma contract emit'. -// To regenerate, run: prisma contract emit -import type { - CodecTypes as PgVectorTypes, - ParameterizedOutputTypesForTypeParams as PgVectorParameterizedOutputTypes, -} from '@prisma/orm-extension-pgvector/codec-types'; -import type { QueryOperationTypes as PgVectorQueryOperationTypes } from '@prisma/orm-extension-pgvector/operation-types'; -import type { - CodecTypes as PgTypes, - ParameterizedOutputTypesForTypeParams as PgParameterizedOutputTypes, -} from '@prisma/orm-postgres/target/codec-types'; -import type { QueryOperationTypes as PgTargetQueryOps } from '@prisma/orm-postgres/target/operation-types'; - -import type { - ColumnTypesFromStorage, - ContractWithTypeMaps, - RelationKeys, - TypeMaps as TypeMapsType, -} from '@prisma/orm-postgres/family-contract/types'; -import type { - Contract as ContractType, - ExecutionHashBase, - FieldTypesFromDomain, - NamespaceId, - ProfileHashBase, - StorageHashBase, - ValueObjectTypesFromStorage, -} from '@prisma/orm-postgres/contract/types'; - -export type StorageHash = - StorageHashBase<'99a4393ee9f877db228baee1164325543fa018be780fd1253250d7d81539f38e'>; -export type ExecutionHash = - ExecutionHashBase<'eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655'>; -export type ProfileHash = - ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; - -export type CodecTypes = PgTypes & PgVectorTypes; -export type LaneCodecTypes = CodecTypes; -export type QueryOperationTypes = PgTargetQueryOps & - PgVectorQueryOperationTypes; -export type AggregateTypes = { - readonly avg: { - readonly byCodec: { - readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/float4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/interval-string@1': { - readonly output: 'pg/interval-string@1'; - readonly nullable: true; - }; - readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - }; - }; - readonly avgDecimal: { - readonly byCodec: { - readonly 'pg/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - }; - }; - readonly count: { - readonly byCodec: {}; - readonly withoutInput: { readonly output: 'pg/int8number@1'; readonly nullable: false }; - readonly anyInput: { readonly output: 'pg/int8number@1'; readonly nullable: false }; - }; - readonly countBigInt: { - readonly byCodec: {}; - readonly withoutInput: { readonly output: 'pg/int8@1'; readonly nullable: false }; - readonly anyInput: { readonly output: 'pg/int8@1'; readonly nullable: false }; - }; - readonly max: { - readonly byCodec: { - readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true }; - readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true }; - readonly 'pg/date-temporal@1': { - readonly output: 'pg/date-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true }; - readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true }; - readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; - readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true }; - readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/interval-string@1': { - readonly output: 'pg/interval-string@1'; - readonly nullable: true; - }; - readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; - readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true }; - readonly 'pg/time-temporal@1': { - readonly output: 'pg/time-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-date@1': { - readonly output: 'pg/timestamp-date@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-instant@1': { - readonly output: 'pg/timestamp-instant@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-string@1': { - readonly output: 'pg/timestamp-string@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-temporal@1': { - readonly output: 'pg/timestamp-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-date@1': { - readonly output: 'pg/timestamptz-date@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-string@1': { - readonly output: 'pg/timestamptz-string@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-temporal@1': { - readonly output: 'pg/timestamptz-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { - readonly output: 'pg/unboundedint@1'; - readonly nullable: true; - }; - readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true }; - readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true }; - readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true }; - readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - }; - }; - readonly min: { - readonly byCodec: { - readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true }; - readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true }; - readonly 'pg/date-temporal@1': { - readonly output: 'pg/date-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true }; - readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true }; - readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; - readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true }; - readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/interval-string@1': { - readonly output: 'pg/interval-string@1'; - readonly nullable: true; - }; - readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; - readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true }; - readonly 'pg/time-temporal@1': { - readonly output: 'pg/time-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-date@1': { - readonly output: 'pg/timestamp-date@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-instant@1': { - readonly output: 'pg/timestamp-instant@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-string@1': { - readonly output: 'pg/timestamp-string@1'; - readonly nullable: true; - }; - readonly 'pg/timestamp-temporal@1': { - readonly output: 'pg/timestamp-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-date@1': { - readonly output: 'pg/timestamptz-date@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-string@1': { - readonly output: 'pg/timestamptz-string@1'; - readonly nullable: true; - }; - readonly 'pg/timestamptz-temporal@1': { - readonly output: 'pg/timestamptz-temporal@1'; - readonly nullable: true; - }; - readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { - readonly output: 'pg/unboundedint@1'; - readonly nullable: true; - }; - readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true }; - readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true }; - readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true }; - readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; - }; - }; - readonly stddev: { - readonly byCodec: { - readonly 'pg/float@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/float4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/float8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'sql/float@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - }; - }; - readonly sum: { - readonly byCodec: { - readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; - readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'pg/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - readonly 'pg/interval-string@1': { - readonly output: 'pg/interval-string@1'; - readonly nullable: true; - }; - readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; - readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { - readonly output: 'pg/unboundedint@1'; - readonly nullable: true; - }; - readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; - readonly 'sql/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; - }; - }; - readonly sumBigInt: { - readonly byCodec: { - readonly 'pg/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - readonly 'pg/int2@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - readonly 'pg/int4@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - readonly 'pg/int8@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true }; - readonly 'pg/int8number@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true }; - readonly 'pg/unboundedint@1': { - readonly output: 'pg/unboundedint@1'; - readonly nullable: true; - }; - readonly 'sql/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; - }; - }; -}; -type DefaultLiteralValue = CodecId extends keyof CodecTypes - ? Encoded extends CodecTypes[CodecId]['json'] - ? Encoded - : CodecTypes[CodecId]['json'] - : Encoded; -type ParameterizedOutputTypeFunctions = - PgParameterizedOutputTypes | PgVectorParameterizedOutputTypes; -type ValueObjectOutputTypes = ValueObjectTypesFromStorage< - ContractBase['storage']['namespaces'], - CodecTypes, - ParameterizedOutputTypeFunctions, - FieldTypeOverrides, - 'output' ->; -type ValueObjectInputTypes = ValueObjectTypesFromStorage< - ContractBase['storage']['namespaces'], - CodecTypes, - ParameterizedOutputTypeFunctions, - FieldTypeOverrides, - 'input' ->; -export type AddressOutput = ValueObjectOutputTypes['public']['Address']; -export type AddressInput = ValueObjectInputTypes['public']['Address']; -type FieldTypeOverrides = { - readonly public: { - readonly models: { - readonly Post: { - readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 }; - }; - readonly Task: { - readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; - }; - readonly User: { - readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; - }; - }; - }; -}; -export type FieldOutputTypes = FieldTypesFromDomain< - ContractBase['domain']['namespaces'], - CodecTypes, - ParameterizedOutputTypeFunctions, - FieldTypeOverrides, - 'output', - ContractBase['storage']['namespaces'] ->; -export type FieldInputTypes = FieldTypesFromDomain< - ContractBase['domain']['namespaces'], - CodecTypes, - ParameterizedOutputTypeFunctions, - FieldTypeOverrides, - 'input', - ContractBase['storage']['namespaces'] ->; -type ColumnTypeOverrides = { - readonly public: { - readonly post: { readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 } }; - readonly task: { - readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; - }; - readonly user: { - readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; - }; - }; -}; -export type StorageColumnTypes = ColumnTypesFromStorage< - ContractBase['storage'], - CodecTypes, - ParameterizedOutputTypeFunctions, - ColumnTypeOverrides, - 'output' ->; -export type StorageColumnInputTypes = ColumnTypesFromStorage< - ContractBase['storage'], - CodecTypes, - ParameterizedOutputTypeFunctions, - ColumnTypeOverrides, - 'input' ->; - -export namespace Models { - export type public_Bug = { - createdAt: FieldOutputTypes['public']['Task']['createdAt']; - description: FieldOutputTypes['public']['Task']['description']; - id: FieldOutputTypes['public']['Task']['id']; - status: FieldOutputTypes['public']['Task']['status']; - title: FieldOutputTypes['public']['Task']['title']; - type: 'bug'; - userId: FieldOutputTypes['public']['Task']['userId']; - severity: FieldOutputTypes['public']['Bug']['severity']; - stepsToRepro: FieldOutputTypes['public']['Bug']['stepsToRepro']; - user: public_User; - readonly [RelationKeys]?: 'user'; - }; - export type public_Feature = { - createdAt: FieldOutputTypes['public']['Task']['createdAt']; - description: FieldOutputTypes['public']['Task']['description']; - id: FieldOutputTypes['public']['Task']['id']; - status: FieldOutputTypes['public']['Task']['status']; - title: FieldOutputTypes['public']['Task']['title']; - type: 'feature'; - userId: FieldOutputTypes['public']['Task']['userId']; - priority: FieldOutputTypes['public']['Feature']['priority']; - targetRelease: FieldOutputTypes['public']['Feature']['targetRelease']; - user: public_User; - readonly [RelationKeys]?: 'user'; - }; - export type public_Post = { - body: FieldOutputTypes['public']['Post']['body']; - createdAt: FieldOutputTypes['public']['Post']['createdAt']; - embedding: FieldOutputTypes['public']['Post']['embedding']; - expiresAt: FieldOutputTypes['public']['Post']['expiresAt']; - id: FieldOutputTypes['public']['Post']['id']; - impressionCount: FieldOutputTypes['public']['Post']['impressionCount']; - priority: FieldOutputTypes['public']['Post']['priority']; - reachScore: FieldOutputTypes['public']['Post']['reachScore']; - title: FieldOutputTypes['public']['Post']['title']; - userId: FieldOutputTypes['public']['Post']['userId']; - viewCount: FieldOutputTypes['public']['Post']['viewCount']; - tags: public_Tag[]; - user: public_User; - readonly [RelationKeys]?: 'tags' | 'user'; - }; - export type public_PostTag = { - postId: FieldOutputTypes['public']['PostTag']['postId']; - tagId: FieldOutputTypes['public']['PostTag']['tagId']; - post: public_Post; - tag: public_Tag; - readonly [RelationKeys]?: 'post' | 'tag'; - }; - export type public_Tag = { - id: FieldOutputTypes['public']['Tag']['id']; - label: FieldOutputTypes['public']['Tag']['label']; - posts: public_Post[]; - readonly [RelationKeys]?: 'posts'; - }; - export type public_Task = { - createdAt: FieldOutputTypes['public']['Task']['createdAt']; - description: FieldOutputTypes['public']['Task']['description']; - id: FieldOutputTypes['public']['Task']['id']; - status: FieldOutputTypes['public']['Task']['status']; - title: FieldOutputTypes['public']['Task']['title']; - type: 'bug' | 'feature'; - userId: FieldOutputTypes['public']['Task']['userId']; - user: public_User; - readonly [RelationKeys]?: 'user'; - }; - export type public_User = { - address: FieldOutputTypes['public']['User']['address']; - createdAt: FieldOutputTypes['public']['User']['createdAt']; - displayName: FieldOutputTypes['public']['User']['displayName']; - email: FieldOutputTypes['public']['User']['email']; - id: FieldOutputTypes['public']['User']['id']; - kind: FieldOutputTypes['public']['User']['kind']; - posts: public_Post[]; - tasks: public_AnyTask[]; - readonly [RelationKeys]?: 'posts' | 'tasks'; - }; - export type public_AnyTask = public_Bug | public_Feature; -} - -export declare const models: { - public: { - Bug: Models.public_Bug; - Feature: Models.public_Feature; - Post: Models.public_Post; - PostTag: Models.public_PostTag; - Tag: Models.public_Tag; - Task: Models.public_Task; - User: Models.public_User; - AnyTask: Models.public_AnyTask; - }; -}; - -export type TypeMaps = TypeMapsType< - CodecTypes, - QueryOperationTypes, - FieldOutputTypes, - FieldInputTypes, - StorageColumnTypes, - StorageColumnInputTypes, - AggregateTypes ->; - -type ContractBase = Omit< - ContractType<{ - readonly namespaces: { - readonly public: { - readonly id: 'public'; - readonly kind: 'postgres-schema'; - readonly entries: { - readonly table: { - readonly bug: { - columns: { - readonly id: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly severity: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - readonly stepsToRepro: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: true; - readonly many: false; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly []; - foreignKeys: readonly [ - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'bug'; - readonly columns: readonly ['id']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'task'; - readonly columns: readonly ['id']; - }; - }, - ]; - }; - readonly feature: { - columns: { - readonly id: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly priority: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - readonly targetRelease: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: true; - readonly many: false; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly []; - foreignKeys: readonly [ - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'feature'; - readonly columns: readonly ['id']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'task'; - readonly columns: readonly ['id']; - }; - }, - ]; - }; - readonly post: { - columns: { - readonly body: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: true; - readonly many: false; - }; - readonly createdAt: { - readonly dataType: 'pg/timestamptz'; - readonly codecId: 'pg/timestamptz-temporal@1'; - readonly nullable: false; - readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; - readonly many: false; - }; - readonly embedding: { - readonly dataType: 'pgvector/vector'; - readonly codecId: 'pg/vector@1'; - readonly nullable: true; - readonly typeParams: { readonly length: 1536 }; - readonly many: false; - }; - readonly expiresAt: { - readonly dataType: 'pg/timestamptz'; - readonly codecId: 'pg/timestamptz-temporal@1'; - readonly nullable: false; - readonly default: { - readonly kind: 'function'; - readonly expression: "(now() + '7 days'::interval)"; - }; - readonly many: false; - }; - readonly id: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly impressionCount: { - readonly dataType: 'pg/int8'; - readonly codecId: 'pg/int8@1'; - readonly nullable: true; - readonly many: false; - }; - readonly priority: { - readonly dataType: 'pg/int4'; - readonly codecId: 'pg/int4@1'; - readonly nullable: false; - readonly default: { - readonly kind: 'literal'; - readonly value: DefaultLiteralValue<'pg/int4@1', 0>; - }; - readonly many: false; - }; - readonly reachScore: { - readonly dataType: 'pg/numeric'; - readonly codecId: 'pg/unboundedint@1'; - readonly nullable: true; - readonly many: false; - }; - readonly title: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - readonly userId: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly viewCount: { - readonly dataType: 'pg/int8'; - readonly codecId: 'pg/int8number@1'; - readonly nullable: true; - readonly many: false; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly [ - { - readonly name: 'post_search_f03270be'; - readonly prefix: 'post_search'; - readonly columns: readonly ['title', 'body']; - readonly unique: false; - readonly type: 'fullText'; - readonly options: { - readonly language: 'english'; - readonly weightGroups: readonly [readonly ['title'], readonly ['body']]; - }; - }, - { - readonly name: 'post_title_search_e0dd1131'; - readonly prefix: 'post_title_search'; - readonly columns: readonly ['title']; - readonly unique: false; - readonly type: 'fullText'; - readonly options: { - readonly language: 'english'; - readonly weightGroups: readonly [readonly ['title']]; - }; - }, - { - readonly name: 'post_userId_idx_a489d58a'; - readonly prefix: 'post_userId_idx'; - readonly columns: readonly ['userId']; - readonly unique: false; - }, - ]; - foreignKeys: readonly [ - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'post'; - readonly columns: readonly ['userId']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'user'; - readonly columns: readonly ['id']; - }; - }, - ]; - }; - readonly post_tag: { - columns: { - readonly postId: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly tagId: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - }; - primaryKey: { readonly columns: readonly ['postId', 'tagId'] }; - uniques: readonly []; - indexes: readonly [ - { - readonly name: 'post_tag_postId_idx_a7a72715'; - readonly prefix: 'post_tag_postId_idx'; - readonly columns: readonly ['postId']; - readonly unique: false; - }, - { - readonly name: 'post_tag_tagId_idx_86854244'; - readonly prefix: 'post_tag_tagId_idx'; - readonly columns: readonly ['tagId']; - readonly unique: false; - }, - ]; - foreignKeys: readonly [ - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'post_tag'; - readonly columns: readonly ['postId']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'post'; - readonly columns: readonly ['id']; - }; - }, - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'post_tag'; - readonly columns: readonly ['tagId']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'tag'; - readonly columns: readonly ['id']; - }; - }, - ]; - }; - readonly tag: { - columns: { - readonly id: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly label: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly [{ readonly columns: readonly ['label'] }]; - indexes: readonly []; - foreignKeys: readonly []; - }; - readonly task: { - columns: { - readonly createdAt: { - readonly dataType: 'pg/timestamptz'; - readonly codecId: 'pg/timestamptz-temporal@1'; - readonly nullable: false; - readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; - readonly many: false; - }; - readonly description: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: true; - readonly many: false; - }; - readonly id: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly status: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly default: { - readonly kind: 'literal'; - readonly value: DefaultLiteralValue<'pg/text@1', 'open'>; - }; - readonly many: false; - }; - readonly title: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - readonly type: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - readonly userId: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly [ - { - readonly name: 'task_userId_idx_a489d58a'; - readonly prefix: 'task_userId_idx'; - readonly columns: readonly ['userId']; - readonly unique: false; - }, - ]; - foreignKeys: readonly [ - { - readonly source: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'task'; - readonly columns: readonly ['userId']; - }; - readonly target: { - readonly namespaceId: 'public' & NamespaceId; - readonly tableName: 'user'; - readonly columns: readonly ['id']; - }; - }, - ]; - }; - readonly user: { - columns: { - readonly address: { - readonly dataType: 'pg/jsonb'; - readonly codecId: 'pg/jsonb@1'; - readonly nullable: true; - readonly many: false; - }; - readonly createdAt: { - readonly dataType: 'pg/timestamptz'; - readonly codecId: 'pg/timestamptz-temporal@1'; - readonly nullable: false; - readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; - readonly many: false; - }; - readonly displayName: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - readonly email: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - readonly id: { - readonly dataType: 'pg/uuid'; - readonly codecId: 'pg/uuid@1'; - readonly nullable: false; - readonly many: false; - }; - readonly kind: { - readonly dataType: 'pg/text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - readonly many: false; - }; - }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly []; - foreignKeys: readonly []; - }; - }; - readonly valueSet: { - readonly Priority: { readonly kind: 'valueSet'; readonly values: readonly [0, 1, 2] }; - readonly 'Task.type': { - readonly kind: 'valueSet'; - readonly values: readonly ['bug', 'feature']; - }; - readonly user_type: { - readonly kind: 'valueSet'; - readonly values: readonly ['admin', 'user']; - }; - }; - readonly valueObject: { - readonly Address: { - readonly fields: { - readonly city: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly country: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly street: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly zip: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - }; - }; - }; - }; - }; - readonly storageHash: StorageHash; - }>, - 'roots' | 'domain' -> & { - readonly target: 'postgres'; - readonly targetFamily: 'sql'; - readonly roots: { - readonly post: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; - readonly post_tag: { readonly namespace: 'public' & NamespaceId; readonly model: 'PostTag' }; - readonly tag: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; - readonly task: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; - readonly user: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; - }; - readonly domain: { - readonly namespaces: { - readonly public: { - readonly models: { - readonly Bug: { - readonly fields: { - readonly severity: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly stepsToRepro: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - readonly relations: Record; - readonly storage: { - readonly table: 'bug'; - readonly namespaceId: 'public'; - readonly fields: { - readonly severity: { readonly column: 'severity' }; - readonly stepsToRepro: { readonly column: 'stepsToRepro' }; - }; - }; - readonly base: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; - }; - readonly Feature: { - readonly fields: { - readonly priority: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly targetRelease: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - readonly relations: Record; - readonly storage: { - readonly table: 'feature'; - readonly namespaceId: 'public'; - readonly fields: { - readonly priority: { readonly column: 'priority' }; - readonly targetRelease: { readonly column: 'targetRelease' }; - }; - }; - readonly base: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; - }; - readonly Post: { - readonly fields: { - readonly body: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly createdAt: { - readonly nullable: false; - readonly type: { - readonly kind: 'scalar'; - readonly codecId: 'pg/timestamptz-temporal@1'; - }; - }; - readonly embedding: { - readonly nullable: true; - readonly type: { - readonly kind: 'scalar'; - readonly codecId: 'pg/vector@1'; - readonly typeParams: { readonly length: 1536 }; - }; - }; - readonly expiresAt: { - readonly nullable: false; - readonly type: { - readonly kind: 'scalar'; - readonly codecId: 'pg/timestamptz-temporal@1'; - }; - }; - readonly id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly impressionCount: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int8@1' }; - }; - readonly priority: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int4@1' }; - }; - readonly reachScore: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/unboundedint@1' }; - }; - readonly title: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly viewCount: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int8number@1' }; - }; - }; - readonly relations: { - readonly tags: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; - readonly cardinality: 'N:M'; - readonly on: { - readonly localFields: readonly ['id']; - readonly targetFields: readonly ['postId']; - }; - readonly through: { - readonly table: 'post_tag'; - readonly namespaceId: 'public'; - readonly parentColumns: readonly ['postId']; - readonly childColumns: readonly ['tagId']; - readonly targetColumns: readonly ['id']; - }; - }; - readonly user: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['userId']; - readonly targetFields: readonly ['id']; - }; - }; - }; - readonly storage: { - readonly table: 'post'; - readonly namespaceId: 'public'; - readonly fields: { - readonly body: { readonly column: 'body' }; - readonly createdAt: { readonly column: 'createdAt' }; - readonly embedding: { readonly column: 'embedding' }; - readonly expiresAt: { readonly column: 'expiresAt' }; - readonly id: { readonly column: 'id' }; - readonly impressionCount: { readonly column: 'impressionCount' }; - readonly priority: { readonly column: 'priority' }; - readonly reachScore: { readonly column: 'reachScore' }; - readonly title: { readonly column: 'title' }; - readonly userId: { readonly column: 'userId' }; - readonly viewCount: { readonly column: 'viewCount' }; - }; - }; - }; - readonly PostTag: { - readonly fields: { - readonly postId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly tagId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - }; - readonly relations: { - readonly post: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['postId']; - readonly targetFields: readonly ['id']; - }; - }; - readonly tag: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['tagId']; - readonly targetFields: readonly ['id']; - }; - }; - }; - readonly storage: { - readonly table: 'post_tag'; - readonly namespaceId: 'public'; - readonly fields: { - readonly postId: { readonly column: 'postId' }; - readonly tagId: { readonly column: 'tagId' }; - }; - }; - }; - readonly Tag: { - readonly fields: { - readonly id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly label: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - readonly relations: { - readonly posts: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; - readonly cardinality: 'N:M'; - readonly on: { - readonly localFields: readonly ['id']; - readonly targetFields: readonly ['tagId']; - }; - readonly through: { - readonly table: 'post_tag'; - readonly namespaceId: 'public'; - readonly parentColumns: readonly ['tagId']; - readonly childColumns: readonly ['postId']; - readonly targetColumns: readonly ['id']; - }; - }; - }; - readonly storage: { - readonly table: 'tag'; - readonly namespaceId: 'public'; - readonly fields: { - readonly id: { readonly column: 'id' }; - readonly label: { readonly column: 'label' }; - }; - }; - }; - readonly Task: { - readonly fields: { - readonly createdAt: { - readonly nullable: false; - readonly type: { - readonly kind: 'scalar'; - readonly codecId: 'pg/timestamptz-temporal@1'; - }; - }; - readonly description: { - readonly nullable: true; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly status: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly title: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly type: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly userId: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - }; - readonly relations: { - readonly user: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; - readonly cardinality: 'N:1'; - readonly nullable: false; - readonly on: { - readonly localFields: readonly ['userId']; - readonly targetFields: readonly ['id']; - }; - }; - }; - readonly storage: { - readonly table: 'task'; - readonly namespaceId: 'public'; - readonly fields: { - readonly createdAt: { readonly column: 'createdAt' }; - readonly description: { readonly column: 'description' }; - readonly id: { readonly column: 'id' }; - readonly status: { readonly column: 'status' }; - readonly title: { readonly column: 'title' }; - readonly type: { readonly column: 'type' }; - readonly userId: { readonly column: 'userId' }; - }; - }; - readonly discriminator: { readonly field: 'type' }; - readonly variants: { - readonly bug: { readonly namespace: 'public' & NamespaceId; readonly model: 'Bug' }; - readonly feature: { - readonly namespace: 'public' & NamespaceId; - readonly model: 'Feature'; - }; - }; - }; - readonly User: { - readonly fields: { - readonly address: { - readonly nullable: true; - readonly type: { - readonly kind: 'valueObject'; - readonly plane: 'storage'; - readonly namespaceId: 'public'; - readonly entityKind: 'valueObject'; - readonly entityName: 'Address'; - }; - }; - readonly createdAt: { - readonly nullable: false; - readonly type: { - readonly kind: 'scalar'; - readonly codecId: 'pg/timestamptz-temporal@1'; - }; - }; - readonly displayName: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly email: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; - }; - readonly kind: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - }; - readonly relations: { - readonly posts: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; - readonly cardinality: '1:N'; - readonly on: { - readonly localFields: readonly ['id']; - readonly targetFields: readonly ['userId']; - }; - }; - readonly tasks: { - readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; - readonly cardinality: '1:N'; - readonly on: { - readonly localFields: readonly ['id']; - readonly targetFields: readonly ['userId']; - }; - }; - }; - readonly storage: { - readonly table: 'user'; - readonly namespaceId: 'public'; - readonly fields: { - readonly address: { readonly column: 'address' }; - readonly createdAt: { readonly column: 'createdAt' }; - readonly displayName: { readonly column: 'displayName' }; - readonly email: { readonly column: 'email' }; - readonly id: { readonly column: 'id' }; - readonly kind: { readonly column: 'kind' }; - }; - }; - }; - }; - readonly enum: { - readonly Priority: { - readonly codecId: 'pg/int4@1'; - readonly members: readonly [ - { readonly name: 'Low'; readonly value: 0 }, - { readonly name: 'High'; readonly value: 1 }, - { readonly name: 'Urgent'; readonly value: 2 }, - ]; - }; - readonly user_type: { - readonly codecId: 'pg/text@1'; - readonly members: readonly [ - { readonly name: 'admin'; readonly value: 'admin' }, - { readonly name: 'user'; readonly value: 'user' }, - ]; - }; - }; - readonly enumMemberTypes?: { - readonly Priority: readonly [ - { readonly name: 'Low'; readonly value: 0 }, - { readonly name: 'High'; readonly value: 1 }, - { readonly name: 'Urgent'; readonly value: 2 }, - ]; - readonly user_type: readonly [ - { readonly name: 'admin'; readonly value: 'admin' }, - { readonly name: 'user'; readonly value: 'user' }, - ]; - }; - }; - }; - }; - readonly capabilities: { - readonly postgres: { - readonly distinctOn: true; - readonly forKeyShare: true; - readonly forNoKeyUpdate: true; - readonly jsonAgg: true; - readonly lateral: true; - readonly limit: true; - readonly orderBy: true; - readonly 'pgvector.cosine': true; - readonly returning: true; - }; - readonly sql: { - readonly checkConstraint: true; - readonly defaultInInsert: true; - readonly enums: true; - readonly forShare: true; - readonly forUpdate: true; - readonly insertOnConflictSkip: true; - readonly insertOnConflictWithoutTarget: true; - readonly lateral: true; - readonly lockNowait: true; - readonly lockOf: true; - readonly lockSkipLocked: true; - readonly returning: true; - readonly scalarList: true; - }; - }; - readonly extensions: { - readonly 'demo/engagement-stats': { - readonly familyId: 'sql'; - readonly id: 'demo/engagement-stats'; - readonly kind: 'extension'; - readonly targetId: 'postgres'; - readonly types: { - readonly aggregateDescriptors: readonly [ - { - readonly input: { readonly kind: 'trait'; readonly trait: 'numeric' }; - readonly lower: unknown; - readonly nullable: true; - readonly operation: 'stddev'; - readonly output: { readonly codecId: 'pg/numeric@1'; readonly kind: 'codec' }; - }, - ]; - }; - readonly version: '0.0.1'; - }; - readonly pgvector: { - readonly capabilities: { readonly postgres: { readonly 'pgvector.cosine': true } }; - readonly familyId: 'sql'; - readonly id: 'pgvector'; - readonly kind: 'extension'; - readonly targetId: 'postgres'; - readonly types: { - readonly codecTypes: { - readonly import: { - readonly alias: 'PgVectorTypes'; - readonly named: 'CodecTypes'; - readonly package: '@internal/extension-pgvector/codec-types'; - }; - readonly parameterizedOutputTypes: { - readonly alias: 'PgVectorParameterizedOutputTypes'; - readonly named: 'ParameterizedOutputTypesForTypeParams'; - readonly package: '@internal/extension-pgvector/codec-types'; - }; - }; - readonly operationTypes: { - readonly import: { - readonly alias: 'PgVectorOperationTypes'; - readonly named: 'OperationTypes'; - readonly package: '@internal/extension-pgvector/operation-types'; - }; - }; - readonly queryOperationTypes: { - readonly import: { - readonly alias: 'PgVectorQueryOperationTypes'; - readonly named: 'QueryOperationTypes'; - readonly package: '@internal/extension-pgvector/operation-types'; - }; - }; - }; - readonly version: '0.0.1'; - }; - }; - readonly execution: { - readonly executionHash: ExecutionHash; - readonly mutations: { - readonly defaults: readonly [ - { - readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; - readonly ref: { - readonly entry: 'post'; - readonly field: 'id'; - readonly namespace: 'public'; - }; - }, - { - readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; - readonly ref: { - readonly entry: 'tag'; - readonly field: 'id'; - readonly namespace: 'public'; - }; - }, - { - readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; - readonly ref: { - readonly entry: 'task'; - readonly field: 'id'; - readonly namespace: 'public'; - }; - }, - { - readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; - readonly ref: { - readonly entry: 'user'; - readonly field: 'id'; - readonly namespace: 'public'; - }; - }, - ]; - }; - }; - readonly meta: {}; - readonly profileHash: ProfileHash; -}; - -export type Contract = ContractWithTypeMaps; - -export type Namespaces = Contract['storage']['namespaces']; diff --git a/examples/prisma-8-demo/migrations/snapshots/99a4393ee9f877db228baee1164325543fa018be780fd1253250d7d81539f38e/contract.json b/examples/prisma-8-demo/migrations/snapshots/99a4393ee9f877db228baee1164325543fa018be780fd1253250d7d81539f38e/contract.json deleted file mode 100644 index e646c1acd07b..000000000000 --- a/examples/prisma-8-demo/migrations/snapshots/99a4393ee9f877db228baee1164325543fa018be780fd1253250d7d81539f38e/contract.json +++ /dev/null @@ -1 +0,0 @@ -{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{"postgres":{"distinctOn":true,"forKeyShare":true,"forNoKeyUpdate":true,"jsonAgg":true,"lateral":true,"limit":true,"orderBy":true,"pgvector.cosine":true,"returning":true},"sql":{"checkConstraint":true,"defaultInInsert":true,"enums":true,"forShare":true,"forUpdate":true,"insertOnConflictSkip":true,"insertOnConflictWithoutTarget":true,"lateral":true,"lockNowait":true,"lockOf":true,"lockSkipLocked":true,"returning":true,"scalarList":true}},"domain":{"namespaces":{"public":{"enum":{"Priority":{"codecId":"pg/int4@1","members":[{"name":"Low","value":0},{"name":"High","value":1},{"name":"Urgent","value":2}]},"user_type":{"codecId":"pg/text@1","members":[{"name":"admin","value":"admin"},{"name":"user","value":"user"}]}},"models":{"Bug":{"base":{"model":"Task","namespace":"public"},"fields":{"severity":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"stepsToRepro":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"severity":{"column":"severity"},"stepsToRepro":{"column":"stepsToRepro"}},"namespaceId":"public","table":"bug"}},"Feature":{"base":{"model":"Task","namespace":"public"},"fields":{"priority":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"targetRelease":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"priority":{"column":"priority"},"targetRelease":{"column":"targetRelease"}},"namespaceId":"public","table":"feature"}},"Post":{"fields":{"body":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"embedding":{"nullable":true,"type":{"codecId":"pg/vector@1","kind":"scalar","typeParams":{"length":1536}}},"expiresAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"impressionCount":{"nullable":true,"type":{"codecId":"pg/int8@1","kind":"scalar"}},"priority":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"Priority","namespaceId":"public","plane":"domain"}},"reachScore":{"nullable":true,"type":{"codecId":"pg/unboundedint@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"viewCount":{"nullable":true,"type":{"codecId":"pg/int8number@1","kind":"scalar"}}},"relations":{"tags":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["postId"]},"through":{"childColumns":["tagId"],"namespaceId":"public","parentColumns":["postId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Tag","namespace":"public"}},"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"body":{"column":"body"},"createdAt":{"column":"createdAt"},"embedding":{"column":"embedding"},"expiresAt":{"column":"expiresAt"},"id":{"column":"id"},"impressionCount":{"column":"impressionCount"},"priority":{"column":"priority"},"reachScore":{"column":"reachScore"},"title":{"column":"title"},"userId":{"column":"userId"},"viewCount":{"column":"viewCount"}},"namespaceId":"public","table":"post"}},"PostTag":{"fields":{"postId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"tagId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"post":{"cardinality":"N:1","nullable":false,"on":{"localFields":["postId"],"targetFields":["id"]},"to":{"model":"Post","namespace":"public"}},"tag":{"cardinality":"N:1","nullable":false,"on":{"localFields":["tagId"],"targetFields":["id"]},"to":{"model":"Tag","namespace":"public"}}},"storage":{"fields":{"postId":{"column":"postId"},"tagId":{"column":"tagId"}},"namespaceId":"public","table":"post_tag"}},"Tag":{"fields":{"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"label":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{"posts":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["tagId"]},"through":{"childColumns":["postId"],"namespaceId":"public","parentColumns":["tagId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Post","namespace":"public"}}},"storage":{"fields":{"id":{"column":"id"},"label":{"column":"label"}},"namespaceId":"public","table":"tag"}},"Task":{"discriminator":{"field":"type"},"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"description":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"status":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"type":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"description":{"column":"description"},"id":{"column":"id"},"status":{"column":"status"},"title":{"column":"title"},"type":{"column":"type"},"userId":{"column":"userId"}},"namespaceId":"public","table":"task"},"variants":{"bug":{"model":"Bug","namespace":"public"},"feature":{"model":"Feature","namespace":"public"}}},"User":{"fields":{"address":{"nullable":true,"type":{"entityKind":"valueObject","entityName":"Address","kind":"valueObject","namespaceId":"public","plane":"storage"}},"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"displayName":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"user_type","namespaceId":"public","plane":"domain"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Post","namespace":"public"}},"tasks":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Task","namespace":"public"}}},"storage":{"fields":{"address":{"column":"address"},"createdAt":{"column":"createdAt"},"displayName":{"column":"displayName"},"email":{"column":"email"},"id":{"column":"id"},"kind":{"column":"kind"}},"namespaceId":"public","table":"user"}}}}}},"execution":{"executionHash":"eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655","mutations":{"defaults":[{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"post","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"tag","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"task","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"user","field":"id","namespace":"public"}}]}},"extensions":{"demo/engagement-stats":{"familyId":"sql","id":"demo/engagement-stats","kind":"extension","targetId":"postgres","types":{"aggregateDescriptors":[{"input":{"kind":"trait","trait":"numeric"},"nullable":true,"operation":"stddev","output":{"codecId":"pg/numeric@1","kind":"codec"}}]},"version":"0.0.1"},"pgvector":{"capabilities":{"postgres":{"pgvector.cosine":true}},"familyId":"sql","id":"pgvector","kind":"extension","targetId":"postgres","types":{"codecTypes":{"import":{"alias":"PgVectorTypes","named":"CodecTypes","package":"@internal/extension-pgvector/codec-types"},"parameterizedOutputTypes":{"alias":"PgVectorParameterizedOutputTypes","named":"ParameterizedOutputTypesForTypeParams","package":"@internal/extension-pgvector/codec-types"}},"operationTypes":{"import":{"alias":"PgVectorOperationTypes","named":"OperationTypes","package":"@internal/extension-pgvector/operation-types"}},"queryOperationTypes":{"import":{"alias":"PgVectorQueryOperationTypes","named":"QueryOperationTypes","package":"@internal/extension-pgvector/operation-types"}}},"version":"0.0.1"}},"meta":{},"profileHash":"3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2","roots":{"post":{"model":"Post","namespace":"public"},"post_tag":{"model":"PostTag","namespace":"public"},"tag":{"model":"Tag","namespace":"public"},"task":{"model":"Task","namespace":"public"},"user":{"model":"User","namespace":"public"}},"schemaVersion":"1","storage":{"namespaces":{"public":{"entries":{"table":{"bug":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"severity":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"stepsToRepro":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"bug"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"feature":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"priority":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"targetRelease":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"feature"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"post":{"checks":[{"expression":"\"priority\" IN (0, 1, 2)","name":"post_priority_check_b236ace9","prefix":"post_priority_check"}],"columns":{"body":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true},"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"embedding":{"codecId":"pg/vector@1","dataType":"pgvector/vector","nullable":true,"typeParams":{"length":1536}},"expiresAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"(now() + '7 days'::interval)","kind":"function"},"nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"impressionCount":{"codecId":"pg/int8@1","dataType":"pg/int8","nullable":true},"priority":{"codecId":"pg/int4@1","dataType":"pg/int4","default":{"kind":"literal","value":0},"nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Priority","namespaceId":"public","plane":"storage"}},"reachScore":{"codecId":"pg/unboundedint@1","dataType":"pg/numeric","nullable":true},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"viewCount":{"codecId":"pg/int8number@1","dataType":"pg/int8","nullable":true}},"foreignKeys":[{"index":{"name":"post_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"post"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["title","body"],"name":"post_search_f03270be","options":{"language":"english","weightGroups":[["title"],["body"]]},"prefix":"post_search","type":"fullText","unique":false},{"columns":["title"],"name":"post_title_search_e0dd1131","options":{"language":"english","weightGroups":[["title"]]},"prefix":"post_title_search","type":"fullText","unique":false},{"columns":["userId"],"name":"post_userId_idx_a489d58a","prefix":"post_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"post_tag":{"columns":{"postId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"tagId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"post_tag_postId_idx_a7a72715"},"source":{"columns":["postId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"post"}},{"index":{"name":"post_tag_tagId_idx_86854244"},"source":{"columns":["tagId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"tag"}}],"indexes":[{"columns":["postId"],"name":"post_tag_postId_idx_a7a72715","prefix":"post_tag_postId_idx","unique":false},{"columns":["tagId"],"name":"post_tag_tagId_idx_86854244","prefix":"post_tag_tagId_idx","unique":false}],"primaryKey":{"columns":["postId","tagId"]},"uniques":[]},"tag":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"label":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[{"columns":["label"]}]},"task":{"checks":[{"expression":"\"type\" IN ('bug', 'feature')","name":"task_type_check_cda3f5e7","prefix":"task_type_check"}],"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"description":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"status":{"codecId":"pg/text@1","dataType":"pg/text","default":{"kind":"literal","value":"open"},"nullable":false},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"type":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Task.type","namespaceId":"public","plane":"storage"}},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"task_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"task"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"task_userId_idx_a489d58a","prefix":"task_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"user":{"checks":[{"expression":"\"address\" IS NULL OR jsonb_path_match(\"address\", 'strict $.type() == \"null\" || ($.type() == \"object\" && exists($.keyvalue() ? (@.key == \"city\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"country\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"street\" && (@.value.type() == \"string\"))) && !exists($.keyvalue() ? (@.key == \"zip\" && !(@.value.type() == \"null\" || (@.value.type() == \"string\")))) && !exists($.keyvalue() ? (@.key != \"city\" && @.key != \"country\" && @.key != \"street\" && @.key != \"zip\")))') IS TRUE","name":"user_address_value_object_69432dc1","prefix":"user_address_value_object"},{"expression":"\"kind\" IN ('admin', 'user')","name":"user_kind_check_836d43ef","prefix":"user_kind_check"}],"columns":{"address":{"codecId":"pg/jsonb@1","dataType":"pg/jsonb","nullable":true},"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"displayName":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"email":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"kind":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"user_type","namespaceId":"public","plane":"storage"}}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]}},"valueObject":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}}}},"valueSet":{"Priority":{"kind":"valueSet","values":[0,1,2]},"Task.type":{"kind":"valueSet","values":["bug","feature"]},"user_type":{"kind":"valueSet","values":["admin","user"]}}},"id":"public","kind":"postgres-schema"}},"storageHash":"99a4393ee9f877db228baee1164325543fa018be780fd1253250d7d81539f38e"},"target":"postgres","targetFamily":"sql"} diff --git a/packages/2-sql-family/1-core/schema-ir/test/naming.test.ts b/packages/2-sql-family/1-core/schema-ir/test/naming.test.ts index 6948b226144b..ccb154d0d570 100644 --- a/packages/2-sql-family/1-core/schema-ir/test/naming.test.ts +++ b/packages/2-sql-family/1-core/schema-ir/test/naming.test.ts @@ -343,7 +343,7 @@ describe('derivedCheckPrefixes', () => { new Set([ 'custom_oauth_providers_acceptable_client_ids_check', 'custom_oauth_providers_acceptable_client_ids_elem_not_', - 'custom_oauth_providers_acceptable_client_ids_value_obj', + 'custom_oauth_providers_acceptable_client_ids_shape', ]), ); }); From 708aaea1114a2960e2f84b33dd4cc13e264a0eb4 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 19:03:16 +0200 Subject: [PATCH 082/104] TML-3537: re-emit contracts and the prisma-8-demo migrations with the _shape check names Signed-off-by: willbot Signed-off-by: Will Madden --- .../src/prisma/contract.d.ts | 2 +- .../src/prisma/contract.json | 6 +- .../app/20260422T0720_initial/migration.json | 4 +- .../app/20260422T0720_initial/migration.ts | 6 +- .../app/20260422T0720_initial/ops.json | 2 +- .../migration.json | 6 +- .../migration.ts | 8 +- .../migration.json | 6 +- .../migration.ts | 8 +- .../migration.json | 6 +- .../migration.ts | 8 +- .../migration.json | 6 +- .../migration.ts | 8 +- .../contract.d.ts | 1431 ++++++++++++++++ .../contract.json | 1 + .../contract.d.ts | 1420 ++++++++++++++++ .../contract.json | 1 + .../contract.d.ts | 1401 ++++++++++++++++ .../contract.json | 1 + .../contract.d.ts | 1454 +++++++++++++++++ .../contract.json | 1 + .../contract.d.ts | 1365 ++++++++++++++++ .../contract.json | 1 + .../prisma-8-demo/src/prisma/contract.d.ts | 2 +- .../prisma-8-demo/src/prisma/contract.json | 6 +- .../soft-delete/generated/contract.d.ts | 2 +- .../soft-delete/generated/contract.json | 6 +- .../value-objects/generated/contract.d.ts | 2 +- .../value-objects/generated/contract.json | 6 +- .../soft-delete/generated/contract.d.ts | 2 +- .../soft-delete/generated/contract.json | 6 +- .../value-objects/generated/contract.d.ts | 2 +- .../value-objects/generated/contract.json | 6 +- .../fixtures/generated/sql-contract.d.ts | 2 +- .../fixtures/generated/sql-contract.json | 10 +- 35 files changed, 7140 insertions(+), 64 deletions(-) create mode 100644 examples/prisma-8-demo/migrations/snapshots/53cf82d46a1c1c79995d8ea54e981886bbdce3d4cd9944506960643877c474f5/contract.d.ts create mode 100644 examples/prisma-8-demo/migrations/snapshots/53cf82d46a1c1c79995d8ea54e981886bbdce3d4cd9944506960643877c474f5/contract.json create mode 100644 examples/prisma-8-demo/migrations/snapshots/705398c87a4f2c7e2ac0a49002b9061963f726b8875ffe53da1feac8df00b040/contract.d.ts create mode 100644 examples/prisma-8-demo/migrations/snapshots/705398c87a4f2c7e2ac0a49002b9061963f726b8875ffe53da1feac8df00b040/contract.json create mode 100644 examples/prisma-8-demo/migrations/snapshots/db733e1df89f66886f7dd6a2ea54fc5be48e65d81aadd193d1e7f220ccf14f5e/contract.d.ts create mode 100644 examples/prisma-8-demo/migrations/snapshots/db733e1df89f66886f7dd6a2ea54fc5be48e65d81aadd193d1e7f220ccf14f5e/contract.json create mode 100644 examples/prisma-8-demo/migrations/snapshots/e1916bd73bb2388302d81a1eaa17125c0cedb6ce47b489a7adeeb41c2ef50ebf/contract.d.ts create mode 100644 examples/prisma-8-demo/migrations/snapshots/e1916bd73bb2388302d81a1eaa17125c0cedb6ce47b489a7adeeb41c2ef50ebf/contract.json create mode 100644 examples/prisma-8-demo/migrations/snapshots/fc039db97d116c1f5e6e56af25521c69e05232b49dc6e0058c9e3d3fb93eb263/contract.d.ts create mode 100644 examples/prisma-8-demo/migrations/snapshots/fc039db97d116c1f5e6e56af25521c69e05232b49dc6e0058c9e3d3fb93eb263/contract.json diff --git a/examples/prisma-8-cloudflare-worker/src/prisma/contract.d.ts b/examples/prisma-8-cloudflare-worker/src/prisma/contract.d.ts index 6ab584379d66..0803641ee61e 100644 --- a/examples/prisma-8-cloudflare-worker/src/prisma/contract.d.ts +++ b/examples/prisma-8-cloudflare-worker/src/prisma/contract.d.ts @@ -24,7 +24,7 @@ import type { } from '@prisma/orm-postgres/contract/types'; export type StorageHash = - StorageHashBase<'01135ceb8a6ba131344b2edb3a823effbca7c99558cc540281005bd3c9f093a6'>; + StorageHashBase<'aec35593db935d2bb2a8ce976972281fea82f22acbad88e81f6ec60b1bba210f'>; export type ExecutionHash = ExecutionHashBase<'0a7f0d7c621b64f307b872110f0effe4cd04d5410d04e913f6550ef107d46788'>; export type ProfileHash = diff --git a/examples/prisma-8-cloudflare-worker/src/prisma/contract.json b/examples/prisma-8-cloudflare-worker/src/prisma/contract.json index f33cf85c91e6..68c80593644d 100644 --- a/examples/prisma-8-cloudflare-worker/src/prisma/contract.json +++ b/examples/prisma-8-cloudflare-worker/src/prisma/contract.json @@ -667,8 +667,8 @@ "checks": [ { "expression": "\"address\" IS NULL OR jsonb_path_match(\"address\", 'strict $.type() == \"null\" || ($.type() == \"object\" && exists($.keyvalue() ? (@.key == \"city\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"country\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"street\" && (@.value.type() == \"string\"))) && !exists($.keyvalue() ? (@.key == \"zip\" && !(@.value.type() == \"null\" || (@.value.type() == \"string\")))) && !exists($.keyvalue() ? (@.key != \"city\" && @.key != \"country\" && @.key != \"street\" && @.key != \"zip\")))') IS TRUE", - "name": "user_address_value_object_69432dc1", - "prefix": "user_address_value_object" + "name": "user_address_shape_69432dc1", + "prefix": "user_address_shape" }, { "expression": "\"kind\" IN ('admin', 'user')", @@ -783,7 +783,7 @@ "kind": "postgres-schema" } }, - "storageHash": "01135ceb8a6ba131344b2edb3a823effbca7c99558cc540281005bd3c9f093a6" + "storageHash": "aec35593db935d2bb2a8ce976972281fea82f22acbad88e81f6ec60b1bba210f" }, "execution": { "executionHash": "0a7f0d7c621b64f307b872110f0effe4cd04d5410d04e913f6550ef107d46788", diff --git a/examples/prisma-8-demo/migrations/app/20260422T0720_initial/migration.json b/examples/prisma-8-demo/migrations/app/20260422T0720_initial/migration.json index f39fba49e8db..7997c3214ad8 100644 --- a/examples/prisma-8-demo/migrations/app/20260422T0720_initial/migration.json +++ b/examples/prisma-8-demo/migrations/app/20260422T0720_initial/migration.json @@ -1,7 +1,7 @@ { "from": null, - "to": "11d8695d1b8ea9c748834093ac4111ce68145827709363b7c748c2892be84146", + "to": "fc039db97d116c1f5e6e56af25521c69e05232b49dc6e0058c9e3d3fb93eb263", "providedInvariants": [], "createdAt": "2026-06-19T15:33:14.992Z", - "migrationHash": "649ede9e1ba6844054d945bf044bd63eea000c88617340e1206717a7cfe3e90a" + "migrationHash": "2c5c33804cfc36206f142a1ee2741fa1e5542244b9754cd569ceca6a6730d307" } diff --git a/examples/prisma-8-demo/migrations/app/20260422T0720_initial/migration.ts b/examples/prisma-8-demo/migrations/app/20260422T0720_initial/migration.ts index c76279819fad..c75627b0937b 100644 --- a/examples/prisma-8-demo/migrations/app/20260422T0720_initial/migration.ts +++ b/examples/prisma-8-demo/migrations/app/20260422T0720_initial/migration.ts @@ -10,8 +10,8 @@ import { rawSql, unique, } from '@prisma/orm-postgres/target/migration'; -import type { Contract as End } from '../../snapshots/11d8695d1b8ea9c748834093ac4111ce68145827709363b7c748c2892be84146/contract'; -import endContract from '../../snapshots/11d8695d1b8ea9c748834093ac4111ce68145827709363b7c748c2892be84146/contract.json' with { +import type { Contract as End } from '../../snapshots/fc039db97d116c1f5e6e56af25521c69e05232b49dc6e0058c9e3d3fb93eb263/contract'; +import endContract from '../../snapshots/fc039db97d116c1f5e6e56af25521c69e05232b49dc6e0058c9e3d3fb93eb263/contract.json' with { type: 'json', }; @@ -87,7 +87,7 @@ export default class M extends Migration { constraints: [ primaryKey(['id']), checkExpression( - 'user_address_value_object_69432dc1', + 'user_address_shape_69432dc1', '"address" IS NULL OR jsonb_path_match("address", \'strict $.type() == "null" || ($.type() == "object" && exists($.keyvalue() ? (@.key == "city" && (@.value.type() == "string"))) && exists($.keyvalue() ? (@.key == "country" && (@.value.type() == "string"))) && exists($.keyvalue() ? (@.key == "street" && (@.value.type() == "string"))) && !exists($.keyvalue() ? (@.key == "zip" && !(@.value.type() == "null" || (@.value.type() == "string")))) && !exists($.keyvalue() ? (@.key != "city" && @.key != "country" && @.key != "street" && @.key != "zip")))\') IS TRUE', ), checkExpression('user_kind_check_836d43ef', "\"kind\" IN ('admin', 'user')"), diff --git a/examples/prisma-8-demo/migrations/app/20260422T0720_initial/ops.json b/examples/prisma-8-demo/migrations/app/20260422T0720_initial/ops.json index 1e915303a888..90044b7cea51 100644 --- a/examples/prisma-8-demo/migrations/app/20260422T0720_initial/ops.json +++ b/examples/prisma-8-demo/migrations/app/20260422T0720_initial/ops.json @@ -190,7 +190,7 @@ "execute": [ { "description": "create table \"user\"", - "sql": "CREATE TABLE \"public\".\"user\" (\n \"address\" jsonb,\n \"createdAt\" timestamptz DEFAULT (now()) NOT NULL,\n \"displayName\" text NOT NULL,\n \"email\" text NOT NULL,\n \"id\" uuid NOT NULL,\n \"kind\" text NOT NULL,\n PRIMARY KEY (\"id\"),\n CONSTRAINT \"user_address_value_object_69432dc1\" CHECK (\"address\" IS NULL OR jsonb_path_match(\"address\", 'strict $.type() == \"null\" || ($.type() == \"object\" && exists($.keyvalue() ? (@.key == \"city\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"country\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"street\" && (@.value.type() == \"string\"))) && !exists($.keyvalue() ? (@.key == \"zip\" && !(@.value.type() == \"null\" || (@.value.type() == \"string\")))) && !exists($.keyvalue() ? (@.key != \"city\" && @.key != \"country\" && @.key != \"street\" && @.key != \"zip\")))') IS TRUE),\n CONSTRAINT \"user_kind_check_836d43ef\" CHECK (\"kind\" IN ('admin', 'user'))\n)", + "sql": "CREATE TABLE \"public\".\"user\" (\n \"address\" jsonb,\n \"createdAt\" timestamptz DEFAULT (now()) NOT NULL,\n \"displayName\" text NOT NULL,\n \"email\" text NOT NULL,\n \"id\" uuid NOT NULL,\n \"kind\" text NOT NULL,\n PRIMARY KEY (\"id\"),\n CONSTRAINT \"user_address_shape_69432dc1\" CHECK (\"address\" IS NULL OR jsonb_path_match(\"address\", 'strict $.type() == \"null\" || ($.type() == \"object\" && exists($.keyvalue() ? (@.key == \"city\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"country\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"street\" && (@.value.type() == \"string\"))) && !exists($.keyvalue() ? (@.key == \"zip\" && !(@.value.type() == \"null\" || (@.value.type() == \"string\")))) && !exists($.keyvalue() ? (@.key != \"city\" && @.key != \"country\" && @.key != \"street\" && @.key != \"zip\")))') IS TRUE),\n CONSTRAINT \"user_kind_check_836d43ef\" CHECK (\"kind\" IN ('admin', 'user'))\n)", "params": [] } ], diff --git a/examples/prisma-8-demo/migrations/app/20260810T1108_add_post_engagement_counters/migration.json b/examples/prisma-8-demo/migrations/app/20260810T1108_add_post_engagement_counters/migration.json index 7e7cf66be30b..e622cfcdf9d1 100644 --- a/examples/prisma-8-demo/migrations/app/20260810T1108_add_post_engagement_counters/migration.json +++ b/examples/prisma-8-demo/migrations/app/20260810T1108_add_post_engagement_counters/migration.json @@ -1,7 +1,7 @@ { - "from": "11d8695d1b8ea9c748834093ac4111ce68145827709363b7c748c2892be84146", - "to": "3c2324f6061012e9d50f869b3e6ae6aa41159acd559b1cc5459bc64e2ccb0f4e", + "from": "fc039db97d116c1f5e6e56af25521c69e05232b49dc6e0058c9e3d3fb93eb263", + "to": "db733e1df89f66886f7dd6a2ea54fc5be48e65d81aadd193d1e7f220ccf14f5e", "providedInvariants": [], "createdAt": "2026-08-10T11:08:15.318Z", - "migrationHash": "4fac9889c6dbe41bf57c3cab8c5757be2ae20cf3e5255e7680006b164252b235" + "migrationHash": "08c1ab86ecb9a102e4d319103d6351f63b264d37dfa6e955a8ae89b6db8084f8" } diff --git a/examples/prisma-8-demo/migrations/app/20260810T1108_add_post_engagement_counters/migration.ts b/examples/prisma-8-demo/migrations/app/20260810T1108_add_post_engagement_counters/migration.ts index 1598443c392f..5c8e267a2186 100755 --- a/examples/prisma-8-demo/migrations/app/20260810T1108_add_post_engagement_counters/migration.ts +++ b/examples/prisma-8-demo/migrations/app/20260810T1108_add_post_engagement_counters/migration.ts @@ -1,11 +1,11 @@ #!/usr/bin/env -S node import { col, Migration, MigrationCLI } from '@prisma/orm-postgres/migration'; -import type { Contract as End } from '../../snapshots/3c2324f6061012e9d50f869b3e6ae6aa41159acd559b1cc5459bc64e2ccb0f4e/contract'; -import endContract from '../../snapshots/3c2324f6061012e9d50f869b3e6ae6aa41159acd559b1cc5459bc64e2ccb0f4e/contract.json' with { +import type { Contract as End } from '../../snapshots/db733e1df89f66886f7dd6a2ea54fc5be48e65d81aadd193d1e7f220ccf14f5e/contract'; +import endContract from '../../snapshots/db733e1df89f66886f7dd6a2ea54fc5be48e65d81aadd193d1e7f220ccf14f5e/contract.json' with { type: 'json', }; -import type { Contract as Start } from '../../snapshots/11d8695d1b8ea9c748834093ac4111ce68145827709363b7c748c2892be84146/contract'; -import startContract from '../../snapshots/11d8695d1b8ea9c748834093ac4111ce68145827709363b7c748c2892be84146/contract.json' with { +import type { Contract as Start } from '../../snapshots/fc039db97d116c1f5e6e56af25521c69e05232b49dc6e0058c9e3d3fb93eb263/contract'; +import startContract from '../../snapshots/fc039db97d116c1f5e6e56af25521c69e05232b49dc6e0058c9e3d3fb93eb263/contract.json' with { type: 'json', }; diff --git a/examples/prisma-8-demo/migrations/app/20260917T0818_add_post_expires_at/migration.json b/examples/prisma-8-demo/migrations/app/20260917T0818_add_post_expires_at/migration.json index dc3290d54471..3fec93e8daab 100644 --- a/examples/prisma-8-demo/migrations/app/20260917T0818_add_post_expires_at/migration.json +++ b/examples/prisma-8-demo/migrations/app/20260917T0818_add_post_expires_at/migration.json @@ -1,7 +1,7 @@ { - "from": "3c2324f6061012e9d50f869b3e6ae6aa41159acd559b1cc5459bc64e2ccb0f4e", - "to": "1c819419d4357de21d9aa9404a0b1d3639e3bd33c3fe6176207c53b51365fa1f", + "from": "db733e1df89f66886f7dd6a2ea54fc5be48e65d81aadd193d1e7f220ccf14f5e", + "to": "705398c87a4f2c7e2ac0a49002b9061963f726b8875ffe53da1feac8df00b040", "providedInvariants": [], "createdAt": "2026-09-17T08:18:38.345Z", - "migrationHash": "eca5ce6d981ff0b55c6213e7f9ed947e73ba9dad012f372b9b02b024b2526309" + "migrationHash": "6d20b99b0fcf0814942532ee11ba97378a74fa794b964e2258b41163bc7911c7" } diff --git a/examples/prisma-8-demo/migrations/app/20260917T0818_add_post_expires_at/migration.ts b/examples/prisma-8-demo/migrations/app/20260917T0818_add_post_expires_at/migration.ts index f7e4583551fd..d335973ad496 100755 --- a/examples/prisma-8-demo/migrations/app/20260917T0818_add_post_expires_at/migration.ts +++ b/examples/prisma-8-demo/migrations/app/20260917T0818_add_post_expires_at/migration.ts @@ -1,11 +1,11 @@ #!/usr/bin/env -S node import { col, fn, Migration, MigrationCLI } from '@prisma/orm-postgres/migration'; -import type { Contract as End } from '../../snapshots/1c819419d4357de21d9aa9404a0b1d3639e3bd33c3fe6176207c53b51365fa1f/contract'; -import endContract from '../../snapshots/1c819419d4357de21d9aa9404a0b1d3639e3bd33c3fe6176207c53b51365fa1f/contract.json' with { +import type { Contract as End } from '../../snapshots/705398c87a4f2c7e2ac0a49002b9061963f726b8875ffe53da1feac8df00b040/contract'; +import endContract from '../../snapshots/705398c87a4f2c7e2ac0a49002b9061963f726b8875ffe53da1feac8df00b040/contract.json' with { type: 'json', }; -import type { Contract as Start } from '../../snapshots/3c2324f6061012e9d50f869b3e6ae6aa41159acd559b1cc5459bc64e2ccb0f4e/contract'; -import startContract from '../../snapshots/3c2324f6061012e9d50f869b3e6ae6aa41159acd559b1cc5459bc64e2ccb0f4e/contract.json' with { +import type { Contract as Start } from '../../snapshots/db733e1df89f66886f7dd6a2ea54fc5be48e65d81aadd193d1e7f220ccf14f5e/contract'; +import startContract from '../../snapshots/db733e1df89f66886f7dd6a2ea54fc5be48e65d81aadd193d1e7f220ccf14f5e/contract.json' with { type: 'json', }; diff --git a/examples/prisma-8-demo/migrations/app/20260922T1218_add_post_title_search/migration.json b/examples/prisma-8-demo/migrations/app/20260922T1218_add_post_title_search/migration.json index b3d9e977bd4a..4bd71245bc65 100644 --- a/examples/prisma-8-demo/migrations/app/20260922T1218_add_post_title_search/migration.json +++ b/examples/prisma-8-demo/migrations/app/20260922T1218_add_post_title_search/migration.json @@ -1,7 +1,7 @@ { - "from": "1c819419d4357de21d9aa9404a0b1d3639e3bd33c3fe6176207c53b51365fa1f", - "to": "9301c8a3b2ec83c89b4eb12568ccb0c9e916ba199d8743ab38ed468483d6921f", + "from": "705398c87a4f2c7e2ac0a49002b9061963f726b8875ffe53da1feac8df00b040", + "to": "53cf82d46a1c1c79995d8ea54e981886bbdce3d4cd9944506960643877c474f5", "providedInvariants": [], "createdAt": "2026-09-22T12:18:55.150Z", - "migrationHash": "ac4b947f7363460ecaea06bf886099b04082020c0c02d791fa6a872bb14534e8" + "migrationHash": "cbe96b340e61c87aa4bb148032c9500140473569e820f4d0f4c29e960f27cda0" } diff --git a/examples/prisma-8-demo/migrations/app/20260922T1218_add_post_title_search/migration.ts b/examples/prisma-8-demo/migrations/app/20260922T1218_add_post_title_search/migration.ts index c1c68f9cbd85..43660ecfa642 100755 --- a/examples/prisma-8-demo/migrations/app/20260922T1218_add_post_title_search/migration.ts +++ b/examples/prisma-8-demo/migrations/app/20260922T1218_add_post_title_search/migration.ts @@ -1,11 +1,11 @@ #!/usr/bin/env -S node import { Migration, MigrationCLI } from '@prisma/orm-postgres/migration'; -import type { Contract as Start } from '../../snapshots/1c819419d4357de21d9aa9404a0b1d3639e3bd33c3fe6176207c53b51365fa1f/contract'; -import startContract from '../../snapshots/1c819419d4357de21d9aa9404a0b1d3639e3bd33c3fe6176207c53b51365fa1f/contract.json' with { +import type { Contract as Start } from '../../snapshots/705398c87a4f2c7e2ac0a49002b9061963f726b8875ffe53da1feac8df00b040/contract'; +import startContract from '../../snapshots/705398c87a4f2c7e2ac0a49002b9061963f726b8875ffe53da1feac8df00b040/contract.json' with { type: 'json', }; -import type { Contract as End } from '../../snapshots/9301c8a3b2ec83c89b4eb12568ccb0c9e916ba199d8743ab38ed468483d6921f/contract'; -import endContract from '../../snapshots/9301c8a3b2ec83c89b4eb12568ccb0c9e916ba199d8743ab38ed468483d6921f/contract.json' with { +import type { Contract as End } from '../../snapshots/53cf82d46a1c1c79995d8ea54e981886bbdce3d4cd9944506960643877c474f5/contract'; +import endContract from '../../snapshots/53cf82d46a1c1c79995d8ea54e981886bbdce3d4cd9944506960643877c474f5/contract.json' with { type: 'json', }; diff --git a/examples/prisma-8-demo/migrations/app/20261009T1354_add_post_body_search/migration.json b/examples/prisma-8-demo/migrations/app/20261009T1354_add_post_body_search/migration.json index 88bce7dc68e6..8ec5fb6bd543 100644 --- a/examples/prisma-8-demo/migrations/app/20261009T1354_add_post_body_search/migration.json +++ b/examples/prisma-8-demo/migrations/app/20261009T1354_add_post_body_search/migration.json @@ -1,7 +1,7 @@ { - "from": "9301c8a3b2ec83c89b4eb12568ccb0c9e916ba199d8743ab38ed468483d6921f", - "to": "99a4393ee9f877db228baee1164325543fa018be780fd1253250d7d81539f38e", + "from": "53cf82d46a1c1c79995d8ea54e981886bbdce3d4cd9944506960643877c474f5", + "to": "e1916bd73bb2388302d81a1eaa17125c0cedb6ce47b489a7adeeb41c2ef50ebf", "providedInvariants": [], "createdAt": "2026-10-09T13:54:52.344Z", - "migrationHash": "523dbd078828411759a63f37d5155bba4c75ebc38d158d985a43802bd5121d90" + "migrationHash": "8d96c62996481cbd9466a0b3df3ff8686345b0343f384bc21d0a7d36114634fe" } diff --git a/examples/prisma-8-demo/migrations/app/20261009T1354_add_post_body_search/migration.ts b/examples/prisma-8-demo/migrations/app/20261009T1354_add_post_body_search/migration.ts index ab1c27daa02e..074d9344b649 100755 --- a/examples/prisma-8-demo/migrations/app/20261009T1354_add_post_body_search/migration.ts +++ b/examples/prisma-8-demo/migrations/app/20261009T1354_add_post_body_search/migration.ts @@ -1,11 +1,11 @@ #!/usr/bin/env -S node import { col, Migration, MigrationCLI } from '@prisma/orm-postgres/migration'; -import type { Contract as End } from '../../snapshots/99a4393ee9f877db228baee1164325543fa018be780fd1253250d7d81539f38e/contract'; -import endContract from '../../snapshots/99a4393ee9f877db228baee1164325543fa018be780fd1253250d7d81539f38e/contract.json' with { +import type { Contract as End } from '../../snapshots/e1916bd73bb2388302d81a1eaa17125c0cedb6ce47b489a7adeeb41c2ef50ebf/contract'; +import endContract from '../../snapshots/e1916bd73bb2388302d81a1eaa17125c0cedb6ce47b489a7adeeb41c2ef50ebf/contract.json' with { type: 'json', }; -import type { Contract as Start } from '../../snapshots/9301c8a3b2ec83c89b4eb12568ccb0c9e916ba199d8743ab38ed468483d6921f/contract'; -import startContract from '../../snapshots/9301c8a3b2ec83c89b4eb12568ccb0c9e916ba199d8743ab38ed468483d6921f/contract.json' with { +import type { Contract as Start } from '../../snapshots/53cf82d46a1c1c79995d8ea54e981886bbdce3d4cd9944506960643877c474f5/contract'; +import startContract from '../../snapshots/53cf82d46a1c1c79995d8ea54e981886bbdce3d4cd9944506960643877c474f5/contract.json' with { type: 'json', }; diff --git a/examples/prisma-8-demo/migrations/snapshots/53cf82d46a1c1c79995d8ea54e981886bbdce3d4cd9944506960643877c474f5/contract.d.ts b/examples/prisma-8-demo/migrations/snapshots/53cf82d46a1c1c79995d8ea54e981886bbdce3d4cd9944506960643877c474f5/contract.d.ts new file mode 100644 index 000000000000..0129d1913fa2 --- /dev/null +++ b/examples/prisma-8-demo/migrations/snapshots/53cf82d46a1c1c79995d8ea54e981886bbdce3d4cd9944506960643877c474f5/contract.d.ts @@ -0,0 +1,1431 @@ +// ⚠️ GENERATED FILE - DO NOT EDIT +// This file is automatically generated by 'prisma contract emit'. +// To regenerate, run: prisma contract emit +import type { + CodecTypes as PgVectorTypes, + ParameterizedOutputTypesForTypeParams as PgVectorParameterizedOutputTypes, +} from '@prisma/orm-extension-pgvector/codec-types'; +import type { QueryOperationTypes as PgVectorQueryOperationTypes } from '@prisma/orm-extension-pgvector/operation-types'; +import type { + CodecTypes as PgTypes, + ParameterizedOutputTypesForTypeParams as PgParameterizedOutputTypes, +} from '@prisma/orm-postgres/target/codec-types'; +import type { QueryOperationTypes as PgTargetQueryOps } from '@prisma/orm-postgres/target/operation-types'; + +import type { + ColumnTypesFromStorage, + ContractWithTypeMaps, + RelationKeys, + TypeMaps as TypeMapsType, +} from '@prisma/orm-postgres/family-contract/types'; +import type { + Contract as ContractType, + ExecutionHashBase, + FieldTypesFromDomain, + NamespaceId, + ProfileHashBase, + StorageHashBase, + ValueObjectTypesFromStorage, +} from '@prisma/orm-postgres/contract/types'; + +export type StorageHash = + StorageHashBase<'53cf82d46a1c1c79995d8ea54e981886bbdce3d4cd9944506960643877c474f5'>; +export type ExecutionHash = + ExecutionHashBase<'eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655'>; +export type ProfileHash = + ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; + +export type CodecTypes = PgTypes & PgVectorTypes; +export type LaneCodecTypes = CodecTypes; +export type QueryOperationTypes = PgTargetQueryOps & + PgVectorQueryOperationTypes; +export type AggregateTypes = { + readonly avg: { + readonly byCodec: { + readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + }; + }; + readonly avgDecimal: { + readonly byCodec: { + readonly 'pg/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + }; + }; + readonly count: { + readonly byCodec: {}; + readonly withoutInput: { readonly output: 'pg/int8number@1'; readonly nullable: false }; + readonly anyInput: { readonly output: 'pg/int8number@1'; readonly nullable: false }; + }; + readonly countBigInt: { + readonly byCodec: {}; + readonly withoutInput: { readonly output: 'pg/int8@1'; readonly nullable: false }; + readonly anyInput: { readonly output: 'pg/int8@1'; readonly nullable: false }; + }; + readonly max: { + readonly byCodec: { + readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true }; + readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true }; + readonly 'pg/date-temporal@1': { + readonly output: 'pg/date-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true }; + readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; + readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { + readonly output: 'pg/time-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-string@1': { + readonly output: 'pg/timestamp-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-temporal@1': { + readonly output: 'pg/timestamp-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-date@1': { + readonly output: 'pg/timestamptz-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-string@1': { + readonly output: 'pg/timestamptz-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-temporal@1': { + readonly output: 'pg/timestamptz-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true }; + readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true }; + readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + }; + }; + readonly min: { + readonly byCodec: { + readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true }; + readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true }; + readonly 'pg/date-temporal@1': { + readonly output: 'pg/date-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true }; + readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; + readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { + readonly output: 'pg/time-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-string@1': { + readonly output: 'pg/timestamp-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-temporal@1': { + readonly output: 'pg/timestamp-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-date@1': { + readonly output: 'pg/timestamptz-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-string@1': { + readonly output: 'pg/timestamptz-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-temporal@1': { + readonly output: 'pg/timestamptz-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true }; + readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true }; + readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + }; + }; + readonly stddev: { + readonly byCodec: { + readonly 'pg/float@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + }; + }; + readonly sum: { + readonly byCodec: { + readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + }; + }; + readonly sumBigInt: { + readonly byCodec: { + readonly 'pg/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'sql/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + }; + }; +}; +type DefaultLiteralValue = CodecId extends keyof CodecTypes + ? Encoded extends CodecTypes[CodecId]['json'] + ? Encoded + : CodecTypes[CodecId]['json'] + : Encoded; +type ParameterizedOutputTypeFunctions = + PgParameterizedOutputTypes | PgVectorParameterizedOutputTypes; +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output' +>; +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input' +>; +export type AddressOutput = ValueObjectOutputTypes['public']['Address']; +export type AddressInput = ValueObjectInputTypes['public']['Address']; +type FieldTypeOverrides = { + readonly public: { + readonly models: { + readonly Post: { + readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 }; + }; + readonly Task: { + readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; + }; + readonly User: { + readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; + }; + }; + }; +}; +export type FieldOutputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output', + ContractBase['storage']['namespaces'] +>; +export type FieldInputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input', + ContractBase['storage']['namespaces'] +>; +type ColumnTypeOverrides = { + readonly public: { + readonly post: { readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 } }; + readonly task: { + readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; + }; + readonly user: { + readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; + }; + }; +}; +export type StorageColumnTypes = ColumnTypesFromStorage< + ContractBase['storage'], + CodecTypes, + ParameterizedOutputTypeFunctions, + ColumnTypeOverrides, + 'output' +>; +export type StorageColumnInputTypes = ColumnTypesFromStorage< + ContractBase['storage'], + CodecTypes, + ParameterizedOutputTypeFunctions, + ColumnTypeOverrides, + 'input' +>; + +export namespace Models { + export type public_Bug = { + createdAt: FieldOutputTypes['public']['Task']['createdAt']; + description: FieldOutputTypes['public']['Task']['description']; + id: FieldOutputTypes['public']['Task']['id']; + status: FieldOutputTypes['public']['Task']['status']; + title: FieldOutputTypes['public']['Task']['title']; + type: 'bug'; + userId: FieldOutputTypes['public']['Task']['userId']; + severity: FieldOutputTypes['public']['Bug']['severity']; + stepsToRepro: FieldOutputTypes['public']['Bug']['stepsToRepro']; + user: public_User; + readonly [RelationKeys]?: 'user'; + }; + export type public_Feature = { + createdAt: FieldOutputTypes['public']['Task']['createdAt']; + description: FieldOutputTypes['public']['Task']['description']; + id: FieldOutputTypes['public']['Task']['id']; + status: FieldOutputTypes['public']['Task']['status']; + title: FieldOutputTypes['public']['Task']['title']; + type: 'feature'; + userId: FieldOutputTypes['public']['Task']['userId']; + priority: FieldOutputTypes['public']['Feature']['priority']; + targetRelease: FieldOutputTypes['public']['Feature']['targetRelease']; + user: public_User; + readonly [RelationKeys]?: 'user'; + }; + export type public_Post = { + createdAt: FieldOutputTypes['public']['Post']['createdAt']; + embedding: FieldOutputTypes['public']['Post']['embedding']; + expiresAt: FieldOutputTypes['public']['Post']['expiresAt']; + id: FieldOutputTypes['public']['Post']['id']; + impressionCount: FieldOutputTypes['public']['Post']['impressionCount']; + priority: FieldOutputTypes['public']['Post']['priority']; + reachScore: FieldOutputTypes['public']['Post']['reachScore']; + title: FieldOutputTypes['public']['Post']['title']; + userId: FieldOutputTypes['public']['Post']['userId']; + viewCount: FieldOutputTypes['public']['Post']['viewCount']; + tags: public_Tag[]; + user: public_User; + readonly [RelationKeys]?: 'tags' | 'user'; + }; + export type public_PostTag = { + postId: FieldOutputTypes['public']['PostTag']['postId']; + tagId: FieldOutputTypes['public']['PostTag']['tagId']; + post: public_Post; + tag: public_Tag; + readonly [RelationKeys]?: 'post' | 'tag'; + }; + export type public_Tag = { + id: FieldOutputTypes['public']['Tag']['id']; + label: FieldOutputTypes['public']['Tag']['label']; + posts: public_Post[]; + readonly [RelationKeys]?: 'posts'; + }; + export type public_Task = { + createdAt: FieldOutputTypes['public']['Task']['createdAt']; + description: FieldOutputTypes['public']['Task']['description']; + id: FieldOutputTypes['public']['Task']['id']; + status: FieldOutputTypes['public']['Task']['status']; + title: FieldOutputTypes['public']['Task']['title']; + type: 'bug' | 'feature'; + userId: FieldOutputTypes['public']['Task']['userId']; + user: public_User; + readonly [RelationKeys]?: 'user'; + }; + export type public_User = { + address: FieldOutputTypes['public']['User']['address']; + createdAt: FieldOutputTypes['public']['User']['createdAt']; + displayName: FieldOutputTypes['public']['User']['displayName']; + email: FieldOutputTypes['public']['User']['email']; + id: FieldOutputTypes['public']['User']['id']; + kind: FieldOutputTypes['public']['User']['kind']; + posts: public_Post[]; + tasks: public_AnyTask[]; + readonly [RelationKeys]?: 'posts' | 'tasks'; + }; + export type public_AnyTask = public_Bug | public_Feature; +} + +export declare const models: { + public: { + Bug: Models.public_Bug; + Feature: Models.public_Feature; + Post: Models.public_Post; + PostTag: Models.public_PostTag; + Tag: Models.public_Tag; + Task: Models.public_Task; + User: Models.public_User; + AnyTask: Models.public_AnyTask; + }; +}; + +export type TypeMaps = TypeMapsType< + CodecTypes, + QueryOperationTypes, + FieldOutputTypes, + FieldInputTypes, + StorageColumnTypes, + StorageColumnInputTypes, + AggregateTypes +>; + +type ContractBase = Omit< + ContractType<{ + readonly namespaces: { + readonly public: { + readonly id: 'public'; + readonly kind: 'postgres-schema'; + readonly entries: { + readonly table: { + readonly bug: { + columns: { + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly severity: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly stepsToRepro: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: true; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'bug'; + readonly columns: readonly ['id']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'task'; + readonly columns: readonly ['id']; + }; + }, + ]; + }; + readonly feature: { + columns: { + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly priority: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly targetRelease: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: true; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'feature'; + readonly columns: readonly ['id']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'task'; + readonly columns: readonly ['id']; + }; + }, + ]; + }; + readonly post: { + columns: { + readonly createdAt: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-temporal@1'; + readonly nullable: false; + readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; + readonly many: false; + }; + readonly embedding: { + readonly dataType: 'pgvector/vector'; + readonly codecId: 'pg/vector@1'; + readonly nullable: true; + readonly typeParams: { readonly length: 1536 }; + readonly many: false; + }; + readonly expiresAt: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-temporal@1'; + readonly nullable: false; + readonly default: { + readonly kind: 'function'; + readonly expression: "(now() + '7 days'::interval)"; + }; + readonly many: false; + }; + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly impressionCount: { + readonly dataType: 'pg/int8'; + readonly codecId: 'pg/int8@1'; + readonly nullable: true; + readonly many: false; + }; + readonly priority: { + readonly dataType: 'pg/int4'; + readonly codecId: 'pg/int4@1'; + readonly nullable: false; + readonly default: { + readonly kind: 'literal'; + readonly value: DefaultLiteralValue<'pg/int4@1', 0>; + }; + readonly many: false; + }; + readonly reachScore: { + readonly dataType: 'pg/numeric'; + readonly codecId: 'pg/unboundedint@1'; + readonly nullable: true; + readonly many: false; + }; + readonly title: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly userId: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly viewCount: { + readonly dataType: 'pg/int8'; + readonly codecId: 'pg/int8number@1'; + readonly nullable: true; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly [ + { + readonly name: 'post_title_search_e0dd1131'; + readonly prefix: 'post_title_search'; + readonly columns: readonly ['title']; + readonly unique: false; + readonly type: 'fullText'; + readonly options: { + readonly language: 'english'; + readonly weightGroups: readonly [readonly ['title']]; + }; + }, + { + readonly name: 'post_userId_idx_a489d58a'; + readonly prefix: 'post_userId_idx'; + readonly columns: readonly ['userId']; + readonly unique: false; + }, + ]; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'post'; + readonly columns: readonly ['userId']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'user'; + readonly columns: readonly ['id']; + }; + }, + ]; + }; + readonly post_tag: { + columns: { + readonly postId: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly tagId: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['postId', 'tagId'] }; + uniques: readonly []; + indexes: readonly [ + { + readonly name: 'post_tag_postId_idx_a7a72715'; + readonly prefix: 'post_tag_postId_idx'; + readonly columns: readonly ['postId']; + readonly unique: false; + }, + { + readonly name: 'post_tag_tagId_idx_86854244'; + readonly prefix: 'post_tag_tagId_idx'; + readonly columns: readonly ['tagId']; + readonly unique: false; + }, + ]; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'post_tag'; + readonly columns: readonly ['postId']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'post'; + readonly columns: readonly ['id']; + }; + }, + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'post_tag'; + readonly columns: readonly ['tagId']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'tag'; + readonly columns: readonly ['id']; + }; + }, + ]; + }; + readonly tag: { + columns: { + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly label: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly [{ readonly columns: readonly ['label'] }]; + indexes: readonly []; + foreignKeys: readonly []; + }; + readonly task: { + columns: { + readonly createdAt: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-temporal@1'; + readonly nullable: false; + readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; + readonly many: false; + }; + readonly description: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: true; + readonly many: false; + }; + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly status: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly default: { + readonly kind: 'literal'; + readonly value: DefaultLiteralValue<'pg/text@1', 'open'>; + }; + readonly many: false; + }; + readonly title: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly type: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly userId: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly [ + { + readonly name: 'task_userId_idx_a489d58a'; + readonly prefix: 'task_userId_idx'; + readonly columns: readonly ['userId']; + readonly unique: false; + }, + ]; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'task'; + readonly columns: readonly ['userId']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'user'; + readonly columns: readonly ['id']; + }; + }, + ]; + }; + readonly user: { + columns: { + readonly address: { + readonly dataType: 'pg/jsonb'; + readonly codecId: 'pg/jsonb@1'; + readonly nullable: true; + readonly many: false; + }; + readonly createdAt: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-temporal@1'; + readonly nullable: false; + readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; + readonly many: false; + }; + readonly displayName: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly email: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly kind: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly []; + }; + }; + readonly valueSet: { + readonly Priority: { readonly kind: 'valueSet'; readonly values: readonly [0, 1, 2] }; + readonly 'Task.type': { + readonly kind: 'valueSet'; + readonly values: readonly ['bug', 'feature']; + }; + readonly user_type: { + readonly kind: 'valueSet'; + readonly values: readonly ['admin', 'user']; + }; + }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + }; + }; + }; + }; + }; + readonly storageHash: StorageHash; + }>, + 'roots' | 'domain' +> & { + readonly target: 'postgres'; + readonly targetFamily: 'sql'; + readonly roots: { + readonly post: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; + readonly post_tag: { readonly namespace: 'public' & NamespaceId; readonly model: 'PostTag' }; + readonly tag: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; + readonly task: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; + readonly user: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; + }; + readonly domain: { + readonly namespaces: { + readonly public: { + readonly models: { + readonly Bug: { + readonly fields: { + readonly severity: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly stepsToRepro: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'bug'; + readonly namespaceId: 'public'; + readonly fields: { + readonly severity: { readonly column: 'severity' }; + readonly stepsToRepro: { readonly column: 'stepsToRepro' }; + }; + }; + readonly base: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; + }; + readonly Feature: { + readonly fields: { + readonly priority: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly targetRelease: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'feature'; + readonly namespaceId: 'public'; + readonly fields: { + readonly priority: { readonly column: 'priority' }; + readonly targetRelease: { readonly column: 'targetRelease' }; + }; + }; + readonly base: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; + }; + readonly Post: { + readonly fields: { + readonly createdAt: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-temporal@1'; + }; + }; + readonly embedding: { + readonly nullable: true; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/vector@1'; + readonly typeParams: { readonly length: 1536 }; + }; + }; + readonly expiresAt: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-temporal@1'; + }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly impressionCount: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int8@1' }; + }; + readonly priority: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int4@1' }; + }; + readonly reachScore: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/unboundedint@1' }; + }; + readonly title: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly userId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly viewCount: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int8number@1' }; + }; + }; + readonly relations: { + readonly tags: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; + readonly cardinality: 'N:M'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['postId']; + }; + readonly through: { + readonly table: 'post_tag'; + readonly namespaceId: 'public'; + readonly parentColumns: readonly ['postId']; + readonly childColumns: readonly ['tagId']; + readonly targetColumns: readonly ['id']; + }; + }; + readonly user: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['userId']; + readonly targetFields: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly table: 'post'; + readonly namespaceId: 'public'; + readonly fields: { + readonly createdAt: { readonly column: 'createdAt' }; + readonly embedding: { readonly column: 'embedding' }; + readonly expiresAt: { readonly column: 'expiresAt' }; + readonly id: { readonly column: 'id' }; + readonly impressionCount: { readonly column: 'impressionCount' }; + readonly priority: { readonly column: 'priority' }; + readonly reachScore: { readonly column: 'reachScore' }; + readonly title: { readonly column: 'title' }; + readonly userId: { readonly column: 'userId' }; + readonly viewCount: { readonly column: 'viewCount' }; + }; + }; + }; + readonly PostTag: { + readonly fields: { + readonly postId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly tagId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + }; + readonly relations: { + readonly post: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['postId']; + readonly targetFields: readonly ['id']; + }; + }; + readonly tag: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['tagId']; + readonly targetFields: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly table: 'post_tag'; + readonly namespaceId: 'public'; + readonly fields: { + readonly postId: { readonly column: 'postId' }; + readonly tagId: { readonly column: 'tagId' }; + }; + }; + }; + readonly Tag: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly label: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: { + readonly posts: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; + readonly cardinality: 'N:M'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['tagId']; + }; + readonly through: { + readonly table: 'post_tag'; + readonly namespaceId: 'public'; + readonly parentColumns: readonly ['tagId']; + readonly childColumns: readonly ['postId']; + readonly targetColumns: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly table: 'tag'; + readonly namespaceId: 'public'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly label: { readonly column: 'label' }; + }; + }; + }; + readonly Task: { + readonly fields: { + readonly createdAt: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-temporal@1'; + }; + }; + readonly description: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly status: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly title: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly type: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly userId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + }; + readonly relations: { + readonly user: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['userId']; + readonly targetFields: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly table: 'task'; + readonly namespaceId: 'public'; + readonly fields: { + readonly createdAt: { readonly column: 'createdAt' }; + readonly description: { readonly column: 'description' }; + readonly id: { readonly column: 'id' }; + readonly status: { readonly column: 'status' }; + readonly title: { readonly column: 'title' }; + readonly type: { readonly column: 'type' }; + readonly userId: { readonly column: 'userId' }; + }; + }; + readonly discriminator: { readonly field: 'type' }; + readonly variants: { + readonly bug: { readonly namespace: 'public' & NamespaceId; readonly model: 'Bug' }; + readonly feature: { + readonly namespace: 'public' & NamespaceId; + readonly model: 'Feature'; + }; + }; + }; + readonly User: { + readonly fields: { + readonly address: { + readonly nullable: true; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: 'public'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; + }; + readonly createdAt: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-temporal@1'; + }; + }; + readonly displayName: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly kind: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: { + readonly posts: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; + readonly cardinality: '1:N'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['userId']; + }; + }; + readonly tasks: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; + readonly cardinality: '1:N'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['userId']; + }; + }; + }; + readonly storage: { + readonly table: 'user'; + readonly namespaceId: 'public'; + readonly fields: { + readonly address: { readonly column: 'address' }; + readonly createdAt: { readonly column: 'createdAt' }; + readonly displayName: { readonly column: 'displayName' }; + readonly email: { readonly column: 'email' }; + readonly id: { readonly column: 'id' }; + readonly kind: { readonly column: 'kind' }; + }; + }; + }; + }; + readonly enum: { + readonly Priority: { + readonly codecId: 'pg/int4@1'; + readonly members: readonly [ + { readonly name: 'Low'; readonly value: 0 }, + { readonly name: 'High'; readonly value: 1 }, + { readonly name: 'Urgent'; readonly value: 2 }, + ]; + }; + readonly user_type: { + readonly codecId: 'pg/text@1'; + readonly members: readonly [ + { readonly name: 'admin'; readonly value: 'admin' }, + { readonly name: 'user'; readonly value: 'user' }, + ]; + }; + }; + readonly enumMemberTypes?: { + readonly Priority: readonly [ + { readonly name: 'Low'; readonly value: 0 }, + { readonly name: 'High'; readonly value: 1 }, + { readonly name: 'Urgent'; readonly value: 2 }, + ]; + readonly user_type: readonly [ + { readonly name: 'admin'; readonly value: 'admin' }, + { readonly name: 'user'; readonly value: 'user' }, + ]; + }; + }; + }; + }; + readonly capabilities: { + readonly postgres: { + readonly distinctOn: true; + readonly forKeyShare: true; + readonly forNoKeyUpdate: true; + readonly jsonAgg: true; + readonly lateral: true; + readonly limit: true; + readonly orderBy: true; + readonly 'pgvector.cosine': true; + readonly returning: true; + }; + readonly sql: { + readonly checkConstraint: true; + readonly defaultInInsert: true; + readonly enums: true; + readonly forShare: true; + readonly forUpdate: true; + readonly insertOnConflictSkip: true; + readonly insertOnConflictWithoutTarget: true; + readonly lateral: true; + readonly lockNowait: true; + readonly lockOf: true; + readonly lockSkipLocked: true; + readonly returning: true; + readonly scalarList: true; + }; + }; + readonly extensions: { + readonly 'demo/engagement-stats': { + readonly familyId: 'sql'; + readonly id: 'demo/engagement-stats'; + readonly kind: 'extension'; + readonly targetId: 'postgres'; + readonly types: { + readonly aggregateDescriptors: readonly [ + { + readonly input: { readonly kind: 'trait'; readonly trait: 'numeric' }; + readonly lower: unknown; + readonly nullable: true; + readonly operation: 'stddev'; + readonly output: { readonly codecId: 'pg/numeric@1'; readonly kind: 'codec' }; + }, + ]; + }; + readonly version: '0.0.1'; + }; + readonly pgvector: { + readonly capabilities: { readonly postgres: { readonly 'pgvector.cosine': true } }; + readonly familyId: 'sql'; + readonly id: 'pgvector'; + readonly kind: 'extension'; + readonly targetId: 'postgres'; + readonly types: { + readonly codecTypes: { + readonly import: { + readonly alias: 'PgVectorTypes'; + readonly named: 'CodecTypes'; + readonly package: '@internal/extension-pgvector/codec-types'; + }; + readonly parameterizedOutputTypes: { + readonly alias: 'PgVectorParameterizedOutputTypes'; + readonly named: 'ParameterizedOutputTypesForTypeParams'; + readonly package: '@internal/extension-pgvector/codec-types'; + }; + }; + readonly operationTypes: { + readonly import: { + readonly alias: 'PgVectorOperationTypes'; + readonly named: 'OperationTypes'; + readonly package: '@internal/extension-pgvector/operation-types'; + }; + }; + readonly queryOperationTypes: { + readonly import: { + readonly alias: 'PgVectorQueryOperationTypes'; + readonly named: 'QueryOperationTypes'; + readonly package: '@internal/extension-pgvector/operation-types'; + }; + }; + }; + readonly version: '0.0.1'; + }; + }; + readonly execution: { + readonly executionHash: ExecutionHash; + readonly mutations: { + readonly defaults: readonly [ + { + readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; + readonly ref: { + readonly entry: 'post'; + readonly field: 'id'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; + readonly ref: { + readonly entry: 'tag'; + readonly field: 'id'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; + readonly ref: { + readonly entry: 'task'; + readonly field: 'id'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; + readonly ref: { + readonly entry: 'user'; + readonly field: 'id'; + readonly namespace: 'public'; + }; + }, + ]; + }; + }; + readonly meta: {}; + readonly profileHash: ProfileHash; +}; + +export type Contract = ContractWithTypeMaps; + +export type Namespaces = Contract['storage']['namespaces']; diff --git a/examples/prisma-8-demo/migrations/snapshots/53cf82d46a1c1c79995d8ea54e981886bbdce3d4cd9944506960643877c474f5/contract.json b/examples/prisma-8-demo/migrations/snapshots/53cf82d46a1c1c79995d8ea54e981886bbdce3d4cd9944506960643877c474f5/contract.json new file mode 100644 index 000000000000..6bf3b40e6ed7 --- /dev/null +++ b/examples/prisma-8-demo/migrations/snapshots/53cf82d46a1c1c79995d8ea54e981886bbdce3d4cd9944506960643877c474f5/contract.json @@ -0,0 +1 @@ +{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{"postgres":{"distinctOn":true,"forKeyShare":true,"forNoKeyUpdate":true,"jsonAgg":true,"lateral":true,"limit":true,"orderBy":true,"pgvector.cosine":true,"returning":true},"sql":{"checkConstraint":true,"defaultInInsert":true,"enums":true,"forShare":true,"forUpdate":true,"insertOnConflictSkip":true,"insertOnConflictWithoutTarget":true,"lateral":true,"lockNowait":true,"lockOf":true,"lockSkipLocked":true,"returning":true,"scalarList":true}},"domain":{"namespaces":{"public":{"enum":{"Priority":{"codecId":"pg/int4@1","members":[{"name":"Low","value":0},{"name":"High","value":1},{"name":"Urgent","value":2}]},"user_type":{"codecId":"pg/text@1","members":[{"name":"admin","value":"admin"},{"name":"user","value":"user"}]}},"models":{"Bug":{"base":{"model":"Task","namespace":"public"},"fields":{"severity":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"stepsToRepro":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"severity":{"column":"severity"},"stepsToRepro":{"column":"stepsToRepro"}},"namespaceId":"public","table":"bug"}},"Feature":{"base":{"model":"Task","namespace":"public"},"fields":{"priority":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"targetRelease":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"priority":{"column":"priority"},"targetRelease":{"column":"targetRelease"}},"namespaceId":"public","table":"feature"}},"Post":{"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"embedding":{"nullable":true,"type":{"codecId":"pg/vector@1","kind":"scalar","typeParams":{"length":1536}}},"expiresAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"impressionCount":{"nullable":true,"type":{"codecId":"pg/int8@1","kind":"scalar"}},"priority":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"Priority","namespaceId":"public","plane":"domain"}},"reachScore":{"nullable":true,"type":{"codecId":"pg/unboundedint@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"viewCount":{"nullable":true,"type":{"codecId":"pg/int8number@1","kind":"scalar"}}},"relations":{"tags":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["postId"]},"through":{"childColumns":["tagId"],"namespaceId":"public","parentColumns":["postId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Tag","namespace":"public"}},"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"embedding":{"column":"embedding"},"expiresAt":{"column":"expiresAt"},"id":{"column":"id"},"impressionCount":{"column":"impressionCount"},"priority":{"column":"priority"},"reachScore":{"column":"reachScore"},"title":{"column":"title"},"userId":{"column":"userId"},"viewCount":{"column":"viewCount"}},"namespaceId":"public","table":"post"}},"PostTag":{"fields":{"postId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"tagId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"post":{"cardinality":"N:1","nullable":false,"on":{"localFields":["postId"],"targetFields":["id"]},"to":{"model":"Post","namespace":"public"}},"tag":{"cardinality":"N:1","nullable":false,"on":{"localFields":["tagId"],"targetFields":["id"]},"to":{"model":"Tag","namespace":"public"}}},"storage":{"fields":{"postId":{"column":"postId"},"tagId":{"column":"tagId"}},"namespaceId":"public","table":"post_tag"}},"Tag":{"fields":{"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"label":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{"posts":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["tagId"]},"through":{"childColumns":["postId"],"namespaceId":"public","parentColumns":["tagId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Post","namespace":"public"}}},"storage":{"fields":{"id":{"column":"id"},"label":{"column":"label"}},"namespaceId":"public","table":"tag"}},"Task":{"discriminator":{"field":"type"},"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"description":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"status":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"type":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"description":{"column":"description"},"id":{"column":"id"},"status":{"column":"status"},"title":{"column":"title"},"type":{"column":"type"},"userId":{"column":"userId"}},"namespaceId":"public","table":"task"},"variants":{"bug":{"model":"Bug","namespace":"public"},"feature":{"model":"Feature","namespace":"public"}}},"User":{"fields":{"address":{"nullable":true,"type":{"entityKind":"valueObject","entityName":"Address","kind":"valueObject","namespaceId":"public","plane":"storage"}},"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"displayName":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"user_type","namespaceId":"public","plane":"domain"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Post","namespace":"public"}},"tasks":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Task","namespace":"public"}}},"storage":{"fields":{"address":{"column":"address"},"createdAt":{"column":"createdAt"},"displayName":{"column":"displayName"},"email":{"column":"email"},"id":{"column":"id"},"kind":{"column":"kind"}},"namespaceId":"public","table":"user"}}}}}},"execution":{"executionHash":"eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655","mutations":{"defaults":[{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"post","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"tag","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"task","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"user","field":"id","namespace":"public"}}]}},"extensions":{"demo/engagement-stats":{"familyId":"sql","id":"demo/engagement-stats","kind":"extension","targetId":"postgres","types":{"aggregateDescriptors":[{"input":{"kind":"trait","trait":"numeric"},"nullable":true,"operation":"stddev","output":{"codecId":"pg/numeric@1","kind":"codec"}}]},"version":"0.0.1"},"pgvector":{"capabilities":{"postgres":{"pgvector.cosine":true}},"familyId":"sql","id":"pgvector","kind":"extension","targetId":"postgres","types":{"codecTypes":{"import":{"alias":"PgVectorTypes","named":"CodecTypes","package":"@internal/extension-pgvector/codec-types"},"parameterizedOutputTypes":{"alias":"PgVectorParameterizedOutputTypes","named":"ParameterizedOutputTypesForTypeParams","package":"@internal/extension-pgvector/codec-types"}},"operationTypes":{"import":{"alias":"PgVectorOperationTypes","named":"OperationTypes","package":"@internal/extension-pgvector/operation-types"}},"queryOperationTypes":{"import":{"alias":"PgVectorQueryOperationTypes","named":"QueryOperationTypes","package":"@internal/extension-pgvector/operation-types"}}},"version":"0.0.1"}},"meta":{},"profileHash":"3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2","roots":{"post":{"model":"Post","namespace":"public"},"post_tag":{"model":"PostTag","namespace":"public"},"tag":{"model":"Tag","namespace":"public"},"task":{"model":"Task","namespace":"public"},"user":{"model":"User","namespace":"public"}},"schemaVersion":"1","storage":{"namespaces":{"public":{"entries":{"table":{"bug":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"severity":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"stepsToRepro":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"bug"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"feature":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"priority":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"targetRelease":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"feature"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"post":{"checks":[{"expression":"\"priority\" IN (0, 1, 2)","name":"post_priority_check_b236ace9","prefix":"post_priority_check"}],"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"embedding":{"codecId":"pg/vector@1","dataType":"pgvector/vector","nullable":true,"typeParams":{"length":1536}},"expiresAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"(now() + '7 days'::interval)","kind":"function"},"nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"impressionCount":{"codecId":"pg/int8@1","dataType":"pg/int8","nullable":true},"priority":{"codecId":"pg/int4@1","dataType":"pg/int4","default":{"kind":"literal","value":0},"nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Priority","namespaceId":"public","plane":"storage"}},"reachScore":{"codecId":"pg/unboundedint@1","dataType":"pg/numeric","nullable":true},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"viewCount":{"codecId":"pg/int8number@1","dataType":"pg/int8","nullable":true}},"foreignKeys":[{"index":{"name":"post_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"post"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["title"],"name":"post_title_search_e0dd1131","options":{"language":"english","weightGroups":[["title"]]},"prefix":"post_title_search","type":"fullText","unique":false},{"columns":["userId"],"name":"post_userId_idx_a489d58a","prefix":"post_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"post_tag":{"columns":{"postId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"tagId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"post_tag_postId_idx_a7a72715"},"source":{"columns":["postId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"post"}},{"index":{"name":"post_tag_tagId_idx_86854244"},"source":{"columns":["tagId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"tag"}}],"indexes":[{"columns":["postId"],"name":"post_tag_postId_idx_a7a72715","prefix":"post_tag_postId_idx","unique":false},{"columns":["tagId"],"name":"post_tag_tagId_idx_86854244","prefix":"post_tag_tagId_idx","unique":false}],"primaryKey":{"columns":["postId","tagId"]},"uniques":[]},"tag":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"label":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[{"columns":["label"]}]},"task":{"checks":[{"expression":"\"type\" IN ('bug', 'feature')","name":"task_type_check_cda3f5e7","prefix":"task_type_check"}],"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"description":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"status":{"codecId":"pg/text@1","dataType":"pg/text","default":{"kind":"literal","value":"open"},"nullable":false},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"type":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Task.type","namespaceId":"public","plane":"storage"}},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"task_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"task"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"task_userId_idx_a489d58a","prefix":"task_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"user":{"checks":[{"expression":"\"address\" IS NULL OR jsonb_path_match(\"address\", 'strict $.type() == \"null\" || ($.type() == \"object\" && exists($.keyvalue() ? (@.key == \"city\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"country\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"street\" && (@.value.type() == \"string\"))) && !exists($.keyvalue() ? (@.key == \"zip\" && !(@.value.type() == \"null\" || (@.value.type() == \"string\")))) && !exists($.keyvalue() ? (@.key != \"city\" && @.key != \"country\" && @.key != \"street\" && @.key != \"zip\")))') IS TRUE","name":"user_address_shape_69432dc1","prefix":"user_address_shape"},{"expression":"\"kind\" IN ('admin', 'user')","name":"user_kind_check_836d43ef","prefix":"user_kind_check"}],"columns":{"address":{"codecId":"pg/jsonb@1","dataType":"pg/jsonb","nullable":true},"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"displayName":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"email":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"kind":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"user_type","namespaceId":"public","plane":"storage"}}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]}},"valueObject":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}}}},"valueSet":{"Priority":{"kind":"valueSet","values":[0,1,2]},"Task.type":{"kind":"valueSet","values":["bug","feature"]},"user_type":{"kind":"valueSet","values":["admin","user"]}}},"id":"public","kind":"postgres-schema"}},"storageHash":"53cf82d46a1c1c79995d8ea54e981886bbdce3d4cd9944506960643877c474f5"},"target":"postgres","targetFamily":"sql"} diff --git a/examples/prisma-8-demo/migrations/snapshots/705398c87a4f2c7e2ac0a49002b9061963f726b8875ffe53da1feac8df00b040/contract.d.ts b/examples/prisma-8-demo/migrations/snapshots/705398c87a4f2c7e2ac0a49002b9061963f726b8875ffe53da1feac8df00b040/contract.d.ts new file mode 100644 index 000000000000..cb836967d516 --- /dev/null +++ b/examples/prisma-8-demo/migrations/snapshots/705398c87a4f2c7e2ac0a49002b9061963f726b8875ffe53da1feac8df00b040/contract.d.ts @@ -0,0 +1,1420 @@ +// ⚠️ GENERATED FILE - DO NOT EDIT +// This file is automatically generated by 'prisma contract emit'. +// To regenerate, run: prisma contract emit +import type { + CodecTypes as PgVectorTypes, + ParameterizedOutputTypesForTypeParams as PgVectorParameterizedOutputTypes, +} from '@prisma/orm-extension-pgvector/codec-types'; +import type { QueryOperationTypes as PgVectorQueryOperationTypes } from '@prisma/orm-extension-pgvector/operation-types'; +import type { + CodecTypes as PgTypes, + ParameterizedOutputTypesForTypeParams as PgParameterizedOutputTypes, +} from '@prisma/orm-postgres/target/codec-types'; +import type { QueryOperationTypes as PgTargetQueryOps } from '@prisma/orm-postgres/target/operation-types'; + +import type { + ColumnTypesFromStorage, + ContractWithTypeMaps, + RelationKeys, + TypeMaps as TypeMapsType, +} from '@prisma/orm-postgres/family-contract/types'; +import type { + Contract as ContractType, + ExecutionHashBase, + FieldTypesFromDomain, + NamespaceId, + ProfileHashBase, + StorageHashBase, + ValueObjectTypesFromStorage, +} from '@prisma/orm-postgres/contract/types'; + +export type StorageHash = + StorageHashBase<'705398c87a4f2c7e2ac0a49002b9061963f726b8875ffe53da1feac8df00b040'>; +export type ExecutionHash = + ExecutionHashBase<'eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655'>; +export type ProfileHash = + ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; + +export type CodecTypes = PgTypes & PgVectorTypes; +export type LaneCodecTypes = CodecTypes; +export type QueryOperationTypes = PgTargetQueryOps & + PgVectorQueryOperationTypes; +export type AggregateTypes = { + readonly avg: { + readonly byCodec: { + readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + }; + }; + readonly avgDecimal: { + readonly byCodec: { + readonly 'pg/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + }; + }; + readonly count: { + readonly byCodec: {}; + readonly withoutInput: { readonly output: 'pg/int8number@1'; readonly nullable: false }; + readonly anyInput: { readonly output: 'pg/int8number@1'; readonly nullable: false }; + }; + readonly countBigInt: { + readonly byCodec: {}; + readonly withoutInput: { readonly output: 'pg/int8@1'; readonly nullable: false }; + readonly anyInput: { readonly output: 'pg/int8@1'; readonly nullable: false }; + }; + readonly max: { + readonly byCodec: { + readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true }; + readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true }; + readonly 'pg/date-temporal@1': { + readonly output: 'pg/date-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true }; + readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; + readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { + readonly output: 'pg/time-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-string@1': { + readonly output: 'pg/timestamp-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-temporal@1': { + readonly output: 'pg/timestamp-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-date@1': { + readonly output: 'pg/timestamptz-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-string@1': { + readonly output: 'pg/timestamptz-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-temporal@1': { + readonly output: 'pg/timestamptz-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true }; + readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true }; + readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + }; + }; + readonly min: { + readonly byCodec: { + readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true }; + readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true }; + readonly 'pg/date-temporal@1': { + readonly output: 'pg/date-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true }; + readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; + readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { + readonly output: 'pg/time-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-string@1': { + readonly output: 'pg/timestamp-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-temporal@1': { + readonly output: 'pg/timestamp-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-date@1': { + readonly output: 'pg/timestamptz-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-string@1': { + readonly output: 'pg/timestamptz-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-temporal@1': { + readonly output: 'pg/timestamptz-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true }; + readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true }; + readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + }; + }; + readonly stddev: { + readonly byCodec: { + readonly 'pg/float@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + }; + }; + readonly sum: { + readonly byCodec: { + readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + }; + }; + readonly sumBigInt: { + readonly byCodec: { + readonly 'pg/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'sql/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + }; + }; +}; +type DefaultLiteralValue = CodecId extends keyof CodecTypes + ? Encoded extends CodecTypes[CodecId]['json'] + ? Encoded + : CodecTypes[CodecId]['json'] + : Encoded; +type ParameterizedOutputTypeFunctions = + PgParameterizedOutputTypes | PgVectorParameterizedOutputTypes; +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output' +>; +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input' +>; +export type AddressOutput = ValueObjectOutputTypes['public']['Address']; +export type AddressInput = ValueObjectInputTypes['public']['Address']; +type FieldTypeOverrides = { + readonly public: { + readonly models: { + readonly Post: { + readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 }; + }; + readonly Task: { + readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; + }; + readonly User: { + readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; + }; + }; + }; +}; +export type FieldOutputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output', + ContractBase['storage']['namespaces'] +>; +export type FieldInputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input', + ContractBase['storage']['namespaces'] +>; +type ColumnTypeOverrides = { + readonly public: { + readonly post: { readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 } }; + readonly task: { + readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; + }; + readonly user: { + readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; + }; + }; +}; +export type StorageColumnTypes = ColumnTypesFromStorage< + ContractBase['storage'], + CodecTypes, + ParameterizedOutputTypeFunctions, + ColumnTypeOverrides, + 'output' +>; +export type StorageColumnInputTypes = ColumnTypesFromStorage< + ContractBase['storage'], + CodecTypes, + ParameterizedOutputTypeFunctions, + ColumnTypeOverrides, + 'input' +>; + +export namespace Models { + export type public_Bug = { + createdAt: FieldOutputTypes['public']['Task']['createdAt']; + description: FieldOutputTypes['public']['Task']['description']; + id: FieldOutputTypes['public']['Task']['id']; + status: FieldOutputTypes['public']['Task']['status']; + title: FieldOutputTypes['public']['Task']['title']; + type: 'bug'; + userId: FieldOutputTypes['public']['Task']['userId']; + severity: FieldOutputTypes['public']['Bug']['severity']; + stepsToRepro: FieldOutputTypes['public']['Bug']['stepsToRepro']; + user: public_User; + readonly [RelationKeys]?: 'user'; + }; + export type public_Feature = { + createdAt: FieldOutputTypes['public']['Task']['createdAt']; + description: FieldOutputTypes['public']['Task']['description']; + id: FieldOutputTypes['public']['Task']['id']; + status: FieldOutputTypes['public']['Task']['status']; + title: FieldOutputTypes['public']['Task']['title']; + type: 'feature'; + userId: FieldOutputTypes['public']['Task']['userId']; + priority: FieldOutputTypes['public']['Feature']['priority']; + targetRelease: FieldOutputTypes['public']['Feature']['targetRelease']; + user: public_User; + readonly [RelationKeys]?: 'user'; + }; + export type public_Post = { + createdAt: FieldOutputTypes['public']['Post']['createdAt']; + embedding: FieldOutputTypes['public']['Post']['embedding']; + expiresAt: FieldOutputTypes['public']['Post']['expiresAt']; + id: FieldOutputTypes['public']['Post']['id']; + impressionCount: FieldOutputTypes['public']['Post']['impressionCount']; + priority: FieldOutputTypes['public']['Post']['priority']; + reachScore: FieldOutputTypes['public']['Post']['reachScore']; + title: FieldOutputTypes['public']['Post']['title']; + userId: FieldOutputTypes['public']['Post']['userId']; + viewCount: FieldOutputTypes['public']['Post']['viewCount']; + tags: public_Tag[]; + user: public_User; + readonly [RelationKeys]?: 'tags' | 'user'; + }; + export type public_PostTag = { + postId: FieldOutputTypes['public']['PostTag']['postId']; + tagId: FieldOutputTypes['public']['PostTag']['tagId']; + post: public_Post; + tag: public_Tag; + readonly [RelationKeys]?: 'post' | 'tag'; + }; + export type public_Tag = { + id: FieldOutputTypes['public']['Tag']['id']; + label: FieldOutputTypes['public']['Tag']['label']; + posts: public_Post[]; + readonly [RelationKeys]?: 'posts'; + }; + export type public_Task = { + createdAt: FieldOutputTypes['public']['Task']['createdAt']; + description: FieldOutputTypes['public']['Task']['description']; + id: FieldOutputTypes['public']['Task']['id']; + status: FieldOutputTypes['public']['Task']['status']; + title: FieldOutputTypes['public']['Task']['title']; + type: 'bug' | 'feature'; + userId: FieldOutputTypes['public']['Task']['userId']; + user: public_User; + readonly [RelationKeys]?: 'user'; + }; + export type public_User = { + address: FieldOutputTypes['public']['User']['address']; + createdAt: FieldOutputTypes['public']['User']['createdAt']; + displayName: FieldOutputTypes['public']['User']['displayName']; + email: FieldOutputTypes['public']['User']['email']; + id: FieldOutputTypes['public']['User']['id']; + kind: FieldOutputTypes['public']['User']['kind']; + posts: public_Post[]; + tasks: public_AnyTask[]; + readonly [RelationKeys]?: 'posts' | 'tasks'; + }; + export type public_AnyTask = public_Bug | public_Feature; +} + +export declare const models: { + public: { + Bug: Models.public_Bug; + Feature: Models.public_Feature; + Post: Models.public_Post; + PostTag: Models.public_PostTag; + Tag: Models.public_Tag; + Task: Models.public_Task; + User: Models.public_User; + AnyTask: Models.public_AnyTask; + }; +}; + +export type TypeMaps = TypeMapsType< + CodecTypes, + QueryOperationTypes, + FieldOutputTypes, + FieldInputTypes, + StorageColumnTypes, + StorageColumnInputTypes, + AggregateTypes +>; + +type ContractBase = Omit< + ContractType<{ + readonly namespaces: { + readonly public: { + readonly id: 'public'; + readonly kind: 'postgres-schema'; + readonly entries: { + readonly table: { + readonly bug: { + columns: { + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly severity: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly stepsToRepro: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: true; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'bug'; + readonly columns: readonly ['id']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'task'; + readonly columns: readonly ['id']; + }; + }, + ]; + }; + readonly feature: { + columns: { + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly priority: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly targetRelease: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: true; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'feature'; + readonly columns: readonly ['id']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'task'; + readonly columns: readonly ['id']; + }; + }, + ]; + }; + readonly post: { + columns: { + readonly createdAt: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-temporal@1'; + readonly nullable: false; + readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; + readonly many: false; + }; + readonly embedding: { + readonly dataType: 'pgvector/vector'; + readonly codecId: 'pg/vector@1'; + readonly nullable: true; + readonly typeParams: { readonly length: 1536 }; + readonly many: false; + }; + readonly expiresAt: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-temporal@1'; + readonly nullable: false; + readonly default: { + readonly kind: 'function'; + readonly expression: "(now() + '7 days'::interval)"; + }; + readonly many: false; + }; + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly impressionCount: { + readonly dataType: 'pg/int8'; + readonly codecId: 'pg/int8@1'; + readonly nullable: true; + readonly many: false; + }; + readonly priority: { + readonly dataType: 'pg/int4'; + readonly codecId: 'pg/int4@1'; + readonly nullable: false; + readonly default: { + readonly kind: 'literal'; + readonly value: DefaultLiteralValue<'pg/int4@1', 0>; + }; + readonly many: false; + }; + readonly reachScore: { + readonly dataType: 'pg/numeric'; + readonly codecId: 'pg/unboundedint@1'; + readonly nullable: true; + readonly many: false; + }; + readonly title: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly userId: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly viewCount: { + readonly dataType: 'pg/int8'; + readonly codecId: 'pg/int8number@1'; + readonly nullable: true; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly [ + { + readonly name: 'post_userId_idx_a489d58a'; + readonly prefix: 'post_userId_idx'; + readonly columns: readonly ['userId']; + readonly unique: false; + }, + ]; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'post'; + readonly columns: readonly ['userId']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'user'; + readonly columns: readonly ['id']; + }; + }, + ]; + }; + readonly post_tag: { + columns: { + readonly postId: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly tagId: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['postId', 'tagId'] }; + uniques: readonly []; + indexes: readonly [ + { + readonly name: 'post_tag_postId_idx_a7a72715'; + readonly prefix: 'post_tag_postId_idx'; + readonly columns: readonly ['postId']; + readonly unique: false; + }, + { + readonly name: 'post_tag_tagId_idx_86854244'; + readonly prefix: 'post_tag_tagId_idx'; + readonly columns: readonly ['tagId']; + readonly unique: false; + }, + ]; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'post_tag'; + readonly columns: readonly ['postId']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'post'; + readonly columns: readonly ['id']; + }; + }, + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'post_tag'; + readonly columns: readonly ['tagId']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'tag'; + readonly columns: readonly ['id']; + }; + }, + ]; + }; + readonly tag: { + columns: { + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly label: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly [{ readonly columns: readonly ['label'] }]; + indexes: readonly []; + foreignKeys: readonly []; + }; + readonly task: { + columns: { + readonly createdAt: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-temporal@1'; + readonly nullable: false; + readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; + readonly many: false; + }; + readonly description: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: true; + readonly many: false; + }; + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly status: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly default: { + readonly kind: 'literal'; + readonly value: DefaultLiteralValue<'pg/text@1', 'open'>; + }; + readonly many: false; + }; + readonly title: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly type: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly userId: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly [ + { + readonly name: 'task_userId_idx_a489d58a'; + readonly prefix: 'task_userId_idx'; + readonly columns: readonly ['userId']; + readonly unique: false; + }, + ]; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'task'; + readonly columns: readonly ['userId']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'user'; + readonly columns: readonly ['id']; + }; + }, + ]; + }; + readonly user: { + columns: { + readonly address: { + readonly dataType: 'pg/jsonb'; + readonly codecId: 'pg/jsonb@1'; + readonly nullable: true; + readonly many: false; + }; + readonly createdAt: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-temporal@1'; + readonly nullable: false; + readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; + readonly many: false; + }; + readonly displayName: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly email: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly kind: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly []; + }; + }; + readonly valueSet: { + readonly Priority: { readonly kind: 'valueSet'; readonly values: readonly [0, 1, 2] }; + readonly 'Task.type': { + readonly kind: 'valueSet'; + readonly values: readonly ['bug', 'feature']; + }; + readonly user_type: { + readonly kind: 'valueSet'; + readonly values: readonly ['admin', 'user']; + }; + }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + }; + }; + }; + }; + }; + readonly storageHash: StorageHash; + }>, + 'roots' | 'domain' +> & { + readonly target: 'postgres'; + readonly targetFamily: 'sql'; + readonly roots: { + readonly post: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; + readonly post_tag: { readonly namespace: 'public' & NamespaceId; readonly model: 'PostTag' }; + readonly tag: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; + readonly task: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; + readonly user: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; + }; + readonly domain: { + readonly namespaces: { + readonly public: { + readonly models: { + readonly Bug: { + readonly fields: { + readonly severity: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly stepsToRepro: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'bug'; + readonly namespaceId: 'public'; + readonly fields: { + readonly severity: { readonly column: 'severity' }; + readonly stepsToRepro: { readonly column: 'stepsToRepro' }; + }; + }; + readonly base: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; + }; + readonly Feature: { + readonly fields: { + readonly priority: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly targetRelease: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'feature'; + readonly namespaceId: 'public'; + readonly fields: { + readonly priority: { readonly column: 'priority' }; + readonly targetRelease: { readonly column: 'targetRelease' }; + }; + }; + readonly base: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; + }; + readonly Post: { + readonly fields: { + readonly createdAt: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-temporal@1'; + }; + }; + readonly embedding: { + readonly nullable: true; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/vector@1'; + readonly typeParams: { readonly length: 1536 }; + }; + }; + readonly expiresAt: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-temporal@1'; + }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly impressionCount: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int8@1' }; + }; + readonly priority: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int4@1' }; + }; + readonly reachScore: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/unboundedint@1' }; + }; + readonly title: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly userId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly viewCount: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int8number@1' }; + }; + }; + readonly relations: { + readonly tags: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; + readonly cardinality: 'N:M'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['postId']; + }; + readonly through: { + readonly table: 'post_tag'; + readonly namespaceId: 'public'; + readonly parentColumns: readonly ['postId']; + readonly childColumns: readonly ['tagId']; + readonly targetColumns: readonly ['id']; + }; + }; + readonly user: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['userId']; + readonly targetFields: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly table: 'post'; + readonly namespaceId: 'public'; + readonly fields: { + readonly createdAt: { readonly column: 'createdAt' }; + readonly embedding: { readonly column: 'embedding' }; + readonly expiresAt: { readonly column: 'expiresAt' }; + readonly id: { readonly column: 'id' }; + readonly impressionCount: { readonly column: 'impressionCount' }; + readonly priority: { readonly column: 'priority' }; + readonly reachScore: { readonly column: 'reachScore' }; + readonly title: { readonly column: 'title' }; + readonly userId: { readonly column: 'userId' }; + readonly viewCount: { readonly column: 'viewCount' }; + }; + }; + }; + readonly PostTag: { + readonly fields: { + readonly postId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly tagId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + }; + readonly relations: { + readonly post: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['postId']; + readonly targetFields: readonly ['id']; + }; + }; + readonly tag: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['tagId']; + readonly targetFields: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly table: 'post_tag'; + readonly namespaceId: 'public'; + readonly fields: { + readonly postId: { readonly column: 'postId' }; + readonly tagId: { readonly column: 'tagId' }; + }; + }; + }; + readonly Tag: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly label: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: { + readonly posts: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; + readonly cardinality: 'N:M'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['tagId']; + }; + readonly through: { + readonly table: 'post_tag'; + readonly namespaceId: 'public'; + readonly parentColumns: readonly ['tagId']; + readonly childColumns: readonly ['postId']; + readonly targetColumns: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly table: 'tag'; + readonly namespaceId: 'public'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly label: { readonly column: 'label' }; + }; + }; + }; + readonly Task: { + readonly fields: { + readonly createdAt: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-temporal@1'; + }; + }; + readonly description: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly status: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly title: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly type: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly userId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + }; + readonly relations: { + readonly user: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['userId']; + readonly targetFields: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly table: 'task'; + readonly namespaceId: 'public'; + readonly fields: { + readonly createdAt: { readonly column: 'createdAt' }; + readonly description: { readonly column: 'description' }; + readonly id: { readonly column: 'id' }; + readonly status: { readonly column: 'status' }; + readonly title: { readonly column: 'title' }; + readonly type: { readonly column: 'type' }; + readonly userId: { readonly column: 'userId' }; + }; + }; + readonly discriminator: { readonly field: 'type' }; + readonly variants: { + readonly bug: { readonly namespace: 'public' & NamespaceId; readonly model: 'Bug' }; + readonly feature: { + readonly namespace: 'public' & NamespaceId; + readonly model: 'Feature'; + }; + }; + }; + readonly User: { + readonly fields: { + readonly address: { + readonly nullable: true; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: 'public'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; + }; + readonly createdAt: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-temporal@1'; + }; + }; + readonly displayName: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly kind: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: { + readonly posts: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; + readonly cardinality: '1:N'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['userId']; + }; + }; + readonly tasks: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; + readonly cardinality: '1:N'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['userId']; + }; + }; + }; + readonly storage: { + readonly table: 'user'; + readonly namespaceId: 'public'; + readonly fields: { + readonly address: { readonly column: 'address' }; + readonly createdAt: { readonly column: 'createdAt' }; + readonly displayName: { readonly column: 'displayName' }; + readonly email: { readonly column: 'email' }; + readonly id: { readonly column: 'id' }; + readonly kind: { readonly column: 'kind' }; + }; + }; + }; + }; + readonly enum: { + readonly Priority: { + readonly codecId: 'pg/int4@1'; + readonly members: readonly [ + { readonly name: 'Low'; readonly value: 0 }, + { readonly name: 'High'; readonly value: 1 }, + { readonly name: 'Urgent'; readonly value: 2 }, + ]; + }; + readonly user_type: { + readonly codecId: 'pg/text@1'; + readonly members: readonly [ + { readonly name: 'admin'; readonly value: 'admin' }, + { readonly name: 'user'; readonly value: 'user' }, + ]; + }; + }; + readonly enumMemberTypes?: { + readonly Priority: readonly [ + { readonly name: 'Low'; readonly value: 0 }, + { readonly name: 'High'; readonly value: 1 }, + { readonly name: 'Urgent'; readonly value: 2 }, + ]; + readonly user_type: readonly [ + { readonly name: 'admin'; readonly value: 'admin' }, + { readonly name: 'user'; readonly value: 'user' }, + ]; + }; + }; + }; + }; + readonly capabilities: { + readonly postgres: { + readonly distinctOn: true; + readonly forKeyShare: true; + readonly forNoKeyUpdate: true; + readonly jsonAgg: true; + readonly lateral: true; + readonly limit: true; + readonly orderBy: true; + readonly 'pgvector.cosine': true; + readonly returning: true; + }; + readonly sql: { + readonly checkConstraint: true; + readonly defaultInInsert: true; + readonly enums: true; + readonly forShare: true; + readonly forUpdate: true; + readonly insertOnConflictSkip: true; + readonly insertOnConflictWithoutTarget: true; + readonly lateral: true; + readonly lockNowait: true; + readonly lockOf: true; + readonly lockSkipLocked: true; + readonly returning: true; + readonly scalarList: true; + }; + }; + readonly extensions: { + readonly 'demo/engagement-stats': { + readonly familyId: 'sql'; + readonly id: 'demo/engagement-stats'; + readonly kind: 'extension'; + readonly targetId: 'postgres'; + readonly types: { + readonly aggregateDescriptors: readonly [ + { + readonly input: { readonly kind: 'trait'; readonly trait: 'numeric' }; + readonly lower: unknown; + readonly nullable: true; + readonly operation: 'stddev'; + readonly output: { readonly codecId: 'pg/numeric@1'; readonly kind: 'codec' }; + }, + ]; + }; + readonly version: '0.0.1'; + }; + readonly pgvector: { + readonly capabilities: { readonly postgres: { readonly 'pgvector.cosine': true } }; + readonly familyId: 'sql'; + readonly id: 'pgvector'; + readonly kind: 'extension'; + readonly targetId: 'postgres'; + readonly types: { + readonly codecTypes: { + readonly import: { + readonly alias: 'PgVectorTypes'; + readonly named: 'CodecTypes'; + readonly package: '@internal/extension-pgvector/codec-types'; + }; + readonly parameterizedOutputTypes: { + readonly alias: 'PgVectorParameterizedOutputTypes'; + readonly named: 'ParameterizedOutputTypesForTypeParams'; + readonly package: '@internal/extension-pgvector/codec-types'; + }; + }; + readonly operationTypes: { + readonly import: { + readonly alias: 'PgVectorOperationTypes'; + readonly named: 'OperationTypes'; + readonly package: '@internal/extension-pgvector/operation-types'; + }; + }; + readonly queryOperationTypes: { + readonly import: { + readonly alias: 'PgVectorQueryOperationTypes'; + readonly named: 'QueryOperationTypes'; + readonly package: '@internal/extension-pgvector/operation-types'; + }; + }; + }; + readonly version: '0.0.1'; + }; + }; + readonly execution: { + readonly executionHash: ExecutionHash; + readonly mutations: { + readonly defaults: readonly [ + { + readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; + readonly ref: { + readonly entry: 'post'; + readonly field: 'id'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; + readonly ref: { + readonly entry: 'tag'; + readonly field: 'id'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; + readonly ref: { + readonly entry: 'task'; + readonly field: 'id'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; + readonly ref: { + readonly entry: 'user'; + readonly field: 'id'; + readonly namespace: 'public'; + }; + }, + ]; + }; + }; + readonly meta: {}; + readonly profileHash: ProfileHash; +}; + +export type Contract = ContractWithTypeMaps; + +export type Namespaces = Contract['storage']['namespaces']; diff --git a/examples/prisma-8-demo/migrations/snapshots/705398c87a4f2c7e2ac0a49002b9061963f726b8875ffe53da1feac8df00b040/contract.json b/examples/prisma-8-demo/migrations/snapshots/705398c87a4f2c7e2ac0a49002b9061963f726b8875ffe53da1feac8df00b040/contract.json new file mode 100644 index 000000000000..06e0179d5161 --- /dev/null +++ b/examples/prisma-8-demo/migrations/snapshots/705398c87a4f2c7e2ac0a49002b9061963f726b8875ffe53da1feac8df00b040/contract.json @@ -0,0 +1 @@ +{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{"postgres":{"distinctOn":true,"forKeyShare":true,"forNoKeyUpdate":true,"jsonAgg":true,"lateral":true,"limit":true,"orderBy":true,"pgvector.cosine":true,"returning":true},"sql":{"checkConstraint":true,"defaultInInsert":true,"enums":true,"forShare":true,"forUpdate":true,"insertOnConflictSkip":true,"insertOnConflictWithoutTarget":true,"lateral":true,"lockNowait":true,"lockOf":true,"lockSkipLocked":true,"returning":true,"scalarList":true}},"domain":{"namespaces":{"public":{"enum":{"Priority":{"codecId":"pg/int4@1","members":[{"name":"Low","value":0},{"name":"High","value":1},{"name":"Urgent","value":2}]},"user_type":{"codecId":"pg/text@1","members":[{"name":"admin","value":"admin"},{"name":"user","value":"user"}]}},"models":{"Bug":{"base":{"model":"Task","namespace":"public"},"fields":{"severity":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"stepsToRepro":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"severity":{"column":"severity"},"stepsToRepro":{"column":"stepsToRepro"}},"namespaceId":"public","table":"bug"}},"Feature":{"base":{"model":"Task","namespace":"public"},"fields":{"priority":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"targetRelease":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"priority":{"column":"priority"},"targetRelease":{"column":"targetRelease"}},"namespaceId":"public","table":"feature"}},"Post":{"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"embedding":{"nullable":true,"type":{"codecId":"pg/vector@1","kind":"scalar","typeParams":{"length":1536}}},"expiresAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"impressionCount":{"nullable":true,"type":{"codecId":"pg/int8@1","kind":"scalar"}},"priority":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"Priority","namespaceId":"public","plane":"domain"}},"reachScore":{"nullable":true,"type":{"codecId":"pg/unboundedint@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"viewCount":{"nullable":true,"type":{"codecId":"pg/int8number@1","kind":"scalar"}}},"relations":{"tags":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["postId"]},"through":{"childColumns":["tagId"],"namespaceId":"public","parentColumns":["postId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Tag","namespace":"public"}},"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"embedding":{"column":"embedding"},"expiresAt":{"column":"expiresAt"},"id":{"column":"id"},"impressionCount":{"column":"impressionCount"},"priority":{"column":"priority"},"reachScore":{"column":"reachScore"},"title":{"column":"title"},"userId":{"column":"userId"},"viewCount":{"column":"viewCount"}},"namespaceId":"public","table":"post"}},"PostTag":{"fields":{"postId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"tagId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"post":{"cardinality":"N:1","nullable":false,"on":{"localFields":["postId"],"targetFields":["id"]},"to":{"model":"Post","namespace":"public"}},"tag":{"cardinality":"N:1","nullable":false,"on":{"localFields":["tagId"],"targetFields":["id"]},"to":{"model":"Tag","namespace":"public"}}},"storage":{"fields":{"postId":{"column":"postId"},"tagId":{"column":"tagId"}},"namespaceId":"public","table":"post_tag"}},"Tag":{"fields":{"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"label":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{"posts":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["tagId"]},"through":{"childColumns":["postId"],"namespaceId":"public","parentColumns":["tagId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Post","namespace":"public"}}},"storage":{"fields":{"id":{"column":"id"},"label":{"column":"label"}},"namespaceId":"public","table":"tag"}},"Task":{"discriminator":{"field":"type"},"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"description":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"status":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"type":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"description":{"column":"description"},"id":{"column":"id"},"status":{"column":"status"},"title":{"column":"title"},"type":{"column":"type"},"userId":{"column":"userId"}},"namespaceId":"public","table":"task"},"variants":{"bug":{"model":"Bug","namespace":"public"},"feature":{"model":"Feature","namespace":"public"}}},"User":{"fields":{"address":{"nullable":true,"type":{"entityKind":"valueObject","entityName":"Address","kind":"valueObject","namespaceId":"public","plane":"storage"}},"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"displayName":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"user_type","namespaceId":"public","plane":"domain"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Post","namespace":"public"}},"tasks":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Task","namespace":"public"}}},"storage":{"fields":{"address":{"column":"address"},"createdAt":{"column":"createdAt"},"displayName":{"column":"displayName"},"email":{"column":"email"},"id":{"column":"id"},"kind":{"column":"kind"}},"namespaceId":"public","table":"user"}}}}}},"execution":{"executionHash":"eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655","mutations":{"defaults":[{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"post","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"tag","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"task","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"user","field":"id","namespace":"public"}}]}},"extensions":{"demo/engagement-stats":{"familyId":"sql","id":"demo/engagement-stats","kind":"extension","targetId":"postgres","types":{"aggregateDescriptors":[{"input":{"kind":"trait","trait":"numeric"},"nullable":true,"operation":"stddev","output":{"codecId":"pg/numeric@1","kind":"codec"}}]},"version":"0.0.1"},"pgvector":{"capabilities":{"postgres":{"pgvector.cosine":true}},"familyId":"sql","id":"pgvector","kind":"extension","targetId":"postgres","types":{"codecTypes":{"import":{"alias":"PgVectorTypes","named":"CodecTypes","package":"@internal/extension-pgvector/codec-types"},"parameterizedOutputTypes":{"alias":"PgVectorParameterizedOutputTypes","named":"ParameterizedOutputTypesForTypeParams","package":"@internal/extension-pgvector/codec-types"}},"operationTypes":{"import":{"alias":"PgVectorOperationTypes","named":"OperationTypes","package":"@internal/extension-pgvector/operation-types"}},"queryOperationTypes":{"import":{"alias":"PgVectorQueryOperationTypes","named":"QueryOperationTypes","package":"@internal/extension-pgvector/operation-types"}}},"version":"0.0.1"}},"meta":{},"profileHash":"3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2","roots":{"post":{"model":"Post","namespace":"public"},"post_tag":{"model":"PostTag","namespace":"public"},"tag":{"model":"Tag","namespace":"public"},"task":{"model":"Task","namespace":"public"},"user":{"model":"User","namespace":"public"}},"schemaVersion":"1","storage":{"namespaces":{"public":{"entries":{"table":{"bug":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"severity":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"stepsToRepro":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"bug"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"feature":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"priority":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"targetRelease":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"feature"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"post":{"checks":[{"expression":"\"priority\" IN (0, 1, 2)","name":"post_priority_check_b236ace9","prefix":"post_priority_check"}],"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"embedding":{"codecId":"pg/vector@1","dataType":"pgvector/vector","nullable":true,"typeParams":{"length":1536}},"expiresAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"(now() + '7 days'::interval)","kind":"function"},"nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"impressionCount":{"codecId":"pg/int8@1","dataType":"pg/int8","nullable":true},"priority":{"codecId":"pg/int4@1","dataType":"pg/int4","default":{"kind":"literal","value":0},"nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Priority","namespaceId":"public","plane":"storage"}},"reachScore":{"codecId":"pg/unboundedint@1","dataType":"pg/numeric","nullable":true},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"viewCount":{"codecId":"pg/int8number@1","dataType":"pg/int8","nullable":true}},"foreignKeys":[{"index":{"name":"post_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"post"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"post_userId_idx_a489d58a","prefix":"post_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"post_tag":{"columns":{"postId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"tagId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"post_tag_postId_idx_a7a72715"},"source":{"columns":["postId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"post"}},{"index":{"name":"post_tag_tagId_idx_86854244"},"source":{"columns":["tagId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"tag"}}],"indexes":[{"columns":["postId"],"name":"post_tag_postId_idx_a7a72715","prefix":"post_tag_postId_idx","unique":false},{"columns":["tagId"],"name":"post_tag_tagId_idx_86854244","prefix":"post_tag_tagId_idx","unique":false}],"primaryKey":{"columns":["postId","tagId"]},"uniques":[]},"tag":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"label":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[{"columns":["label"]}]},"task":{"checks":[{"expression":"\"type\" IN ('bug', 'feature')","name":"task_type_check_cda3f5e7","prefix":"task_type_check"}],"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"description":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"status":{"codecId":"pg/text@1","dataType":"pg/text","default":{"kind":"literal","value":"open"},"nullable":false},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"type":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Task.type","namespaceId":"public","plane":"storage"}},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"task_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"task"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"task_userId_idx_a489d58a","prefix":"task_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"user":{"checks":[{"expression":"\"address\" IS NULL OR jsonb_path_match(\"address\", 'strict $.type() == \"null\" || ($.type() == \"object\" && exists($.keyvalue() ? (@.key == \"city\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"country\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"street\" && (@.value.type() == \"string\"))) && !exists($.keyvalue() ? (@.key == \"zip\" && !(@.value.type() == \"null\" || (@.value.type() == \"string\")))) && !exists($.keyvalue() ? (@.key != \"city\" && @.key != \"country\" && @.key != \"street\" && @.key != \"zip\")))') IS TRUE","name":"user_address_shape_69432dc1","prefix":"user_address_shape"},{"expression":"\"kind\" IN ('admin', 'user')","name":"user_kind_check_836d43ef","prefix":"user_kind_check"}],"columns":{"address":{"codecId":"pg/jsonb@1","dataType":"pg/jsonb","nullable":true},"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"displayName":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"email":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"kind":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"user_type","namespaceId":"public","plane":"storage"}}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]}},"valueObject":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}}}},"valueSet":{"Priority":{"kind":"valueSet","values":[0,1,2]},"Task.type":{"kind":"valueSet","values":["bug","feature"]},"user_type":{"kind":"valueSet","values":["admin","user"]}}},"id":"public","kind":"postgres-schema"}},"storageHash":"705398c87a4f2c7e2ac0a49002b9061963f726b8875ffe53da1feac8df00b040"},"target":"postgres","targetFamily":"sql"} diff --git a/examples/prisma-8-demo/migrations/snapshots/db733e1df89f66886f7dd6a2ea54fc5be48e65d81aadd193d1e7f220ccf14f5e/contract.d.ts b/examples/prisma-8-demo/migrations/snapshots/db733e1df89f66886f7dd6a2ea54fc5be48e65d81aadd193d1e7f220ccf14f5e/contract.d.ts new file mode 100644 index 000000000000..c70d5c511845 --- /dev/null +++ b/examples/prisma-8-demo/migrations/snapshots/db733e1df89f66886f7dd6a2ea54fc5be48e65d81aadd193d1e7f220ccf14f5e/contract.d.ts @@ -0,0 +1,1401 @@ +// ⚠️ GENERATED FILE - DO NOT EDIT +// This file is automatically generated by 'prisma contract emit'. +// To regenerate, run: prisma contract emit +import type { + CodecTypes as PgVectorTypes, + ParameterizedOutputTypesForTypeParams as PgVectorParameterizedOutputTypes, +} from '@prisma/orm-extension-pgvector/codec-types'; +import type { QueryOperationTypes as PgVectorQueryOperationTypes } from '@prisma/orm-extension-pgvector/operation-types'; +import type { + CodecTypes as PgTypes, + ParameterizedOutputTypesForTypeParams as PgParameterizedOutputTypes, +} from '@prisma/orm-postgres/target/codec-types'; +import type { QueryOperationTypes as PgTargetQueryOps } from '@prisma/orm-postgres/target/operation-types'; + +import type { + ColumnTypesFromStorage, + ContractWithTypeMaps, + RelationKeys, + TypeMaps as TypeMapsType, +} from '@prisma/orm-postgres/family-contract/types'; +import type { + Contract as ContractType, + ExecutionHashBase, + FieldTypesFromDomain, + NamespaceId, + ProfileHashBase, + StorageHashBase, + ValueObjectTypesFromStorage, +} from '@prisma/orm-postgres/contract/types'; + +export type StorageHash = + StorageHashBase<'db733e1df89f66886f7dd6a2ea54fc5be48e65d81aadd193d1e7f220ccf14f5e'>; +export type ExecutionHash = + ExecutionHashBase<'eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655'>; +export type ProfileHash = + ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; + +export type CodecTypes = PgTypes & PgVectorTypes; +export type LaneCodecTypes = CodecTypes; +export type QueryOperationTypes = PgTargetQueryOps & + PgVectorQueryOperationTypes; +export type AggregateTypes = { + readonly avg: { + readonly byCodec: { + readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + }; + }; + readonly avgDecimal: { + readonly byCodec: { + readonly 'pg/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + }; + }; + readonly count: { + readonly byCodec: {}; + readonly withoutInput: { readonly output: 'pg/int8number@1'; readonly nullable: false }; + readonly anyInput: { readonly output: 'pg/int8number@1'; readonly nullable: false }; + }; + readonly countBigInt: { + readonly byCodec: {}; + readonly withoutInput: { readonly output: 'pg/int8@1'; readonly nullable: false }; + readonly anyInput: { readonly output: 'pg/int8@1'; readonly nullable: false }; + }; + readonly max: { + readonly byCodec: { + readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true }; + readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true }; + readonly 'pg/date-temporal@1': { + readonly output: 'pg/date-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true }; + readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; + readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { + readonly output: 'pg/time-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-string@1': { + readonly output: 'pg/timestamp-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-temporal@1': { + readonly output: 'pg/timestamp-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-date@1': { + readonly output: 'pg/timestamptz-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-string@1': { + readonly output: 'pg/timestamptz-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-temporal@1': { + readonly output: 'pg/timestamptz-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true }; + readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true }; + readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + }; + }; + readonly min: { + readonly byCodec: { + readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true }; + readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true }; + readonly 'pg/date-temporal@1': { + readonly output: 'pg/date-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true }; + readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; + readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { + readonly output: 'pg/time-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-string@1': { + readonly output: 'pg/timestamp-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-temporal@1': { + readonly output: 'pg/timestamp-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-date@1': { + readonly output: 'pg/timestamptz-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-string@1': { + readonly output: 'pg/timestamptz-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-temporal@1': { + readonly output: 'pg/timestamptz-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true }; + readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true }; + readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + }; + }; + readonly stddev: { + readonly byCodec: { + readonly 'pg/float@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + }; + }; + readonly sum: { + readonly byCodec: { + readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + }; + }; + readonly sumBigInt: { + readonly byCodec: { + readonly 'pg/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'sql/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + }; + }; +}; +type DefaultLiteralValue = CodecId extends keyof CodecTypes + ? Encoded extends CodecTypes[CodecId]['json'] + ? Encoded + : CodecTypes[CodecId]['json'] + : Encoded; +type ParameterizedOutputTypeFunctions = + PgParameterizedOutputTypes | PgVectorParameterizedOutputTypes; +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output' +>; +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input' +>; +export type AddressOutput = ValueObjectOutputTypes['public']['Address']; +export type AddressInput = ValueObjectInputTypes['public']['Address']; +type FieldTypeOverrides = { + readonly public: { + readonly models: { + readonly Post: { + readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 }; + }; + readonly Task: { + readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; + }; + readonly User: { + readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; + }; + }; + }; +}; +export type FieldOutputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output', + ContractBase['storage']['namespaces'] +>; +export type FieldInputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input', + ContractBase['storage']['namespaces'] +>; +type ColumnTypeOverrides = { + readonly public: { + readonly post: { readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 } }; + readonly task: { + readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; + }; + readonly user: { + readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; + }; + }; +}; +export type StorageColumnTypes = ColumnTypesFromStorage< + ContractBase['storage'], + CodecTypes, + ParameterizedOutputTypeFunctions, + ColumnTypeOverrides, + 'output' +>; +export type StorageColumnInputTypes = ColumnTypesFromStorage< + ContractBase['storage'], + CodecTypes, + ParameterizedOutputTypeFunctions, + ColumnTypeOverrides, + 'input' +>; + +export namespace Models { + export type public_Bug = { + createdAt: FieldOutputTypes['public']['Task']['createdAt']; + description: FieldOutputTypes['public']['Task']['description']; + id: FieldOutputTypes['public']['Task']['id']; + status: FieldOutputTypes['public']['Task']['status']; + title: FieldOutputTypes['public']['Task']['title']; + type: 'bug'; + userId: FieldOutputTypes['public']['Task']['userId']; + severity: FieldOutputTypes['public']['Bug']['severity']; + stepsToRepro: FieldOutputTypes['public']['Bug']['stepsToRepro']; + user: public_User; + readonly [RelationKeys]?: 'user'; + }; + export type public_Feature = { + createdAt: FieldOutputTypes['public']['Task']['createdAt']; + description: FieldOutputTypes['public']['Task']['description']; + id: FieldOutputTypes['public']['Task']['id']; + status: FieldOutputTypes['public']['Task']['status']; + title: FieldOutputTypes['public']['Task']['title']; + type: 'feature'; + userId: FieldOutputTypes['public']['Task']['userId']; + priority: FieldOutputTypes['public']['Feature']['priority']; + targetRelease: FieldOutputTypes['public']['Feature']['targetRelease']; + user: public_User; + readonly [RelationKeys]?: 'user'; + }; + export type public_Post = { + createdAt: FieldOutputTypes['public']['Post']['createdAt']; + embedding: FieldOutputTypes['public']['Post']['embedding']; + id: FieldOutputTypes['public']['Post']['id']; + impressionCount: FieldOutputTypes['public']['Post']['impressionCount']; + priority: FieldOutputTypes['public']['Post']['priority']; + reachScore: FieldOutputTypes['public']['Post']['reachScore']; + title: FieldOutputTypes['public']['Post']['title']; + userId: FieldOutputTypes['public']['Post']['userId']; + viewCount: FieldOutputTypes['public']['Post']['viewCount']; + tags: public_Tag[]; + user: public_User; + readonly [RelationKeys]?: 'tags' | 'user'; + }; + export type public_PostTag = { + postId: FieldOutputTypes['public']['PostTag']['postId']; + tagId: FieldOutputTypes['public']['PostTag']['tagId']; + post: public_Post; + tag: public_Tag; + readonly [RelationKeys]?: 'post' | 'tag'; + }; + export type public_Tag = { + id: FieldOutputTypes['public']['Tag']['id']; + label: FieldOutputTypes['public']['Tag']['label']; + posts: public_Post[]; + readonly [RelationKeys]?: 'posts'; + }; + export type public_Task = { + createdAt: FieldOutputTypes['public']['Task']['createdAt']; + description: FieldOutputTypes['public']['Task']['description']; + id: FieldOutputTypes['public']['Task']['id']; + status: FieldOutputTypes['public']['Task']['status']; + title: FieldOutputTypes['public']['Task']['title']; + type: 'bug' | 'feature'; + userId: FieldOutputTypes['public']['Task']['userId']; + user: public_User; + readonly [RelationKeys]?: 'user'; + }; + export type public_User = { + address: FieldOutputTypes['public']['User']['address']; + createdAt: FieldOutputTypes['public']['User']['createdAt']; + displayName: FieldOutputTypes['public']['User']['displayName']; + email: FieldOutputTypes['public']['User']['email']; + id: FieldOutputTypes['public']['User']['id']; + kind: FieldOutputTypes['public']['User']['kind']; + posts: public_Post[]; + tasks: public_AnyTask[]; + readonly [RelationKeys]?: 'posts' | 'tasks'; + }; + export type public_AnyTask = public_Bug | public_Feature; +} + +export declare const models: { + public: { + Bug: Models.public_Bug; + Feature: Models.public_Feature; + Post: Models.public_Post; + PostTag: Models.public_PostTag; + Tag: Models.public_Tag; + Task: Models.public_Task; + User: Models.public_User; + AnyTask: Models.public_AnyTask; + }; +}; + +export type TypeMaps = TypeMapsType< + CodecTypes, + QueryOperationTypes, + FieldOutputTypes, + FieldInputTypes, + StorageColumnTypes, + StorageColumnInputTypes, + AggregateTypes +>; + +type ContractBase = Omit< + ContractType<{ + readonly namespaces: { + readonly public: { + readonly id: 'public'; + readonly kind: 'postgres-schema'; + readonly entries: { + readonly table: { + readonly bug: { + columns: { + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly severity: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly stepsToRepro: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: true; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'bug'; + readonly columns: readonly ['id']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'task'; + readonly columns: readonly ['id']; + }; + }, + ]; + }; + readonly feature: { + columns: { + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly priority: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly targetRelease: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: true; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'feature'; + readonly columns: readonly ['id']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'task'; + readonly columns: readonly ['id']; + }; + }, + ]; + }; + readonly post: { + columns: { + readonly createdAt: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-temporal@1'; + readonly nullable: false; + readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; + readonly many: false; + }; + readonly embedding: { + readonly dataType: 'pgvector/vector'; + readonly codecId: 'pg/vector@1'; + readonly nullable: true; + readonly typeParams: { readonly length: 1536 }; + readonly many: false; + }; + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly impressionCount: { + readonly dataType: 'pg/int8'; + readonly codecId: 'pg/int8@1'; + readonly nullable: true; + readonly many: false; + }; + readonly priority: { + readonly dataType: 'pg/int4'; + readonly codecId: 'pg/int4@1'; + readonly nullable: false; + readonly default: { + readonly kind: 'literal'; + readonly value: DefaultLiteralValue<'pg/int4@1', 0>; + }; + readonly many: false; + }; + readonly reachScore: { + readonly dataType: 'pg/numeric'; + readonly codecId: 'pg/unboundedint@1'; + readonly nullable: true; + readonly many: false; + }; + readonly title: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly userId: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly viewCount: { + readonly dataType: 'pg/int8'; + readonly codecId: 'pg/int8number@1'; + readonly nullable: true; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly [ + { + readonly name: 'post_userId_idx_a489d58a'; + readonly prefix: 'post_userId_idx'; + readonly columns: readonly ['userId']; + readonly unique: false; + }, + ]; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'post'; + readonly columns: readonly ['userId']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'user'; + readonly columns: readonly ['id']; + }; + }, + ]; + }; + readonly post_tag: { + columns: { + readonly postId: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly tagId: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['postId', 'tagId'] }; + uniques: readonly []; + indexes: readonly [ + { + readonly name: 'post_tag_postId_idx_a7a72715'; + readonly prefix: 'post_tag_postId_idx'; + readonly columns: readonly ['postId']; + readonly unique: false; + }, + { + readonly name: 'post_tag_tagId_idx_86854244'; + readonly prefix: 'post_tag_tagId_idx'; + readonly columns: readonly ['tagId']; + readonly unique: false; + }, + ]; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'post_tag'; + readonly columns: readonly ['postId']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'post'; + readonly columns: readonly ['id']; + }; + }, + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'post_tag'; + readonly columns: readonly ['tagId']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'tag'; + readonly columns: readonly ['id']; + }; + }, + ]; + }; + readonly tag: { + columns: { + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly label: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly [{ readonly columns: readonly ['label'] }]; + indexes: readonly []; + foreignKeys: readonly []; + }; + readonly task: { + columns: { + readonly createdAt: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-temporal@1'; + readonly nullable: false; + readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; + readonly many: false; + }; + readonly description: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: true; + readonly many: false; + }; + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly status: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly default: { + readonly kind: 'literal'; + readonly value: DefaultLiteralValue<'pg/text@1', 'open'>; + }; + readonly many: false; + }; + readonly title: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly type: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly userId: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly [ + { + readonly name: 'task_userId_idx_a489d58a'; + readonly prefix: 'task_userId_idx'; + readonly columns: readonly ['userId']; + readonly unique: false; + }, + ]; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'task'; + readonly columns: readonly ['userId']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'user'; + readonly columns: readonly ['id']; + }; + }, + ]; + }; + readonly user: { + columns: { + readonly address: { + readonly dataType: 'pg/jsonb'; + readonly codecId: 'pg/jsonb@1'; + readonly nullable: true; + readonly many: false; + }; + readonly createdAt: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-temporal@1'; + readonly nullable: false; + readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; + readonly many: false; + }; + readonly displayName: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly email: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly kind: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly []; + }; + }; + readonly valueSet: { + readonly Priority: { readonly kind: 'valueSet'; readonly values: readonly [0, 1, 2] }; + readonly 'Task.type': { + readonly kind: 'valueSet'; + readonly values: readonly ['bug', 'feature']; + }; + readonly user_type: { + readonly kind: 'valueSet'; + readonly values: readonly ['admin', 'user']; + }; + }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + }; + }; + }; + }; + }; + readonly storageHash: StorageHash; + }>, + 'roots' | 'domain' +> & { + readonly target: 'postgres'; + readonly targetFamily: 'sql'; + readonly roots: { + readonly post: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; + readonly post_tag: { readonly namespace: 'public' & NamespaceId; readonly model: 'PostTag' }; + readonly tag: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; + readonly task: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; + readonly user: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; + }; + readonly domain: { + readonly namespaces: { + readonly public: { + readonly models: { + readonly Bug: { + readonly fields: { + readonly severity: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly stepsToRepro: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'bug'; + readonly namespaceId: 'public'; + readonly fields: { + readonly severity: { readonly column: 'severity' }; + readonly stepsToRepro: { readonly column: 'stepsToRepro' }; + }; + }; + readonly base: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; + }; + readonly Feature: { + readonly fields: { + readonly priority: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly targetRelease: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'feature'; + readonly namespaceId: 'public'; + readonly fields: { + readonly priority: { readonly column: 'priority' }; + readonly targetRelease: { readonly column: 'targetRelease' }; + }; + }; + readonly base: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; + }; + readonly Post: { + readonly fields: { + readonly createdAt: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-temporal@1'; + }; + }; + readonly embedding: { + readonly nullable: true; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/vector@1'; + readonly typeParams: { readonly length: 1536 }; + }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly impressionCount: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int8@1' }; + }; + readonly priority: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int4@1' }; + }; + readonly reachScore: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/unboundedint@1' }; + }; + readonly title: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly userId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly viewCount: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int8number@1' }; + }; + }; + readonly relations: { + readonly tags: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; + readonly cardinality: 'N:M'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['postId']; + }; + readonly through: { + readonly table: 'post_tag'; + readonly namespaceId: 'public'; + readonly parentColumns: readonly ['postId']; + readonly childColumns: readonly ['tagId']; + readonly targetColumns: readonly ['id']; + }; + }; + readonly user: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['userId']; + readonly targetFields: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly table: 'post'; + readonly namespaceId: 'public'; + readonly fields: { + readonly createdAt: { readonly column: 'createdAt' }; + readonly embedding: { readonly column: 'embedding' }; + readonly id: { readonly column: 'id' }; + readonly impressionCount: { readonly column: 'impressionCount' }; + readonly priority: { readonly column: 'priority' }; + readonly reachScore: { readonly column: 'reachScore' }; + readonly title: { readonly column: 'title' }; + readonly userId: { readonly column: 'userId' }; + readonly viewCount: { readonly column: 'viewCount' }; + }; + }; + }; + readonly PostTag: { + readonly fields: { + readonly postId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly tagId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + }; + readonly relations: { + readonly post: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['postId']; + readonly targetFields: readonly ['id']; + }; + }; + readonly tag: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['tagId']; + readonly targetFields: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly table: 'post_tag'; + readonly namespaceId: 'public'; + readonly fields: { + readonly postId: { readonly column: 'postId' }; + readonly tagId: { readonly column: 'tagId' }; + }; + }; + }; + readonly Tag: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly label: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: { + readonly posts: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; + readonly cardinality: 'N:M'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['tagId']; + }; + readonly through: { + readonly table: 'post_tag'; + readonly namespaceId: 'public'; + readonly parentColumns: readonly ['tagId']; + readonly childColumns: readonly ['postId']; + readonly targetColumns: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly table: 'tag'; + readonly namespaceId: 'public'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly label: { readonly column: 'label' }; + }; + }; + }; + readonly Task: { + readonly fields: { + readonly createdAt: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-temporal@1'; + }; + }; + readonly description: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly status: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly title: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly type: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly userId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + }; + readonly relations: { + readonly user: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['userId']; + readonly targetFields: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly table: 'task'; + readonly namespaceId: 'public'; + readonly fields: { + readonly createdAt: { readonly column: 'createdAt' }; + readonly description: { readonly column: 'description' }; + readonly id: { readonly column: 'id' }; + readonly status: { readonly column: 'status' }; + readonly title: { readonly column: 'title' }; + readonly type: { readonly column: 'type' }; + readonly userId: { readonly column: 'userId' }; + }; + }; + readonly discriminator: { readonly field: 'type' }; + readonly variants: { + readonly bug: { readonly namespace: 'public' & NamespaceId; readonly model: 'Bug' }; + readonly feature: { + readonly namespace: 'public' & NamespaceId; + readonly model: 'Feature'; + }; + }; + }; + readonly User: { + readonly fields: { + readonly address: { + readonly nullable: true; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: 'public'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; + }; + readonly createdAt: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-temporal@1'; + }; + }; + readonly displayName: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly kind: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: { + readonly posts: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; + readonly cardinality: '1:N'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['userId']; + }; + }; + readonly tasks: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; + readonly cardinality: '1:N'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['userId']; + }; + }; + }; + readonly storage: { + readonly table: 'user'; + readonly namespaceId: 'public'; + readonly fields: { + readonly address: { readonly column: 'address' }; + readonly createdAt: { readonly column: 'createdAt' }; + readonly displayName: { readonly column: 'displayName' }; + readonly email: { readonly column: 'email' }; + readonly id: { readonly column: 'id' }; + readonly kind: { readonly column: 'kind' }; + }; + }; + }; + }; + readonly enum: { + readonly Priority: { + readonly codecId: 'pg/int4@1'; + readonly members: readonly [ + { readonly name: 'Low'; readonly value: 0 }, + { readonly name: 'High'; readonly value: 1 }, + { readonly name: 'Urgent'; readonly value: 2 }, + ]; + }; + readonly user_type: { + readonly codecId: 'pg/text@1'; + readonly members: readonly [ + { readonly name: 'admin'; readonly value: 'admin' }, + { readonly name: 'user'; readonly value: 'user' }, + ]; + }; + }; + readonly enumMemberTypes?: { + readonly Priority: readonly [ + { readonly name: 'Low'; readonly value: 0 }, + { readonly name: 'High'; readonly value: 1 }, + { readonly name: 'Urgent'; readonly value: 2 }, + ]; + readonly user_type: readonly [ + { readonly name: 'admin'; readonly value: 'admin' }, + { readonly name: 'user'; readonly value: 'user' }, + ]; + }; + }; + }; + }; + readonly capabilities: { + readonly postgres: { + readonly distinctOn: true; + readonly forKeyShare: true; + readonly forNoKeyUpdate: true; + readonly jsonAgg: true; + readonly lateral: true; + readonly limit: true; + readonly orderBy: true; + readonly 'pgvector.cosine': true; + readonly returning: true; + }; + readonly sql: { + readonly checkConstraint: true; + readonly defaultInInsert: true; + readonly enums: true; + readonly forShare: true; + readonly forUpdate: true; + readonly insertOnConflictSkip: true; + readonly insertOnConflictWithoutTarget: true; + readonly lateral: true; + readonly lockNowait: true; + readonly lockOf: true; + readonly lockSkipLocked: true; + readonly returning: true; + readonly scalarList: true; + }; + }; + readonly extensions: { + readonly 'demo/engagement-stats': { + readonly familyId: 'sql'; + readonly id: 'demo/engagement-stats'; + readonly kind: 'extension'; + readonly targetId: 'postgres'; + readonly types: { + readonly aggregateDescriptors: readonly [ + { + readonly input: { readonly kind: 'trait'; readonly trait: 'numeric' }; + readonly lower: unknown; + readonly nullable: true; + readonly operation: 'stddev'; + readonly output: { readonly codecId: 'pg/numeric@1'; readonly kind: 'codec' }; + }, + ]; + }; + readonly version: '0.0.1'; + }; + readonly pgvector: { + readonly capabilities: { readonly postgres: { readonly 'pgvector.cosine': true } }; + readonly familyId: 'sql'; + readonly id: 'pgvector'; + readonly kind: 'extension'; + readonly targetId: 'postgres'; + readonly types: { + readonly codecTypes: { + readonly import: { + readonly alias: 'PgVectorTypes'; + readonly named: 'CodecTypes'; + readonly package: '@internal/extension-pgvector/codec-types'; + }; + readonly parameterizedOutputTypes: { + readonly alias: 'PgVectorParameterizedOutputTypes'; + readonly named: 'ParameterizedOutputTypesForTypeParams'; + readonly package: '@internal/extension-pgvector/codec-types'; + }; + }; + readonly operationTypes: { + readonly import: { + readonly alias: 'PgVectorOperationTypes'; + readonly named: 'OperationTypes'; + readonly package: '@internal/extension-pgvector/operation-types'; + }; + }; + readonly queryOperationTypes: { + readonly import: { + readonly alias: 'PgVectorQueryOperationTypes'; + readonly named: 'QueryOperationTypes'; + readonly package: '@internal/extension-pgvector/operation-types'; + }; + }; + }; + readonly version: '0.0.1'; + }; + }; + readonly execution: { + readonly executionHash: ExecutionHash; + readonly mutations: { + readonly defaults: readonly [ + { + readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; + readonly ref: { + readonly entry: 'post'; + readonly field: 'id'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; + readonly ref: { + readonly entry: 'tag'; + readonly field: 'id'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; + readonly ref: { + readonly entry: 'task'; + readonly field: 'id'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; + readonly ref: { + readonly entry: 'user'; + readonly field: 'id'; + readonly namespace: 'public'; + }; + }, + ]; + }; + }; + readonly meta: {}; + readonly profileHash: ProfileHash; +}; + +export type Contract = ContractWithTypeMaps; + +export type Namespaces = Contract['storage']['namespaces']; diff --git a/examples/prisma-8-demo/migrations/snapshots/db733e1df89f66886f7dd6a2ea54fc5be48e65d81aadd193d1e7f220ccf14f5e/contract.json b/examples/prisma-8-demo/migrations/snapshots/db733e1df89f66886f7dd6a2ea54fc5be48e65d81aadd193d1e7f220ccf14f5e/contract.json new file mode 100644 index 000000000000..e3befc2b1c54 --- /dev/null +++ b/examples/prisma-8-demo/migrations/snapshots/db733e1df89f66886f7dd6a2ea54fc5be48e65d81aadd193d1e7f220ccf14f5e/contract.json @@ -0,0 +1 @@ +{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{"postgres":{"distinctOn":true,"forKeyShare":true,"forNoKeyUpdate":true,"jsonAgg":true,"lateral":true,"limit":true,"orderBy":true,"pgvector.cosine":true,"returning":true},"sql":{"checkConstraint":true,"defaultInInsert":true,"enums":true,"forShare":true,"forUpdate":true,"insertOnConflictSkip":true,"insertOnConflictWithoutTarget":true,"lateral":true,"lockNowait":true,"lockOf":true,"lockSkipLocked":true,"returning":true,"scalarList":true}},"domain":{"namespaces":{"public":{"enum":{"Priority":{"codecId":"pg/int4@1","members":[{"name":"Low","value":0},{"name":"High","value":1},{"name":"Urgent","value":2}]},"user_type":{"codecId":"pg/text@1","members":[{"name":"admin","value":"admin"},{"name":"user","value":"user"}]}},"models":{"Bug":{"base":{"model":"Task","namespace":"public"},"fields":{"severity":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"stepsToRepro":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"severity":{"column":"severity"},"stepsToRepro":{"column":"stepsToRepro"}},"namespaceId":"public","table":"bug"}},"Feature":{"base":{"model":"Task","namespace":"public"},"fields":{"priority":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"targetRelease":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"priority":{"column":"priority"},"targetRelease":{"column":"targetRelease"}},"namespaceId":"public","table":"feature"}},"Post":{"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"embedding":{"nullable":true,"type":{"codecId":"pg/vector@1","kind":"scalar","typeParams":{"length":1536}}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"impressionCount":{"nullable":true,"type":{"codecId":"pg/int8@1","kind":"scalar"}},"priority":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"Priority","namespaceId":"public","plane":"domain"}},"reachScore":{"nullable":true,"type":{"codecId":"pg/unboundedint@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"viewCount":{"nullable":true,"type":{"codecId":"pg/int8number@1","kind":"scalar"}}},"relations":{"tags":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["postId"]},"through":{"childColumns":["tagId"],"namespaceId":"public","parentColumns":["postId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Tag","namespace":"public"}},"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"embedding":{"column":"embedding"},"id":{"column":"id"},"impressionCount":{"column":"impressionCount"},"priority":{"column":"priority"},"reachScore":{"column":"reachScore"},"title":{"column":"title"},"userId":{"column":"userId"},"viewCount":{"column":"viewCount"}},"namespaceId":"public","table":"post"}},"PostTag":{"fields":{"postId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"tagId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"post":{"cardinality":"N:1","nullable":false,"on":{"localFields":["postId"],"targetFields":["id"]},"to":{"model":"Post","namespace":"public"}},"tag":{"cardinality":"N:1","nullable":false,"on":{"localFields":["tagId"],"targetFields":["id"]},"to":{"model":"Tag","namespace":"public"}}},"storage":{"fields":{"postId":{"column":"postId"},"tagId":{"column":"tagId"}},"namespaceId":"public","table":"post_tag"}},"Tag":{"fields":{"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"label":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{"posts":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["tagId"]},"through":{"childColumns":["postId"],"namespaceId":"public","parentColumns":["tagId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Post","namespace":"public"}}},"storage":{"fields":{"id":{"column":"id"},"label":{"column":"label"}},"namespaceId":"public","table":"tag"}},"Task":{"discriminator":{"field":"type"},"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"description":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"status":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"type":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"description":{"column":"description"},"id":{"column":"id"},"status":{"column":"status"},"title":{"column":"title"},"type":{"column":"type"},"userId":{"column":"userId"}},"namespaceId":"public","table":"task"},"variants":{"bug":{"model":"Bug","namespace":"public"},"feature":{"model":"Feature","namespace":"public"}}},"User":{"fields":{"address":{"nullable":true,"type":{"entityKind":"valueObject","entityName":"Address","kind":"valueObject","namespaceId":"public","plane":"storage"}},"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"displayName":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"user_type","namespaceId":"public","plane":"domain"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Post","namespace":"public"}},"tasks":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Task","namespace":"public"}}},"storage":{"fields":{"address":{"column":"address"},"createdAt":{"column":"createdAt"},"displayName":{"column":"displayName"},"email":{"column":"email"},"id":{"column":"id"},"kind":{"column":"kind"}},"namespaceId":"public","table":"user"}}}}}},"execution":{"executionHash":"eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655","mutations":{"defaults":[{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"post","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"tag","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"task","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"user","field":"id","namespace":"public"}}]}},"extensions":{"demo/engagement-stats":{"familyId":"sql","id":"demo/engagement-stats","kind":"extension","targetId":"postgres","types":{"aggregateDescriptors":[{"input":{"kind":"trait","trait":"numeric"},"nullable":true,"operation":"stddev","output":{"codecId":"pg/numeric@1","kind":"codec"}}]},"version":"0.0.1"},"pgvector":{"capabilities":{"postgres":{"pgvector.cosine":true}},"familyId":"sql","id":"pgvector","kind":"extension","targetId":"postgres","types":{"codecTypes":{"import":{"alias":"PgVectorTypes","named":"CodecTypes","package":"@internal/extension-pgvector/codec-types"},"parameterizedOutputTypes":{"alias":"PgVectorParameterizedOutputTypes","named":"ParameterizedOutputTypesForTypeParams","package":"@internal/extension-pgvector/codec-types"}},"operationTypes":{"import":{"alias":"PgVectorOperationTypes","named":"OperationTypes","package":"@internal/extension-pgvector/operation-types"}},"queryOperationTypes":{"import":{"alias":"PgVectorQueryOperationTypes","named":"QueryOperationTypes","package":"@internal/extension-pgvector/operation-types"}}},"version":"0.0.1"}},"meta":{},"profileHash":"3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2","roots":{"post":{"model":"Post","namespace":"public"},"post_tag":{"model":"PostTag","namespace":"public"},"tag":{"model":"Tag","namespace":"public"},"task":{"model":"Task","namespace":"public"},"user":{"model":"User","namespace":"public"}},"schemaVersion":"1","storage":{"namespaces":{"public":{"entries":{"table":{"bug":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"severity":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"stepsToRepro":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"bug"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"feature":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"priority":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"targetRelease":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"feature"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"post":{"checks":[{"expression":"\"priority\" IN (0, 1, 2)","name":"post_priority_check_b236ace9","prefix":"post_priority_check"}],"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"embedding":{"codecId":"pg/vector@1","dataType":"pgvector/vector","nullable":true,"typeParams":{"length":1536}},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"impressionCount":{"codecId":"pg/int8@1","dataType":"pg/int8","nullable":true},"priority":{"codecId":"pg/int4@1","dataType":"pg/int4","default":{"kind":"literal","value":0},"nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Priority","namespaceId":"public","plane":"storage"}},"reachScore":{"codecId":"pg/unboundedint@1","dataType":"pg/numeric","nullable":true},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"viewCount":{"codecId":"pg/int8number@1","dataType":"pg/int8","nullable":true}},"foreignKeys":[{"index":{"name":"post_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"post"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"post_userId_idx_a489d58a","prefix":"post_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"post_tag":{"columns":{"postId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"tagId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"post_tag_postId_idx_a7a72715"},"source":{"columns":["postId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"post"}},{"index":{"name":"post_tag_tagId_idx_86854244"},"source":{"columns":["tagId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"tag"}}],"indexes":[{"columns":["postId"],"name":"post_tag_postId_idx_a7a72715","prefix":"post_tag_postId_idx","unique":false},{"columns":["tagId"],"name":"post_tag_tagId_idx_86854244","prefix":"post_tag_tagId_idx","unique":false}],"primaryKey":{"columns":["postId","tagId"]},"uniques":[]},"tag":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"label":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[{"columns":["label"]}]},"task":{"checks":[{"expression":"\"type\" IN ('bug', 'feature')","name":"task_type_check_cda3f5e7","prefix":"task_type_check"}],"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"description":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"status":{"codecId":"pg/text@1","dataType":"pg/text","default":{"kind":"literal","value":"open"},"nullable":false},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"type":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Task.type","namespaceId":"public","plane":"storage"}},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"task_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"task"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"task_userId_idx_a489d58a","prefix":"task_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"user":{"checks":[{"expression":"\"address\" IS NULL OR jsonb_path_match(\"address\", 'strict $.type() == \"null\" || ($.type() == \"object\" && exists($.keyvalue() ? (@.key == \"city\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"country\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"street\" && (@.value.type() == \"string\"))) && !exists($.keyvalue() ? (@.key == \"zip\" && !(@.value.type() == \"null\" || (@.value.type() == \"string\")))) && !exists($.keyvalue() ? (@.key != \"city\" && @.key != \"country\" && @.key != \"street\" && @.key != \"zip\")))') IS TRUE","name":"user_address_shape_69432dc1","prefix":"user_address_shape"},{"expression":"\"kind\" IN ('admin', 'user')","name":"user_kind_check_836d43ef","prefix":"user_kind_check"}],"columns":{"address":{"codecId":"pg/jsonb@1","dataType":"pg/jsonb","nullable":true},"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"displayName":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"email":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"kind":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"user_type","namespaceId":"public","plane":"storage"}}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]}},"valueObject":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}}}},"valueSet":{"Priority":{"kind":"valueSet","values":[0,1,2]},"Task.type":{"kind":"valueSet","values":["bug","feature"]},"user_type":{"kind":"valueSet","values":["admin","user"]}}},"id":"public","kind":"postgres-schema"}},"storageHash":"db733e1df89f66886f7dd6a2ea54fc5be48e65d81aadd193d1e7f220ccf14f5e"},"target":"postgres","targetFamily":"sql"} diff --git a/examples/prisma-8-demo/migrations/snapshots/e1916bd73bb2388302d81a1eaa17125c0cedb6ce47b489a7adeeb41c2ef50ebf/contract.d.ts b/examples/prisma-8-demo/migrations/snapshots/e1916bd73bb2388302d81a1eaa17125c0cedb6ce47b489a7adeeb41c2ef50ebf/contract.d.ts new file mode 100644 index 000000000000..f84d528883aa --- /dev/null +++ b/examples/prisma-8-demo/migrations/snapshots/e1916bd73bb2388302d81a1eaa17125c0cedb6ce47b489a7adeeb41c2ef50ebf/contract.d.ts @@ -0,0 +1,1454 @@ +// ⚠️ GENERATED FILE - DO NOT EDIT +// This file is automatically generated by 'prisma contract emit'. +// To regenerate, run: prisma contract emit +import type { + CodecTypes as PgVectorTypes, + ParameterizedOutputTypesForTypeParams as PgVectorParameterizedOutputTypes, +} from '@prisma/orm-extension-pgvector/codec-types'; +import type { QueryOperationTypes as PgVectorQueryOperationTypes } from '@prisma/orm-extension-pgvector/operation-types'; +import type { + CodecTypes as PgTypes, + ParameterizedOutputTypesForTypeParams as PgParameterizedOutputTypes, +} from '@prisma/orm-postgres/target/codec-types'; +import type { QueryOperationTypes as PgTargetQueryOps } from '@prisma/orm-postgres/target/operation-types'; + +import type { + ColumnTypesFromStorage, + ContractWithTypeMaps, + RelationKeys, + TypeMaps as TypeMapsType, +} from '@prisma/orm-postgres/family-contract/types'; +import type { + Contract as ContractType, + ExecutionHashBase, + FieldTypesFromDomain, + NamespaceId, + ProfileHashBase, + StorageHashBase, + ValueObjectTypesFromStorage, +} from '@prisma/orm-postgres/contract/types'; + +export type StorageHash = + StorageHashBase<'e1916bd73bb2388302d81a1eaa17125c0cedb6ce47b489a7adeeb41c2ef50ebf'>; +export type ExecutionHash = + ExecutionHashBase<'eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655'>; +export type ProfileHash = + ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; + +export type CodecTypes = PgTypes & PgVectorTypes; +export type LaneCodecTypes = CodecTypes; +export type QueryOperationTypes = PgTargetQueryOps & + PgVectorQueryOperationTypes; +export type AggregateTypes = { + readonly avg: { + readonly byCodec: { + readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + }; + }; + readonly avgDecimal: { + readonly byCodec: { + readonly 'pg/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + }; + }; + readonly count: { + readonly byCodec: {}; + readonly withoutInput: { readonly output: 'pg/int8number@1'; readonly nullable: false }; + readonly anyInput: { readonly output: 'pg/int8number@1'; readonly nullable: false }; + }; + readonly countBigInt: { + readonly byCodec: {}; + readonly withoutInput: { readonly output: 'pg/int8@1'; readonly nullable: false }; + readonly anyInput: { readonly output: 'pg/int8@1'; readonly nullable: false }; + }; + readonly max: { + readonly byCodec: { + readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true }; + readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true }; + readonly 'pg/date-temporal@1': { + readonly output: 'pg/date-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true }; + readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; + readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { + readonly output: 'pg/time-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-string@1': { + readonly output: 'pg/timestamp-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-temporal@1': { + readonly output: 'pg/timestamp-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-date@1': { + readonly output: 'pg/timestamptz-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-string@1': { + readonly output: 'pg/timestamptz-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-temporal@1': { + readonly output: 'pg/timestamptz-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true }; + readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true }; + readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + }; + }; + readonly min: { + readonly byCodec: { + readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true }; + readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true }; + readonly 'pg/date-temporal@1': { + readonly output: 'pg/date-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true }; + readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; + readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { + readonly output: 'pg/time-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-string@1': { + readonly output: 'pg/timestamp-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-temporal@1': { + readonly output: 'pg/timestamp-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-date@1': { + readonly output: 'pg/timestamptz-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-string@1': { + readonly output: 'pg/timestamptz-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-temporal@1': { + readonly output: 'pg/timestamptz-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true }; + readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true }; + readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + }; + }; + readonly stddev: { + readonly byCodec: { + readonly 'pg/float@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + }; + }; + readonly sum: { + readonly byCodec: { + readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + }; + }; + readonly sumBigInt: { + readonly byCodec: { + readonly 'pg/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'sql/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + }; + }; +}; +type DefaultLiteralValue = CodecId extends keyof CodecTypes + ? Encoded extends CodecTypes[CodecId]['json'] + ? Encoded + : CodecTypes[CodecId]['json'] + : Encoded; +type ParameterizedOutputTypeFunctions = + PgParameterizedOutputTypes | PgVectorParameterizedOutputTypes; +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output' +>; +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input' +>; +export type AddressOutput = ValueObjectOutputTypes['public']['Address']; +export type AddressInput = ValueObjectInputTypes['public']['Address']; +type FieldTypeOverrides = { + readonly public: { + readonly models: { + readonly Post: { + readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 }; + }; + readonly Task: { + readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; + }; + readonly User: { + readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; + }; + }; + }; +}; +export type FieldOutputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output', + ContractBase['storage']['namespaces'] +>; +export type FieldInputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input', + ContractBase['storage']['namespaces'] +>; +type ColumnTypeOverrides = { + readonly public: { + readonly post: { readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 } }; + readonly task: { + readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; + }; + readonly user: { + readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; + }; + }; +}; +export type StorageColumnTypes = ColumnTypesFromStorage< + ContractBase['storage'], + CodecTypes, + ParameterizedOutputTypeFunctions, + ColumnTypeOverrides, + 'output' +>; +export type StorageColumnInputTypes = ColumnTypesFromStorage< + ContractBase['storage'], + CodecTypes, + ParameterizedOutputTypeFunctions, + ColumnTypeOverrides, + 'input' +>; + +export namespace Models { + export type public_Bug = { + createdAt: FieldOutputTypes['public']['Task']['createdAt']; + description: FieldOutputTypes['public']['Task']['description']; + id: FieldOutputTypes['public']['Task']['id']; + status: FieldOutputTypes['public']['Task']['status']; + title: FieldOutputTypes['public']['Task']['title']; + type: 'bug'; + userId: FieldOutputTypes['public']['Task']['userId']; + severity: FieldOutputTypes['public']['Bug']['severity']; + stepsToRepro: FieldOutputTypes['public']['Bug']['stepsToRepro']; + user: public_User; + readonly [RelationKeys]?: 'user'; + }; + export type public_Feature = { + createdAt: FieldOutputTypes['public']['Task']['createdAt']; + description: FieldOutputTypes['public']['Task']['description']; + id: FieldOutputTypes['public']['Task']['id']; + status: FieldOutputTypes['public']['Task']['status']; + title: FieldOutputTypes['public']['Task']['title']; + type: 'feature'; + userId: FieldOutputTypes['public']['Task']['userId']; + priority: FieldOutputTypes['public']['Feature']['priority']; + targetRelease: FieldOutputTypes['public']['Feature']['targetRelease']; + user: public_User; + readonly [RelationKeys]?: 'user'; + }; + export type public_Post = { + body: FieldOutputTypes['public']['Post']['body']; + createdAt: FieldOutputTypes['public']['Post']['createdAt']; + embedding: FieldOutputTypes['public']['Post']['embedding']; + expiresAt: FieldOutputTypes['public']['Post']['expiresAt']; + id: FieldOutputTypes['public']['Post']['id']; + impressionCount: FieldOutputTypes['public']['Post']['impressionCount']; + priority: FieldOutputTypes['public']['Post']['priority']; + reachScore: FieldOutputTypes['public']['Post']['reachScore']; + title: FieldOutputTypes['public']['Post']['title']; + userId: FieldOutputTypes['public']['Post']['userId']; + viewCount: FieldOutputTypes['public']['Post']['viewCount']; + tags: public_Tag[]; + user: public_User; + readonly [RelationKeys]?: 'tags' | 'user'; + }; + export type public_PostTag = { + postId: FieldOutputTypes['public']['PostTag']['postId']; + tagId: FieldOutputTypes['public']['PostTag']['tagId']; + post: public_Post; + tag: public_Tag; + readonly [RelationKeys]?: 'post' | 'tag'; + }; + export type public_Tag = { + id: FieldOutputTypes['public']['Tag']['id']; + label: FieldOutputTypes['public']['Tag']['label']; + posts: public_Post[]; + readonly [RelationKeys]?: 'posts'; + }; + export type public_Task = { + createdAt: FieldOutputTypes['public']['Task']['createdAt']; + description: FieldOutputTypes['public']['Task']['description']; + id: FieldOutputTypes['public']['Task']['id']; + status: FieldOutputTypes['public']['Task']['status']; + title: FieldOutputTypes['public']['Task']['title']; + type: 'bug' | 'feature'; + userId: FieldOutputTypes['public']['Task']['userId']; + user: public_User; + readonly [RelationKeys]?: 'user'; + }; + export type public_User = { + address: FieldOutputTypes['public']['User']['address']; + createdAt: FieldOutputTypes['public']['User']['createdAt']; + displayName: FieldOutputTypes['public']['User']['displayName']; + email: FieldOutputTypes['public']['User']['email']; + id: FieldOutputTypes['public']['User']['id']; + kind: FieldOutputTypes['public']['User']['kind']; + posts: public_Post[]; + tasks: public_AnyTask[]; + readonly [RelationKeys]?: 'posts' | 'tasks'; + }; + export type public_AnyTask = public_Bug | public_Feature; +} + +export declare const models: { + public: { + Bug: Models.public_Bug; + Feature: Models.public_Feature; + Post: Models.public_Post; + PostTag: Models.public_PostTag; + Tag: Models.public_Tag; + Task: Models.public_Task; + User: Models.public_User; + AnyTask: Models.public_AnyTask; + }; +}; + +export type TypeMaps = TypeMapsType< + CodecTypes, + QueryOperationTypes, + FieldOutputTypes, + FieldInputTypes, + StorageColumnTypes, + StorageColumnInputTypes, + AggregateTypes +>; + +type ContractBase = Omit< + ContractType<{ + readonly namespaces: { + readonly public: { + readonly id: 'public'; + readonly kind: 'postgres-schema'; + readonly entries: { + readonly table: { + readonly bug: { + columns: { + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly severity: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly stepsToRepro: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: true; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'bug'; + readonly columns: readonly ['id']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'task'; + readonly columns: readonly ['id']; + }; + }, + ]; + }; + readonly feature: { + columns: { + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly priority: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly targetRelease: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: true; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'feature'; + readonly columns: readonly ['id']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'task'; + readonly columns: readonly ['id']; + }; + }, + ]; + }; + readonly post: { + columns: { + readonly body: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: true; + readonly many: false; + }; + readonly createdAt: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-temporal@1'; + readonly nullable: false; + readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; + readonly many: false; + }; + readonly embedding: { + readonly dataType: 'pgvector/vector'; + readonly codecId: 'pg/vector@1'; + readonly nullable: true; + readonly typeParams: { readonly length: 1536 }; + readonly many: false; + }; + readonly expiresAt: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-temporal@1'; + readonly nullable: false; + readonly default: { + readonly kind: 'function'; + readonly expression: "(now() + '7 days'::interval)"; + }; + readonly many: false; + }; + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly impressionCount: { + readonly dataType: 'pg/int8'; + readonly codecId: 'pg/int8@1'; + readonly nullable: true; + readonly many: false; + }; + readonly priority: { + readonly dataType: 'pg/int4'; + readonly codecId: 'pg/int4@1'; + readonly nullable: false; + readonly default: { + readonly kind: 'literal'; + readonly value: DefaultLiteralValue<'pg/int4@1', 0>; + }; + readonly many: false; + }; + readonly reachScore: { + readonly dataType: 'pg/numeric'; + readonly codecId: 'pg/unboundedint@1'; + readonly nullable: true; + readonly many: false; + }; + readonly title: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly userId: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly viewCount: { + readonly dataType: 'pg/int8'; + readonly codecId: 'pg/int8number@1'; + readonly nullable: true; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly [ + { + readonly name: 'post_search_f03270be'; + readonly prefix: 'post_search'; + readonly columns: readonly ['title', 'body']; + readonly unique: false; + readonly type: 'fullText'; + readonly options: { + readonly language: 'english'; + readonly weightGroups: readonly [readonly ['title'], readonly ['body']]; + }; + }, + { + readonly name: 'post_title_search_e0dd1131'; + readonly prefix: 'post_title_search'; + readonly columns: readonly ['title']; + readonly unique: false; + readonly type: 'fullText'; + readonly options: { + readonly language: 'english'; + readonly weightGroups: readonly [readonly ['title']]; + }; + }, + { + readonly name: 'post_userId_idx_a489d58a'; + readonly prefix: 'post_userId_idx'; + readonly columns: readonly ['userId']; + readonly unique: false; + }, + ]; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'post'; + readonly columns: readonly ['userId']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'user'; + readonly columns: readonly ['id']; + }; + }, + ]; + }; + readonly post_tag: { + columns: { + readonly postId: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly tagId: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['postId', 'tagId'] }; + uniques: readonly []; + indexes: readonly [ + { + readonly name: 'post_tag_postId_idx_a7a72715'; + readonly prefix: 'post_tag_postId_idx'; + readonly columns: readonly ['postId']; + readonly unique: false; + }, + { + readonly name: 'post_tag_tagId_idx_86854244'; + readonly prefix: 'post_tag_tagId_idx'; + readonly columns: readonly ['tagId']; + readonly unique: false; + }, + ]; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'post_tag'; + readonly columns: readonly ['postId']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'post'; + readonly columns: readonly ['id']; + }; + }, + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'post_tag'; + readonly columns: readonly ['tagId']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'tag'; + readonly columns: readonly ['id']; + }; + }, + ]; + }; + readonly tag: { + columns: { + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly label: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly [{ readonly columns: readonly ['label'] }]; + indexes: readonly []; + foreignKeys: readonly []; + }; + readonly task: { + columns: { + readonly createdAt: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-temporal@1'; + readonly nullable: false; + readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; + readonly many: false; + }; + readonly description: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: true; + readonly many: false; + }; + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly status: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly default: { + readonly kind: 'literal'; + readonly value: DefaultLiteralValue<'pg/text@1', 'open'>; + }; + readonly many: false; + }; + readonly title: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly type: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly userId: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly [ + { + readonly name: 'task_userId_idx_a489d58a'; + readonly prefix: 'task_userId_idx'; + readonly columns: readonly ['userId']; + readonly unique: false; + }, + ]; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'task'; + readonly columns: readonly ['userId']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'user'; + readonly columns: readonly ['id']; + }; + }, + ]; + }; + readonly user: { + columns: { + readonly address: { + readonly dataType: 'pg/jsonb'; + readonly codecId: 'pg/jsonb@1'; + readonly nullable: true; + readonly many: false; + }; + readonly createdAt: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-temporal@1'; + readonly nullable: false; + readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; + readonly many: false; + }; + readonly displayName: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly email: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly kind: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly []; + }; + }; + readonly valueSet: { + readonly Priority: { readonly kind: 'valueSet'; readonly values: readonly [0, 1, 2] }; + readonly 'Task.type': { + readonly kind: 'valueSet'; + readonly values: readonly ['bug', 'feature']; + }; + readonly user_type: { + readonly kind: 'valueSet'; + readonly values: readonly ['admin', 'user']; + }; + }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + }; + }; + }; + }; + }; + readonly storageHash: StorageHash; + }>, + 'roots' | 'domain' +> & { + readonly target: 'postgres'; + readonly targetFamily: 'sql'; + readonly roots: { + readonly post: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; + readonly post_tag: { readonly namespace: 'public' & NamespaceId; readonly model: 'PostTag' }; + readonly tag: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; + readonly task: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; + readonly user: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; + }; + readonly domain: { + readonly namespaces: { + readonly public: { + readonly models: { + readonly Bug: { + readonly fields: { + readonly severity: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly stepsToRepro: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'bug'; + readonly namespaceId: 'public'; + readonly fields: { + readonly severity: { readonly column: 'severity' }; + readonly stepsToRepro: { readonly column: 'stepsToRepro' }; + }; + }; + readonly base: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; + }; + readonly Feature: { + readonly fields: { + readonly priority: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly targetRelease: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'feature'; + readonly namespaceId: 'public'; + readonly fields: { + readonly priority: { readonly column: 'priority' }; + readonly targetRelease: { readonly column: 'targetRelease' }; + }; + }; + readonly base: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; + }; + readonly Post: { + readonly fields: { + readonly body: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly createdAt: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-temporal@1'; + }; + }; + readonly embedding: { + readonly nullable: true; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/vector@1'; + readonly typeParams: { readonly length: 1536 }; + }; + }; + readonly expiresAt: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-temporal@1'; + }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly impressionCount: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int8@1' }; + }; + readonly priority: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int4@1' }; + }; + readonly reachScore: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/unboundedint@1' }; + }; + readonly title: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly userId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly viewCount: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int8number@1' }; + }; + }; + readonly relations: { + readonly tags: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; + readonly cardinality: 'N:M'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['postId']; + }; + readonly through: { + readonly table: 'post_tag'; + readonly namespaceId: 'public'; + readonly parentColumns: readonly ['postId']; + readonly childColumns: readonly ['tagId']; + readonly targetColumns: readonly ['id']; + }; + }; + readonly user: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['userId']; + readonly targetFields: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly table: 'post'; + readonly namespaceId: 'public'; + readonly fields: { + readonly body: { readonly column: 'body' }; + readonly createdAt: { readonly column: 'createdAt' }; + readonly embedding: { readonly column: 'embedding' }; + readonly expiresAt: { readonly column: 'expiresAt' }; + readonly id: { readonly column: 'id' }; + readonly impressionCount: { readonly column: 'impressionCount' }; + readonly priority: { readonly column: 'priority' }; + readonly reachScore: { readonly column: 'reachScore' }; + readonly title: { readonly column: 'title' }; + readonly userId: { readonly column: 'userId' }; + readonly viewCount: { readonly column: 'viewCount' }; + }; + }; + }; + readonly PostTag: { + readonly fields: { + readonly postId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly tagId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + }; + readonly relations: { + readonly post: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['postId']; + readonly targetFields: readonly ['id']; + }; + }; + readonly tag: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['tagId']; + readonly targetFields: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly table: 'post_tag'; + readonly namespaceId: 'public'; + readonly fields: { + readonly postId: { readonly column: 'postId' }; + readonly tagId: { readonly column: 'tagId' }; + }; + }; + }; + readonly Tag: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly label: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: { + readonly posts: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; + readonly cardinality: 'N:M'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['tagId']; + }; + readonly through: { + readonly table: 'post_tag'; + readonly namespaceId: 'public'; + readonly parentColumns: readonly ['tagId']; + readonly childColumns: readonly ['postId']; + readonly targetColumns: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly table: 'tag'; + readonly namespaceId: 'public'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly label: { readonly column: 'label' }; + }; + }; + }; + readonly Task: { + readonly fields: { + readonly createdAt: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-temporal@1'; + }; + }; + readonly description: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly status: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly title: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly type: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly userId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + }; + readonly relations: { + readonly user: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['userId']; + readonly targetFields: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly table: 'task'; + readonly namespaceId: 'public'; + readonly fields: { + readonly createdAt: { readonly column: 'createdAt' }; + readonly description: { readonly column: 'description' }; + readonly id: { readonly column: 'id' }; + readonly status: { readonly column: 'status' }; + readonly title: { readonly column: 'title' }; + readonly type: { readonly column: 'type' }; + readonly userId: { readonly column: 'userId' }; + }; + }; + readonly discriminator: { readonly field: 'type' }; + readonly variants: { + readonly bug: { readonly namespace: 'public' & NamespaceId; readonly model: 'Bug' }; + readonly feature: { + readonly namespace: 'public' & NamespaceId; + readonly model: 'Feature'; + }; + }; + }; + readonly User: { + readonly fields: { + readonly address: { + readonly nullable: true; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: 'public'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; + }; + readonly createdAt: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-temporal@1'; + }; + }; + readonly displayName: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly kind: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: { + readonly posts: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; + readonly cardinality: '1:N'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['userId']; + }; + }; + readonly tasks: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; + readonly cardinality: '1:N'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['userId']; + }; + }; + }; + readonly storage: { + readonly table: 'user'; + readonly namespaceId: 'public'; + readonly fields: { + readonly address: { readonly column: 'address' }; + readonly createdAt: { readonly column: 'createdAt' }; + readonly displayName: { readonly column: 'displayName' }; + readonly email: { readonly column: 'email' }; + readonly id: { readonly column: 'id' }; + readonly kind: { readonly column: 'kind' }; + }; + }; + }; + }; + readonly enum: { + readonly Priority: { + readonly codecId: 'pg/int4@1'; + readonly members: readonly [ + { readonly name: 'Low'; readonly value: 0 }, + { readonly name: 'High'; readonly value: 1 }, + { readonly name: 'Urgent'; readonly value: 2 }, + ]; + }; + readonly user_type: { + readonly codecId: 'pg/text@1'; + readonly members: readonly [ + { readonly name: 'admin'; readonly value: 'admin' }, + { readonly name: 'user'; readonly value: 'user' }, + ]; + }; + }; + readonly enumMemberTypes?: { + readonly Priority: readonly [ + { readonly name: 'Low'; readonly value: 0 }, + { readonly name: 'High'; readonly value: 1 }, + { readonly name: 'Urgent'; readonly value: 2 }, + ]; + readonly user_type: readonly [ + { readonly name: 'admin'; readonly value: 'admin' }, + { readonly name: 'user'; readonly value: 'user' }, + ]; + }; + }; + }; + }; + readonly capabilities: { + readonly postgres: { + readonly distinctOn: true; + readonly forKeyShare: true; + readonly forNoKeyUpdate: true; + readonly jsonAgg: true; + readonly lateral: true; + readonly limit: true; + readonly orderBy: true; + readonly 'pgvector.cosine': true; + readonly returning: true; + }; + readonly sql: { + readonly checkConstraint: true; + readonly defaultInInsert: true; + readonly enums: true; + readonly forShare: true; + readonly forUpdate: true; + readonly insertOnConflictSkip: true; + readonly insertOnConflictWithoutTarget: true; + readonly lateral: true; + readonly lockNowait: true; + readonly lockOf: true; + readonly lockSkipLocked: true; + readonly returning: true; + readonly scalarList: true; + }; + }; + readonly extensions: { + readonly 'demo/engagement-stats': { + readonly familyId: 'sql'; + readonly id: 'demo/engagement-stats'; + readonly kind: 'extension'; + readonly targetId: 'postgres'; + readonly types: { + readonly aggregateDescriptors: readonly [ + { + readonly input: { readonly kind: 'trait'; readonly trait: 'numeric' }; + readonly lower: unknown; + readonly nullable: true; + readonly operation: 'stddev'; + readonly output: { readonly codecId: 'pg/numeric@1'; readonly kind: 'codec' }; + }, + ]; + }; + readonly version: '0.0.1'; + }; + readonly pgvector: { + readonly capabilities: { readonly postgres: { readonly 'pgvector.cosine': true } }; + readonly familyId: 'sql'; + readonly id: 'pgvector'; + readonly kind: 'extension'; + readonly targetId: 'postgres'; + readonly types: { + readonly codecTypes: { + readonly import: { + readonly alias: 'PgVectorTypes'; + readonly named: 'CodecTypes'; + readonly package: '@internal/extension-pgvector/codec-types'; + }; + readonly parameterizedOutputTypes: { + readonly alias: 'PgVectorParameterizedOutputTypes'; + readonly named: 'ParameterizedOutputTypesForTypeParams'; + readonly package: '@internal/extension-pgvector/codec-types'; + }; + }; + readonly operationTypes: { + readonly import: { + readonly alias: 'PgVectorOperationTypes'; + readonly named: 'OperationTypes'; + readonly package: '@internal/extension-pgvector/operation-types'; + }; + }; + readonly queryOperationTypes: { + readonly import: { + readonly alias: 'PgVectorQueryOperationTypes'; + readonly named: 'QueryOperationTypes'; + readonly package: '@internal/extension-pgvector/operation-types'; + }; + }; + }; + readonly version: '0.0.1'; + }; + }; + readonly execution: { + readonly executionHash: ExecutionHash; + readonly mutations: { + readonly defaults: readonly [ + { + readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; + readonly ref: { + readonly entry: 'post'; + readonly field: 'id'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; + readonly ref: { + readonly entry: 'tag'; + readonly field: 'id'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; + readonly ref: { + readonly entry: 'task'; + readonly field: 'id'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; + readonly ref: { + readonly entry: 'user'; + readonly field: 'id'; + readonly namespace: 'public'; + }; + }, + ]; + }; + }; + readonly meta: {}; + readonly profileHash: ProfileHash; +}; + +export type Contract = ContractWithTypeMaps; + +export type Namespaces = Contract['storage']['namespaces']; diff --git a/examples/prisma-8-demo/migrations/snapshots/e1916bd73bb2388302d81a1eaa17125c0cedb6ce47b489a7adeeb41c2ef50ebf/contract.json b/examples/prisma-8-demo/migrations/snapshots/e1916bd73bb2388302d81a1eaa17125c0cedb6ce47b489a7adeeb41c2ef50ebf/contract.json new file mode 100644 index 000000000000..12ad86c15dc9 --- /dev/null +++ b/examples/prisma-8-demo/migrations/snapshots/e1916bd73bb2388302d81a1eaa17125c0cedb6ce47b489a7adeeb41c2ef50ebf/contract.json @@ -0,0 +1 @@ +{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{"postgres":{"distinctOn":true,"forKeyShare":true,"forNoKeyUpdate":true,"jsonAgg":true,"lateral":true,"limit":true,"orderBy":true,"pgvector.cosine":true,"returning":true},"sql":{"checkConstraint":true,"defaultInInsert":true,"enums":true,"forShare":true,"forUpdate":true,"insertOnConflictSkip":true,"insertOnConflictWithoutTarget":true,"lateral":true,"lockNowait":true,"lockOf":true,"lockSkipLocked":true,"returning":true,"scalarList":true}},"domain":{"namespaces":{"public":{"enum":{"Priority":{"codecId":"pg/int4@1","members":[{"name":"Low","value":0},{"name":"High","value":1},{"name":"Urgent","value":2}]},"user_type":{"codecId":"pg/text@1","members":[{"name":"admin","value":"admin"},{"name":"user","value":"user"}]}},"models":{"Bug":{"base":{"model":"Task","namespace":"public"},"fields":{"severity":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"stepsToRepro":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"severity":{"column":"severity"},"stepsToRepro":{"column":"stepsToRepro"}},"namespaceId":"public","table":"bug"}},"Feature":{"base":{"model":"Task","namespace":"public"},"fields":{"priority":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"targetRelease":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"priority":{"column":"priority"},"targetRelease":{"column":"targetRelease"}},"namespaceId":"public","table":"feature"}},"Post":{"fields":{"body":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"embedding":{"nullable":true,"type":{"codecId":"pg/vector@1","kind":"scalar","typeParams":{"length":1536}}},"expiresAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"impressionCount":{"nullable":true,"type":{"codecId":"pg/int8@1","kind":"scalar"}},"priority":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"Priority","namespaceId":"public","plane":"domain"}},"reachScore":{"nullable":true,"type":{"codecId":"pg/unboundedint@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"viewCount":{"nullable":true,"type":{"codecId":"pg/int8number@1","kind":"scalar"}}},"relations":{"tags":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["postId"]},"through":{"childColumns":["tagId"],"namespaceId":"public","parentColumns":["postId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Tag","namespace":"public"}},"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"body":{"column":"body"},"createdAt":{"column":"createdAt"},"embedding":{"column":"embedding"},"expiresAt":{"column":"expiresAt"},"id":{"column":"id"},"impressionCount":{"column":"impressionCount"},"priority":{"column":"priority"},"reachScore":{"column":"reachScore"},"title":{"column":"title"},"userId":{"column":"userId"},"viewCount":{"column":"viewCount"}},"namespaceId":"public","table":"post"}},"PostTag":{"fields":{"postId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"tagId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"post":{"cardinality":"N:1","nullable":false,"on":{"localFields":["postId"],"targetFields":["id"]},"to":{"model":"Post","namespace":"public"}},"tag":{"cardinality":"N:1","nullable":false,"on":{"localFields":["tagId"],"targetFields":["id"]},"to":{"model":"Tag","namespace":"public"}}},"storage":{"fields":{"postId":{"column":"postId"},"tagId":{"column":"tagId"}},"namespaceId":"public","table":"post_tag"}},"Tag":{"fields":{"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"label":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{"posts":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["tagId"]},"through":{"childColumns":["postId"],"namespaceId":"public","parentColumns":["tagId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Post","namespace":"public"}}},"storage":{"fields":{"id":{"column":"id"},"label":{"column":"label"}},"namespaceId":"public","table":"tag"}},"Task":{"discriminator":{"field":"type"},"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"description":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"status":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"type":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"description":{"column":"description"},"id":{"column":"id"},"status":{"column":"status"},"title":{"column":"title"},"type":{"column":"type"},"userId":{"column":"userId"}},"namespaceId":"public","table":"task"},"variants":{"bug":{"model":"Bug","namespace":"public"},"feature":{"model":"Feature","namespace":"public"}}},"User":{"fields":{"address":{"nullable":true,"type":{"entityKind":"valueObject","entityName":"Address","kind":"valueObject","namespaceId":"public","plane":"storage"}},"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"displayName":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"user_type","namespaceId":"public","plane":"domain"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Post","namespace":"public"}},"tasks":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Task","namespace":"public"}}},"storage":{"fields":{"address":{"column":"address"},"createdAt":{"column":"createdAt"},"displayName":{"column":"displayName"},"email":{"column":"email"},"id":{"column":"id"},"kind":{"column":"kind"}},"namespaceId":"public","table":"user"}}}}}},"execution":{"executionHash":"eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655","mutations":{"defaults":[{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"post","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"tag","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"task","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"user","field":"id","namespace":"public"}}]}},"extensions":{"demo/engagement-stats":{"familyId":"sql","id":"demo/engagement-stats","kind":"extension","targetId":"postgres","types":{"aggregateDescriptors":[{"input":{"kind":"trait","trait":"numeric"},"nullable":true,"operation":"stddev","output":{"codecId":"pg/numeric@1","kind":"codec"}}]},"version":"0.0.1"},"pgvector":{"capabilities":{"postgres":{"pgvector.cosine":true}},"familyId":"sql","id":"pgvector","kind":"extension","targetId":"postgres","types":{"codecTypes":{"import":{"alias":"PgVectorTypes","named":"CodecTypes","package":"@internal/extension-pgvector/codec-types"},"parameterizedOutputTypes":{"alias":"PgVectorParameterizedOutputTypes","named":"ParameterizedOutputTypesForTypeParams","package":"@internal/extension-pgvector/codec-types"}},"operationTypes":{"import":{"alias":"PgVectorOperationTypes","named":"OperationTypes","package":"@internal/extension-pgvector/operation-types"}},"queryOperationTypes":{"import":{"alias":"PgVectorQueryOperationTypes","named":"QueryOperationTypes","package":"@internal/extension-pgvector/operation-types"}}},"version":"0.0.1"}},"meta":{},"profileHash":"3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2","roots":{"post":{"model":"Post","namespace":"public"},"post_tag":{"model":"PostTag","namespace":"public"},"tag":{"model":"Tag","namespace":"public"},"task":{"model":"Task","namespace":"public"},"user":{"model":"User","namespace":"public"}},"schemaVersion":"1","storage":{"namespaces":{"public":{"entries":{"table":{"bug":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"severity":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"stepsToRepro":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"bug"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"feature":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"priority":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"targetRelease":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"feature"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"post":{"checks":[{"expression":"\"priority\" IN (0, 1, 2)","name":"post_priority_check_b236ace9","prefix":"post_priority_check"}],"columns":{"body":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true},"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"embedding":{"codecId":"pg/vector@1","dataType":"pgvector/vector","nullable":true,"typeParams":{"length":1536}},"expiresAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"(now() + '7 days'::interval)","kind":"function"},"nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"impressionCount":{"codecId":"pg/int8@1","dataType":"pg/int8","nullable":true},"priority":{"codecId":"pg/int4@1","dataType":"pg/int4","default":{"kind":"literal","value":0},"nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Priority","namespaceId":"public","plane":"storage"}},"reachScore":{"codecId":"pg/unboundedint@1","dataType":"pg/numeric","nullable":true},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"viewCount":{"codecId":"pg/int8number@1","dataType":"pg/int8","nullable":true}},"foreignKeys":[{"index":{"name":"post_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"post"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["title","body"],"name":"post_search_f03270be","options":{"language":"english","weightGroups":[["title"],["body"]]},"prefix":"post_search","type":"fullText","unique":false},{"columns":["title"],"name":"post_title_search_e0dd1131","options":{"language":"english","weightGroups":[["title"]]},"prefix":"post_title_search","type":"fullText","unique":false},{"columns":["userId"],"name":"post_userId_idx_a489d58a","prefix":"post_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"post_tag":{"columns":{"postId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"tagId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"post_tag_postId_idx_a7a72715"},"source":{"columns":["postId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"post"}},{"index":{"name":"post_tag_tagId_idx_86854244"},"source":{"columns":["tagId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"tag"}}],"indexes":[{"columns":["postId"],"name":"post_tag_postId_idx_a7a72715","prefix":"post_tag_postId_idx","unique":false},{"columns":["tagId"],"name":"post_tag_tagId_idx_86854244","prefix":"post_tag_tagId_idx","unique":false}],"primaryKey":{"columns":["postId","tagId"]},"uniques":[]},"tag":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"label":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[{"columns":["label"]}]},"task":{"checks":[{"expression":"\"type\" IN ('bug', 'feature')","name":"task_type_check_cda3f5e7","prefix":"task_type_check"}],"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"description":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"status":{"codecId":"pg/text@1","dataType":"pg/text","default":{"kind":"literal","value":"open"},"nullable":false},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"type":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Task.type","namespaceId":"public","plane":"storage"}},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"task_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"task"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"task_userId_idx_a489d58a","prefix":"task_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"user":{"checks":[{"expression":"\"address\" IS NULL OR jsonb_path_match(\"address\", 'strict $.type() == \"null\" || ($.type() == \"object\" && exists($.keyvalue() ? (@.key == \"city\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"country\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"street\" && (@.value.type() == \"string\"))) && !exists($.keyvalue() ? (@.key == \"zip\" && !(@.value.type() == \"null\" || (@.value.type() == \"string\")))) && !exists($.keyvalue() ? (@.key != \"city\" && @.key != \"country\" && @.key != \"street\" && @.key != \"zip\")))') IS TRUE","name":"user_address_shape_69432dc1","prefix":"user_address_shape"},{"expression":"\"kind\" IN ('admin', 'user')","name":"user_kind_check_836d43ef","prefix":"user_kind_check"}],"columns":{"address":{"codecId":"pg/jsonb@1","dataType":"pg/jsonb","nullable":true},"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"displayName":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"email":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"kind":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"user_type","namespaceId":"public","plane":"storage"}}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]}},"valueObject":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}}}},"valueSet":{"Priority":{"kind":"valueSet","values":[0,1,2]},"Task.type":{"kind":"valueSet","values":["bug","feature"]},"user_type":{"kind":"valueSet","values":["admin","user"]}}},"id":"public","kind":"postgres-schema"}},"storageHash":"e1916bd73bb2388302d81a1eaa17125c0cedb6ce47b489a7adeeb41c2ef50ebf"},"target":"postgres","targetFamily":"sql"} diff --git a/examples/prisma-8-demo/migrations/snapshots/fc039db97d116c1f5e6e56af25521c69e05232b49dc6e0058c9e3d3fb93eb263/contract.d.ts b/examples/prisma-8-demo/migrations/snapshots/fc039db97d116c1f5e6e56af25521c69e05232b49dc6e0058c9e3d3fb93eb263/contract.d.ts new file mode 100644 index 000000000000..2a5d5f3a2c78 --- /dev/null +++ b/examples/prisma-8-demo/migrations/snapshots/fc039db97d116c1f5e6e56af25521c69e05232b49dc6e0058c9e3d3fb93eb263/contract.d.ts @@ -0,0 +1,1365 @@ +// ⚠️ GENERATED FILE - DO NOT EDIT +// This file is automatically generated by 'prisma contract emit'. +// To regenerate, run: prisma contract emit +import type { + CodecTypes as PgVectorTypes, + ParameterizedOutputTypesForTypeParams as PgVectorParameterizedOutputTypes, +} from '@prisma/orm-extension-pgvector/codec-types'; +import type { QueryOperationTypes as PgVectorQueryOperationTypes } from '@prisma/orm-extension-pgvector/operation-types'; +import type { + CodecTypes as PgTypes, + ParameterizedOutputTypesForTypeParams as PgParameterizedOutputTypes, +} from '@prisma/orm-postgres/target/codec-types'; +import type { QueryOperationTypes as PgTargetQueryOps } from '@prisma/orm-postgres/target/operation-types'; + +import type { + ColumnTypesFromStorage, + ContractWithTypeMaps, + RelationKeys, + TypeMaps as TypeMapsType, +} from '@prisma/orm-postgres/family-contract/types'; +import type { + Contract as ContractType, + ExecutionHashBase, + FieldTypesFromDomain, + NamespaceId, + ProfileHashBase, + StorageHashBase, + ValueObjectTypesFromStorage, +} from '@prisma/orm-postgres/contract/types'; + +export type StorageHash = + StorageHashBase<'fc039db97d116c1f5e6e56af25521c69e05232b49dc6e0058c9e3d3fb93eb263'>; +export type ExecutionHash = + ExecutionHashBase<'eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655'>; +export type ProfileHash = + ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; + +export type CodecTypes = PgTypes & PgVectorTypes; +export type LaneCodecTypes = CodecTypes; +export type QueryOperationTypes = PgTargetQueryOps & + PgVectorQueryOperationTypes; +export type AggregateTypes = { + readonly avg: { + readonly byCodec: { + readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + }; + }; + readonly avgDecimal: { + readonly byCodec: { + readonly 'pg/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + }; + }; + readonly count: { + readonly byCodec: {}; + readonly withoutInput: { readonly output: 'pg/int8number@1'; readonly nullable: false }; + readonly anyInput: { readonly output: 'pg/int8number@1'; readonly nullable: false }; + }; + readonly countBigInt: { + readonly byCodec: {}; + readonly withoutInput: { readonly output: 'pg/int8@1'; readonly nullable: false }; + readonly anyInput: { readonly output: 'pg/int8@1'; readonly nullable: false }; + }; + readonly max: { + readonly byCodec: { + readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true }; + readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true }; + readonly 'pg/date-temporal@1': { + readonly output: 'pg/date-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true }; + readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; + readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { + readonly output: 'pg/time-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-string@1': { + readonly output: 'pg/timestamp-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-temporal@1': { + readonly output: 'pg/timestamp-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-date@1': { + readonly output: 'pg/timestamptz-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-string@1': { + readonly output: 'pg/timestamptz-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-temporal@1': { + readonly output: 'pg/timestamptz-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true }; + readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true }; + readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + }; + }; + readonly min: { + readonly byCodec: { + readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true }; + readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true }; + readonly 'pg/date-temporal@1': { + readonly output: 'pg/date-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true }; + readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; + readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { + readonly output: 'pg/time-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-string@1': { + readonly output: 'pg/timestamp-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-temporal@1': { + readonly output: 'pg/timestamp-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-date@1': { + readonly output: 'pg/timestamptz-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-string@1': { + readonly output: 'pg/timestamptz-string@1'; + readonly nullable: true; + }; + readonly 'pg/timestamptz-temporal@1': { + readonly output: 'pg/timestamptz-temporal@1'; + readonly nullable: true; + }; + readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true }; + readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true }; + readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true }; + }; + }; + readonly stddev: { + readonly byCodec: { + readonly 'pg/float@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'sql/float@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + }; + }; + readonly sum: { + readonly byCodec: { + readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true }; + readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; + readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; + readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'sql/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + }; + }; + readonly sumBigInt: { + readonly byCodec: { + readonly 'pg/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int2@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int4@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + readonly 'pg/int8@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true }; + readonly 'pg/int8number@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true }; + readonly 'pg/unboundedint@1': { + readonly output: 'pg/unboundedint@1'; + readonly nullable: true; + }; + readonly 'sql/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; + }; + }; +}; +type DefaultLiteralValue = CodecId extends keyof CodecTypes + ? Encoded extends CodecTypes[CodecId]['json'] + ? Encoded + : CodecTypes[CodecId]['json'] + : Encoded; +type ParameterizedOutputTypeFunctions = + PgParameterizedOutputTypes | PgVectorParameterizedOutputTypes; +type ValueObjectOutputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output' +>; +type ValueObjectInputTypes = ValueObjectTypesFromStorage< + ContractBase['storage']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input' +>; +export type AddressOutput = ValueObjectOutputTypes['public']['Address']; +export type AddressInput = ValueObjectInputTypes['public']['Address']; +type FieldTypeOverrides = { + readonly public: { + readonly models: { + readonly Post: { + readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 }; + }; + readonly Task: { + readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; + }; + readonly User: { + readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; + }; + }; + }; +}; +export type FieldOutputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output', + ContractBase['storage']['namespaces'] +>; +export type FieldInputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input', + ContractBase['storage']['namespaces'] +>; +type ColumnTypeOverrides = { + readonly public: { + readonly post: { readonly priority: { readonly output: 0 | 1 | 2; readonly input: 0 | 1 | 2 } }; + readonly task: { + readonly type: { readonly output: 'bug' | 'feature'; readonly input: 'bug' | 'feature' }; + }; + readonly user: { + readonly kind: { readonly output: 'admin' | 'user'; readonly input: 'admin' | 'user' }; + }; + }; +}; +export type StorageColumnTypes = ColumnTypesFromStorage< + ContractBase['storage'], + CodecTypes, + ParameterizedOutputTypeFunctions, + ColumnTypeOverrides, + 'output' +>; +export type StorageColumnInputTypes = ColumnTypesFromStorage< + ContractBase['storage'], + CodecTypes, + ParameterizedOutputTypeFunctions, + ColumnTypeOverrides, + 'input' +>; + +export namespace Models { + export type public_Bug = { + createdAt: FieldOutputTypes['public']['Task']['createdAt']; + description: FieldOutputTypes['public']['Task']['description']; + id: FieldOutputTypes['public']['Task']['id']; + status: FieldOutputTypes['public']['Task']['status']; + title: FieldOutputTypes['public']['Task']['title']; + type: 'bug'; + userId: FieldOutputTypes['public']['Task']['userId']; + severity: FieldOutputTypes['public']['Bug']['severity']; + stepsToRepro: FieldOutputTypes['public']['Bug']['stepsToRepro']; + user: public_User; + readonly [RelationKeys]?: 'user'; + }; + export type public_Feature = { + createdAt: FieldOutputTypes['public']['Task']['createdAt']; + description: FieldOutputTypes['public']['Task']['description']; + id: FieldOutputTypes['public']['Task']['id']; + status: FieldOutputTypes['public']['Task']['status']; + title: FieldOutputTypes['public']['Task']['title']; + type: 'feature'; + userId: FieldOutputTypes['public']['Task']['userId']; + priority: FieldOutputTypes['public']['Feature']['priority']; + targetRelease: FieldOutputTypes['public']['Feature']['targetRelease']; + user: public_User; + readonly [RelationKeys]?: 'user'; + }; + export type public_Post = { + createdAt: FieldOutputTypes['public']['Post']['createdAt']; + embedding: FieldOutputTypes['public']['Post']['embedding']; + id: FieldOutputTypes['public']['Post']['id']; + priority: FieldOutputTypes['public']['Post']['priority']; + title: FieldOutputTypes['public']['Post']['title']; + userId: FieldOutputTypes['public']['Post']['userId']; + tags: public_Tag[]; + user: public_User; + readonly [RelationKeys]?: 'tags' | 'user'; + }; + export type public_PostTag = { + postId: FieldOutputTypes['public']['PostTag']['postId']; + tagId: FieldOutputTypes['public']['PostTag']['tagId']; + post: public_Post; + tag: public_Tag; + readonly [RelationKeys]?: 'post' | 'tag'; + }; + export type public_Tag = { + id: FieldOutputTypes['public']['Tag']['id']; + label: FieldOutputTypes['public']['Tag']['label']; + posts: public_Post[]; + readonly [RelationKeys]?: 'posts'; + }; + export type public_Task = { + createdAt: FieldOutputTypes['public']['Task']['createdAt']; + description: FieldOutputTypes['public']['Task']['description']; + id: FieldOutputTypes['public']['Task']['id']; + status: FieldOutputTypes['public']['Task']['status']; + title: FieldOutputTypes['public']['Task']['title']; + type: 'bug' | 'feature'; + userId: FieldOutputTypes['public']['Task']['userId']; + user: public_User; + readonly [RelationKeys]?: 'user'; + }; + export type public_User = { + address: FieldOutputTypes['public']['User']['address']; + createdAt: FieldOutputTypes['public']['User']['createdAt']; + displayName: FieldOutputTypes['public']['User']['displayName']; + email: FieldOutputTypes['public']['User']['email']; + id: FieldOutputTypes['public']['User']['id']; + kind: FieldOutputTypes['public']['User']['kind']; + posts: public_Post[]; + tasks: public_AnyTask[]; + readonly [RelationKeys]?: 'posts' | 'tasks'; + }; + export type public_AnyTask = public_Bug | public_Feature; +} + +export declare const models: { + public: { + Bug: Models.public_Bug; + Feature: Models.public_Feature; + Post: Models.public_Post; + PostTag: Models.public_PostTag; + Tag: Models.public_Tag; + Task: Models.public_Task; + User: Models.public_User; + AnyTask: Models.public_AnyTask; + }; +}; + +export type TypeMaps = TypeMapsType< + CodecTypes, + QueryOperationTypes, + FieldOutputTypes, + FieldInputTypes, + StorageColumnTypes, + StorageColumnInputTypes, + AggregateTypes +>; + +type ContractBase = Omit< + ContractType<{ + readonly namespaces: { + readonly public: { + readonly id: 'public'; + readonly kind: 'postgres-schema'; + readonly entries: { + readonly table: { + readonly bug: { + columns: { + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly severity: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly stepsToRepro: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: true; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'bug'; + readonly columns: readonly ['id']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'task'; + readonly columns: readonly ['id']; + }; + }, + ]; + }; + readonly feature: { + columns: { + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly priority: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly targetRelease: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: true; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'feature'; + readonly columns: readonly ['id']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'task'; + readonly columns: readonly ['id']; + }; + }, + ]; + }; + readonly post: { + columns: { + readonly createdAt: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-temporal@1'; + readonly nullable: false; + readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; + readonly many: false; + }; + readonly embedding: { + readonly dataType: 'pgvector/vector'; + readonly codecId: 'pg/vector@1'; + readonly nullable: true; + readonly typeParams: { readonly length: 1536 }; + readonly many: false; + }; + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly priority: { + readonly dataType: 'pg/int4'; + readonly codecId: 'pg/int4@1'; + readonly nullable: false; + readonly default: { + readonly kind: 'literal'; + readonly value: DefaultLiteralValue<'pg/int4@1', 0>; + }; + readonly many: false; + }; + readonly title: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly userId: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly [ + { + readonly name: 'post_userId_idx_a489d58a'; + readonly prefix: 'post_userId_idx'; + readonly columns: readonly ['userId']; + readonly unique: false; + }, + ]; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'post'; + readonly columns: readonly ['userId']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'user'; + readonly columns: readonly ['id']; + }; + }, + ]; + }; + readonly post_tag: { + columns: { + readonly postId: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly tagId: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['postId', 'tagId'] }; + uniques: readonly []; + indexes: readonly [ + { + readonly name: 'post_tag_postId_idx_a7a72715'; + readonly prefix: 'post_tag_postId_idx'; + readonly columns: readonly ['postId']; + readonly unique: false; + }, + { + readonly name: 'post_tag_tagId_idx_86854244'; + readonly prefix: 'post_tag_tagId_idx'; + readonly columns: readonly ['tagId']; + readonly unique: false; + }, + ]; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'post_tag'; + readonly columns: readonly ['postId']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'post'; + readonly columns: readonly ['id']; + }; + }, + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'post_tag'; + readonly columns: readonly ['tagId']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'tag'; + readonly columns: readonly ['id']; + }; + }, + ]; + }; + readonly tag: { + columns: { + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly label: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly [{ readonly columns: readonly ['label'] }]; + indexes: readonly []; + foreignKeys: readonly []; + }; + readonly task: { + columns: { + readonly createdAt: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-temporal@1'; + readonly nullable: false; + readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; + readonly many: false; + }; + readonly description: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: true; + readonly many: false; + }; + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly status: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly default: { + readonly kind: 'literal'; + readonly value: DefaultLiteralValue<'pg/text@1', 'open'>; + }; + readonly many: false; + }; + readonly title: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly type: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly userId: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly [ + { + readonly name: 'task_userId_idx_a489d58a'; + readonly prefix: 'task_userId_idx'; + readonly columns: readonly ['userId']; + readonly unique: false; + }, + ]; + foreignKeys: readonly [ + { + readonly source: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'task'; + readonly columns: readonly ['userId']; + }; + readonly target: { + readonly namespaceId: 'public' & NamespaceId; + readonly tableName: 'user'; + readonly columns: readonly ['id']; + }; + }, + ]; + }; + readonly user: { + columns: { + readonly address: { + readonly dataType: 'pg/jsonb'; + readonly codecId: 'pg/jsonb@1'; + readonly nullable: true; + readonly many: false; + }; + readonly createdAt: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-temporal@1'; + readonly nullable: false; + readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; + readonly many: false; + }; + readonly displayName: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly email: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + readonly id: { + readonly dataType: 'pg/uuid'; + readonly codecId: 'pg/uuid@1'; + readonly nullable: false; + readonly many: false; + }; + readonly kind: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly []; + }; + }; + readonly valueSet: { + readonly Priority: { readonly kind: 'valueSet'; readonly values: readonly [0, 1, 2] }; + readonly 'Task.type': { + readonly kind: 'valueSet'; + readonly values: readonly ['bug', 'feature']; + }; + readonly user_type: { + readonly kind: 'valueSet'; + readonly values: readonly ['admin', 'user']; + }; + }; + readonly valueObject: { + readonly Address: { + readonly fields: { + readonly city: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly country: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly street: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly zip: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + }; + }; + }; + }; + }; + readonly storageHash: StorageHash; + }>, + 'roots' | 'domain' +> & { + readonly target: 'postgres'; + readonly targetFamily: 'sql'; + readonly roots: { + readonly post: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; + readonly post_tag: { readonly namespace: 'public' & NamespaceId; readonly model: 'PostTag' }; + readonly tag: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; + readonly task: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; + readonly user: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; + }; + readonly domain: { + readonly namespaces: { + readonly public: { + readonly models: { + readonly Bug: { + readonly fields: { + readonly severity: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly stepsToRepro: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'bug'; + readonly namespaceId: 'public'; + readonly fields: { + readonly severity: { readonly column: 'severity' }; + readonly stepsToRepro: { readonly column: 'stepsToRepro' }; + }; + }; + readonly base: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; + }; + readonly Feature: { + readonly fields: { + readonly priority: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly targetRelease: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'feature'; + readonly namespaceId: 'public'; + readonly fields: { + readonly priority: { readonly column: 'priority' }; + readonly targetRelease: { readonly column: 'targetRelease' }; + }; + }; + readonly base: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; + }; + readonly Post: { + readonly fields: { + readonly createdAt: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-temporal@1'; + }; + }; + readonly embedding: { + readonly nullable: true; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/vector@1'; + readonly typeParams: { readonly length: 1536 }; + }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly priority: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int4@1' }; + }; + readonly title: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly userId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + }; + readonly relations: { + readonly tags: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; + readonly cardinality: 'N:M'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['postId']; + }; + readonly through: { + readonly table: 'post_tag'; + readonly namespaceId: 'public'; + readonly parentColumns: readonly ['postId']; + readonly childColumns: readonly ['tagId']; + readonly targetColumns: readonly ['id']; + }; + }; + readonly user: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['userId']; + readonly targetFields: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly table: 'post'; + readonly namespaceId: 'public'; + readonly fields: { + readonly createdAt: { readonly column: 'createdAt' }; + readonly embedding: { readonly column: 'embedding' }; + readonly id: { readonly column: 'id' }; + readonly priority: { readonly column: 'priority' }; + readonly title: { readonly column: 'title' }; + readonly userId: { readonly column: 'userId' }; + }; + }; + }; + readonly PostTag: { + readonly fields: { + readonly postId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly tagId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + }; + readonly relations: { + readonly post: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['postId']; + readonly targetFields: readonly ['id']; + }; + }; + readonly tag: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Tag' }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['tagId']; + readonly targetFields: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly table: 'post_tag'; + readonly namespaceId: 'public'; + readonly fields: { + readonly postId: { readonly column: 'postId' }; + readonly tagId: { readonly column: 'tagId' }; + }; + }; + }; + readonly Tag: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly label: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: { + readonly posts: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; + readonly cardinality: 'N:M'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['tagId']; + }; + readonly through: { + readonly table: 'post_tag'; + readonly namespaceId: 'public'; + readonly parentColumns: readonly ['tagId']; + readonly childColumns: readonly ['postId']; + readonly targetColumns: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly table: 'tag'; + readonly namespaceId: 'public'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly label: { readonly column: 'label' }; + }; + }; + }; + readonly Task: { + readonly fields: { + readonly createdAt: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-temporal@1'; + }; + }; + readonly description: { + readonly nullable: true; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly status: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly title: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly type: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly userId: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + }; + readonly relations: { + readonly user: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; + readonly cardinality: 'N:1'; + readonly nullable: false; + readonly on: { + readonly localFields: readonly ['userId']; + readonly targetFields: readonly ['id']; + }; + }; + }; + readonly storage: { + readonly table: 'task'; + readonly namespaceId: 'public'; + readonly fields: { + readonly createdAt: { readonly column: 'createdAt' }; + readonly description: { readonly column: 'description' }; + readonly id: { readonly column: 'id' }; + readonly status: { readonly column: 'status' }; + readonly title: { readonly column: 'title' }; + readonly type: { readonly column: 'type' }; + readonly userId: { readonly column: 'userId' }; + }; + }; + readonly discriminator: { readonly field: 'type' }; + readonly variants: { + readonly bug: { readonly namespace: 'public' & NamespaceId; readonly model: 'Bug' }; + readonly feature: { + readonly namespace: 'public' & NamespaceId; + readonly model: 'Feature'; + }; + }; + }; + readonly User: { + readonly fields: { + readonly address: { + readonly nullable: true; + readonly type: { + readonly kind: 'valueObject'; + readonly plane: 'storage'; + readonly namespaceId: 'public'; + readonly entityKind: 'valueObject'; + readonly entityName: 'Address'; + }; + }; + readonly createdAt: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-temporal@1'; + }; + }; + readonly displayName: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly email: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/uuid@1' }; + }; + readonly kind: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + }; + readonly relations: { + readonly posts: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' }; + readonly cardinality: '1:N'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['userId']; + }; + }; + readonly tasks: { + readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Task' }; + readonly cardinality: '1:N'; + readonly on: { + readonly localFields: readonly ['id']; + readonly targetFields: readonly ['userId']; + }; + }; + }; + readonly storage: { + readonly table: 'user'; + readonly namespaceId: 'public'; + readonly fields: { + readonly address: { readonly column: 'address' }; + readonly createdAt: { readonly column: 'createdAt' }; + readonly displayName: { readonly column: 'displayName' }; + readonly email: { readonly column: 'email' }; + readonly id: { readonly column: 'id' }; + readonly kind: { readonly column: 'kind' }; + }; + }; + }; + }; + readonly enum: { + readonly Priority: { + readonly codecId: 'pg/int4@1'; + readonly members: readonly [ + { readonly name: 'Low'; readonly value: 0 }, + { readonly name: 'High'; readonly value: 1 }, + { readonly name: 'Urgent'; readonly value: 2 }, + ]; + }; + readonly user_type: { + readonly codecId: 'pg/text@1'; + readonly members: readonly [ + { readonly name: 'admin'; readonly value: 'admin' }, + { readonly name: 'user'; readonly value: 'user' }, + ]; + }; + }; + readonly enumMemberTypes?: { + readonly Priority: readonly [ + { readonly name: 'Low'; readonly value: 0 }, + { readonly name: 'High'; readonly value: 1 }, + { readonly name: 'Urgent'; readonly value: 2 }, + ]; + readonly user_type: readonly [ + { readonly name: 'admin'; readonly value: 'admin' }, + { readonly name: 'user'; readonly value: 'user' }, + ]; + }; + }; + }; + }; + readonly capabilities: { + readonly postgres: { + readonly distinctOn: true; + readonly forKeyShare: true; + readonly forNoKeyUpdate: true; + readonly jsonAgg: true; + readonly lateral: true; + readonly limit: true; + readonly orderBy: true; + readonly 'pgvector.cosine': true; + readonly returning: true; + }; + readonly sql: { + readonly checkConstraint: true; + readonly defaultInInsert: true; + readonly enums: true; + readonly forShare: true; + readonly forUpdate: true; + readonly insertOnConflictSkip: true; + readonly insertOnConflictWithoutTarget: true; + readonly lateral: true; + readonly lockNowait: true; + readonly lockOf: true; + readonly lockSkipLocked: true; + readonly returning: true; + readonly scalarList: true; + }; + }; + readonly extensions: { + readonly 'demo/engagement-stats': { + readonly familyId: 'sql'; + readonly id: 'demo/engagement-stats'; + readonly kind: 'extension'; + readonly targetId: 'postgres'; + readonly types: { + readonly aggregateDescriptors: readonly [ + { + readonly input: { readonly kind: 'trait'; readonly trait: 'numeric' }; + readonly lower: unknown; + readonly nullable: true; + readonly operation: 'stddev'; + readonly output: { readonly codecId: 'pg/numeric@1'; readonly kind: 'codec' }; + }, + ]; + }; + readonly version: '0.0.1'; + }; + readonly pgvector: { + readonly capabilities: { readonly postgres: { readonly 'pgvector.cosine': true } }; + readonly familyId: 'sql'; + readonly id: 'pgvector'; + readonly kind: 'extension'; + readonly targetId: 'postgres'; + readonly types: { + readonly codecTypes: { + readonly import: { + readonly alias: 'PgVectorTypes'; + readonly named: 'CodecTypes'; + readonly package: '@internal/extension-pgvector/codec-types'; + }; + readonly parameterizedOutputTypes: { + readonly alias: 'PgVectorParameterizedOutputTypes'; + readonly named: 'ParameterizedOutputTypesForTypeParams'; + readonly package: '@internal/extension-pgvector/codec-types'; + }; + }; + readonly operationTypes: { + readonly import: { + readonly alias: 'PgVectorOperationTypes'; + readonly named: 'OperationTypes'; + readonly package: '@internal/extension-pgvector/operation-types'; + }; + }; + readonly queryOperationTypes: { + readonly import: { + readonly alias: 'PgVectorQueryOperationTypes'; + readonly named: 'QueryOperationTypes'; + readonly package: '@internal/extension-pgvector/operation-types'; + }; + }; + }; + readonly version: '0.0.1'; + }; + }; + readonly execution: { + readonly executionHash: ExecutionHash; + readonly mutations: { + readonly defaults: readonly [ + { + readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; + readonly ref: { + readonly entry: 'post'; + readonly field: 'id'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; + readonly ref: { + readonly entry: 'tag'; + readonly field: 'id'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; + readonly ref: { + readonly entry: 'task'; + readonly field: 'id'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { readonly id: 'uuidv4'; readonly kind: 'generator' }; + readonly ref: { + readonly entry: 'user'; + readonly field: 'id'; + readonly namespace: 'public'; + }; + }, + ]; + }; + }; + readonly meta: {}; + readonly profileHash: ProfileHash; +}; + +export type Contract = ContractWithTypeMaps; + +export type Namespaces = Contract['storage']['namespaces']; diff --git a/examples/prisma-8-demo/migrations/snapshots/fc039db97d116c1f5e6e56af25521c69e05232b49dc6e0058c9e3d3fb93eb263/contract.json b/examples/prisma-8-demo/migrations/snapshots/fc039db97d116c1f5e6e56af25521c69e05232b49dc6e0058c9e3d3fb93eb263/contract.json new file mode 100644 index 000000000000..d6662051f1a1 --- /dev/null +++ b/examples/prisma-8-demo/migrations/snapshots/fc039db97d116c1f5e6e56af25521c69e05232b49dc6e0058c9e3d3fb93eb263/contract.json @@ -0,0 +1 @@ +{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{"postgres":{"distinctOn":true,"forKeyShare":true,"forNoKeyUpdate":true,"jsonAgg":true,"lateral":true,"limit":true,"orderBy":true,"pgvector.cosine":true,"returning":true},"sql":{"checkConstraint":true,"defaultInInsert":true,"enums":true,"forShare":true,"forUpdate":true,"insertOnConflictSkip":true,"insertOnConflictWithoutTarget":true,"lateral":true,"lockNowait":true,"lockOf":true,"lockSkipLocked":true,"returning":true,"scalarList":true}},"domain":{"namespaces":{"public":{"enum":{"Priority":{"codecId":"pg/int4@1","members":[{"name":"Low","value":0},{"name":"High","value":1},{"name":"Urgent","value":2}]},"user_type":{"codecId":"pg/text@1","members":[{"name":"admin","value":"admin"},{"name":"user","value":"user"}]}},"models":{"Bug":{"base":{"model":"Task","namespace":"public"},"fields":{"severity":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"stepsToRepro":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"severity":{"column":"severity"},"stepsToRepro":{"column":"stepsToRepro"}},"namespaceId":"public","table":"bug"}},"Feature":{"base":{"model":"Task","namespace":"public"},"fields":{"priority":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"targetRelease":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"priority":{"column":"priority"},"targetRelease":{"column":"targetRelease"}},"namespaceId":"public","table":"feature"}},"Post":{"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"embedding":{"nullable":true,"type":{"codecId":"pg/vector@1","kind":"scalar","typeParams":{"length":1536}}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"priority":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"Priority","namespaceId":"public","plane":"domain"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"tags":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["postId"]},"through":{"childColumns":["tagId"],"namespaceId":"public","parentColumns":["postId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Tag","namespace":"public"}},"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"embedding":{"column":"embedding"},"id":{"column":"id"},"priority":{"column":"priority"},"title":{"column":"title"},"userId":{"column":"userId"}},"namespaceId":"public","table":"post"}},"PostTag":{"fields":{"postId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"tagId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"post":{"cardinality":"N:1","nullable":false,"on":{"localFields":["postId"],"targetFields":["id"]},"to":{"model":"Post","namespace":"public"}},"tag":{"cardinality":"N:1","nullable":false,"on":{"localFields":["tagId"],"targetFields":["id"]},"to":{"model":"Tag","namespace":"public"}}},"storage":{"fields":{"postId":{"column":"postId"},"tagId":{"column":"tagId"}},"namespaceId":"public","table":"post_tag"}},"Tag":{"fields":{"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"label":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{"posts":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["tagId"]},"through":{"childColumns":["postId"],"namespaceId":"public","parentColumns":["tagId"],"table":"post_tag","targetColumns":["id"]},"to":{"model":"Post","namespace":"public"}}},"storage":{"fields":{"id":{"column":"id"},"label":{"column":"label"}},"namespaceId":"public","table":"tag"}},"Task":{"discriminator":{"field":"type"},"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"description":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"status":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"type":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"userId":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}}},"relations":{"user":{"cardinality":"N:1","nullable":false,"on":{"localFields":["userId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"description":{"column":"description"},"id":{"column":"id"},"status":{"column":"status"},"title":{"column":"title"},"type":{"column":"type"},"userId":{"column":"userId"}},"namespaceId":"public","table":"task"},"variants":{"bug":{"model":"Bug","namespace":"public"},"feature":{"model":"Feature","namespace":"public"}}},"User":{"fields":{"address":{"nullable":true,"type":{"entityKind":"valueObject","entityName":"Address","kind":"valueObject","namespaceId":"public","plane":"storage"}},"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamptz-temporal@1","kind":"scalar"}},"displayName":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"email":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/uuid@1","kind":"scalar"}},"kind":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"},"valueSet":{"entityKind":"enum","entityName":"user_type","namespaceId":"public","plane":"domain"}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Post","namespace":"public"}},"tasks":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["userId"]},"to":{"model":"Task","namespace":"public"}}},"storage":{"fields":{"address":{"column":"address"},"createdAt":{"column":"createdAt"},"displayName":{"column":"displayName"},"email":{"column":"email"},"id":{"column":"id"},"kind":{"column":"kind"}},"namespaceId":"public","table":"user"}}}}}},"execution":{"executionHash":"eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655","mutations":{"defaults":[{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"post","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"tag","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"task","field":"id","namespace":"public"}},{"onCreate":{"id":"uuidv4","kind":"generator"},"ref":{"entry":"user","field":"id","namespace":"public"}}]}},"extensions":{"demo/engagement-stats":{"familyId":"sql","id":"demo/engagement-stats","kind":"extension","targetId":"postgres","types":{"aggregateDescriptors":[{"input":{"kind":"trait","trait":"numeric"},"nullable":true,"operation":"stddev","output":{"codecId":"pg/numeric@1","kind":"codec"}}]},"version":"0.0.1"},"pgvector":{"capabilities":{"postgres":{"pgvector.cosine":true}},"familyId":"sql","id":"pgvector","kind":"extension","targetId":"postgres","types":{"codecTypes":{"import":{"alias":"PgVectorTypes","named":"CodecTypes","package":"@internal/extension-pgvector/codec-types"},"parameterizedOutputTypes":{"alias":"PgVectorParameterizedOutputTypes","named":"ParameterizedOutputTypesForTypeParams","package":"@internal/extension-pgvector/codec-types"}},"operationTypes":{"import":{"alias":"PgVectorOperationTypes","named":"OperationTypes","package":"@internal/extension-pgvector/operation-types"}},"queryOperationTypes":{"import":{"alias":"PgVectorQueryOperationTypes","named":"QueryOperationTypes","package":"@internal/extension-pgvector/operation-types"}}},"version":"0.0.1"}},"meta":{},"profileHash":"3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2","roots":{"post":{"model":"Post","namespace":"public"},"post_tag":{"model":"PostTag","namespace":"public"},"tag":{"model":"Tag","namespace":"public"},"task":{"model":"Task","namespace":"public"},"user":{"model":"User","namespace":"public"}},"schemaVersion":"1","storage":{"namespaces":{"public":{"entries":{"table":{"bug":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"severity":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"stepsToRepro":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"bug"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"feature":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"priority":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"targetRelease":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","source":{"columns":["id"],"namespaceId":"public","tableName":"feature"},"target":{"columns":["id"],"namespaceId":"public","tableName":"task"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"post":{"checks":[{"expression":"\"priority\" IN (0, 1, 2)","name":"post_priority_check_b236ace9","prefix":"post_priority_check"}],"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"embedding":{"codecId":"pg/vector@1","dataType":"pgvector/vector","nullable":true,"typeParams":{"length":1536}},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"priority":{"codecId":"pg/int4@1","dataType":"pg/int4","default":{"kind":"literal","value":0},"nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Priority","namespaceId":"public","plane":"storage"}},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"post_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"post"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"post_userId_idx_a489d58a","prefix":"post_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"post_tag":{"columns":{"postId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"tagId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"post_tag_postId_idx_a7a72715"},"source":{"columns":["postId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"post"}},{"index":{"name":"post_tag_tagId_idx_86854244"},"source":{"columns":["tagId"],"namespaceId":"public","tableName":"post_tag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"tag"}}],"indexes":[{"columns":["postId"],"name":"post_tag_postId_idx_a7a72715","prefix":"post_tag_postId_idx","unique":false},{"columns":["tagId"],"name":"post_tag_tagId_idx_86854244","prefix":"post_tag_tagId_idx","unique":false}],"primaryKey":{"columns":["postId","tagId"]},"uniques":[]},"tag":{"columns":{"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"label":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[{"columns":["label"]}]},"task":{"checks":[{"expression":"\"type\" IN ('bug', 'feature')","name":"task_type_check_cda3f5e7","prefix":"task_type_check"}],"columns":{"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"description":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"status":{"codecId":"pg/text@1","dataType":"pg/text","default":{"kind":"literal","value":"open"},"nullable":false},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"type":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"Task.type","namespaceId":"public","plane":"storage"}},"userId":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false}},"foreignKeys":[{"index":{"name":"task_userId_idx_a489d58a"},"source":{"columns":["userId"],"namespaceId":"public","tableName":"task"},"target":{"columns":["id"],"namespaceId":"public","tableName":"user"}}],"indexes":[{"columns":["userId"],"name":"task_userId_idx_a489d58a","prefix":"task_userId_idx","unique":false}],"primaryKey":{"columns":["id"]},"uniques":[]},"user":{"checks":[{"expression":"\"address\" IS NULL OR jsonb_path_match(\"address\", 'strict $.type() == \"null\" || ($.type() == \"object\" && exists($.keyvalue() ? (@.key == \"city\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"country\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"street\" && (@.value.type() == \"string\"))) && !exists($.keyvalue() ? (@.key == \"zip\" && !(@.value.type() == \"null\" || (@.value.type() == \"string\")))) && !exists($.keyvalue() ? (@.key != \"city\" && @.key != \"country\" && @.key != \"street\" && @.key != \"zip\")))') IS TRUE","name":"user_address_shape_69432dc1","prefix":"user_address_shape"},{"expression":"\"kind\" IN ('admin', 'user')","name":"user_kind_check_836d43ef","prefix":"user_kind_check"}],"columns":{"address":{"codecId":"pg/jsonb@1","dataType":"pg/jsonb","nullable":true},"createdAt":{"codecId":"pg/timestamptz-temporal@1","dataType":"pg/timestamptz","default":{"expression":"now()","kind":"function"},"nullable":false},"displayName":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"email":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"id":{"codecId":"pg/uuid@1","dataType":"pg/uuid","nullable":false},"kind":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false,"valueSet":{"entityKind":"valueSet","entityName":"user_type","namespaceId":"public","plane":"storage"}}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]}},"valueObject":{"Address":{"fields":{"city":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"country":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"street":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"zip":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}}}}},"valueSet":{"Priority":{"kind":"valueSet","values":[0,1,2]},"Task.type":{"kind":"valueSet","values":["bug","feature"]},"user_type":{"kind":"valueSet","values":["admin","user"]}}},"id":"public","kind":"postgres-schema"}},"storageHash":"fc039db97d116c1f5e6e56af25521c69e05232b49dc6e0058c9e3d3fb93eb263"},"target":"postgres","targetFamily":"sql"} diff --git a/examples/prisma-8-demo/src/prisma/contract.d.ts b/examples/prisma-8-demo/src/prisma/contract.d.ts index 229fda1ada33..f84d528883aa 100644 --- a/examples/prisma-8-demo/src/prisma/contract.d.ts +++ b/examples/prisma-8-demo/src/prisma/contract.d.ts @@ -29,7 +29,7 @@ import type { } from '@prisma/orm-postgres/contract/types'; export type StorageHash = - StorageHashBase<'99a4393ee9f877db228baee1164325543fa018be780fd1253250d7d81539f38e'>; + StorageHashBase<'e1916bd73bb2388302d81a1eaa17125c0cedb6ce47b489a7adeeb41c2ef50ebf'>; export type ExecutionHash = ExecutionHashBase<'eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655'>; export type ProfileHash = diff --git a/examples/prisma-8-demo/src/prisma/contract.json b/examples/prisma-8-demo/src/prisma/contract.json index 11f4bc74989a..bba1c4b65e80 100644 --- a/examples/prisma-8-demo/src/prisma/contract.json +++ b/examples/prisma-8-demo/src/prisma/contract.json @@ -1127,8 +1127,8 @@ "checks": [ { "expression": "\"address\" IS NULL OR jsonb_path_match(\"address\", 'strict $.type() == \"null\" || ($.type() == \"object\" && exists($.keyvalue() ? (@.key == \"city\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"country\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"street\" && (@.value.type() == \"string\"))) && !exists($.keyvalue() ? (@.key == \"zip\" && !(@.value.type() == \"null\" || (@.value.type() == \"string\")))) && !exists($.keyvalue() ? (@.key != \"city\" && @.key != \"country\" && @.key != \"street\" && @.key != \"zip\")))') IS TRUE", - "name": "user_address_value_object_69432dc1", - "prefix": "user_address_value_object" + "name": "user_address_shape_69432dc1", + "prefix": "user_address_shape" }, { "expression": "\"kind\" IN ('admin', 'user')", @@ -1251,7 +1251,7 @@ "kind": "postgres-schema" } }, - "storageHash": "99a4393ee9f877db228baee1164325543fa018be780fd1253250d7d81539f38e" + "storageHash": "e1916bd73bb2388302d81a1eaa17125c0cedb6ce47b489a7adeeb41c2ef50ebf" }, "execution": { "executionHash": "eb9bffd4c01945792e83de10d571b20df42f5bf40405ec8c642c6592f7dc4655", diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/soft-delete/generated/contract.d.ts b/packages/3-extensions/sql-orm-client/test/fixtures/soft-delete/generated/contract.d.ts index c486db551394..ac6b16a295f1 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/soft-delete/generated/contract.d.ts +++ b/packages/3-extensions/sql-orm-client/test/fixtures/soft-delete/generated/contract.d.ts @@ -24,7 +24,7 @@ import type { } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'21a3ae2aab3f4f03b59fa65df3d452ec1d41d9ce5926b3fba6bd70cf39070dcb'>; + StorageHashBase<'388580bf7258d1c37e6113016201f45f9054b3a56527588f9d8f6646473ea9ef'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/soft-delete/generated/contract.json b/packages/3-extensions/sql-orm-client/test/fixtures/soft-delete/generated/contract.json index cd387f209afb..e28ee18ce55c 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/soft-delete/generated/contract.json +++ b/packages/3-extensions/sql-orm-client/test/fixtures/soft-delete/generated/contract.json @@ -473,8 +473,8 @@ "checks": [ { "expression": "jsonb_path_match(\"addresses\", 'strict $.type() == \"array\" && !exists($[*] ? (!((@.type() == \"object\" && exists(@.keyvalue() ? (@.key == \"street\" && (@.value.type() == \"string\"))) && !exists(@.keyvalue() ? (@.key != \"street\"))))))') IS TRUE", - "name": "tags_addresses_value_object_c752a33a", - "prefix": "tags_addresses_value_object" + "name": "tags_addresses_shape_c752a33a", + "prefix": "tags_addresses_shape" }, { "expression": "array_position(\"labels\", NULL) IS NULL", @@ -573,7 +573,7 @@ "kind": "postgres-schema" } }, - "storageHash": "21a3ae2aab3f4f03b59fa65df3d452ec1d41d9ce5926b3fba6bd70cf39070dcb" + "storageHash": "388580bf7258d1c37e6113016201f45f9054b3a56527588f9d8f6646473ea9ef" }, "capabilities": { "postgres": { diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/value-objects/generated/contract.d.ts b/packages/3-extensions/sql-orm-client/test/fixtures/value-objects/generated/contract.d.ts index cd66f00a0c0e..00525f6dcdc3 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/value-objects/generated/contract.d.ts +++ b/packages/3-extensions/sql-orm-client/test/fixtures/value-objects/generated/contract.d.ts @@ -24,7 +24,7 @@ import type { } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'e6640da7edb7ea952996d8d210526e990703e8a81f588f70b6c097dc5870a9af'>; + StorageHashBase<'312f3bd7f0c8c146f44a26148cb251da42b2d8d449c3d6ad0bdf7ae7823df718'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/value-objects/generated/contract.json b/packages/3-extensions/sql-orm-client/test/fixtures/value-objects/generated/contract.json index 81164ba0cbdd..ba2542ad8405 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/value-objects/generated/contract.json +++ b/packages/3-extensions/sql-orm-client/test/fixtures/value-objects/generated/contract.json @@ -70,8 +70,8 @@ "checks": [ { "expression": "\"address\" IS NULL OR jsonb_path_match(\"address\", 'strict $.type() == \"null\" || ($.type() == \"object\" && exists($.keyvalue() ? (@.key == \"city\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"street\" && (@.value.type() == \"string\"))) && !exists($.keyvalue() ? (@.key == \"zip\" && !(@.value.type() == \"null\" || (@.value.type() == \"string\")))) && !exists($.keyvalue() ? (@.key != \"city\" && @.key != \"street\" && @.key != \"zip\")))') IS TRUE", - "name": "users_address_value_object_0c7d1c3a", - "prefix": "users_address_value_object" + "name": "users_address_shape_0c7d1c3a", + "prefix": "users_address_shape" } ], "columns": { @@ -133,7 +133,7 @@ "kind": "postgres-schema" } }, - "storageHash": "e6640da7edb7ea952996d8d210526e990703e8a81f588f70b6c097dc5870a9af" + "storageHash": "312f3bd7f0c8c146f44a26148cb251da42b2d8d449c3d6ad0bdf7ae7823df718" }, "capabilities": { "postgres": { diff --git a/test/integration/test/sql-orm-client/fixtures/soft-delete/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/soft-delete/generated/contract.d.ts index c486db551394..ac6b16a295f1 100644 --- a/test/integration/test/sql-orm-client/fixtures/soft-delete/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/soft-delete/generated/contract.d.ts @@ -24,7 +24,7 @@ import type { } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'21a3ae2aab3f4f03b59fa65df3d452ec1d41d9ce5926b3fba6bd70cf39070dcb'>; + StorageHashBase<'388580bf7258d1c37e6113016201f45f9054b3a56527588f9d8f6646473ea9ef'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; diff --git a/test/integration/test/sql-orm-client/fixtures/soft-delete/generated/contract.json b/test/integration/test/sql-orm-client/fixtures/soft-delete/generated/contract.json index cd387f209afb..e28ee18ce55c 100644 --- a/test/integration/test/sql-orm-client/fixtures/soft-delete/generated/contract.json +++ b/test/integration/test/sql-orm-client/fixtures/soft-delete/generated/contract.json @@ -473,8 +473,8 @@ "checks": [ { "expression": "jsonb_path_match(\"addresses\", 'strict $.type() == \"array\" && !exists($[*] ? (!((@.type() == \"object\" && exists(@.keyvalue() ? (@.key == \"street\" && (@.value.type() == \"string\"))) && !exists(@.keyvalue() ? (@.key != \"street\"))))))') IS TRUE", - "name": "tags_addresses_value_object_c752a33a", - "prefix": "tags_addresses_value_object" + "name": "tags_addresses_shape_c752a33a", + "prefix": "tags_addresses_shape" }, { "expression": "array_position(\"labels\", NULL) IS NULL", @@ -573,7 +573,7 @@ "kind": "postgres-schema" } }, - "storageHash": "21a3ae2aab3f4f03b59fa65df3d452ec1d41d9ce5926b3fba6bd70cf39070dcb" + "storageHash": "388580bf7258d1c37e6113016201f45f9054b3a56527588f9d8f6646473ea9ef" }, "capabilities": { "postgres": { diff --git a/test/integration/test/sql-orm-client/fixtures/value-objects/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/value-objects/generated/contract.d.ts index cd66f00a0c0e..00525f6dcdc3 100644 --- a/test/integration/test/sql-orm-client/fixtures/value-objects/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/value-objects/generated/contract.d.ts @@ -24,7 +24,7 @@ import type { } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'e6640da7edb7ea952996d8d210526e990703e8a81f588f70b6c097dc5870a9af'>; + StorageHashBase<'312f3bd7f0c8c146f44a26148cb251da42b2d8d449c3d6ad0bdf7ae7823df718'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; diff --git a/test/integration/test/sql-orm-client/fixtures/value-objects/generated/contract.json b/test/integration/test/sql-orm-client/fixtures/value-objects/generated/contract.json index 81164ba0cbdd..ba2542ad8405 100644 --- a/test/integration/test/sql-orm-client/fixtures/value-objects/generated/contract.json +++ b/test/integration/test/sql-orm-client/fixtures/value-objects/generated/contract.json @@ -70,8 +70,8 @@ "checks": [ { "expression": "\"address\" IS NULL OR jsonb_path_match(\"address\", 'strict $.type() == \"null\" || ($.type() == \"object\" && exists($.keyvalue() ? (@.key == \"city\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"street\" && (@.value.type() == \"string\"))) && !exists($.keyvalue() ? (@.key == \"zip\" && !(@.value.type() == \"null\" || (@.value.type() == \"string\")))) && !exists($.keyvalue() ? (@.key != \"city\" && @.key != \"street\" && @.key != \"zip\")))') IS TRUE", - "name": "users_address_value_object_0c7d1c3a", - "prefix": "users_address_value_object" + "name": "users_address_shape_0c7d1c3a", + "prefix": "users_address_shape" } ], "columns": { @@ -133,7 +133,7 @@ "kind": "postgres-schema" } }, - "storageHash": "e6640da7edb7ea952996d8d210526e990703e8a81f588f70b6c097dc5870a9af" + "storageHash": "312f3bd7f0c8c146f44a26148cb251da42b2d8d449c3d6ad0bdf7ae7823df718" }, "capabilities": { "postgres": { diff --git a/test/integration/test/value-objects/fixtures/generated/sql-contract.d.ts b/test/integration/test/value-objects/fixtures/generated/sql-contract.d.ts index af72c5e8afdd..3404cacfb94e 100644 --- a/test/integration/test/value-objects/fixtures/generated/sql-contract.d.ts +++ b/test/integration/test/value-objects/fixtures/generated/sql-contract.d.ts @@ -24,7 +24,7 @@ import type { } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'4216574839577232bdfaee2c8202fc9044a7873412428368c9ee430266747fab'>; + StorageHashBase<'d1f566293d77a0d2d3dc4b1fbcec129b2a6a25ac509570d6f757dc83665939b4'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; diff --git a/test/integration/test/value-objects/fixtures/generated/sql-contract.json b/test/integration/test/value-objects/fixtures/generated/sql-contract.json index 2ce5d0610f9b..f61ae8700f77 100644 --- a/test/integration/test/value-objects/fixtures/generated/sql-contract.json +++ b/test/integration/test/value-objects/fixtures/generated/sql-contract.json @@ -83,13 +83,13 @@ "checks": [ { "expression": "jsonb_path_match(\"location\", 'strict $.type() == \"object\" && exists($.keyvalue() ? (@.key == \"city\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"street\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"zip\" && (@.value.type() == \"string\"))) && !exists($.keyvalue() ? (@.key != \"city\" && @.key != \"street\" && @.key != \"zip\"))') IS TRUE", - "name": "shop_location_value_object_9799c974", - "prefix": "shop_location_value_object" + "name": "shop_location_shape_9799c974", + "prefix": "shop_location_shape" }, { "expression": "\"notes\" IS NULL OR jsonb_path_match(\"notes\", 'strict $.type() == \"null\" || ($.type() == \"object\" && exists($.keyvalue() ? (@.key == \"city\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"street\" && (@.value.type() == \"string\"))) && exists($.keyvalue() ? (@.key == \"zip\" && (@.value.type() == \"string\"))) && !exists($.keyvalue() ? (@.key != \"city\" && @.key != \"street\" && @.key != \"zip\")))') IS TRUE", - "name": "shop_notes_value_object_5554921a", - "prefix": "shop_notes_value_object" + "name": "shop_notes_shape_5554921a", + "prefix": "shop_notes_shape" } ], "columns": { @@ -156,7 +156,7 @@ "kind": "postgres-schema" } }, - "storageHash": "4216574839577232bdfaee2c8202fc9044a7873412428368c9ee430266747fab" + "storageHash": "d1f566293d77a0d2d3dc4b1fbcec129b2a6a25ac509570d6f757dc83665939b4" }, "capabilities": { "postgres": { From bb6b0be965f5439ff930b751c90432748c71220a Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 19:04:13 +0200 Subject: [PATCH 083/104] TML-3537: the upgrade guide and the Data Contract doc say adding a check reads every row under a lock, and NOT VALID checks are deferred Signed-off-by: willbot Signed-off-by: Will Madden --- docs/architecture docs/subsystems/1. Data Contract.md | 2 +- projects/migration-statements/deferred.md | 6 ++++++ .../pending/value-objects-in-storage/app/instructions.md | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/architecture docs/subsystems/1. Data Contract.md b/docs/architecture docs/subsystems/1. Data Contract.md index b1d2e8c51ef1..c9aba434a6ae 100644 --- a/docs/architecture docs/subsystems/1. Data Contract.md +++ b/docs/architecture docs/subsystems/1. Data Contract.md @@ -321,7 +321,7 @@ A `pg/int4@1` enum `Int4Level` with members `Low = 1` and `High = 10`, on column A list column gets `<@` containment instead of `IN`. In the same fixture, `levels FloatMixed[]`, whose members are written `1.5` and `NaN` and stored as `1.5` and `"NaN"`, gets `array_remove("levels", NULL) <@ '{1.5,"NaN"}'`, plus the derived check `array_position("levels", NULL) IS NULL` because its elements are not nullable. -On a `managed` Postgres table two more columns derive checks ([ADR 309](../adrs/ADR%20309%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md)). A discriminator column with no enum gets a membership check, `
__check`, holding the values of its `.` value set. A column that holds a value object gets a check named `
__shape`: one strict `jsonpath` predicate that the column value is an object (or, for a list, an array of objects) with each required member present, no other key, each scalar member of its JSON type, each enum member one of its value set's values, and each nested value object checked the same way. A nullable column accepts SQL `NULL` and JSON `null`; the ORM writes SQL `NULL` for an absent value. A `Decimal` member must be a JSON string, as the ORM writes it. Each Postgres codec declares on its descriptor the JSON type a member of it has (`valueObjectJsonType`), or that it holds any JSON value, or that only its presence is checked. A required member whose codec declares presence only must be present and not JSON `null`; a `Json` member may hold any value. Below the point where a value object refers back into itself, any object is accepted, as the MongoDB validator does. +On a `managed` Postgres table two more columns derive checks ([ADR 309](../adrs/ADR%20309%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md)). A discriminator column with no enum gets a membership check, `
__check`, holding the values of its `.` value set. A column that holds a value object gets a check named `
__shape`: one strict `jsonpath` predicate that the column value is an object (or, for a list, an array of objects) with each required member present, no other key, each scalar member of its JSON type, each enum member one of its value set's values, and each nested value object checked the same way. A nullable column accepts SQL `NULL` and JSON `null`; the ORM writes SQL `NULL` for an absent value. A `Decimal` member must be a JSON string, as the ORM writes it. Each Postgres codec declares on its descriptor the JSON type a member of it has (`valueObjectJsonType`), or that it holds any JSON value, or that only its presence is checked. A required member whose codec declares presence only must be present and not JSON `null`; a `Json` member may hold any value. Below the point where a value object refers back into itself, any object is accepted, as the MongoDB validator does. Adding a check takes a lock on the table while Postgres reads every row to check it. Any later change to a value object renames the check, so the check is dropped and added again, and the rows are read again. `contract infer` produces no value objects and no polymorphism, so on a live table with these checks it prints each one as an authored `@@check` with its exact name in `map:`, as it does for an enum membership check. diff --git a/projects/migration-statements/deferred.md b/projects/migration-statements/deferred.md index 28c647fad7ff..3acac9e1dd7c 100644 --- a/projects/migration-statements/deferred.md +++ b/projects/migration-statements/deferred.md @@ -144,6 +144,12 @@ The re-run advice has a second gap of the same kind, found in the slice 3b PR 1 Slice 3b plans each row rewrite as one plain operation: an `UPDATE` for an enum value rename, a discriminator conversion or a value object key rename, and a `DELETE` for a removed variant's rows. A migration runs in one transaction, so on a large production table the rewrite blocks writes to every row it touches until it commits, and next to DDL such as a check rebuild it blocks the whole table. Nothing batches it or runs it in the background. 3a's `--backfill` and type conversions have the same cost. Will ruled that 3b's goal is that the planner sees these changes; making the rewrites cheap is a later optimization. +## Derived checks are added with a full table read under lock + +**Found:** whole-branch review of the value object storage PR (2026-10-23). + +The planner adds each derived check, value object shape checks included, with a plain `ALTER TABLE … ADD CONSTRAINT … CHECK (…)`. Postgres holds a lock on the table while it reads every row. The check's name holds a hash of its expression, so any change to a value object drops the check and adds it again, and the rows are read again. Adding the check `NOT VALID` and then running `VALIDATE CONSTRAINT` takes a weaker lock while the rows are read. It changes how every derived check is planned, enum checks included, so it belongs with the row rewrite cost above. + ## `--delete billing` is not offered as a next action, and the CLI copies the engine's unused-flag error **Found:** slice 3b PR 1 system-design review (2026-10-21), S04. diff --git a/upgrade-instructions/pending/value-objects-in-storage/app/instructions.md b/upgrade-instructions/pending/value-objects-in-storage/app/instructions.md index 4d26aab9306e..6aa1543d9fbf 100644 --- a/upgrade-instructions/pending/value-objects-in-storage/app/instructions.md +++ b/upgrade-instructions/pending/value-objects-in-storage/app/instructions.md @@ -87,6 +87,8 @@ WHERE NOT ("address" IS NULL OR jsonb_path_match("address", 'strict … ') IS TR SELECT id, type FROM "public"."task" WHERE NOT ("type" IN ('bug', 'feature')); ``` +Adding a check takes a lock on the table while Postgres reads every row to check it. Any later change to a value object renames the check, so the check is dropped and added again, and the rows are read again. Apply the migration when the table can wait for that read. + Copy the expression from the preview between `CHECK (` and the closing `)`. Fix or delete each row the query returns, for example by removing the extra keys with `UPDATE "public"."user" SET address = address - 'extra' WHERE address ? 'extra'`, then apply the migration. A migration that fails on a row changes nothing, and the error names the check; fix the rows and run `prisma db migrate` again. ## `discriminator-typed-by-variant-values` From 41386201d3285fa6f3de035ccc182d415f7ac733 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 19:07:34 +0200 Subject: [PATCH 084/104] TML-3537: migration plan reports changedElementCoordinates for every migration it writes, and decides a migration with no operations from its operations Signed-off-by: willbot Signed-off-by: Will Madden --- .../subsystems/7. Migration System.md | 2 +- packages/1-framework/3-tooling/cli/README.md | 2 +- .../control-api/operations/migration-plan.ts | 44 ++++++++++++++----- .../3-tooling/cli/src/orm/migration/plan.ts | 2 +- .../orm/migration-plan.no-operations.test.ts | 35 +++++++++++++-- .../plans-with-no-operations.e2e.test.ts | 4 +- ...plans-with-no-operations.mongo.e2e.test.ts | 4 +- ...lans-with-no-operations.sqlite.e2e.test.ts | 6 +-- 8 files changed, 74 insertions(+), 25 deletions(-) diff --git a/docs/architecture docs/subsystems/7. Migration System.md b/docs/architecture docs/subsystems/7. Migration System.md index 35b44fcdea19..c69f23354fcb 100644 --- a/docs/architecture docs/subsystems/7. Migration System.md +++ b/docs/architecture docs/subsystems/7. Migration System.md @@ -172,7 +172,7 @@ The from-contract always materialises by reading the content-addressed snapshot When `--to` is omitted, `to_contract` is the emitted contract; when `--to` is supplied, `to_contract` is the resolved destination. -**A plan with no operations.** Two different storage hashes are two different database states, so a migration between them is expected to do something. When the planner produces no operations for a changed storage hash anyway, `migration plan` still writes the package, with an empty `ops.json`, and says so: the summary, a warning in the human output, names each storage entity whose content differs between the two contracts, and `--json` lists them as `changedStorageEntities` (entity coordinates, from `changedElementCoordinates` in `@internal/framework-components/ir`; a coordinate in the unbound namespace leaves out `namespaceId`, as `appliedStatements` does). `db migrate` and `db update` apply such a migration by moving the marker to the new hash; every runner already does this for an edge between two different hashes. This holds for a baseline with no operations and for the delta after it. Only a greenfield plan (no earlier contract) with no operations is refused, with `MIGRATION.PLANNING_FAILED` of kind `nothingToCreate`, since the contract describes nothing to create. When the storage hash does not change, nothing is written, as before. +**A plan with no operations.** Two different storage hashes are two different database states, so a migration between them is expected to do something. When the planner produces no operations for a changed storage hash anyway, `migration plan` still writes the package, with an empty `ops.json`, and says so: the summary, a warning in the human output, names each storage entity whose content differs between the two contracts, and `--json` lists them as `changedElementCoordinates` (entity coordinates, from `changedElementCoordinates` in `@internal/framework-components/ir`; a coordinate in the unbound namespace leaves out `namespaceId`, as `appliedStatements` does). The field is filled for every migration `migration plan` writes, and a migration written with no operations is one whose `operations` is empty. `noOp` means something else: nothing was written, because the storage hash did not change. `db migrate` and `db update` apply such a migration by moving the marker to the new hash; every runner already does this for an edge between two different hashes. This holds for a baseline with no operations and for the delta after it. Only a greenfield plan (no earlier contract) with no operations is refused, with `MIGRATION.PLANNING_FAILED` of kind `nothingToCreate`, since the contract describes nothing to create. When the storage hash does not change, nothing is written, as before. An auto-baseline dates the delta package at the current time and the baseline package one minute earlier, so the baseline sorts first. Folder names have minute resolution, so packages planned within the same minute sort by name. diff --git a/packages/1-framework/3-tooling/cli/README.md b/packages/1-framework/3-tooling/cli/README.md index da6b36489095..0a542bc1db8d 100644 --- a/packages/1-framework/3-tooling/cli/README.md +++ b/packages/1-framework/3-tooling/cli/README.md @@ -1093,7 +1093,7 @@ prisma migration plan [--config ] [--name ] [--from ] [--t - `migrations//ops.json` — planned operations (empty list `[]` if placeholders blocked the planner) - `migrations/snapshots//contract.{json,d.ts}` — bookend contracts, written write-if-absent, keyed by each contract's storage hash (one entry for `from` when applicable, one for `to`) -**Rename statements:** each `--rename` names the old and the new name in contract vocabulary, never a table or a column. Each side is one of `namespace`, `Model`, `namespace.Model`, `Model.field`, `namespace.Model.field`, `Enum.value` or `namespace.Enum.value`, and both sides name the same kind of thing: a namespace, a model, a field of the same model, or a value of the same enum. Names match exactly, including case. A name without a namespace resolves when exactly one namespace declares the model. A field's model is named as the destination contract names it, so a model rename followed by a field rename on it is `--rename Profile:User --rename User.name:User.fullName`. The old name must exist in the starting contract and not in the destination; the new name must exist in the destination and not in the starting contract. Statements resolve before anything is written; a statement that is malformed fails with `MIGRATION.STATEMENT_INVALID`, and one that does not resolve fails with `MIGRATION.STATEMENT_UNRESOLVED`, naming what it searched and what it found. Each error's next step is the statement to type instead: the accepted forms, the statement written the right way round, the statements a model-and-field mix may have meant, or the order that works. A statement that has already been applied (the origin already has the new name and not the old one) says to leave it out. Statements cannot swap two names in one plan, and the error gives the three plans that do, through a temporary name. A plan from an empty database (`--from @empty`, or greenfield) has no models to rename, so every statement is unresolved. A statement whose namespace changes, such as `--rename public.Invoice:billing.Invoice`, moves the table to the new schema on Postgres, creating the schema first when the database lacks it; a move into the unbound namespace is refused. One name on each side of a namespace of the origin contract, `--rename billing:finance`, renames the namespace on Postgres; a statement that reads both as a namespace and as a model rename is refused, and `public` and a schema another contract space declares are not renamed. Namespace renames run before the other statements, whatever order they are given in. When a name is both a namespace and a model, name the model with its namespace, or give both statements. An enum value is named through its enum by its member name, `--rename Status.ARCHIVED:Status.RETIRED`, never by its stored value; on Postgres and SQLite the plan replaces the old stored value with the new member's in every column the enum types, with `this.replaceStoredValue(...)` calls in `migration.ts`, between the drop of the old check and the creation of the new one on Postgres. A member renamed with its stored value kept plans nothing, and its statement is listed with no operations. A rename into a stored value another member of the origin enum holds is refused, since it would merge the data of the two members. Without a snapshot of the contract the database is at, no enum value is known to have changed: nothing is asked about it, `--delete Status.ARCHIVED` answers nothing and is refused as unused, and on Postgres the apply fails at the new check while data holds the old value. MongoDB refuses enum value statements. Renaming value objects is not supported in this release. The planner renames the table or column, and the constraints and indexes named after it, ahead of the rest of the plan. On Postgres, a check, a row-level-security policy, or an expression or partial index whose SQL names a renamed column is dropped and created again, and adding a check back reads every row of the table under an exclusive lock, so on a large table such a rename takes time; `migration.ts` contains the same `...this.renameTable(...)` and `...this.renameColumn(...)` calls you could have written by hand. On MongoDB a field is named by its name in the contract, as on SQL (`id @map("_id")` is `User.id`), and a rename that keeps the stored name plans nothing. A model rename renames the collection, which keeps its documents, indexes and validator (`renameCollection`); a field rename renames the field in every document that holds it (`renameField`), and only in that variant's documents for a variant's field; `migration.ts` calls these factories. The statements are listed under `Statements applied` after the operations, each with its number of operations, and as `appliedStatements` in `--json` output, each with its `statement`, `description` and `operationIndexes`, the positions in `operations` of the operations it accounts for. A coordinate in a contract with no namespaces has no `namespaceId` in the JSON. A statement whose storage does not change, such as a model whose table name is kept with `@@map`, is listed with no operations. When the storage did not change at all and no statement needs an operation, no migration package is written and the output says so; when the storage changed but the planner planned nothing, the migration is written with no operations, statements or not, and the output names the storage entities that changed (`changedStorageEntities` in `--json` output). A statement the planner cannot carry out, for example on a table whose control policy is not `managed`, fails planning with `MIGRATION.PLANNING_FAILED`, and the refused statement is carried in the error's conflicts. +**Rename statements:** each `--rename` names the old and the new name in contract vocabulary, never a table or a column. Each side is one of `namespace`, `Model`, `namespace.Model`, `Model.field`, `namespace.Model.field`, `Enum.value` or `namespace.Enum.value`, and both sides name the same kind of thing: a namespace, a model, a field of the same model, or a value of the same enum. Names match exactly, including case. A name without a namespace resolves when exactly one namespace declares the model. A field's model is named as the destination contract names it, so a model rename followed by a field rename on it is `--rename Profile:User --rename User.name:User.fullName`. The old name must exist in the starting contract and not in the destination; the new name must exist in the destination and not in the starting contract. Statements resolve before anything is written; a statement that is malformed fails with `MIGRATION.STATEMENT_INVALID`, and one that does not resolve fails with `MIGRATION.STATEMENT_UNRESOLVED`, naming what it searched and what it found. Each error's next step is the statement to type instead: the accepted forms, the statement written the right way round, the statements a model-and-field mix may have meant, or the order that works. A statement that has already been applied (the origin already has the new name and not the old one) says to leave it out. Statements cannot swap two names in one plan, and the error gives the three plans that do, through a temporary name. A plan from an empty database (`--from @empty`, or greenfield) has no models to rename, so every statement is unresolved. A statement whose namespace changes, such as `--rename public.Invoice:billing.Invoice`, moves the table to the new schema on Postgres, creating the schema first when the database lacks it; a move into the unbound namespace is refused. One name on each side of a namespace of the origin contract, `--rename billing:finance`, renames the namespace on Postgres; a statement that reads both as a namespace and as a model rename is refused, and `public` and a schema another contract space declares are not renamed. Namespace renames run before the other statements, whatever order they are given in. When a name is both a namespace and a model, name the model with its namespace, or give both statements. An enum value is named through its enum by its member name, `--rename Status.ARCHIVED:Status.RETIRED`, never by its stored value; on Postgres and SQLite the plan replaces the old stored value with the new member's in every column the enum types, with `this.replaceStoredValue(...)` calls in `migration.ts`, between the drop of the old check and the creation of the new one on Postgres. A member renamed with its stored value kept plans nothing, and its statement is listed with no operations. A rename into a stored value another member of the origin enum holds is refused, since it would merge the data of the two members. Without a snapshot of the contract the database is at, no enum value is known to have changed: nothing is asked about it, `--delete Status.ARCHIVED` answers nothing and is refused as unused, and on Postgres the apply fails at the new check while data holds the old value. MongoDB refuses enum value statements. Renaming value objects is not supported in this release. The planner renames the table or column, and the constraints and indexes named after it, ahead of the rest of the plan. On Postgres, a check, a row-level-security policy, or an expression or partial index whose SQL names a renamed column is dropped and created again, and adding a check back reads every row of the table under an exclusive lock, so on a large table such a rename takes time; `migration.ts` contains the same `...this.renameTable(...)` and `...this.renameColumn(...)` calls you could have written by hand. On MongoDB a field is named by its name in the contract, as on SQL (`id @map("_id")` is `User.id`), and a rename that keeps the stored name plans nothing. A model rename renames the collection, which keeps its documents, indexes and validator (`renameCollection`); a field rename renames the field in every document that holds it (`renameField`), and only in that variant's documents for a variant's field; `migration.ts` calls these factories. The statements are listed under `Statements applied` after the operations, each with its number of operations, and as `appliedStatements` in `--json` output, each with its `statement`, `description` and `operationIndexes`, the positions in `operations` of the operations it accounts for. A coordinate in a contract with no namespaces has no `namespaceId` in the JSON. A statement whose storage does not change, such as a model whose table name is kept with `@@map`, is listed with no operations. When the storage did not change at all and no statement needs an operation, no migration package is written and the output says so; when the storage changed but the planner planned nothing, the migration is written with no operations, statements or not, and the output names the storage entities that changed. In `--json` output, `changedElementCoordinates` lists the storage entities that differ between the origin and the destination for every written migration, with operations or without; a migration written with no operations has an empty `operations` and `noOp: false`. `noOp: true` means no migration was written, because the storage hash did not change, and then `changedElementCoordinates` is empty. A statement the planner cannot carry out, for example on a table whose control policy is not `managed`, fails planning with `MIGRATION.PLANNING_FAILED`, and the refused statement is carried in the error's conflicts. **Data loss:** a plan that would lose data is written only once you say what each such operation means. The command asks one question per model, field or storage name whose data an operation would lose (dropping a table or a column, or a type change that can change values), before it writes anything, including the baseline package of an auto-baseline. Each question is answered with a statement: diff --git a/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts b/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts index 5ef7c918de06..eca8299b0fcd 100644 --- a/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts +++ b/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts @@ -373,8 +373,8 @@ async function answerPlannedDataLoss(input: { return ok({ delta, deletes }); } -/** A storage entity a plan with no operations names: its coordinate, without the internal unbound namespace id. */ -type ChangedStorageEntity = Omit & { +/** The coordinate of a storage entity a plan changed, without the internal unbound namespace id. */ +type ChangedElementCoordinate = Omit & { readonly namespaceId?: string; }; @@ -450,11 +450,9 @@ export interface MigrationPlanResult { /** The new required fields whose existing rows the plan gives a temporary value. */ readonly temporaryValues: readonly TemporaryValueReport[]; /** - * The storage entities that differ between the origin and the destination, when the storage - * changed but the written migration has no operations. A coordinate in the unbound namespace has - * no `namespaceId`, since that id is internal. + * The storage entities that differ between the origin and the destination. Empty when nothing was written (`noOp`), since the storage hashes are equal. A written migration whose `operations` is empty still lists them. A coordinate in the unbound namespace has no `namespaceId`, since that id is internal. */ - readonly changedStorageEntities?: readonly ChangedStorageEntity[]; + readonly changedElementCoordinates: readonly ChangedElementCoordinate[]; readonly timings: { readonly total: number; }; @@ -726,6 +724,7 @@ async function executeMigrationPlanCommandInner( ...(warnings.length > 0 ? { warnings } : {}), appliedStatements: [], temporaryValues: [], + changedElementCoordinates: [], summary: 'No changes detected between contracts', timings: { total: Date.now() - startTime }, }; @@ -908,6 +907,10 @@ async function executeMigrationPlanCommandInner( placeholderDirs: [baselineDir], appliedStatements: [...statementsWithoutOperations, ...deletes], temporaryValues: baselineLeg.value.temporaryValues, + changedElementCoordinates: changedElementCoordinates( + fromContract?.storage ?? null, + toContract.storage, + ).map(withoutUnboundNamespaceId), summary: placeholderSummary('baseline', [baselinePackageDir], invocationOf(options)), timings: { total: Date.now() - startTime }, }; @@ -933,6 +936,10 @@ async function executeMigrationPlanCommandInner( ...(warnings.length > 0 ? { warnings } : {}), appliedStatements: [...statementsWithoutOperations, ...deletes], temporaryValues: baselineLeg.value.temporaryValues, + changedElementCoordinates: changedElementCoordinates( + fromContract?.storage ?? null, + toContract.storage, + ).map(withoutUnboundNamespaceId), summary: buildAutoBaselinePlanSummary(baselineOps.length, 0, emittedExtensionDirs.length), timings: { total: Date.now() - startTime }, }; @@ -971,6 +978,10 @@ async function executeMigrationPlanCommandInner( ], appliedStatements: [...deltaLeg.appliedStatements, ...deletes], temporaryValues: deltaLeg.temporaryValues, + changedElementCoordinates: changedElementCoordinates( + fromContract?.storage ?? null, + toContract.storage, + ).map(withoutUnboundNamespaceId), summary: placeholderSummary( 'baseline + migration', [baselinePackageDir, deltaPackageDir], @@ -1072,6 +1083,7 @@ async function executeMigrationPlanCommandInner( ...(warnings.length > 0 ? { warnings } : {}), appliedStatements: [...deltaLeg.appliedStatements, ...deletes], temporaryValues: deltaLeg.temporaryValues, + changedElementCoordinates: [], summary: 'No changes to plan: the statements need no operations', timings: { total: Date.now() - startTime }, }; @@ -1097,6 +1109,10 @@ async function executeMigrationPlanCommandInner( ...(fromDefaulted ? { fromDefaulted } : {}), appliedStatements: [...deltaLeg.appliedStatements, ...deletes], temporaryValues: deltaLeg.temporaryValues, + changedElementCoordinates: changedElementCoordinates( + fromContract?.storage ?? null, + toContract.storage, + ).map(withoutUnboundNamespaceId), summary: placeholderSummary('migration', [packageDir], invocationOf(options)), timings: { total: Date.now() - startTime }, }; @@ -1149,21 +1165,27 @@ function withNoOperationsReport( hasNoOperations: boolean, fromContract: Contract | null, toContract: Contract, -): Pick { - if (!hasNoOperations) return { summary }; +): Pick { const changed = changedElementCoordinates(fromContract?.storage ?? null, toContract.storage); + const reported = { changedElementCoordinates: changed.map(withoutUnboundNamespaceId) }; + if (!hasNoOperations) return { ...reported, summary }; const what = changed.length === 0 ? `No storage entity changed; what changed is ${changedStorageKeys(fromContract, toContract).join(', ') || 'outside the storage entities'}.` : `What changed: ${changed.map(storageEntityText).join(', ')}.`; return { - changedStorageEntities: changed.map(({ namespaceId, ...rest }) => - namespaceId === UNBOUND_NAMESPACE_ID ? rest : { namespaceId, ...rest }, - ), + ...reported, summary: `${summary}. The storage the contract describes changed, but the database needs no operation for it, so the migration has none. ${what}`, }; } +function withoutUnboundNamespaceId({ + namespaceId, + ...rest +}: EntityCoordinate): ChangedElementCoordinate { + return namespaceId === UNBOUND_NAMESPACE_ID ? rest : { namespaceId, ...rest }; +} + /** The top-level storage keys whose content differs between two contracts, such as `storage.types`, in key order. */ function changedStorageKeys(fromContract: Contract | null, toContract: Contract): string[] { const plain = (storage: unknown): Record => diff --git a/packages/1-framework/3-tooling/cli/src/orm/migration/plan.ts b/packages/1-framework/3-tooling/cli/src/orm/migration/plan.ts index f8a51500e670..53daeb46d00e 100644 --- a/packages/1-framework/3-tooling/cli/src/orm/migration/plan.ts +++ b/packages/1-framework/3-tooling/cli/src/orm/migration/plan.ts @@ -202,7 +202,7 @@ function planBlocks(result: MigrationPlanResult, migrationsRelative: string): re ...warningBlocks(result), { kind: 'summary', - status: result.changedStorageEntities === undefined ? 'ok' : 'warn', + status: result.operations.length === 0 ? 'warn' : 'ok', text: result.summary, }, ...originNoticeBlocks(result), diff --git a/packages/1-framework/3-tooling/cli/test/orm/migration-plan.no-operations.test.ts b/packages/1-framework/3-tooling/cli/test/orm/migration-plan.no-operations.test.ts index cd631dd4ba6a..a2cf7ba90fd8 100644 --- a/packages/1-framework/3-tooling/cli/test/orm/migration-plan.no-operations.test.ts +++ b/packages/1-framework/3-tooling/cli/test/orm/migration-plan.no-operations.test.ts @@ -93,7 +93,7 @@ const STORAGE_TO: FixtureStorageNamespaces = { }, }; -const CHANGED_STORAGE_ENTITIES = [ +const CHANGED_ELEMENT_COORDINATES = [ { plane: 'storage', namespaceId: 'public', entityKind: 'table', entityName: 'users' }, { plane: 'storage', namespaceId: 'public', entityKind: 'valueSet', entityName: 'Role' }, { plane: 'storage', namespaceId: 'public', entityKind: 'valueSet', entityName: 'Status' }, @@ -164,7 +164,7 @@ function writtenWithNoOperations(to: string) { from: HASH_FROM, to, operations: [], - changedStorageEntities: CHANGED_STORAGE_ENTITIES, + changedElementCoordinates: CHANGED_ELEMENT_COORDINATES, summary: NO_OPERATIONS_SUMMARY, }, }; @@ -211,7 +211,7 @@ describe('migration plan when the planner produces no operations', () => { { exitCode: 0, data: { - changedStorageEntities: [], + changedElementCoordinates: [], summary: 'Planned 0 operation(s). The storage the contract describes changed, but the database needs no operation for it, so the migration has none. No storage entity changed; what changed is storage.types.', }, @@ -220,6 +220,33 @@ describe('migration plan when the planner produces no operations', () => { }, ); + it('names what changed in storage for a migration with operations too, and does not warn', async () => { + const project = await projectWithHistory(); + const harness = createOrmTestCli({ + commands: OFFLINE_COMMANDS, + groups: BIN_GROUPS, + orm: offlineConfig({ project, script: { operations: [ADDITIVE_OP] } }), + }); + + const plan = await harness.run(['migration', 'plan'], { + cwd: project.dir, + isTty: { stdout: true }, + }); + + expect({ + data: plan.presented?.data, + summary: plan.presented?.presentation.human.find((block) => block.kind === 'summary'), + }).toMatchObject({ + data: { + noOp: false, + operations: [expect.objectContaining({ id: ADDITIVE_OP.id })], + changedElementCoordinates: CHANGED_ELEMENT_COORDINATES, + summary: 'Planned 1 operation(s)', + }, + summary: { status: 'ok' }, + }); + }); + it('says so in a warning, above the contract edge', async () => { const project = await projectWithHistory(); @@ -251,7 +278,7 @@ describe('migration plan when the planner produces no operations', () => { from: HASH_FROM, to: HASH_TO, operations: [], - changedStorageEntities: CHANGED_STORAGE_ENTITIES, + changedElementCoordinates: CHANGED_ELEMENT_COORDINATES, summary: `Planned baseline (1 operation(s)) + 0 operation(s). ${NO_OPERATIONS_TEXT}`, }, }); diff --git a/test/integration/test/cli-journeys/plans-with-no-operations.e2e.test.ts b/test/integration/test/cli-journeys/plans-with-no-operations.e2e.test.ts index 4d804b3122f3..0fdf2232cb1a 100644 --- a/test/integration/test/cli-journeys/plans-with-no-operations.e2e.test.ts +++ b/test/integration/test/cli-journeys/plans-with-no-operations.e2e.test.ts @@ -48,7 +48,7 @@ const TO_PSL = FROM_PSL.replace(' id Int @id\n\n', ' id Int @id\n emai interface NoOperationsPlan { readonly operations: readonly unknown[]; - readonly changedStorageEntities: readonly unknown[]; + readonly changedElementCoordinates: readonly unknown[]; readonly summary: string; } @@ -89,7 +89,7 @@ withTempDir(({ createTempDir }) => { expect(plan.exitCode, `N1.05: plan: ${plan.stderr}`).toBe(0); expect(parseJsonOutput(plan), 'N1.05: no operations').toMatchObject({ operations: [], - changedStorageEntities: [ + changedElementCoordinates: [ { plane: 'storage', namespaceId: 'public', diff --git a/test/integration/test/cli-journeys/plans-with-no-operations.mongo.e2e.test.ts b/test/integration/test/cli-journeys/plans-with-no-operations.mongo.e2e.test.ts index 2ce979d7942c..b3dbb23c91bd 100644 --- a/test/integration/test/cli-journeys/plans-with-no-operations.mongo.e2e.test.ts +++ b/test/integration/test/cli-journeys/plans-with-no-operations.mongo.e2e.test.ts @@ -30,7 +30,7 @@ const FIXTURES_DIR = join(fixtureAppDir, 'fixtures/mongo-cli-journeys'); interface NoOperationsPlan { readonly operations: readonly unknown[]; - readonly changedStorageEntities: readonly unknown[]; + readonly changedElementCoordinates: readonly unknown[]; readonly summary: string; } @@ -128,7 +128,7 @@ describe('Journeys (MongoDB): a plan with no operations', { expect(plan.exitCode, `N1.05: plan: ${plan.stderr}`).toBe(0); expect(parseJsonOutput(plan), 'N1.05: no operations').toMatchObject({ operations: [], - changedStorageEntities: [ + changedElementCoordinates: [ expect.objectContaining({ entityKind: 'collection', entityName: 'auth_users' }), ], summary: expect.stringContaining('collection "'), diff --git a/test/integration/test/cli-journeys/plans-with-no-operations.sqlite.e2e.test.ts b/test/integration/test/cli-journeys/plans-with-no-operations.sqlite.e2e.test.ts index 2ab0c00eba42..261d548163bd 100644 --- a/test/integration/test/cli-journeys/plans-with-no-operations.sqlite.e2e.test.ts +++ b/test/integration/test/cli-journeys/plans-with-no-operations.sqlite.e2e.test.ts @@ -47,7 +47,7 @@ const TO_PSL = FROM_PSL.replace( interface NoOperationsPlan { readonly operations: readonly unknown[]; - readonly changedStorageEntities: readonly unknown[]; + readonly changedElementCoordinates: readonly unknown[]; readonly summary: string; } @@ -99,13 +99,13 @@ withTempDir(({ createTempDir }) => { expect( { operations: planned.operations, - changedStorageEntities: planned.changedStorageEntities, + changedElementCoordinates: planned.changedElementCoordinates, summary: planned.summary, }, 'N1.05: no operations', ).toEqual({ operations: [], - changedStorageEntities: [ + changedElementCoordinates: [ { plane: 'storage', entityKind: 'valueSet', entityName: 'Role' }, ], summary: expect.stringContaining('What changed: valueSet "Role".'), From 89861e6efa7eae1931011f9e36ed5c5c995d9489 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 19:12:49 +0200 Subject: [PATCH 085/104] TML-3537: a storage validator beside the domain validator checks the fields of storage value objects, and both family storage validators run it Signed-off-by: willbot Signed-off-by: Will Madden --- .../0-foundation/contract/package.json | 1 + .../contract/src/exports/validate-storage.ts | 1 + .../contract/src/field-problems.ts | 91 +++++++++++ .../contract/src/validate-domain.ts | 143 ++---------------- .../src/validate-storage-value-objects.ts | 49 ++++++ .../contract/test/validate-domain.test.ts | 68 --------- .../validate-storage-value-objects.test.ts | 97 ++++++++++++ .../0-foundation/contract/tsdown.config.ts | 1 + .../mongo-contract/src/validate-storage.ts | 2 + .../1-core/contract/src/validators.ts | 2 + .../@prisma/orm-framework/package.json | 1 + .../9-public/@prisma/orm-mongo/package.json | 1 + .../9-public/@prisma/orm-mysql/package.json | 1 + .../@prisma/orm-postgres/package.json | 1 + .../9-public/@prisma/orm-sqlite/package.json | 1 + projects/migration-statements/deferred.md | 2 +- 16 files changed, 262 insertions(+), 200 deletions(-) create mode 100644 packages/1-framework/0-foundation/contract/src/exports/validate-storage.ts create mode 100644 packages/1-framework/0-foundation/contract/src/field-problems.ts create mode 100644 packages/1-framework/0-foundation/contract/src/validate-storage-value-objects.ts create mode 100644 packages/1-framework/0-foundation/contract/test/validate-storage-value-objects.test.ts diff --git a/packages/1-framework/0-foundation/contract/package.json b/packages/1-framework/0-foundation/contract/package.json index 7f1716b38bbf..6308c58dac01 100644 --- a/packages/1-framework/0-foundation/contract/package.json +++ b/packages/1-framework/0-foundation/contract/package.json @@ -52,6 +52,7 @@ "./types": "./dist/types.mjs", "./validate-domain": "./dist/validate-domain.mjs", "./validate-once": "./dist/validate-once.mjs", + "./validate-storage": "./dist/validate-storage.mjs", "./package.json": "./package.json" }, "engines": { diff --git a/packages/1-framework/0-foundation/contract/src/exports/validate-storage.ts b/packages/1-framework/0-foundation/contract/src/exports/validate-storage.ts new file mode 100644 index 000000000000..48fcabfa8320 --- /dev/null +++ b/packages/1-framework/0-foundation/contract/src/exports/validate-storage.ts @@ -0,0 +1 @@ +export { validateStorageValueObjects } from '../validate-storage-value-objects'; diff --git a/packages/1-framework/0-foundation/contract/src/field-problems.ts b/packages/1-framework/0-foundation/contract/src/field-problems.ts new file mode 100644 index 000000000000..3f1897439f7f --- /dev/null +++ b/packages/1-framework/0-foundation/contract/src/field-problems.ts @@ -0,0 +1,91 @@ +import { blindCast } from '@internal/utils/casts'; +import type { ContractWithDomain } from './domain-envelope'; +import { valueObjectAt } from './storage-value-objects'; + +interface FieldTypeLike { + readonly kind?: string; + readonly plane?: string; + readonly namespaceId?: string; + readonly entityKind?: string; + readonly entityName?: string; + readonly name?: string; + readonly members?: readonly FieldTypeLike[]; +} + +export interface FieldLike { + readonly type?: FieldTypeLike; + readonly valueSet?: { readonly namespaceId?: string; readonly entityName?: string }; + readonly many?: unknown; + readonly elementNullable?: unknown; + readonly dict?: boolean; +} + +export function fieldLike(field: unknown): FieldLike | undefined { + return blindCast(field); +} + +function valueObjectReferenceProblem(type: FieldTypeLike): string | undefined { + if (type.plane === undefined && type.entityKind === undefined) { + return `refers to value object "${type.name}" by name; contracts no longer do: a field refers to its value object by the coordinate of a storage entity under storage.namespaces..entries.valueObject, so re-emit the contract from its source`; + } + if (type.plane !== 'storage' || type.entityKind !== 'valueObject') { + return `refers to a value object with plane "${type.plane}" and entityKind "${type.entityKind}"; a value object reference has plane "storage" and entityKind "valueObject"`; + } + return undefined; +} + +/** What is wrong with the value object references of a field's type, a union's members included, each prefixed with `location`. */ +export function valueObjectTypeProblems( + storage: ContractWithDomain['storage'], + type: FieldTypeLike | undefined, + location: string, +): readonly string[] { + if (type === undefined) return []; + if (type.kind === 'union') { + return (type.members ?? []).flatMap((member) => + valueObjectTypeProblems(storage, member, location), + ); + } + if (type.kind !== 'valueObject') return []; + const problem = valueObjectReferenceProblem(type); + if (problem !== undefined) return [`${location} ${problem}`]; + const namespaceId = type.namespaceId ?? ''; + const entityName = type.entityName ?? ''; + const found = valueObjectAt(storage, { + plane: 'storage', + namespaceId, + entityKind: 'valueObject', + entityName, + }); + return found === undefined + ? [ + `${location} refers to value object "${entityName}" in storage namespace "${namespaceId}", which storage.namespaces.${namespaceId}.entries.valueObject does not have`, + ] + : []; +} + +/** What is wrong with a field's `many`, `elementNullable` and `dict`, each prefixed with `location`. */ +export function fieldModifierProblems(location: string, field: unknown): readonly string[] { + const candidate = fieldLike(field); + const many = candidate?.many; + const isList = + typeof many === 'object' && + many !== null && + !Array.isArray(many) && + Object.keys(many).length === 1 && + 'elementNullable' in many && + typeof many.elementNullable === 'boolean'; + const problems: string[] = []; + if (many !== undefined && many !== false && !isList) { + problems.push( + `${location} has invalid "many"; expected false or an elementNullable descriptor`, + ); + } + if (candidate?.elementNullable !== undefined) { + problems.push(`${location} cannot have sibling "elementNullable"; put it inside "many"`); + } + if (isList && candidate?.dict) { + problems.push(`${location} cannot have both "many" and "dict" modifiers`); + } + return problems; +} diff --git a/packages/1-framework/0-foundation/contract/src/validate-domain.ts b/packages/1-framework/0-foundation/contract/src/validate-domain.ts index 306d4fc389fc..12637c105f57 100644 --- a/packages/1-framework/0-foundation/contract/src/validate-domain.ts +++ b/packages/1-framework/0-foundation/contract/src/validate-domain.ts @@ -1,10 +1,9 @@ -import { blindCast } from '@internal/utils/casts'; import { ContractValidationError } from './contract-validation-error'; import type { CrossReference } from './cross-reference'; import type { ContractWithDomain } from './domain-envelope'; +import { fieldLike, fieldModifierProblems, valueObjectTypeProblems } from './field-problems'; import { isPlainRecord } from './is-plain-record'; import { asNamespaceId, type NamespaceId } from './namespace-id'; -import { valueObjectAt, valueObjectsWithCoordinates } from './storage-value-objects'; export interface DomainRelationShape { readonly to: CrossReference; @@ -89,7 +88,7 @@ export function validateContractDomain(contract: DomainContractShape): void { validateDiscriminators(modelIndex, errors); validateOwnership(contract, modelIndex, errors); validateValueObjectReferences(contract, errors); - validateFieldModifiers(modelIndex, contract, errors); + validateFieldModifiers(modelIndex, errors); if (errors.length > 0) { throw new ContractValidationError( @@ -276,147 +275,29 @@ function validateOwnership( } } -interface FieldTypeLike { - readonly kind?: string; - readonly plane?: string; - readonly namespaceId?: string; - readonly entityKind?: string; - readonly entityName?: string; - readonly name?: string; - readonly members?: readonly FieldTypeLike[]; -} - -interface FieldLike { - readonly type?: FieldTypeLike; - readonly valueSet?: { readonly namespaceId?: string; readonly entityName?: string }; - readonly many?: unknown; - readonly elementNullable?: unknown; - readonly dict?: boolean; -} - -function fieldLike(field: unknown): FieldLike | undefined { - return blindCast(field); -} - -function valueObjectReferenceProblem(type: FieldTypeLike): string | undefined { - if (type.plane === undefined && type.entityKind === undefined) { - return `refers to value object "${type.name}" by name; contracts no longer do: a field refers to its value object by the coordinate of a storage entity under storage.namespaces..entries.valueObject, so re-emit the contract from its source`; - } - if (type.plane !== 'storage' || type.entityKind !== 'valueObject') { - return `refers to a value object with plane "${type.plane}" and entityKind "${type.entityKind}"; a value object reference has plane "storage" and entityKind "valueObject"`; - } - return undefined; -} - function validateValueObjectReferences(contract: DomainContractShape, errors: string[]): void { - function checkType(type: FieldTypeLike | undefined, location: string): void { - if (!type) return; - if (type.kind === 'valueObject') { - const problem = valueObjectReferenceProblem(type); - if (problem !== undefined) { - errors.push(`${location} ${problem}`); - return; - } - const namespaceId = type.namespaceId ?? ''; - const entityName = type.entityName ?? ''; - const found = valueObjectAt(contract.storage, { - plane: 'storage', - namespaceId, - entityKind: 'valueObject', - entityName, - }); - if (found === undefined) { - errors.push( - `${location} refers to value object "${entityName}" in storage namespace "${namespaceId}", which storage.namespaces.${namespaceId}.entries.valueObject does not have`, - ); - } - return; - } - if (type.kind === 'union') { - for (const member of type.members ?? []) checkType(member, location); - } - } - for (const [namespaceKey, namespace] of Object.entries(contract.domain.namespaces)) { const namespaceId = asNamespaceId(namespaceKey); for (const [modelName, model] of Object.entries(namespace.models)) { for (const [fieldName, field] of Object.entries(model.fields)) { - checkType( - fieldLike(field)?.type, - `Model "${namespaceId}:${modelName}" field "${fieldName}"`, + errors.push( + ...valueObjectTypeProblems( + contract.storage, + fieldLike(field)?.type, + `Model "${namespaceId}:${modelName}" field "${fieldName}"`, + ), ); } } } - for (const { - coordinate: { namespaceId, entityName: name }, - valueObject, - } of valueObjectsWithCoordinates(contract.storage)) { - for (const [fieldName, field] of Object.entries(valueObject.fields)) { - const location = `Value object "${namespaceId}:${name}" field "${fieldName}"`; - checkType(fieldLike(field)?.type, location); - const problem = missingValueSetProblem(contract, fieldLike(field)?.valueSet); - if (problem !== undefined) errors.push(`${location} ${problem}`); - } - } } -/** The problem with a value object member's storage value set when storage does not have it. */ -function missingValueSetProblem( - contract: DomainContractShape, - valueSet: FieldLike['valueSet'], -): string | undefined { - if (valueSet === undefined) return undefined; - const namespaceId = valueSet.namespaceId ?? ''; - const entityName = valueSet.entityName ?? ''; - const slot = Object.hasOwn(contract.storage.namespaces, namespaceId) - ? contract.storage.namespaces[namespaceId]?.entries?.['valueSet'] - : undefined; - return isPlainRecord(slot) && Object.hasOwn(slot, entityName) - ? undefined - : `refers to value set "${entityName}" in storage namespace "${namespaceId}", which storage.namespaces.${namespaceId}.entries.valueSet does not have`; -} - -function validateFieldModifiers( - modelIndex: ModelIndex, - contract: DomainContractShape, - errors: string[], -): void { - function validateField(location: string, field: unknown): void { - const candidate = fieldLike(field); - const many = candidate?.many; - const isList = - typeof many === 'object' && - many !== null && - !Array.isArray(many) && - Object.keys(many).length === 1 && - 'elementNullable' in many && - typeof many.elementNullable === 'boolean'; - - if (many !== undefined && many !== false && !isList) { - errors.push( - `${location} has invalid "many"; expected false or an elementNullable descriptor`, - ); - } - if (candidate?.elementNullable !== undefined) { - errors.push(`${location} cannot have sibling "elementNullable"; put it inside "many"`); - } - if (isList && candidate?.dict) { - errors.push(`${location} cannot have both "many" and "dict" modifiers`); - } - } - +function validateFieldModifiers(modelIndex: ModelIndex, errors: string[]): void { for (const { namespaceId, name: modelName, model } of iterateIndexedModels(modelIndex)) { for (const [fieldName, field] of Object.entries(model.fields)) { - validateField(`Model "${namespaceId}:${modelName}" field "${fieldName}"`, field); - } - } - for (const { - coordinate: { namespaceId, entityName: name }, - valueObject, - } of valueObjectsWithCoordinates(contract.storage)) { - for (const [fieldName, field] of Object.entries(valueObject.fields)) { - validateField(`Value object "${namespaceId}:${name}" field "${fieldName}"`, field); + errors.push( + ...fieldModifierProblems(`Model "${namespaceId}:${modelName}" field "${fieldName}"`, field), + ); } } } diff --git a/packages/1-framework/0-foundation/contract/src/validate-storage-value-objects.ts b/packages/1-framework/0-foundation/contract/src/validate-storage-value-objects.ts new file mode 100644 index 000000000000..412309118292 --- /dev/null +++ b/packages/1-framework/0-foundation/contract/src/validate-storage-value-objects.ts @@ -0,0 +1,49 @@ +import { ContractValidationError } from './contract-validation-error'; +import type { ContractWithDomain } from './domain-envelope'; +import { + type FieldLike, + fieldLike, + fieldModifierProblems, + valueObjectTypeProblems, +} from './field-problems'; +import { isPlainRecord } from './is-plain-record'; +import { valueObjectsWithCoordinates } from './storage-value-objects'; + +/** + * Checks the fields of the value objects in storage (ADR 309): each value object a field refers to is in storage, each value set a member names is in storage, and each field's `many` and `dict` are well formed. Both families run it beside `validateContractDomain`, which checks the references from the domain to storage. + */ +export function validateStorageValueObjects(storage: ContractWithDomain['storage']): void { + const errors = valueObjectsWithCoordinates(storage).flatMap( + ({ coordinate: { namespaceId, entityName }, valueObject }) => + Object.entries(valueObject.fields).flatMap(([fieldName, field]) => { + const location = `Value object "${namespaceId}:${entityName}" field "${fieldName}"`; + const valueSetProblem = missingValueSetProblem(storage, fieldLike(field)?.valueSet); + return [ + ...valueObjectTypeProblems(storage, fieldLike(field)?.type, location), + ...(valueSetProblem === undefined ? [] : [`${location} ${valueSetProblem}`]), + ...fieldModifierProblems(location, field), + ]; + }), + ); + if (errors.length > 0) { + throw new ContractValidationError( + `Contract storage validation failed:\n- ${errors.join('\n- ')}`, + 'storage', + ); + } +} + +function missingValueSetProblem( + storage: ContractWithDomain['storage'], + valueSet: FieldLike['valueSet'], +): string | undefined { + if (valueSet === undefined) return undefined; + const namespaceId = valueSet.namespaceId ?? ''; + const entityName = valueSet.entityName ?? ''; + const slot = Object.hasOwn(storage.namespaces, namespaceId) + ? storage.namespaces[namespaceId]?.entries?.['valueSet'] + : undefined; + return isPlainRecord(slot) && Object.hasOwn(slot, entityName) + ? undefined + : `refers to value set "${entityName}" in storage namespace "${namespaceId}", which storage.namespaces.${namespaceId}.entries.valueSet does not have`; +} diff --git a/packages/1-framework/0-foundation/contract/test/validate-domain.test.ts b/packages/1-framework/0-foundation/contract/test/validate-domain.test.ts index d577d406601f..5a4bb632feb0 100644 --- a/packages/1-framework/0-foundation/contract/test/validate-domain.test.ts +++ b/packages/1-framework/0-foundation/contract/test/validate-domain.test.ts @@ -824,19 +824,6 @@ describe('validateContractDomain()', () => { expect(() => validateContractDomain(contract)).not.toThrow(); }); - it('refuses a value object field that refers to a value object storage does not have', () => { - const contract = makeValidContract({ - roots: {}, - models: {}, - valueObjects: { - Wrapper: { fields: { inner: { nullable: false, type: valueObjectType('Nonexistent') } } }, - }, - }); - expect(() => validateContractDomain(contract)).toThrow( - `Value object "__unbound__:Wrapper" field "inner" refers to value object "Nonexistent" in storage namespace "__unbound__", which storage.namespaces.__unbound__.entries.valueObject does not have`, - ); - }); - it('refuses a union member that refers to a value object storage does not have', () => { const contract = makeValidContract({ roots: { users: crossRef('User') }, @@ -878,42 +865,6 @@ describe('validateContractDomain()', () => { expect(() => validateContractDomain(contract)).not.toThrow(); }); - it('refuses a value object member whose storage value set storage does not have', () => { - const member = (entityName: string) => ({ - nullable: false, - type: { kind: 'scalar', codecId: 'pg/text@1' }, - valueSet: { - plane: 'storage', - entityKind: 'valueSet', - namespaceId: UNBOUND_DOMAIN_NAMESPACE_ID, - entityName, - }, - }); - const contract = makeValidContract({ - roots: {}, - models: {}, - valueObjects: { Tagged: { fields: { role: member('Role'), kind: member('Kind') } } }, - }); - const storage = contract.storage.namespaces[UNBOUND_DOMAIN_NAMESPACE_ID]; - const withRole = { - ...contract, - storage: { - namespaces: { - [UNBOUND_DOMAIN_NAMESPACE_ID]: { - id: UNBOUND_DOMAIN_NAMESPACE_ID, - entries: { - ...storage?.entries, - valueSet: { Role: { kind: 'valueSet', values: ['a'] } }, - }, - }, - }, - }, - }; - expect(() => validateContractDomain(withRole)).toThrow( - 'Contract domain validation failed:\n- Value object "__unbound__:Tagged" field "kind" refers to value set "Kind" in storage namespace "__unbound__", which storage.namespaces.__unbound__.entries.valueSet does not have', - ); - }); - it('refuses value objects in the domain plane, and says where they live now', () => { const contract = makeValidContract({ domain: { @@ -985,24 +936,5 @@ describe('validateContractDomain()', () => { }); expect(() => validateContractDomain(contract)).toThrow(/sibling "elementNullable"/); }); - - it('rejects old many: true on a storage value object field when type checking is bypassed', () => { - const contract = makeValidContract({ - roots: {}, - models: {}, - valueObjects: { - Address: { - fields: { - tags: { - nullable: false, - type: { kind: 'scalar', codecId: 'pg/text@1' }, - many: true, - }, - }, - }, - }, - }); - expect(() => validateContractDomain(contract)).toThrow(/Value object.*invalid "many"/); - }); }); }); diff --git a/packages/1-framework/0-foundation/contract/test/validate-storage-value-objects.test.ts b/packages/1-framework/0-foundation/contract/test/validate-storage-value-objects.test.ts new file mode 100644 index 000000000000..d5c80ee1b99a --- /dev/null +++ b/packages/1-framework/0-foundation/contract/test/validate-storage-value-objects.test.ts @@ -0,0 +1,97 @@ +import { describe, expect, it } from 'vitest'; +import { ContractValidationError } from '../src/contract-validation-error'; +import type { StorageNamespace } from '../src/types'; +import { validateContractDomain } from '../src/validate-domain'; +import { validateStorageValueObjects } from '../src/validate-storage-value-objects'; + +const NAMESPACE = '__unbound__'; +const text = { nullable: false, type: { kind: 'scalar', codecId: 'pg/text@1' } }; + +function valueObjectType(name: string) { + return { + kind: 'valueObject', + plane: 'storage', + namespaceId: NAMESPACE, + entityKind: 'valueObject', + entityName: name, + }; +} + +function storageWith(entries: StorageNamespace['entries']) { + return { namespaces: { [NAMESPACE]: { id: NAMESPACE, entries } } }; +} + +function validating(entries: StorageNamespace['entries']) { + return () => validateStorageValueObjects(storageWith(entries)); +} + +describe('validateStorageValueObjects', () => { + it('accepts a value object whose field refers to another value object, and one that refers to itself', () => { + expect( + validating({ + valueObject: { + Address: { fields: { street: text } }, + Profile: { fields: { home: { nullable: true, type: valueObjectType('Address') } } }, + TreeNode: { + fields: { label: text, child: { nullable: true, type: valueObjectType('TreeNode') } }, + }, + }, + }), + ).not.toThrow(); + }); + + it('refuses a value object field that refers to a value object storage does not have', () => { + expect( + validating({ + valueObject: { + Wrapper: { fields: { inner: { nullable: false, type: valueObjectType('Nonexistent') } } }, + }, + }), + ).toThrow( + 'Contract storage validation failed:\n- Value object "__unbound__:Wrapper" field "inner" refers to value object "Nonexistent" in storage namespace "__unbound__", which storage.namespaces.__unbound__.entries.valueObject does not have', + ); + }); + + it('refuses a value object member whose storage value set storage does not have', () => { + const member = (entityName: string) => ({ + ...text, + valueSet: { plane: 'storage', entityKind: 'valueSet', namespaceId: NAMESPACE, entityName }, + }); + expect( + validating({ + valueObject: { Tagged: { fields: { role: member('Role'), kind: member('Kind') } } }, + valueSet: { Role: { kind: 'valueSet', values: ['a'] } }, + }), + ).toThrow( + 'Contract storage validation failed:\n- Value object "__unbound__:Tagged" field "kind" refers to value set "Kind" in storage namespace "__unbound__", which storage.namespaces.__unbound__.entries.valueSet does not have', + ); + }); + + it('refuses the old many: true on a value object field', () => { + expect( + validating({ valueObject: { Address: { fields: { tags: { ...text, many: true } } } } }), + ).toThrow(/Value object "__unbound__:Address" field "tags" has invalid "many"/); + }); + + it('throws a storage validation error', () => { + expect( + validating({ valueObject: { Address: { fields: { tags: { ...text, many: true } } } } }), + ).toThrow(ContractValidationError); + }); +}); + +describe('validateContractDomain', () => { + it('leaves the fields of storage value objects to the storage validator', () => { + expect(() => + validateContractDomain({ + roots: {}, + domain: { namespaces: { [NAMESPACE]: { models: {} } } }, + storage: storageWith({ + valueObject: { + Wrapper: { fields: { inner: { nullable: false, type: valueObjectType('Missing') } } }, + }, + }), + }), + ).not.toThrow(); + }); +}); diff --git a/packages/1-framework/0-foundation/contract/tsdown.config.ts b/packages/1-framework/0-foundation/contract/tsdown.config.ts index fa4eb5c8e9eb..dca3c077e11a 100644 --- a/packages/1-framework/0-foundation/contract/tsdown.config.ts +++ b/packages/1-framework/0-foundation/contract/tsdown.config.ts @@ -8,6 +8,7 @@ export default defineConfig({ 'src/exports/resolve-domain-model.ts', 'src/exports/types.ts', 'src/exports/validate-domain.ts', + 'src/exports/validate-storage.ts', 'src/exports/contract-validation-error.ts', 'src/exports/hashing.ts', 'src/exports/hashing-utils.ts', diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/src/validate-storage.ts b/packages/2-mongo-family/1-foundation/mongo-contract/src/validate-storage.ts index d0edc78041f4..2c9d9cb9ef01 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/src/validate-storage.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/src/validate-storage.ts @@ -1,4 +1,5 @@ import type { ContractModel, ContractRelation } from '@internal/contract/types'; +import { validateStorageValueObjects } from '@internal/contract/validate-storage'; import { structuredError } from '@internal/utils/structured-error'; import { collectionRootModelsOf } from './collection-root-models'; import { lookupFreeRefusalsOf } from './contract-refusals'; @@ -162,6 +163,7 @@ function bothStoredAs( } export function validateMongoStorage(contract: MongoContract): void { + validateStorageValueObjects(contract.storage); const errors: string[] = []; let fieldStorageComplete = true; diff --git a/packages/2-sql-family/1-core/contract/src/validators.ts b/packages/2-sql-family/1-core/contract/src/validators.ts index b4e3f1c7d655..e893f00151b9 100644 --- a/packages/2-sql-family/1-core/contract/src/validators.ts +++ b/packages/2-sql-family/1-core/contract/src/validators.ts @@ -9,6 +9,7 @@ import { removedDomainKeyProblems, validateContractDomain, } from '@internal/contract/validate-domain'; +import { validateStorageValueObjects } from '@internal/contract/validate-storage'; import { createDataTypeLookup, DATA_TYPE_ID_PATTERN, @@ -982,6 +983,7 @@ export function validateExecutionDefaultsTargetMappedColumns(contract: Contract< * counts match their referenced columns. Throws on the first mismatch. */ export function validateSqlStorageConsistency(contract: Contract): void { + validateStorageValueObjects(contract.storage); for (const { namespaceId, tableName, table: rawTable } of eachStorageTable(contract.storage)) { const table = blindCast( rawTable, diff --git a/packages/9-public/@prisma/orm-framework/package.json b/packages/9-public/@prisma/orm-framework/package.json index 7a04d396499b..d30df2bf529e 100644 --- a/packages/9-public/@prisma/orm-framework/package.json +++ b/packages/9-public/@prisma/orm-framework/package.json @@ -75,6 +75,7 @@ "./contract/types": "./dist/contract__types.mjs", "./contract/validate-domain": "./dist/contract__validate-domain.mjs", "./contract/validate-once": "./dist/contract__validate-once.mjs", + "./contract/validate-storage": "./dist/contract__validate-storage.mjs", "./contract-authoring": "./dist/contract-authoring.mjs", "./errors": "./dist/errors.mjs", "./errors/control": "./dist/errors__control.mjs", diff --git a/packages/9-public/@prisma/orm-mongo/package.json b/packages/9-public/@prisma/orm-mongo/package.json index d39da5792df4..4ebe1b11b556 100644 --- a/packages/9-public/@prisma/orm-mongo/package.json +++ b/packages/9-public/@prisma/orm-mongo/package.json @@ -69,6 +69,7 @@ "./contract/types": "./dist/contract__types.mjs", "./contract/validate-domain": "./dist/contract__validate-domain.mjs", "./contract/validate-once": "./dist/contract__validate-once.mjs", + "./contract/validate-storage": "./dist/contract__validate-storage.mjs", "./contract-builder": "./dist/contract-builder.mjs", "./control": "./dist/control.mjs", "./driver": "./dist/driver.mjs", diff --git a/packages/9-public/@prisma/orm-mysql/package.json b/packages/9-public/@prisma/orm-mysql/package.json index 4eab48876f87..3a70c10c001a 100644 --- a/packages/9-public/@prisma/orm-mysql/package.json +++ b/packages/9-public/@prisma/orm-mysql/package.json @@ -66,6 +66,7 @@ "./contract/types": "./dist/contract__types.mjs", "./contract/validate-domain": "./dist/contract__validate-domain.mjs", "./contract/validate-once": "./dist/contract__validate-once.mjs", + "./contract/validate-storage": "./dist/contract__validate-storage.mjs", "./contract-builder": "./dist/contract-builder.mjs", "./control": "./dist/control.mjs", "./driver": "./dist/driver.mjs", diff --git a/packages/9-public/@prisma/orm-postgres/package.json b/packages/9-public/@prisma/orm-postgres/package.json index 89f53195f4d7..623756df4f24 100644 --- a/packages/9-public/@prisma/orm-postgres/package.json +++ b/packages/9-public/@prisma/orm-postgres/package.json @@ -79,6 +79,7 @@ "./contract/types": "./dist/contract__types.mjs", "./contract/validate-domain": "./dist/contract__validate-domain.mjs", "./contract/validate-once": "./dist/contract__validate-once.mjs", + "./contract/validate-storage": "./dist/contract__validate-storage.mjs", "./contract-builder": "./dist/contract-builder.mjs", "./control": "./dist/control.mjs", "./driver": "./dist/driver.mjs", diff --git a/packages/9-public/@prisma/orm-sqlite/package.json b/packages/9-public/@prisma/orm-sqlite/package.json index 6937b3115832..ef918f42df05 100644 --- a/packages/9-public/@prisma/orm-sqlite/package.json +++ b/packages/9-public/@prisma/orm-sqlite/package.json @@ -74,6 +74,7 @@ "./contract/types": "./dist/contract__types.mjs", "./contract/validate-domain": "./dist/contract__validate-domain.mjs", "./contract/validate-once": "./dist/contract__validate-once.mjs", + "./contract/validate-storage": "./dist/contract__validate-storage.mjs", "./contract-builder": "./dist/contract-builder.mjs", "./control": "./dist/control.mjs", "./driver": "./dist/driver.mjs", diff --git a/projects/migration-statements/deferred.md b/projects/migration-statements/deferred.md index 3acac9e1dd7c..0a768ea39b74 100644 --- a/projects/migration-statements/deferred.md +++ b/projects/migration-statements/deferred.md @@ -239,7 +239,7 @@ An enum `A = "a", B = "b"` becomes `B = "a", A = "b"`: each member keeps its nam ## Column value-set references are not checked against storage -A column's `valueSet: { plane: 'storage', entityKind: 'valueSet', entityName }` is not checked to name an existing `storage.namespaces..entries.valueSet` entry, in either family. A hand-edited or corrupt `contract.json` with a dangling reference passes validation and fails later. PR 3 of the remaining-nouns slice adds the check for value object members only. Found in the review of that PR, 2026-10-23. +A column's `valueSet: { plane: 'storage', entityKind: 'valueSet', entityName }` is not checked to name an existing `storage.namespaces..entries.valueSet` entry, in either family. A hand-edited or corrupt `contract.json` with a dangling reference passes validation and fails later. PR 3 of the remaining-nouns slice adds the check for value object members only, in `validateStorageValueObjects` (`@internal/contract/validate-storage`), where the column check belongs too. Found in the review of that PR, 2026-10-23. ## Composite types are not namespaced in SQL PSL From 683d2c0bd95ca307c46caaf86311c082e4d50d2c Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 19:14:34 +0200 Subject: [PATCH 086/104] TML-3537: the refusal of a contract with value objects in the old shape names the upgrade script for a stored contract, and the Mongo schema keeps no copy of it Signed-off-by: willbot Signed-off-by: Will Madden --- .../0-foundation/contract/src/field-problems.ts | 2 +- .../0-foundation/contract/src/validate-domain.ts | 2 +- .../0-foundation/contract/test/validate-domain.test.ts | 4 ++-- .../1-foundation/mongo-contract/src/contract-schema.ts | 10 ++-------- .../test/mongo-contract-serializer-base.test.ts | 2 +- .../1-core/contract/test/storage-value-object.test.ts | 2 +- 6 files changed, 8 insertions(+), 14 deletions(-) diff --git a/packages/1-framework/0-foundation/contract/src/field-problems.ts b/packages/1-framework/0-foundation/contract/src/field-problems.ts index 3f1897439f7f..a2f07c264766 100644 --- a/packages/1-framework/0-foundation/contract/src/field-problems.ts +++ b/packages/1-framework/0-foundation/contract/src/field-problems.ts @@ -26,7 +26,7 @@ export function fieldLike(field: unknown): FieldLike | undefined { function valueObjectReferenceProblem(type: FieldTypeLike): string | undefined { if (type.plane === undefined && type.entityKind === undefined) { - return `refers to value object "${type.name}" by name; contracts no longer do: a field refers to its value object by the coordinate of a storage entity under storage.namespaces..entries.valueObject, so re-emit the contract from its source`; + return `refers to value object "${type.name}" by name; contracts no longer do: a field refers to its value object by the coordinate of a storage entity under storage.namespaces..entries.valueObject, so re-emit the contract from its source, or rewrite a stored contract such as a migration snapshot with value-objects-in-storage.ts, which ships in the value-objects-in-storage scripts of the Prisma 8 upgrade guide`; } if (type.plane !== 'storage' || type.entityKind !== 'valueObject') { return `refers to a value object with plane "${type.plane}" and entityKind "${type.entityKind}"; a value object reference has plane "storage" and entityKind "valueObject"`; diff --git a/packages/1-framework/0-foundation/contract/src/validate-domain.ts b/packages/1-framework/0-foundation/contract/src/validate-domain.ts index 12637c105f57..796be8fb26f8 100644 --- a/packages/1-framework/0-foundation/contract/src/validate-domain.ts +++ b/packages/1-framework/0-foundation/contract/src/validate-domain.ts @@ -71,7 +71,7 @@ export function removedDomainKeyProblems(contract: unknown): readonly string[] { return Object.entries(namespaces).flatMap(([namespaceId, namespace]) => isPlainRecord(namespace) && Object.hasOwn(namespace, 'valueObjects') ? [ - `domain.namespaces.${namespaceId}.valueObjects: contracts no longer store value objects in the domain plane; they are storage entities under storage.namespaces..entries.valueObject, so re-emit the contract from its source`, + `domain.namespaces.${namespaceId}.valueObjects: contracts no longer store value objects in the domain plane; they are storage entities under storage.namespaces..entries.valueObject, so re-emit the contract from its source, or rewrite a stored contract such as a migration snapshot with value-objects-in-storage.ts, which ships in the value-objects-in-storage scripts of the Prisma 8 upgrade guide`, ] : [], ); diff --git a/packages/1-framework/0-foundation/contract/test/validate-domain.test.ts b/packages/1-framework/0-foundation/contract/test/validate-domain.test.ts index 5a4bb632feb0..caef2b4e72d3 100644 --- a/packages/1-framework/0-foundation/contract/test/validate-domain.test.ts +++ b/packages/1-framework/0-foundation/contract/test/validate-domain.test.ts @@ -788,7 +788,7 @@ describe('validateContractDomain()', () => { valueObjects: { Address: address }, }); expect(() => validateContractDomain(contract)).toThrow( - 'Model "__unbound__:User" field "addr" refers to value object "Address" by name; contracts no longer do: a field refers to its value object by the coordinate of a storage entity under storage.namespaces..entries.valueObject, so re-emit the contract from its source', + 'Model "__unbound__:User" field "addr" refers to value object "Address" by name; contracts no longer do: a field refers to its value object by the coordinate of a storage entity under storage.namespaces..entries.valueObject, so re-emit the contract from its source, or rewrite a stored contract such as a migration snapshot with value-objects-in-storage.ts, which ships in the value-objects-in-storage scripts of the Prisma 8 upgrade guide', ); }); @@ -875,7 +875,7 @@ describe('validateContractDomain()', () => { roots: {}, }); expect(() => validateContractDomain(contract)).toThrow( - 'domain.namespaces.public.valueObjects: contracts no longer store value objects in the domain plane; they are storage entities under storage.namespaces..entries.valueObject, so re-emit the contract from its source', + 'domain.namespaces.public.valueObjects: contracts no longer store value objects in the domain plane; they are storage entities under storage.namespaces..entries.valueObject, so re-emit the contract from its source, or rewrite a stored contract such as a migration snapshot with value-objects-in-storage.ts, which ships in the value-objects-in-storage scripts of the Prisma 8 upgrade guide', ); }); }); diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/src/contract-schema.ts b/packages/2-mongo-family/1-foundation/mongo-contract/src/contract-schema.ts index 2098b78a10cc..48e03e485b41 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/src/contract-schema.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/src/contract-schema.ts @@ -468,16 +468,10 @@ export function createMongoContractSchema( domain: type({ namespaces: type({ '[string]': type({ + '+': 'reject', models: type({ '[string]': ModelDefinitionSchema }), 'enum?': type({ '[string]': ContractEnumSchema }), - }).narrow((namespace, ctx) => - Object.hasOwn(namespace, 'valueObjects') - ? ctx.reject({ - expected: - 'a domain namespace without valueObjects: contracts no longer store value objects in the domain plane; they are storage entities under storage.namespaces..entries.valueObject, so re-emit the contract from its source', - }) - : true, - ), + }), }), }), storage: type({ diff --git a/packages/2-mongo-family/9-family/test/mongo-contract-serializer-base.test.ts b/packages/2-mongo-family/9-family/test/mongo-contract-serializer-base.test.ts index 1a506eea175e..ee6ff1d7e416 100644 --- a/packages/2-mongo-family/9-family/test/mongo-contract-serializer-base.test.ts +++ b/packages/2-mongo-family/9-family/test/mongo-contract-serializer-base.test.ts @@ -150,7 +150,7 @@ describe('MongoContractSerializerBase', () => { }, }; expect(() => new RecordingSerializer().deserializeContract(old)).toThrow( - 'Contract structural validation failed: domain.namespaces.__unbound__.valueObjects: contracts no longer store value objects in the domain plane; they are storage entities under storage.namespaces..entries.valueObject, so re-emit the contract from its source', + 'Contract structural validation failed: domain.namespaces.__unbound__.valueObjects: contracts no longer store value objects in the domain plane; they are storage entities under storage.namespaces..entries.valueObject, so re-emit the contract from its source, or rewrite a stored contract such as a migration snapshot with value-objects-in-storage.ts, which ships in the value-objects-in-storage scripts of the Prisma 8 upgrade guide', ); }); }); diff --git a/packages/2-sql-family/1-core/contract/test/storage-value-object.test.ts b/packages/2-sql-family/1-core/contract/test/storage-value-object.test.ts index b016dcaf083e..4413f4af9791 100644 --- a/packages/2-sql-family/1-core/contract/test/storage-value-object.test.ts +++ b/packages/2-sql-family/1-core/contract/test/storage-value-object.test.ts @@ -220,7 +220,7 @@ describe('validateSqlContractFully with a value object in storage', () => { }, }; expect(() => validateSqlContractFully(old)).toThrow( - 'Contract structural validation failed: domain.namespaces.__unbound__.valueObjects: contracts no longer store value objects in the domain plane; they are storage entities under storage.namespaces..entries.valueObject, so re-emit the contract from its source', + 'Contract structural validation failed: domain.namespaces.__unbound__.valueObjects: contracts no longer store value objects in the domain plane; they are storage entities under storage.namespaces..entries.valueObject, so re-emit the contract from its source, or rewrite a stored contract such as a migration snapshot with value-objects-in-storage.ts, which ships in the value-objects-in-storage scripts of the Prisma 8 upgrade guide', ); }); }); From 6a8e655b8d945ea28b43433fe8414dbcaa171683 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 19:15:52 +0200 Subject: [PATCH 087/104] TML-3537: ADR 309 says why a value object has one definition, where its principle stops, and which changes have no operations; the Prisma 6 reader no longer promises @map on composite fields; small doc fixes Signed-off-by: willbot Signed-off-by: Will Madden --- ... and a discriminator column names a value set.md | 13 +++++++++++-- docs/reference/error-reference.md | 2 +- .../0-foundation/contract/src/entity-coordinate.ts | 2 +- .../2-authoring/contract-prisma6/src/interpreter.ts | 2 +- .../expected-diagnostics.json | 2 +- .../slices/remaining-nouns/spec.md | 2 +- 6 files changed, 16 insertions(+), 7 deletions(-) diff --git a/docs/architecture docs/adrs/ADR 309 - Value objects are storage entities, and a discriminator column names a value set.md b/docs/architecture docs/adrs/ADR 309 - Value objects are storage entities, and a discriminator column names a value set.md index 3900cb5a2b06..11e0ba8293a4 100644 --- a/docs/architecture docs/adrs/ADR 309 - Value objects are storage entities, and a discriminator column names a value set.md +++ b/docs/architecture docs/adrs/ADR 309 - Value objects are storage entities, and a discriminator column names a value set.md @@ -90,6 +90,10 @@ Storage already holds stored facts the database does not enforce. SQLite derives **Value objects.** The storage entity keeps the field definition the domain plane used, so every reader of a value object's fields reads the same shape, from the storage plane. Its fields have no stored names of their own: each is stored under its own name, in both families. Scalar members carry their codec and, when an enum types them, a `valueSet` reference to the enum's storage value set, since storage never refers to the domain plane. A member that is a value object refers to its storage entity by coordinate, so a value object that contains itself, such as a `NavItem` whose `children` are `NavItem`s, is one entity that refers to itself. On SQL the column that holds a value object stays a JSON column; on MongoDB the validator is derived from the storage entity, as it is from the definition today. +**Why one definition, not a domain definition and a storage entity.** Models and tables are two definitions: a domain model, a storage table, and the model's `storage` that maps one to the other, so a field can have a stored name different from its own. A value object could follow that pattern, with a domain value object, a storage entity and a mapping between them. It does not, because nothing needs the mapping today: a value object's fields are stored under their own names in both families, so a second definition would hold the same fields and need a check that the two agree. The cost is that a value object field cannot have a stored name of its own without a later change to this shape. Adding one then means a stored-name slot, or a domain definition beside the storage entity, and the hash rules for each. + +**Where the principle stops.** "A fact the planner must see belongs in storage" is applied here to value objects and discriminator values. It is not applied to the names of enum members, which decide which member a stored value reads as; they stay in the domain plane. Whether they should move is open in [TML-3720](https://linear.app/prisma-company/issue/TML-3720). + **The framework defines the entity kind.** Families and packs contribute storage entity kinds ([ADR 221](ADR%20221%20-%20Contract%20IR%20two%20planes%20with%20uniform%20entity%20coordinate%20and%20pack-contributed%20entity%20kinds.md)), but `valueObject` is defined by the framework, because a domain field refers to it and the framework resolves that reference, validates it and types it. So does `valueSet` where a value object member names one. Both families validate a value object against one schema from the framework: each field has `nullable`, always written; `type`; `many` when it is a list; `dict` only as `true`; and an optional `valueSet` reference. Each family still registers its own descriptor and IR class for the kind. **Discriminators.** The value set is keyed `.`, such as `Task.type`, and holds the values sorted, so the order variants are declared in does not change the hash. A discriminator an enum types keeps the enum's value set and check: the column has one value set reference, and the enum already restricts it. Enum value sets are keyed by the enum's name, so only an enum named exactly like a discriminator's key, in the same storage namespace, clashes with it. No authoring surface writes such a name, since PSL names are identifiers and the SQL TypeScript builder has no polymorphism; the build refuses it all the same, naming both and saying to rename the enum. Enum names are not otherwise restricted. A variant stored in its own table changes nothing here: its rows are still told apart by the base table's column. The ORM writes a row only through a variant, so every row's value names a variant, and the derived check (`"type" IN ('bug', 'feature')`) rejects only a value no variant names, as an enum's check does. @@ -108,7 +112,12 @@ jsonb_path_match("address", 'strict $.type() == "object" The value must be an object, or for a list an array of objects. Each required member must be present; a nullable member may be absent or JSON `null`; no other key may be. A scalar member must have the JSON type its codec writes, an enum member must hold a value of its value set, and a nested value object is checked the same way, inline. A nullable column also accepts SQL `NULL` and JSON `null`. Members are read through `keyvalue()`, so an absent member is never an error, and strict mode keeps a comparison from unwrapping an array. A value object that contains itself is checked in full down to where it refers back into itself, and any object is accepted below, as the MongoDB validator does. A `Decimal` member must be a JSON string, as the SQL ORM writes it. Each PostgreSQL codec declares, on its descriptor, what the check accepts for a member of it: the JSON type the SQL ORM writes for its value; any JSON value, for a JSON codec; or presence only. A member whose codec declares presence only is checked to be present and, when it is required, not JSON `null`; a JSON member may hold any value. The check is opaque and content-named ([ADR 244](ADR%20244%20-%20Check%20constraints%20are%20opaque%20wire-named%20expressions.md)), so any change to the value object changes the check's name, and the diff drops the old check and adds the new one. -**Why every change is an operation.** The storage hash identifies a database state. Two different hashes are two different database states, so the migration between them does something to the database. Because the value object and the discriminator values are enforced, a change to either is a change to a check or a validator, which is an operation. A plan that changes the storage hash and produces no operation is therefore unexpected: it means the database cannot tell the two states apart. Such cases will still arise, so the system handles them rather than refusing: `migration plan` writes the package with no operations and says so, naming what changed, and `db migrate` applies it by moving the marker to the new hash. +**Why a change is normally an operation.** The storage hash identifies a database state. Two different hashes are two different database states, so the migration between them is expected to do something to the database. On a target that derives enforcement, a change to a value object or to the discriminator values is a change to a check or a validator, which is an operation. This holds on Postgres, for its derived checks, and on MongoDB, for its collection validators. It does not hold in every case, and the known cases with no operations are: + +- every value object change and every discriminator value change on a target with no check renderer, which today are SQLite and MySQL; +- on Postgres, a change to a member's codec or type parameters that keeps the JSON type the check reads, such as `pg/text@1` to `pg/varchar@1`: the check expression, and so its name, stay the same. + +The system handles these rather than refusing them: `migration plan` writes the package with no operations and reports it, naming the storage entities that changed, and `db migrate` applies it by moving the marker to the new hash. **Who reads them.** The migration planner compares them between the origin and destination contracts, to find renamed and removed value object fields and changed discriminator values, and to order the row rewrite between the old check's drop and the new check's creation, as for an enum value. Type emission and the ORM read value objects from the storage plane through the domain field's reference. @@ -124,7 +133,7 @@ The value must be an object, or for a list an array of objects. Each required me - **Keep value objects in the domain plane, because the database schema does not change.** The tables, columns and checks are the same after a value object field rename, so one could hold that the storage hash should not change and the migration system should ignore it. Then the system can never detect the change, even to refuse it, and the old keys stay in every row without a word. - **Keep them in the domain plane and record such a change as a migration from a hash to itself.** A migration with a hand-written data operation can already do this. But snapshots are saved by storage hash, so a plan's "previous contract" can be several changes old. Fixing that means a different snapshot key, a larger change than this one, and `db update`, which runs no data operations, still could not apply it. -- **Copy the definition into the storage plane and keep the domain copy.** The hash would change, but two copies of one definition invite drift, and the domain plane can already refer to storage. +- **Copy the definition into the storage plane and keep the domain copy.** The hash would change, but while value object fields have no stored names the two copies would hold the same fields, and they could drift apart. The domain plane can already refer to storage. See "Why one definition" above for when a second definition would be worth its cost. - **Feed the domain value objects into the storage hash without moving them.** The hash would change, but the planner compares storage, so it still could not tell what changed, and every reader of the hash would have to know that part of the domain plane feeds it. - **Record the facts in storage without enforcing them.** The hash would change while the database stayed the same, so a change would need a migration with no operations. That contradicts what the hash means, and it leaves rows the contract no longer describes. diff --git a/docs/reference/error-reference.md b/docs/reference/error-reference.md index 08680a7afa50..e5964cd9e465 100644 --- a/docs/reference/error-reference.md +++ b/docs/reference/error-reference.md @@ -683,7 +683,7 @@ An `@@index`, `@@unique`, or `@@fulltext` path that reaches into a composite typ ### PSL.PRISMA6_MONGO_COMPOSITE_MAP_UNSUPPORTED -`@map` on a field of a composite `type`, which the Mongo contract cannot express yet. Removing `@map` renames the stored field, so keep the schema until Prisma 8 supports it. Reported by the Prisma 6 MongoDB contract source (`prisma6Schema`) during `contract emit`, as a finding in the `diagnostics` list of `CONTRACT.SOURCE_LOAD_FAILED`, never on its own. `summary` is `:: `, with only the file when there is no position (the terminal prints the code before it), and `where` carries `path` and, when known, `line`. Payload: none. +`@map` on a field of a composite `type`. Prisma 8 stores each field of a value object under its own name ([ADR 309](../architecture%20docs/adrs/ADR%20309%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md)), so rename the field to the name it is stored under and remove `@map`. Reported by the Prisma 6 MongoDB contract source (`prisma6Schema`) during `contract emit`, as a finding in the `diagnostics` list of `CONTRACT.SOURCE_LOAD_FAILED`, never on its own. `summary` is `:: `, with only the file when there is no position (the terminal prints the code before it), and `where` carries `path` and, when known, `line`. Payload: none. ### PSL.PRISMA6_MONGO_CONTRACT_INVALID diff --git a/packages/1-framework/0-foundation/contract/src/entity-coordinate.ts b/packages/1-framework/0-foundation/contract/src/entity-coordinate.ts index 02c61e53e2c5..a2d403fb1c20 100644 --- a/packages/1-framework/0-foundation/contract/src/entity-coordinate.ts +++ b/packages/1-framework/0-foundation/contract/src/entity-coordinate.ts @@ -1,7 +1,7 @@ /** * Canonical address for a named entity in a contract plane. * - * `plane` is the top-level contract plane the entity lives on. `entityKind` equals the key of the slot the entity lives under (`'table'` for `entries.table`, `'valueObject'` for `entries.valueObject`, `'enum'` for the domain's `enum` slot), with no translation between the two (ADR 224). `entityName` is the entity's key in that slot. + * `plane` is the top-level contract plane the entity lives on. `entityKind` equals the key of the slot the entity lives under (`'valueSet'` for `entries.valueSet`, `'valueObject'` for `entries.valueObject`, `'enum'` for the domain's `enum` slot), with no translation between the two (ADR 224). `entityName` is the entity's key in that slot. * * References between planes obey a directional invariant: domain may refer to storage; storage may never refer to domain. The coordinate carries the plane a validator checks. * diff --git a/packages/2-mongo-family/2-authoring/contract-prisma6/src/interpreter.ts b/packages/2-mongo-family/2-authoring/contract-prisma6/src/interpreter.ts index cce9d5dc5876..22723c5c41fc 100644 --- a/packages/2-mongo-family/2-authoring/contract-prisma6/src/interpreter.ts +++ b/packages/2-mongo-family/2-authoring/contract-prisma6/src/interpreter.ts @@ -877,7 +877,7 @@ function readCompositeFields( ctx.diagnostics.push( prisma6Diagnostic( 'PSL.PRISMA6_MONGO_COMPOSITE_MAP_UNSUPPORTED', - `${label}: @map on a field of a composite type is not supported yet. The stored field keeps the name "${stringArgument(attribute) ?? field.name}", so removing @map renames it in storage; keep the schema as it is until Prisma 8 supports it.`, + `${label}: @map on a field of a composite type is not supported: Prisma 8 stores each field of a value object under its own name. Rename the field to "${stringArgument(attribute) ?? field.name}", the name it is stored under, and remove @map.`, sourceId, attribute.span, ), diff --git a/packages/2-mongo-family/2-authoring/contract-prisma6/test/fixtures/composite-map-unsupported/expected-diagnostics.json b/packages/2-mongo-family/2-authoring/contract-prisma6/test/fixtures/composite-map-unsupported/expected-diagnostics.json index f34dc6db9dbc..65eaed41f11f 100644 --- a/packages/2-mongo-family/2-authoring/contract-prisma6/test/fixtures/composite-map-unsupported/expected-diagnostics.json +++ b/packages/2-mongo-family/2-authoring/contract-prisma6/test/fixtures/composite-map-unsupported/expected-diagnostics.json @@ -3,6 +3,6 @@ "code": "PSL.PRISMA6_MONGO_COMPOSITE_MAP_UNSUPPORTED", "file": "schema.prisma", "line": 12, - "message": "Field \"Address.street\": @map on a field of a composite type is not supported yet. The stored field keeps the name \"street_name\", so removing @map renames it in storage; keep the schema as it is until Prisma 8 supports it." + "message": "Field \"Address.street\": @map on a field of a composite type is not supported: Prisma 8 stores each field of a value object under its own name. Rename the field to \"street_name\", the name it is stored under, and remove @map." } ] diff --git a/projects/migration-statements/slices/remaining-nouns/spec.md b/projects/migration-statements/slices/remaining-nouns/spec.md index 0e087c2f49d8..e5ec3dc4bfb2 100644 --- a/projects/migration-statements/slices/remaining-nouns/spec.md +++ b/projects/migration-statements/slices/remaining-nouns/spec.md @@ -40,7 +40,7 @@ Namespace items are Postgres only: SQLite has no namespaces. Everything else wor 1. **Value objects become storage entities, and a discriminator column names a value set** (agreed with Will, 2026-10-21; [ADR 309](../../../../docs/architecture%20docs/adrs/ADR%20309%20-%20Value%20objects%20are%20storage%20entities,%20and%20a%20discriminator%20column%20names%20a%20value%20set.md)). Today a value object field rename, a discriminator value change and a variant removal with no own columns change only the domain plane. The storage hash covers only the storage plane, so it stays the same, and the migration system cannot see the change: `migration plan` reports "No changes detected", a same-hash migration fails the integrity check, and snapshots are keyed by storage hash with the first write kept. A value object's fields and a discriminator's values are stored data the planner must see, so they belong in storage: - **Value objects** move from `domain.namespaces..valueObjects` to `storage.namespaces..entries.valueObject`, in both families, keeping their definition. A domain field refers to one by entity coordinate, as a field typed by an enum refers to its value set. No copy. - **Discriminator values** are a value set keyed `.`, which the base table's discriminator column names, with a derived check on Postgres as an enum column has. - - **Both are enforced:** on Postgres a value object column gets a derived check on its keys (`?&` and `-` for a flat shape, `jsonpath` for nesting and lists); on MongoDB the validator already lists them. So a change is normally a check or validator change. Will, 2026-10-21: migrations are expected never to be empty, because two storage hashes are two database states, but edge cases will happen, and the system handles them: `migration plan` writes the package with no operations and says what changed, and `db migrate` moves the marker. The "no operations" refusal goes for a plan whose storage hash changed. + - **Both are enforced:** on Postgres a value object column gets a derived check on its shape, one strict `jsonpath` predicate written so an unknown result fails; on MongoDB the validator already lists them. So a change is normally a check or validator change. Will, 2026-10-21: migrations are expected never to be empty, because two storage hashes are two database states, but edge cases will happen, and the system handles them: `migration plan` writes the package with no operations and says what changed, and `db migrate` moves the marker. The "no operations" refusal goes for a plan whose storage hash changed. **Cost:** every contract with value objects or variants is re-emitted with a new storage hash, with an upgrade fragment. The move touches every reader of value objects: both emitters, both type renderers, the ORMs and the MongoDB validator derivation. From 57d70fdfeffb3d8009e45b12c1c5f2d1d25bf9e2 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 19:20:29 +0200 Subject: [PATCH 088/104] TML-3537: the value object check's doc comment sits on its function, and the contract README lists the validate-storage export Signed-off-by: willbot Signed-off-by: Will Madden --- packages/1-framework/0-foundation/contract/README.md | 1 + .../postgres/1-target/src/core/check-expressions.ts | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/1-framework/0-foundation/contract/README.md b/packages/1-framework/0-foundation/contract/README.md index e3ace3e78d87..b2315f201705 100644 --- a/packages/1-framework/0-foundation/contract/README.md +++ b/packages/1-framework/0-foundation/contract/README.md @@ -55,6 +55,7 @@ For SQL contracts, use `@internal/sql-contract-ts/schema-sql` instead. - `./testing`: Test helpers for creating contract fixtures - `./validate-contract`: Contract structure validation - `./validate-domain`: Domain model validation +- `./validate-storage`: Validation of the value objects in storage (`validateStorageValueObjects`) ## Type System diff --git a/packages/3-targets/postgres/1-target/src/core/check-expressions.ts b/packages/3-targets/postgres/1-target/src/core/check-expressions.ts index 4ce9f37d5d2d..c0504015f49e 100644 --- a/packages/3-targets/postgres/1-target/src/core/check-expressions.ts +++ b/packages/3-targets/postgres/1-target/src/core/check-expressions.ts @@ -73,9 +73,6 @@ export function postgresRenderCheckExpressions( return candidates; } -/** - * One strict `jsonpath` predicate over the whole column value. Strict mode keeps a comparison from unwrapping an array, and `IS TRUE` makes a predicate that evaluates to unknown fail the check, as a CHECK would otherwise pass on NULL. Member values are read through `keyvalue()`, so an absent member is never an error. - */ type JsonTypeOfCodec = (codecId: string) => ValueObjectJsonType; /** The JSON type each codec declares for a value object member, read from the codec's descriptor. */ @@ -92,6 +89,9 @@ function jsonTypeReader( }; } +/** + * One strict `jsonpath` predicate over the whole column value. Strict mode keeps a comparison from unwrapping an array, and `IS TRUE` makes a predicate that evaluates to unknown fail the check, as a CHECK would otherwise pass on NULL. Member values are read through `keyvalue()`, so an absent member is never an error. + */ function valueObjectCheck( column: string, columnValueObject: ResolvedColumnValueObject, From 3143a6661949a8390950f8e1aa184b1c6f76ce5e Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 19:42:18 +0200 Subject: [PATCH 089/104] TML-3537: dispatch brief for the fixes from the second whole-branch review Signed-off-by: willbot Signed-off-by: Will Madden --- .../dispatches/19-pr3-review-round-2-fixes.md | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 projects/migration-statements/slices/remaining-nouns/dispatches/19-pr3-review-round-2-fixes.md diff --git a/projects/migration-statements/slices/remaining-nouns/dispatches/19-pr3-review-round-2-fixes.md b/projects/migration-statements/slices/remaining-nouns/dispatches/19-pr3-review-round-2-fixes.md new file mode 100644 index 000000000000..5da3a5af4331 --- /dev/null +++ b/projects/migration-statements/slices/remaining-nouns/dispatches/19-pr3-review-round-2-fixes.md @@ -0,0 +1,83 @@ +# Dispatch 19: fixes from the second whole-branch review of PR 3 + +**Plan:** [`../plan-pr3.md`](../plan-pr3.md) · **ADR:** ADR 309 · **Reviews:** `../reviews/pr3/system-design-review.md` (A18–A26) and `../reviews/pr3/code-review.md` (F6–F9) · **Branch:** `tml-3537-value-objects-storage` + +Each item records the decision; do what it says. Write a failing test first for every behaviour change. + +## Codecs and their JSON type + +1. **A19: name the fact, not the consumer.** + - Rename `valueObjectJsonType` to `jsonType`. It is the JSON type the codec's value is written as inside a JSON document. + - The values are `'string' | 'number' | 'boolean' | 'object' | 'array' | 'any'`. `'any'` means the value can be any JSON value, including `null`, as for `Json`. `'presenceOnly'` goes away. + - Read each codec's encode path, and declare what it actually writes. For example, `pg/enum` writes a string, so it is `'string'`, and `Bytes` writes an object. + - The check enforces the declared type. A required member is never JSON `null` unless its codec is `'any'`. + - Some codecs have no JSON form the SQL ORM can write today. `BigInt` is one: the jsonb codec throws when it writes one. For such a codec, declare the type its value would have, and keep the `deferred.md` entry. List each one in the report. + - Update the codec authoring guide, the error reference and the extension fragment. +2. **F6: a codec without `jsonType` is an error only where a value object member uses it.** + - At runtime, the registries in the runtime adapter and in the control adapter accept a descriptor without `jsonType`. A codec package compiled before this release keeps working in apps that do not put it in a value object. + - In TypeScript the field stays required, so authors who rebuild get a compile error. + - When the build derives a value object check for a member whose codec has no `jsonType`, it refuses with a structured error, not an invariant. The error names: + - the codec id; + - the value object and the member; + - the missing property; + - what to do: the codec's author adds `jsonType`. + + Add the error to the error reference. + - Fix the extension fragment's detection, so it also finds descriptors that implement `AnyPostgresCodecDescriptor` directly. +3. **A20: the family codecs declare their own `jsonType`.** The SQL family codecs (`sql/int` and the others) declare it on the family codec, not through options in the Postgres adapter. +4. **A25: the family resolves the JSON type.** + - The family resolves each member's JSON type when it resolves the value object, into `ResolvedValueObject`. + - The renderer reads that, and drops its `codecLookup` argument. + - Remove `NO_CODECS`. + +## Contract types and schemas + +5. **A18: storage-only functions take storage.** About 30 functions now take `ContractWithDomain['storage']` but read only storage. These include `elementCoordinates`, `changedElementCoordinates`, `entityAt` and SQL's `storageOf`. Type their input `Pick`. Keep `ContractWithDomain` only where the domain is read. +6. **A21: one field type schema.** + - Export one run-time schema for a field's type from the framework. + - The framework's value object member schema, the SQL domain field schema and the Mongo domain field schema all use it. + - SQL's acceptance of a union inside a union then follows whatever the shared schema decides. A union inside a union is refused, as Mongo does. +7. **A22: the published SQL JSON schema matches the refusal.** `data-contract-sql-v1.json` must not allow union or dictionary value object members. Make the generator produce that, or state it in the source schema the generator reads, then regenerate the file. +8. **A24: one name corrected.** + - Rename `StorageValueObjectSchema` to `ContractValueObjectSchema`, since it validates `ContractValueObject`. + - Keep `ValueObjectReference`. Its earlier name read as the opposite of `ContractValueObjectField`, which round 1 decided to fix. + - Keep `anyObject`. Round 1 decided on it. +9. **F7: the SQL union and dictionary refusal names the value object.** The message names the value object and the member: `value object ":" member "" is a dictionary; SQL value objects hold no unions or dictionaries, since their check cannot enforce them`. It does not print the namespace as JSON. + +## CLI + +10. **A26: the JSON key says what users read.** Rename the `--json` key `changedElementCoordinates` to `changedStorageEntities`. That matches the README, the doc comment and the summary text. It stays filled for every written migration. Internal functions keep `changedElementCoordinates`. + +## Upgrade script and docs + +11. **F8: the script normalizes old Mongo value object members.** It adds `nullable: false` where a member has no `nullable`, and removes `dict: false`. The old Mongo schema allowed both forms. Add tests. +12. **A23: ADR wording.** In ADR 221 and ADR 309, say the framework *reads* the `valueSet` slot where a member names one, and *defines* `valueObject`. +13. **F9: ADR 309's two sentences.** Correct the first Consequence, and the reason given for rejecting the "record without enforcing" alternative, so they agree with the list of cases that have no operations. + +## Rules + +- Never run `pnpm test:integration`, `pnpm test:e2e` or `pnpm test:all`. Run only named files inside `test/integration`, with `pnpm test `, and journeys with `pnpm test:journeys `. `pnpm test:packages` is allowed. +- Use `mise exec --` for node and pnpm. Never `npx`. Do not write to `/tmp` or any system temp directory. Save slow output under `wip/` and read the file. Never use bare `git stash`. +- No comments where the code can say it. No bare `as` casts in production code. No `any` type. No file extensions in imports. No backwards-compatibility exports. Framework packages name no family or target. +- No invented nouns. Never hard-wrap Markdown prose. ADRs carry no ticket ids beyond those already there, and no dispatch names. +- Commit with `git commit -s --trailer "Signed-off-by: Will Madden "`. No AI attribution lines. Stage files explicitly. Commit re-emitted files apart from hand-written changes. +- Do not push. Do not open a PR. + +## Done when + +The same checks as dispatch 18: + +- `pnpm build` and `fixtures:check` are clean. +- `pnpm test:packages` passes, apart from the known tarball trust warning. +- Typecheck passes in every example. +- `lint:deps`, `lint:ratchets`, `check:error-reference` and `check:upgrade-coverage --mode pr --prev $(git merge-base HEAD origin/main) --head HEAD` pass. +- Typecheck, lint and `test --coverage` pass in every touched package. +- These pass: + - the journeys `value-object-checks`, `value-object-validator.mongo`, `plans-with-no-operations*` and `enum-value-statements*`; + - `sql-orm-client/value-object-checks`; + - `authored-contract-roundtrip`; + - the upgrade script test. + +## Report + +Plain English, short sentences: each item and what you did, each codec's `jsonType` where it was not obvious, the tests that were red first, the checks, anything you could not do and why, and the commits. From 8e27971b9b9ff8b5b3095da316bbbcab395cd2b2 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 20:00:18 +0200 Subject: [PATCH 090/104] TML-3537: a codec declares jsonType, the JSON type its value is written as; the SQL family resolves each value object member's JSON type, and the check renderer reads only its input jsonType is one of string, number, boolean, object, array or any. The SQL family codecs declare it on their templates, and postgresCodec keeps it. The family reads it when it resolves a value object and refuses a member whose codec declares none with CONTRACT.CODEC_JSON_TYPE_MISSING. The Postgres registries accept a descriptor without it, so an older codec package keeps working where it is not in a value object. Signed-off-by: willbot Signed-off-by: Will Madden --- .../subsystems/1. Data Contract.md | 2 +- docs/reference/codec-authoring-guide.md | 11 +- docs/reference/error-reference.md | 6 +- .../2-sql-family/1-core/contract/package.json | 1 + .../1-core/contract/src/check-expressions.ts | 62 ++++++-- .../1-core/contract/src/codec-json-type.ts | 16 +++ .../1-core/contract/src/contract-errors.ts | 1 + .../contract/src/exports/codec-json-type.ts | 6 + .../contract/test/check-expressions.test.ts | 67 ++++++--- .../1-core/contract/test/test-type-lookups.ts | 16 ++- .../1-core/contract/tsdown.config.ts | 1 + .../contract-ts/src/build-contract.ts | 33 ++--- .../contract-ts/src/lower-column.ts | 31 ++-- .../contract-builder.derived-checks.test.ts | 6 +- .../4-lanes/relational-core/README.md | 2 +- .../relational-core/src/ast/sql-codecs.ts | 6 + .../src/core/arktype-json-codec.ts | 2 +- .../3-extensions/pgvector/src/core/codecs.ts | 2 +- .../3-extensions/postgis/src/core/codecs.ts | 2 +- .../1-target/src/core/check-expressions.ts | 77 ++-------- .../1-target/src/core/codec-descriptor.ts | 31 ++-- .../postgres/1-target/src/core/codecs.ts | 58 ++++---- .../postgres/1-target/src/core/date-codecs.ts | 4 +- .../src/core/interval-string-codec.ts | 2 +- .../src/core/psl-infer/infer-model-blocks.ts | 37 ++--- .../src/core/psl-print/model-attributes.ts | 19 +-- .../src/core/psl-print/psl-contract.ts | 22 ++- .../1-target/src/core/temporal-codecs.ts | 10 +- .../src/core/temporal-string-codecs.ts | 8 +- .../1-target/src/exports/codec-descriptor.ts | 1 - .../check-expressions.integration.test.ts | 52 +++---- .../1-target/test/check-expressions.test.ts | 136 +++++++----------- .../check-expressions.value-object.test.ts | 56 ++++---- ...n-type.test.ts => codec-json-type.test.ts} | 49 +++++-- .../test/postgres-codec-descriptor.test-d.ts | 9 +- .../test/postgres-codec-descriptor.test.ts | 15 +- .../inferred-psl/inferred-psl.check.test.ts | 18 +-- ...erred-psl.list-element-nullability.test.ts | 18 +-- .../1-target/test/psl-print/build-context.ts | 4 +- .../test/lower-to-execute-request.test.ts | 28 ++-- .../alter-column-type.integration.test.ts | 18 +-- .../check-lifecycle-e2e.integration.test.ts | 22 ++- .../native-array-columns.integration.test.ts | 18 +-- ...ostgres-codec-registry-composition.test.ts | 6 +- .../test/sql-renderer.cast-policy.test.ts | 6 +- .../test/value-round-trip.integration.test.ts | 4 +- .../@prisma/orm-family-sql/package.json | 1 + .../9-public/@prisma/orm-mysql/package.json | 1 + .../@prisma/orm-postgres/package.json | 1 + .../9-public/@prisma/orm-sqlite/package.json | 1 + projects/migration-statements/deferred.md | 4 +- .../authoring/extension-text-type.test.ts | 2 +- .../sql-orm-client/include-codecs.test.ts | 14 +- .../extension/instructions.md | 12 +- 54 files changed, 515 insertions(+), 522 deletions(-) create mode 100644 packages/2-sql-family/1-core/contract/src/codec-json-type.ts create mode 100644 packages/2-sql-family/1-core/contract/src/exports/codec-json-type.ts rename packages/3-targets/postgres/1-target/test/{codec-value-object-json-type.test.ts => codec-json-type.test.ts} (51%) diff --git a/docs/architecture docs/subsystems/1. Data Contract.md b/docs/architecture docs/subsystems/1. Data Contract.md index c9aba434a6ae..415af987cc63 100644 --- a/docs/architecture docs/subsystems/1. Data Contract.md +++ b/docs/architecture docs/subsystems/1. Data Contract.md @@ -321,7 +321,7 @@ A `pg/int4@1` enum `Int4Level` with members `Low = 1` and `High = 10`, on column A list column gets `<@` containment instead of `IN`. In the same fixture, `levels FloatMixed[]`, whose members are written `1.5` and `NaN` and stored as `1.5` and `"NaN"`, gets `array_remove("levels", NULL) <@ '{1.5,"NaN"}'`, plus the derived check `array_position("levels", NULL) IS NULL` because its elements are not nullable. -On a `managed` Postgres table two more columns derive checks ([ADR 309](../adrs/ADR%20309%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md)). A discriminator column with no enum gets a membership check, `
__check`, holding the values of its `.` value set. A column that holds a value object gets a check named `
__shape`: one strict `jsonpath` predicate that the column value is an object (or, for a list, an array of objects) with each required member present, no other key, each scalar member of its JSON type, each enum member one of its value set's values, and each nested value object checked the same way. A nullable column accepts SQL `NULL` and JSON `null`; the ORM writes SQL `NULL` for an absent value. A `Decimal` member must be a JSON string, as the ORM writes it. Each Postgres codec declares on its descriptor the JSON type a member of it has (`valueObjectJsonType`), or that it holds any JSON value, or that only its presence is checked. A required member whose codec declares presence only must be present and not JSON `null`; a `Json` member may hold any value. Below the point where a value object refers back into itself, any object is accepted, as the MongoDB validator does. Adding a check takes a lock on the table while Postgres reads every row to check it. Any later change to a value object renames the check, so the check is dropped and added again, and the rows are read again. +On a `managed` Postgres table two more columns derive checks ([ADR 309](../adrs/ADR%20309%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md)). A discriminator column with no enum gets a membership check, `
__check`, holding the values of its `.` value set. A column that holds a value object gets a check named `
__shape`: one strict `jsonpath` predicate that the column value is an object (or, for a list, an array of objects) with each required member present, no other key, each scalar member of its JSON type, each enum member one of its value set's values, and each nested value object checked the same way. A nullable column accepts SQL `NULL` and JSON `null`; the ORM writes SQL `NULL` for an absent value. Each Postgres codec declares on its descriptor the JSON type its value is written as (`jsonType`): a `Decimal` member must be a JSON string and a `Bytes` member a JSON object, as the ORM writes them. A member is of its codec's JSON type, so a required member is never JSON `null`, except a `Json` member, whose type is `any` and which may hold any value. A contract build refuses a member whose codec declares no JSON type. Below the point where a value object refers back into itself, any object is accepted, as the MongoDB validator does. Adding a check takes a lock on the table while Postgres reads every row to check it. Any later change to a value object renames the check, so the check is dropped and added again, and the rows are read again. `contract infer` produces no value objects and no polymorphism, so on a live table with these checks it prints each one as an authored `@@check` with its exact name in `map:`, as it does for an enum membership check. diff --git a/docs/reference/codec-authoring-guide.md b/docs/reference/codec-authoring-guide.md index 07d8e24c2c78..7ed8ad19d577 100644 --- a/docs/reference/codec-authoring-guide.md +++ b/docs/reference/codec-authoring-guide.md @@ -96,7 +96,7 @@ export class PgTextDescriptor extends PostgresCodecDescriptor { return expression; } override readonly dataType = pgText.id; - override readonly valueObjectJsonType = 'string' as const; + override readonly jsonType = 'string' as const; override readonly codecId = 'pg/text@1' as const; override readonly traits = ['equality', 'order', 'textual'] as const; override readonly paramsSchema = undefined; @@ -149,7 +149,7 @@ export class PgVectorDescriptor extends PostgresCodecDescriptor { return CastExpr.as(expression, 'text'); } override readonly dataType = pgvectorVector.id; - override readonly valueObjectJsonType = 'presenceOnly' as const; + override readonly jsonType = 'array' as const; override readonly codecId = 'pg/vector@1' as const; override readonly traits = ['equality'] as const; override readonly paramsSchema: StandardSchemaV1 = pgvectorVectorParams; @@ -236,7 +236,7 @@ export class ArktypeJsonDescriptor extends PostgresCodecDescriptor expression, factory: (descriptor, dataType) => () => new SqlIntCodec(descriptor, dataType), }); ``` -The adapter delegates the wrapped descriptor's codec id, traits, literals and renderers, and builds codecs with the `factory` it is given, which receives the adapted descriptor, the data type and the column's parameters. A template's own factory builds no codec, because it names no data type. A target may pass a subclass of the family codec with its own wire rules, as PostgreSQL does for `int4`. The adapter's parameter schema is the data type's `params`, not the wrapped descriptor's, so a parameter's bound is declared once, on the data type. It adds the PostgreSQL discriminant and target methods. +The adapter delegates the wrapped descriptor's codec id, traits, JSON type, literals and renderers, and builds codecs with the `factory` it is given, which receives the adapted descriptor, the data type and the column's parameters. A template's own factory builds no codec, because it names no data type. A target may pass a subclass of the family codec with its own wire rules, as PostgreSQL does for `int4`. The adapter's parameter schema is the data type's `params`, not the wrapped descriptor's, so a parameter's bound is declared once, on the data type. It adds the PostgreSQL discriminant and target methods. ### SQLite diff --git a/docs/reference/error-reference.md b/docs/reference/error-reference.md index e5964cd9e465..e95b4afb953d 100644 --- a/docs/reference/error-reference.md +++ b/docs/reference/error-reference.md @@ -265,6 +265,10 @@ The SQL emitter is asked to emit an aggregate result row whose declared result c The control plane resolves a codec referenced by the contract (a `CodecRef.codecId`) against the contract's pack stack and finds no registered codec descriptor for that id. Hit during control-plane operations (emit, migration tooling) when a contract references a codec no composed pack provides. Payload: `codecId`. Also raised while a SQL contract is authored, when a column or storage type names a codec the contract's packs do not register, so its database type cannot be named from the codec's data type. A TypeScript `contract.ts` hits this when it uses an extension's codec without listing the extension in `defineContract({ extensions })`. List the pack that provides the codec. Payload (meta): `codecId`. The Mongo `defineContract` builder raises it for a model or value object field whose codec no pack in the contract provides, because the collection validator cannot give the field's BSON type: `Model "" field "" uses codec "", which no pack in the contract provides, so its BSON type is unknown and the contract cannot describe the field.` (`Value object ""` for a value object field). Payload (meta): `modelName` or `valueObjectName`, `fieldName`, `codecId`. +### CONTRACT.CODEC_JSON_TYPE_MISSING + +A contract build derives the check of a value object column on Postgres, and a member of the value object uses a codec whose descriptor declares no `jsonType`, as a codec package compiled before codecs declared one does. The codec's registry accepts it, so an app that does not put the codec in a value object keeps working. The codec's author adds `jsonType`, the JSON type the codec's value is written as inside a JSON document. Payload: `codecId`, `valueObject` (`:`), `member`, `property` (`jsonType`). + ### CONTRACT.CAST_REFUSED A value handed to a data type's cast, or to an authoring entry that reads written text, is not one that type takes: it is not in the shape the source type stores, its magnitude is outside the range the receiving type holds, the text is not a boolean, or the text is not a date or time the receiving type holds. A date or time text is refused when it has an offset its type does not hold or lacks one its type needs, has more fraction digits than its type holds (six, or three for `sqlite/datetime@1`, which holds milliseconds), names a date or time that does not exist, or is outside the range of years its type holds; the message shows text the type takes. The date and time codecs raise it from `toDataTypeValue` for a value their type does not hold, such as a `Temporal` value with digits below one microsecond. Raised by a target's or extension's casts and authoring entries. A contract source reading a written value reports it to the author as the PSL diagnostic `PSL_INVALID_LITERAL` when a cast, an authoring entry or a codec's canonical form raised it, and as `PSL_INVALID_DEFAULT_LITERAL` when a codec raised it for a column default. Payload: `why`, `fix`. @@ -1204,7 +1208,7 @@ A codec projection used `CodecRef.many` against a SQLite codec descriptor: SQLit ### RUNTIME.CODEC_DESCRIPTOR_INVALID -A codec descriptor handed to a target codec-descriptor registry (Postgres, SQLite) is not a valid descriptor for that target: wrong discriminant, missing target descriptor methods, a Postgres descriptor without `valueObjectJsonType`, or a malformed params schema. Extend the target's descriptor class or adapt a generic descriptor with the target's wrapper (`postgresCodec()` / `sqliteCodec()`). Payload: `codecId`. +A codec descriptor handed to a target codec-descriptor registry (Postgres, SQLite) is not a valid descriptor for that target: wrong discriminant, missing target descriptor methods, a Postgres descriptor whose `jsonType` is not a JSON type, or a malformed params schema. Extend the target's descriptor class or adapt a generic descriptor with the target's wrapper (`postgresCodec()` / `sqliteCodec()`). Payload: `codecId`. ### RUNTIME.CODEC_DESCRIPTOR_MISSING diff --git a/packages/2-sql-family/1-core/contract/package.json b/packages/2-sql-family/1-core/contract/package.json index f6732f2c3e9b..9c3bd85012e4 100644 --- a/packages/2-sql-family/1-core/contract/package.json +++ b/packages/2-sql-family/1-core/contract/package.json @@ -46,6 +46,7 @@ "./authored-check-naming": "./dist/authored-check-naming.mjs", "./canonicalization-hooks": "./dist/canonicalization-hooks.mjs", "./check-expressions": "./dist/check-expressions.mjs", + "./codec-json-type": "./dist/codec-json-type.mjs", "./contract-view": "./dist/contract-view.mjs", "./data-type": "./dist/data-type.mjs", "./data-type-support": "./dist/data-type-support.mjs", diff --git a/packages/2-sql-family/1-core/contract/src/check-expressions.ts b/packages/2-sql-family/1-core/contract/src/check-expressions.ts index 5255db253b8e..59858adf6412 100644 --- a/packages/2-sql-family/1-core/contract/src/check-expressions.ts +++ b/packages/2-sql-family/1-core/contract/src/check-expressions.ts @@ -8,8 +8,12 @@ import { valueObjectAt } from '@internal/contract/types'; import type { CodecLookupWithDescriptors } from '@internal/framework-components/codec'; import type { CheckKind } from '@internal/sql-schema-ir/naming'; import { invariant } from '@internal/utils/assertions'; +import { declaredJsonType, type JsonType } from './codec-json-type'; +import { contractError } from './contract-errors'; import { isStorageValueSet } from './ir/storage-value-set'; +type CodecDescriptors = Pick; + /** A value object with each nested value object inlined, as the target's check renderer reads it. */ export interface ResolvedValueObject { readonly fields: Readonly>; @@ -22,12 +26,12 @@ export interface ResolvedValueObjectField { } /** - * `values` holds the stored values of the value set an enum member refers to. `anyObject` stands where a value object that contains itself refers back into itself: any object is accepted there, as the MongoDB validator does. + * `jsonType` is the JSON type the member's codec declares. `values` holds the stored values of the value set an enum member refers to. `anyObject` stands where a value object that contains itself refers back into itself: any object is accepted there, as the MongoDB validator does. */ export type ResolvedValueObjectFieldType = | { readonly kind: 'scalar'; - readonly codecId: string; + readonly jsonType: JsonType; readonly values: readonly (string | number)[] | undefined; } | { readonly kind: 'valueObject'; readonly valueObject: ResolvedValueObject } @@ -58,10 +62,8 @@ export interface CheckExpressionCandidate { readonly expression: string; } -/** A target's check renderer. It reads what it needs of a value object member's codec, such as the JSON type the codec declares, through `codecLookup`. */ export type CheckExpressionRenderer = ( input: CheckExpressionInput, - codecLookup: Pick, ) => ReadonlyArray; /** @@ -74,11 +76,12 @@ export function resolveColumnValueObject( readonly nullable: boolean; readonly many: false | { readonly elementNullable: boolean }; }, + codecs: CodecDescriptors, ): ResolvedColumnValueObject { return { nullable: column.nullable, many: column.many, - valueObject: resolveValueObject(storage, column.reference), + valueObject: resolveValueObject(storage, column.reference, codecs), }; } @@ -94,19 +97,21 @@ export function discriminatorValueSet( } /** - * The value object a reference names, with nested value objects inlined and enum members' values read from their storage value sets. + * The value object a reference names, with nested value objects inlined, enum members' values read from their storage value sets, and each scalar member's JSON type read from its codec. */ export function resolveValueObject( storage: ContractWithDomain['storage'], reference: EntityCoordinate<'storage', 'valueObject'>, + codecs: CodecDescriptors, ): ResolvedValueObject { - return resolveEntered(storage, reference, [referenceKey(reference)]); + return resolveEntered(storage, reference, [referenceKey(reference)], codecs); } function resolveEntered( storage: ContractWithDomain['storage'], reference: EntityCoordinate<'storage', 'valueObject'>, path: readonly string[], + codecs: CodecDescriptors, ): ResolvedValueObject { const valueObject = requiredValueObject(storage, reference); return { @@ -116,18 +121,26 @@ function resolveEntered( { nullable: field.nullable, many: field.many ?? false, - type: resolveFieldType(storage, field, path), + type: resolveFieldType(storage, { reference, name, field }, path, codecs), }, ]), ), }; } +interface ValueObjectMember { + readonly reference: EntityCoordinate<'storage', 'valueObject'>; + readonly name: string; + readonly field: ContractValueObjectField; +} + function resolveFieldType( storage: ContractWithDomain['storage'], - field: ContractValueObjectField, + member: ValueObjectMember, path: readonly string[], + codecs: CodecDescriptors, ): ResolvedValueObjectFieldType { + const { field } = member; const { type } = field; invariant( type.kind !== 'union' && field.dict !== true, @@ -136,13 +149,40 @@ function resolveFieldType( if (type.kind === 'scalar') { return { kind: 'scalar', - codecId: type.codecId, + jsonType: memberJsonType(member, type.codecId, codecs), values: field.valueSet === undefined ? undefined : valueSetValues(storage, field.valueSet), }; } const key = referenceKey(type); if (reachesAny(storage, type, new Set(path))) return { kind: 'anyObject' }; - return { kind: 'valueObject', valueObject: resolveEntered(storage, type, [...path, key]) }; + return { + kind: 'valueObject', + valueObject: resolveEntered(storage, type, [...path, key], codecs), + }; +} + +function memberJsonType( + member: ValueObjectMember, + codecId: string, + codecs: CodecDescriptors, +): JsonType { + const descriptor = codecs.descriptorFor(codecId); + invariant( + descriptor !== undefined, + `value object member codec "${codecId}" is not a codec of the stack; the contract validators refuse a codec the stack does not register`, + ); + const jsonType = declaredJsonType(descriptor); + if (jsonType !== undefined) return jsonType; + const valueObject = `${member.reference.namespaceId}:${member.reference.entityName}`; + throw contractError( + 'CONTRACT.CODEC_JSON_TYPE_MISSING', + `The codec "${codecId}" of value object "${valueObject}" member "${member.name}" declares no jsonType, so the check of the value object cannot be derived.`, + { + why: 'The check of a value object column requires each member to be of the JSON type its codec declares, and this codec was built against a release before codecs declared one.', + fix: `The author of the codec "${codecId}" adds a jsonType to its descriptor, the JSON type its value is written as inside a JSON document.`, + meta: { codecId, valueObject, member: member.name, property: 'jsonType' }, + }, + ); } /** diff --git a/packages/2-sql-family/1-core/contract/src/codec-json-type.ts b/packages/2-sql-family/1-core/contract/src/codec-json-type.ts new file mode 100644 index 000000000000..1386142c9f1f --- /dev/null +++ b/packages/2-sql-family/1-core/contract/src/codec-json-type.ts @@ -0,0 +1,16 @@ +export const JSON_TYPES = ['string', 'number', 'boolean', 'object', 'array', 'any'] as const; + +/** + * The JSON type a codec's value is written as inside a JSON document. `'any'` means the value can be any JSON value, including `null`, as for a JSON codec. + */ +export type JsonType = (typeof JSON_TYPES)[number]; + +export function isJsonType(value: unknown): value is JsonType { + return JSON_TYPES.some((jsonType) => jsonType === value); +} + +/** The JSON type a codec descriptor declares, or `undefined` when it declares none, as a descriptor compiled before codecs declared one does. */ +export function declaredJsonType(descriptor: object): JsonType | undefined { + const jsonType = Reflect.get(descriptor, 'jsonType'); + return isJsonType(jsonType) ? jsonType : undefined; +} diff --git a/packages/2-sql-family/1-core/contract/src/contract-errors.ts b/packages/2-sql-family/1-core/contract/src/contract-errors.ts index 333cbe1802c7..565f20612377 100644 --- a/packages/2-sql-family/1-core/contract/src/contract-errors.ts +++ b/packages/2-sql-family/1-core/contract/src/contract-errors.ts @@ -6,6 +6,7 @@ type ContractCode = `CONTRACT.${ContractSubcode}`; type ContractSubcode = | 'ARGUMENT_INVALID' | 'CODEC_DESCRIPTOR_MISSING' + | 'CODEC_JSON_TYPE_MISSING' | 'DATA_TYPE_UNREGISTERED' | 'INDEX_INVALID' | 'PACK_CONTRIBUTION_INVALID' diff --git a/packages/2-sql-family/1-core/contract/src/exports/codec-json-type.ts b/packages/2-sql-family/1-core/contract/src/exports/codec-json-type.ts new file mode 100644 index 000000000000..1618f4fe8193 --- /dev/null +++ b/packages/2-sql-family/1-core/contract/src/exports/codec-json-type.ts @@ -0,0 +1,6 @@ +export { + declaredJsonType, + isJsonType, + JSON_TYPES, + type JsonType, +} from '../codec-json-type'; diff --git a/packages/2-sql-family/1-core/contract/test/check-expressions.test.ts b/packages/2-sql-family/1-core/contract/test/check-expressions.test.ts index 099a6b25ffe2..3a0874b404c0 100644 --- a/packages/2-sql-family/1-core/contract/test/check-expressions.test.ts +++ b/packages/2-sql-family/1-core/contract/test/check-expressions.test.ts @@ -1,3 +1,4 @@ +import type { AnyCodecDescriptor } from '@internal/framework-components/codec'; import { describe, expect, it } from 'vitest'; import { discriminatorValueSet, @@ -25,6 +26,11 @@ function nested(name: string, extra: Record = {}) { }; } +const codecs = { + descriptorFor: (codecId: string) => + (codecId === 'pg/text@1' ? { codecId, jsonType: 'string' } : { codecId }) as AnyCodecDescriptor, +}; + function storageWith(entries: Record>) { return { namespaces: { public: { id: 'public', entries } } }; } @@ -55,17 +61,17 @@ describe('resolveValueObject', () => { valueSet: { AddressKind: { kind: 'valueSet', values: ['home', 'work'] } }, }); - expect(resolveValueObject(storage, valueObjectRef('Address'))).toEqual({ + expect(resolveValueObject(storage, valueObjectRef('Address'), codecs)).toEqual({ fields: { street: { nullable: false, many: false, - type: { kind: 'scalar', codecId: 'pg/text@1', values: undefined }, + type: { kind: 'scalar', jsonType: 'string', values: undefined }, }, kind: { nullable: true, many: false, - type: { kind: 'scalar', codecId: 'pg/text@1', values: ['home', 'work'] }, + type: { kind: 'scalar', jsonType: 'string', values: ['home', 'work'] }, }, geo: { nullable: false, @@ -77,7 +83,7 @@ describe('resolveValueObject', () => { lat: { nullable: false, many: false, - type: { kind: 'scalar', codecId: 'pg/text@1', values: undefined }, + type: { kind: 'scalar', jsonType: 'string', values: undefined }, }, }, }, @@ -99,12 +105,12 @@ describe('resolveValueObject', () => { }, }); - expect(resolveValueObject(storage, valueObjectRef('NavItem'))).toEqual({ + expect(resolveValueObject(storage, valueObjectRef('NavItem'), codecs)).toEqual({ fields: { label: { nullable: false, many: false, - type: { kind: 'scalar', codecId: 'pg/text@1', values: undefined }, + type: { kind: 'scalar', jsonType: 'string', values: undefined }, }, children: { nullable: false, @@ -124,12 +130,12 @@ describe('resolveValueObject', () => { }, }); - expect(resolveValueObject(storage, valueObjectRef('Folder'))).toEqual({ + expect(resolveValueObject(storage, valueObjectRef('Folder'), codecs)).toEqual({ fields: { name: { nullable: false, many: false, - type: { kind: 'scalar', codecId: 'pg/text@1', values: undefined }, + type: { kind: 'scalar', jsonType: 'string', values: undefined }, }, entry: { nullable: false, many: false, type: { kind: 'anyObject' } }, meta: { @@ -142,7 +148,7 @@ describe('resolveValueObject', () => { owner: { nullable: false, many: false, - type: { kind: 'scalar', codecId: 'pg/text@1', values: undefined }, + type: { kind: 'scalar', jsonType: 'string', values: undefined }, }, }, }, @@ -152,8 +158,33 @@ describe('resolveValueObject', () => { }); }); + it('refuses a member whose codec declares no JSON type, naming the codec, the member and what to add', () => { + const storage = storageWith({ + valueObject: { + Address: { + fields: { zip: { nullable: false, type: { kind: 'scalar', codecId: 'demo/zip@1' } } }, + }, + }, + }); + expect(() => resolveValueObject(storage, valueObjectRef('Address'), codecs)).toThrow( + expect.objectContaining({ + code: 'CONTRACT.CODEC_JSON_TYPE_MISSING', + message: + 'The codec "demo/zip@1" of value object "public:Address" member "zip" declares no jsonType, so the check of the value object cannot be derived.', + meta: { + codecId: 'demo/zip@1', + valueObject: 'public:Address', + member: 'zip', + property: 'jsonType', + }, + }), + ); + }); + it('refuses a reference storage does not hold', () => { - expect(() => resolveValueObject(storageWith({}), valueObjectRef('Missing'))).toThrow(/Missing/); + expect(() => resolveValueObject(storageWith({}), valueObjectRef('Missing'), codecs)).toThrow( + /Missing/, + ); }); }); @@ -161,15 +192,19 @@ describe('resolveColumnValueObject', () => { it('holds the column nullability and list shape with the resolved value object', () => { const storage = storageWith({ valueObject: { Address: { fields: {} } } }); expect( - resolveColumnValueObject(storage, { - reference: valueObjectRef('Address'), - nullable: true, - many: { elementNullable: false }, - }), + resolveColumnValueObject( + storage, + { + reference: valueObjectRef('Address'), + nullable: true, + many: { elementNullable: false }, + }, + codecs, + ), ).toEqual({ nullable: true, many: { elementNullable: false }, - valueObject: resolveValueObject(storage, valueObjectRef('Address')), + valueObject: resolveValueObject(storage, valueObjectRef('Address'), codecs), }); }); }); diff --git a/packages/2-sql-family/1-core/contract/test/test-type-lookups.ts b/packages/2-sql-family/1-core/contract/test/test-type-lookups.ts index 045a628e720b..ad3616186bd4 100644 --- a/packages/2-sql-family/1-core/contract/test/test-type-lookups.ts +++ b/packages/2-sql-family/1-core/contract/test/test-type-lookups.ts @@ -4,7 +4,7 @@ * Every codec id represents the data type the real stack gives it (`CODEC_DATA_TYPES`), else the * codec id without its version. A data type is written as the name in `names` (by codec id), * `CODEC_TYPE_NAMES` or `DATA_TYPE_NAMES`, else its name part. Each codec stores its values as - * authored and takes any parameters. + * authored and takes any parameters, and declares the JSON type in `CODEC_JSON_TYPES`, else `any`. */ import type { JsonValue } from '@internal/contract/types'; @@ -77,6 +77,14 @@ const DATA_TYPE_NAMES: Readonly> = { 'sqlite/character-varying': 'character varying', }; +const CODEC_JSON_TYPES: Readonly> = { + 'pg/text@1': 'string', + 'pg/int4@1': 'number', + 'pg/bool@1': 'boolean', + 'sql/text@1': 'string', + 'sql/int@1': 'number', +}; + const ENUM_CODEC_IDS: ReadonlySet = new Set(['pg/enum@1']); const ENUM_DATA_TYPE_IDS: ReadonlySet = new Set(['pg/enum']); @@ -202,10 +210,11 @@ export function testSqlTypeLookups( if (own.dataType !== undefined) return own; return { ...own, dataType: dataTypeOfCodec(codecId).id }; } - return { + const descriptor = { codecId, + jsonType: CODEC_JSON_TYPES[codecId] ?? 'any', dataType: dataTypeOfCodec(codecId).id, - traits: ['equality'], + traits: ['equality'] as const, paramsSchema: acceptAnything, isParameterized: true, factory: () => () => @@ -214,6 +223,7 @@ export function testSqlTypeLookups( 'a test codec lookup may have no codec for this id, as a real one may not' >(codecOf(codecId)), }; + return descriptor; }; return { diff --git a/packages/2-sql-family/1-core/contract/tsdown.config.ts b/packages/2-sql-family/1-core/contract/tsdown.config.ts index 6d0adefd1619..d2c100d846e2 100644 --- a/packages/2-sql-family/1-core/contract/tsdown.config.ts +++ b/packages/2-sql-family/1-core/contract/tsdown.config.ts @@ -4,6 +4,7 @@ export default defineConfig({ entry: [ 'src/exports/authored-check-naming.ts', 'src/exports/check-expressions.ts', + 'src/exports/codec-json-type.ts', 'src/exports/contract-view.ts', 'src/exports/data-type.ts', 'src/exports/data-type-support.ts', diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts b/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts index c5b9921c47c3..847e83a35971 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts @@ -214,7 +214,6 @@ function writeDiscriminatorValueSets( tablesByNamespace: Record>, storageValueSetsByNs: Record>, checkRendererFor: (table: StorageTableInput) => CheckExpressionRenderer | undefined, - codecLookup: CodecLookupWithDescriptors, ): void { const defaultNamespaceId = definition.target.defaultNamespaceId; for (const model of definition.models) { @@ -256,16 +255,13 @@ function writeDiscriminatorValueSets( ? [] : lowerRenderedChecks( model.tableName, - render( - { - tableName: model.tableName, - columnName, - many: false, - memberValues: values, - valueObject: undefined, - }, - codecLookup, - ), + render({ + tableName: model.tableName, + columnName, + many: false, + memberValues: values, + valueObject: undefined, + }), ); const namespaceTables = tablesByNamespace[namespaceId] ?? {}; namespaceTables[model.tableName] = { @@ -392,16 +388,11 @@ export function buildSqlContractFromDefinition( namespaceTables[table.tableName] = lowerTable(table, tableLoweringContext); tablesByNamespace[table.namespaceId] = namespaceTables; } - writeDiscriminatorValueSets( - definition, - tablesByNamespace, - storageValueSetsByNs, - (table) => - renderCheckExpressions !== undefined && - effectiveControlPolicy(table.control, definition.defaultControlPolicy) === 'managed' - ? renderCheckExpressions - : undefined, - codecLookup, + writeDiscriminatorValueSets(definition, tablesByNamespace, storageValueSetsByNs, (table) => + renderCheckExpressions !== undefined && + effectiveControlPolicy(table.control, definition.defaultControlPolicy) === 'managed' + ? renderCheckExpressions + : undefined, ); const executionDefaults: ExecutionMutationDefault[] = components.flatMap( diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/lower-column.ts b/packages/2-sql-family/2-authoring/contract-ts/src/lower-column.ts index 4d1a0b4920a1..058139108d8e 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/lower-column.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/lower-column.ts @@ -159,23 +159,24 @@ export function lowerColumn( return { column, derivedChecks: [] }; } const { enumTypeHandle } = description; - const candidates = render( - { - tableName: placement.tableName, - columnName: description.columnName, - many, - memberValues: - enumTypeHandle !== undefined ? checkMemberValues(enumTypeHandle, codecLookup) : undefined, - valueObject: - description.valueObject !== undefined - ? resolveColumnValueObject(context.storage, { + const candidates = render({ + tableName: placement.tableName, + columnName: description.columnName, + many, + memberValues: + enumTypeHandle !== undefined ? checkMemberValues(enumTypeHandle, codecLookup) : undefined, + valueObject: + description.valueObject !== undefined + ? resolveColumnValueObject( + context.storage, + { reference: description.valueObject.reference, nullable: description.nullable, many: description.valueObject.many, - }) - : undefined, - }, - codecLookup, - ); + }, + codecLookup, + ) + : undefined, + }); return { column, derivedChecks: lowerRenderedChecks(placement.tableName, candidates) }; } diff --git a/packages/2-sql-family/2-authoring/contract-ts/test/contract-builder.derived-checks.test.ts b/packages/2-sql-family/2-authoring/contract-ts/test/contract-builder.derived-checks.test.ts index 88942b27ded5..229cc4a2fcb2 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/test/contract-builder.derived-checks.test.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/test/contract-builder.derived-checks.test.ts @@ -120,12 +120,12 @@ const resolvedAddress = { street: { nullable: false, many: false, - type: { kind: 'scalar', codecId: 'pg/text@1', values: undefined }, + type: { kind: 'scalar', jsonType: 'string', values: undefined }, }, kind: { nullable: true, many: false, - type: { kind: 'scalar', codecId: 'pg/text@1', values: ['home', 'work'] }, + type: { kind: 'scalar', jsonType: 'string', values: ['home', 'work'] }, }, geo: { nullable: false, @@ -137,7 +137,7 @@ const resolvedAddress = { lat: { nullable: false, many: false, - type: { kind: 'scalar', codecId: 'pg/text@1', values: undefined }, + type: { kind: 'scalar', jsonType: 'string', values: undefined }, }, }, }, diff --git a/packages/2-sql-family/4-lanes/relational-core/README.md b/packages/2-sql-family/4-lanes/relational-core/README.md index 412a17d74281..fb416f7add6e 100644 --- a/packages/2-sql-family/4-lanes/relational-core/README.md +++ b/packages/2-sql-family/4-lanes/relational-core/README.md @@ -120,7 +120,7 @@ class PgTextCodec extends CodecImpl<'pg/text@1', readonly ['equality'], string, class PgTextDescriptor extends PostgresCodecDescriptor { override readonly dataType = pgText.id; - override readonly valueObjectJsonType = 'string' as const; + override readonly jsonType = 'string' as const; override readonly codecId = 'pg/text@1'; override readonly traits = ['equality'] as const; override readonly paramsSchema = undefined; diff --git a/packages/2-sql-family/4-lanes/relational-core/src/ast/sql-codecs.ts b/packages/2-sql-family/4-lanes/relational-core/src/ast/sql-codecs.ts index ba13b4b06537..36c7b4cdbaaa 100644 --- a/packages/2-sql-family/4-lanes/relational-core/src/ast/sql-codecs.ts +++ b/packages/2-sql-family/4-lanes/relational-core/src/ast/sql-codecs.ts @@ -75,6 +75,7 @@ export class SqlTextCodec extends CodecImpl< } export class SqlTextDescriptor extends CodecDescriptorTemplateImpl { + readonly jsonType = 'string' as const; override readonly codecId = SQL_TEXT_CODEC_ID; override readonly traits = ['equality', 'order', 'textual'] as const; override readonly paramsSchema = undefined; @@ -113,6 +114,7 @@ export class SqlIntCodec extends CodecImpl< } export class SqlIntDescriptor extends CodecDescriptorTemplateImpl { + readonly jsonType = 'number' as const; override readonly codecId = SQL_INT_CODEC_ID; override readonly traits = ['equality', 'order', 'numeric'] as const; override readonly paramsSchema = undefined; @@ -150,6 +152,7 @@ export class SqlFloatCodec extends CodecImpl< } export class SqlFloatDescriptor extends CodecDescriptorTemplateImpl { + readonly jsonType = 'number' as const; override readonly codecId = SQL_FLOAT_CODEC_ID; override readonly traits = ['equality', 'order', 'numeric'] as const; override readonly paramsSchema = undefined; @@ -187,6 +190,7 @@ export class SqlCharCodec extends CodecImpl< } export class SqlCharDescriptor extends CodecDescriptorTemplateImpl { + readonly jsonType = 'string' as const; override readonly codecId = SQL_CHAR_CODEC_ID; override readonly traits = ['equality', 'order', 'textual'] as const; override readonly paramsSchema = undefined; @@ -224,6 +228,7 @@ export class SqlVarcharCodec extends CodecImpl< } export class SqlVarcharDescriptor extends CodecDescriptorTemplateImpl { + readonly jsonType = 'string' as const; override readonly codecId = SQL_VARCHAR_CODEC_ID; override readonly traits = ['equality', 'order', 'textual'] as const; override readonly paramsSchema = undefined; @@ -285,6 +290,7 @@ export class SqlRawCodec extends CodecImpl { + readonly jsonType = 'any' as const; override readonly codecId = SQL_RAW_CODEC_ID; override readonly traits = [] as const; override readonly enumRefusal = diff --git a/packages/3-extensions/arktype-json/src/core/arktype-json-codec.ts b/packages/3-extensions/arktype-json/src/core/arktype-json-codec.ts index 638332886229..5096bff022f4 100644 --- a/packages/3-extensions/arktype-json/src/core/arktype-json-codec.ts +++ b/packages/3-extensions/arktype-json/src/core/arktype-json-codec.ts @@ -179,7 +179,7 @@ const arktypeJsonParamsSchema = type({ }) satisfies StandardSchemaV1; export class ArktypeJsonDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'anyJson' as const; + override readonly jsonType = 'any' as const; /** The document's JSON text, which `fromWire` parses as it parses a row; a document that is a string would otherwise reach it unquoted. */ protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return CastExpr.as(expression, 'text'); diff --git a/packages/3-extensions/pgvector/src/core/codecs.ts b/packages/3-extensions/pgvector/src/core/codecs.ts index f75c813c985b..f13c2de3e0e7 100644 --- a/packages/3-extensions/pgvector/src/core/codecs.ts +++ b/packages/3-extensions/pgvector/src/core/codecs.ts @@ -140,7 +140,7 @@ const vectorTextJsonProjection = (expression: ProjectionExpr): ProjectionExpr => CastExpr.as(expression, 'text'); export class PgVectorDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'presenceOnly' as const; + override readonly jsonType = 'array' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return vectorTextJsonProjection(expression); } diff --git a/packages/3-extensions/postgis/src/core/codecs.ts b/packages/3-extensions/postgis/src/core/codecs.ts index 8ea33885a1bf..85622fdb3fa1 100644 --- a/packages/3-extensions/postgis/src/core/codecs.ts +++ b/packages/3-extensions/postgis/src/core/codecs.ts @@ -115,7 +115,7 @@ export class PostgisGeometryCodec extends CodecImpl< } export class PostgisGeometryDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'presenceOnly' as const; + override readonly jsonType = 'object' as const; /** The HEXEWKB text PostgreSQL prints for a geometry, which `fromWire` reads as it reads a row. A JSON constructor handed the geometry itself may write GeoJSON instead. */ protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return CastExpr.as(expression, 'text'); diff --git a/packages/3-targets/postgres/1-target/src/core/check-expressions.ts b/packages/3-targets/postgres/1-target/src/core/check-expressions.ts index c0504015f49e..01442ff52775 100644 --- a/packages/3-targets/postgres/1-target/src/core/check-expressions.ts +++ b/packages/3-targets/postgres/1-target/src/core/check-expressions.ts @@ -1,4 +1,3 @@ -import type { CodecLookupWithDescriptors } from '@internal/framework-components/codec'; import type { CheckExpressionCandidate, CheckExpressionInput, @@ -8,7 +7,6 @@ import type { ResolvedValueObjectFieldType, } from '@internal/sql-contract/check-expressions'; import { invariant } from '@internal/utils/assertions'; -import { isPostgresCodecDescriptor, type ValueObjectJsonType } from './codec-descriptor'; import { escapeLiteral, quoteIdentifier } from './sql-utils'; /** @@ -27,7 +25,6 @@ import { escapeLiteral, quoteIdentifier } from './sql-utils'; */ export function postgresRenderCheckExpressions( input: CheckExpressionInput, - codecLookup: Pick, ): readonly CheckExpressionCandidate[] { const candidates: CheckExpressionCandidate[] = []; const column = quoteIdentifier(input.columnName); @@ -66,43 +63,23 @@ export function postgresRenderCheckExpressions( candidates.push({ kind: 'shape', columnName: input.columnName, - expression: valueObjectCheck(column, input.valueObject, jsonTypeReader(codecLookup)), + expression: valueObjectCheck(column, input.valueObject), }); } return candidates; } -type JsonTypeOfCodec = (codecId: string) => ValueObjectJsonType; - -/** The JSON type each codec declares for a value object member, read from the codec's descriptor. */ -function jsonTypeReader( - codecLookup: Pick, -): JsonTypeOfCodec { - return (codecId) => { - const descriptor = codecLookup.descriptorFor(codecId); - invariant( - descriptor !== undefined && isPostgresCodecDescriptor(descriptor), - `value object member codec "${codecId}" is not a PostgreSQL codec of the stack; the contract validators refuse a codec the stack does not register`, - ); - return descriptor.valueObjectJsonType; - }; -} - /** * One strict `jsonpath` predicate over the whole column value. Strict mode keeps a comparison from unwrapping an array, and `IS TRUE` makes a predicate that evaluates to unknown fail the check, as a CHECK would otherwise pass on NULL. Member values are read through `keyvalue()`, so an absent member is never an error. */ -function valueObjectCheck( - column: string, - columnValueObject: ResolvedColumnValueObject, - jsonTypeOf: JsonTypeOfCodec, -): string { +function valueObjectCheck(column: string, columnValueObject: ResolvedColumnValueObject): string { const field: ResolvedValueObjectField = { nullable: columnValueObject.nullable, many: columnValueObject.many, type: { kind: 'valueObject', valueObject: columnValueObject.valueObject }, }; - const predicate = fieldPredicate(field, '$', jsonTypeOf); + const predicate = fieldPredicate(field, '$'); invariant(predicate !== undefined, 'a column that holds a value object always has a predicate'); const match = (path: string) => `jsonb_path_match(${column}, '${escapeLiteral(`strict ${path}`)}') IS TRUE`; @@ -112,13 +89,9 @@ function valueObjectCheck( } /** What the value at `path` must satisfy, or `undefined` when any value is accepted. */ -function fieldPredicate( - field: ResolvedValueObjectField, - path: string, - jsonTypeOf: JsonTypeOfCodec, -): string | undefined { - if (field.many === false) return valuePredicate(field.type, path, jsonTypeOf); - const element = valuePredicate(field.type, '@', jsonTypeOf); +function fieldPredicate(field: ResolvedValueObjectField, path: string): string | undefined { + if (field.many === false) return valuePredicate(field.type, path); + const element = valuePredicate(field.type, '@'); const { elementNullable } = field.many; const invalidElement = element === undefined @@ -134,23 +107,16 @@ function fieldPredicate( : `${isArray} && !exists(${path}[*] ? (${invalidElement}))`; } -function valuePredicate( - type: ResolvedValueObjectFieldType, - path: string, - jsonTypeOf: JsonTypeOfCodec, -): string | undefined { +/** A member of any JSON type accepts every value, JSON `null` included; a member of any other JSON type is of that type, so never JSON `null`. */ +function valuePredicate(type: ResolvedValueObjectFieldType, path: string): string | undefined { switch (type.kind) { case 'valueObject': - return objectPredicate(type.valueObject, path, jsonTypeOf); + return objectPredicate(type.valueObject, path); case 'anyObject': return `${path}.type() == "object"`; - case 'scalar': { + case 'scalar': if (type.values !== undefined) return valueSetPredicate(type.values, path); - const jsonType = jsonTypeOf(type.codecId); - return jsonType === 'anyJson' || jsonType === 'presenceOnly' - ? undefined - : `${path}.type() == "${jsonType}"`; - } + return type.jsonType === 'any' ? undefined : `${path}.type() == "${type.jsonType}"`; } } @@ -165,21 +131,13 @@ function valueSetPredicate(values: readonly (string | number)[], path: string): return `(${alternatives('string', strings)}) || (${alternatives('number', numbers)})`; } -function objectPredicate( - valueObject: ResolvedValueObject, - path: string, - jsonTypeOf: JsonTypeOfCodec, -): string { +function objectPredicate(valueObject: ResolvedValueObject, path: string): string { const names = Object.keys(valueObject.fields).sort(); const members = names.flatMap((name) => { const field = valueObject.fields[name]; invariant(field !== undefined, `value object member "${name}" is missing`); const isMember = `@.key == ${jsonPathLiteral(name)}`; - const predicate = requiredMemberPredicate( - field, - fieldPredicate(field, '@.value', jsonTypeOf), - jsonTypeOf, - ); + const predicate = fieldPredicate(field, '@.value'); if (!field.nullable) { return [ predicate === undefined @@ -200,15 +158,6 @@ function objectPredicate( return [`${path}.type() == "object"`, ...members, otherKey].join(' && '); } -function requiredMemberPredicate( - field: ResolvedValueObjectField, - predicate: string | undefined, - jsonTypeOf: JsonTypeOfCodec, -): string | undefined { - if (predicate !== undefined || field.nullable || field.type.kind !== 'scalar') return predicate; - return jsonTypeOf(field.type.codecId) === 'anyJson' ? undefined : '@.value.type() != "null"'; -} - function jsonPathLiteral(value: string | number): string { return typeof value === 'string' ? JSON.stringify(value) : String(value); } diff --git a/packages/3-targets/postgres/1-target/src/core/codec-descriptor.ts b/packages/3-targets/postgres/1-target/src/core/codec-descriptor.ts index 12c08560ab9f..f50debe346fd 100644 --- a/packages/3-targets/postgres/1-target/src/core/codec-descriptor.ts +++ b/packages/3-targets/postgres/1-target/src/core/codec-descriptor.ts @@ -13,6 +13,7 @@ import { type DataTypeId, validateCodecTypeParams, } from '@internal/framework-components/codec'; +import { isJsonType, type JsonType } from '@internal/sql-contract/codec-json-type'; import { CaseExpr, ColumnRef, @@ -39,17 +40,10 @@ const ARRAY_ELEMENT_ALIAS = 'array_element'; const ARRAY_VALUE_COLUMN = 'value'; const ARRAY_ORDINALITY_COLUMN = 'ordinality'; -const VALUE_OBJECT_JSON_TYPES = ['string', 'number', 'boolean', 'anyJson', 'presenceOnly'] as const; - -/** - * What the check of a value object column accepts for a member of a codec: the JSON type the SQL ORM writes for its value (`'string'`, `'number'` or `'boolean'`), any JSON value (`'anyJson'`, for a JSON codec), or any value at all, checked only for being present and, when required, not JSON `null` (`'presenceOnly'`, for a codec whose JSON form the check does not pin down). - */ -export type ValueObjectJsonType = (typeof VALUE_OBJECT_JSON_TYPES)[number]; - export interface AnyPostgresCodecDescriptor extends AnyCodecDescriptor { readonly descriptorKind: typeof POSTGRES_CODEC_DESCRIPTOR_KIND; - /** See {@link ValueObjectJsonType}. Every PostgreSQL codec declares it. */ - readonly valueObjectJsonType: ValueObjectJsonType; + /** The JSON type the codec's value is written as inside a JSON document. Every PostgreSQL codec declares it. */ + readonly jsonType: JsonType; projectJson(expression: ProjectionExpr, ref: CodecRef): ProjectionExpr; } @@ -58,7 +52,7 @@ export abstract class PostgresCodecDescriptor

implements AnyPostgresCodecDescriptor { readonly descriptorKind = POSTGRES_CODEC_DESCRIPTOR_KIND; - abstract readonly valueObjectJsonType: ValueObjectJsonType; + abstract readonly jsonType: JsonType; protected abstract jsonProjection(expression: ProjectionExpr, params: P): ProjectionExpr; @@ -133,8 +127,6 @@ export interface PostgresCodecOptions< readonly dataType: DataType; /** Renders the literal of the value the codec reads from a stored value of `dataType`. A family template renders no literal, because only the target knows the form its data type stores a value in, so the adapted codec renders with this and never with a renderer on the template. */ readonly renderValueLiteral?: (value: JsonValue, side: 'output' | 'input') => string | undefined; - /** See {@link ValueObjectJsonType}. */ - readonly valueObjectJsonType: ValueObjectJsonType; readonly jsonProjection: (expression: ProjectionExpr, params: P) => ProjectionExpr; readonly jsonArrayProjection?: (expression: ProjectionExpr, params: P) => ProjectionExpr; /** @@ -152,13 +144,13 @@ export type AdaptedPostgresCodecDescriptor keyof CodecDescriptorTemplate> > & Pick & - Pick; + Pick; class PostgresCodecDescriptorAdapter< - D extends AnyCodecDescriptorTemplate, + D extends AnyCodecDescriptorTemplate & { readonly jsonType: JsonType }, > extends PostgresCodecDescriptor> { override readonly dataType: DataTypeId; - override readonly valueObjectJsonType: ValueObjectJsonType; + override readonly jsonType: JsonType; override readonly codecId: string; override readonly traits: readonly CodecTrait[]; override readonly paramsSchema: StandardSchemaV1> | undefined; @@ -179,7 +171,7 @@ class PostgresCodecDescriptorAdapter< ) { super(); this.dataType = options.dataType.id; - this.valueObjectJsonType = options.valueObjectJsonType; + this.jsonType = descriptor.jsonType; this.codecId = descriptor.codecId; this.traits = descriptor.traits; this.paramsSchema = blindCast< @@ -229,7 +221,9 @@ class PostgresCodecDescriptorAdapter< } } -export function postgresCodec( +export function postgresCodec< + D extends AnyCodecDescriptorTemplate & { readonly jsonType: JsonType }, +>( descriptor: D, options: PostgresCodecOptions, DescriptorCodec>, ): AdaptedPostgresCodecDescriptor { @@ -268,8 +262,7 @@ export function isPostgresCodecDescriptor(value: unknown): value is AnyPostgresC typeof value.factory === 'function' && 'projectJson' in value && typeof value.projectJson === 'function' && - 'valueObjectJsonType' in value && - VALUE_OBJECT_JSON_TYPES.some((jsonType) => jsonType === value.valueObjectJsonType) + (!('jsonType' in value) || isJsonType(value.jsonType)) ); } diff --git a/packages/3-targets/postgres/1-target/src/core/codecs.ts b/packages/3-targets/postgres/1-target/src/core/codecs.ts index a4cd35447411..a0679dabc659 100644 --- a/packages/3-targets/postgres/1-target/src/core/codecs.ts +++ b/packages/3-targets/postgres/1-target/src/core/codecs.ts @@ -227,7 +227,6 @@ const printedTextJsonProjection = (expression: ProjectionExpr): ProjectionExpr = CastExpr.as(expression, 'text'); export const postgresSqlCharDescriptor = postgresCodec(sqlCharDescriptor, { - valueObjectJsonType: 'string', dataType: pgChar, renderValueLiteral: renderTsLiteral, jsonProjection: unpaddedCharJsonProjection, @@ -235,7 +234,6 @@ export const postgresSqlCharDescriptor = postgresCodec(sqlCharDescriptor, { }); export const postgresSqlVarcharDescriptor = postgresCodec(sqlVarcharDescriptor, { - valueObjectJsonType: 'string', dataType: pgVarchar, renderValueLiteral: renderTsLiteral, jsonProjection: identityJsonProjection, @@ -244,7 +242,6 @@ export const postgresSqlVarcharDescriptor = postgresCodec(sqlVarcharDescriptor, }); export const postgresSqlIntDescriptor = postgresCodec(sqlIntDescriptor, { - valueObjectJsonType: 'number', dataType: pgInt4, renderValueLiteral: renderTsLiteral, jsonProjection: identityJsonProjection, @@ -252,7 +249,6 @@ export const postgresSqlIntDescriptor = postgresCodec(sqlIntDescriptor, { }); export const postgresSqlFloatDescriptor = postgresCodec(sqlFloatDescriptor, { - valueObjectJsonType: 'number', dataType: pgFloat8, renderValueLiteral: floatNumberLiteral, jsonProjection: identityJsonProjection, @@ -260,7 +256,6 @@ export const postgresSqlFloatDescriptor = postgresCodec(sqlFloatDescriptor, { }); export const postgresSqlTextDescriptor = postgresCodec(sqlTextDescriptor, { - valueObjectJsonType: 'string', dataType: pgText, renderValueLiteral: renderTsLiteral, jsonProjection: identityJsonProjection, @@ -281,7 +276,6 @@ const outputFunctionJsonProjection = (expression: ProjectionExpr): ProjectionExp ); export const postgresSqlRawDescriptor = postgresCodec(sqlRawDescriptor, { - valueObjectJsonType: 'presenceOnly', dataType: sqlRawDataType, jsonProjection: outputFunctionJsonProjection, factory: (descriptor, dataType, params) => () => new SqlRawCodec(descriptor, dataType, params), @@ -308,7 +302,7 @@ export class PgTextCodec extends CodecImpl< } export class PgTextDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'string' as const; + override readonly jsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return expression; } @@ -388,7 +382,7 @@ export function isPgEnumParams(value: unknown): value is PgEnumParams { } export class PgEnumDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'presenceOnly' as const; + override readonly jsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return expression; } @@ -514,7 +508,7 @@ export class PgTextArrayCodec extends CodecImpl< } export class PgTextArrayDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'presenceOnly' as const; + override readonly jsonType = 'array' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return printedTextJsonProjection(expression); } @@ -550,7 +544,7 @@ export class PgInt4Codec extends CodecImpl< } export class PgInt4Descriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'number' as const; + override readonly jsonType = 'number' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return expression; } @@ -595,7 +589,7 @@ export class PgInt2Codec extends CodecImpl< } export class PgInt2Descriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'number' as const; + override readonly jsonType = 'number' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return expression; } @@ -646,7 +640,7 @@ export class PgInt8Codec extends CodecImpl< } export class PgInt8Descriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'presenceOnly' as const; + override readonly jsonType = 'number' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return decimalTextJsonProjection(expression); } @@ -699,7 +693,7 @@ export class PgInt8NumberCodec extends CodecImpl< } export class PgInt8NumberDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'number' as const; + override readonly jsonType = 'number' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return decimalTextJsonProjection(expression); } @@ -744,7 +738,7 @@ export class PgFloat4Codec extends CodecImpl< } export class PgFloat4Descriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'number' as const; + override readonly jsonType = 'number' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return expression; } @@ -789,7 +783,7 @@ export class PgFloat8Codec extends CodecImpl< } export class PgFloat8Descriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'number' as const; + override readonly jsonType = 'number' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return expression; } @@ -834,7 +828,7 @@ export class PgBoolCodec extends CodecImpl< } export class PgBoolDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'boolean' as const; + override readonly jsonType = 'boolean' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return expression; } @@ -879,7 +873,7 @@ export class PgNumericCodec extends CodecImpl< } export class PgNumericDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'string' as const; + override readonly jsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return decimalTextJsonProjection(expression); } @@ -929,7 +923,7 @@ export class PgUnboundedIntCodec extends CodecImpl< } export class PgUnboundedIntDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'presenceOnly' as const; + override readonly jsonType = 'number' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return decimalTextJsonProjection(expression); } @@ -980,7 +974,7 @@ export class PgTimetzCodec extends CodecImpl< } export class PgTimetzDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'presenceOnly' as const; + override readonly jsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return expression; } @@ -1025,7 +1019,7 @@ export class PgBitCodec extends CodecImpl< } export class PgBitDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'presenceOnly' as const; + override readonly jsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return expression; } @@ -1070,7 +1064,7 @@ export class PgVarbitCodec extends CodecImpl< } export class PgVarbitDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'presenceOnly' as const; + override readonly jsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return expression; } @@ -1115,7 +1109,7 @@ export class PgByteaCodec extends CodecImpl< } export class PgByteaDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'presenceOnly' as const; + override readonly jsonType = 'object' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return printedTextJsonProjection(expression); } @@ -1159,7 +1153,7 @@ export class PgUuidCodec extends CodecImpl< } export class PgUuidDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'string' as const; + override readonly jsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return expression; } @@ -1204,7 +1198,7 @@ export class PgInetCodec extends CodecImpl< } export class PgInetDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'presenceOnly' as const; + override readonly jsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return expression; } @@ -1265,7 +1259,7 @@ export class PgTsqueryCodec extends CodecImpl< * column, and no traits, because comparing or ordering queries means nothing to an application. */ export class PgTsqueryDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'presenceOnly' as const; + override readonly jsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return expression; } @@ -1325,7 +1319,7 @@ export class PgIntervalCodec extends CodecImpl< } export class PgIntervalDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'presenceOnly' as const; + override readonly jsonType = 'object' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return printedTextJsonProjection(expression); } @@ -1368,7 +1362,7 @@ export class PgJsonCodec extends CodecImpl< } export class PgJsonDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'anyJson' as const; + override readonly jsonType = 'any' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return printedTextJsonProjection(expression); } @@ -1412,7 +1406,7 @@ export class PgJsonbCodec extends CodecImpl< } export class PgJsonbDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'anyJson' as const; + override readonly jsonType = 'any' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return printedTextJsonProjection(expression); } @@ -1454,7 +1448,7 @@ export class PgFloatCodec extends SqlFloatCodec { } export class PgCharDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'string' as const; + override readonly jsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return unpaddedCharJsonProjection(expression); } @@ -1478,7 +1472,7 @@ export const pgCharColumn = (params: LengthParams = {}) => pgCharColumn satisfies ColumnHelperFor; export class PgVarcharDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'string' as const; + override readonly jsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return expression; } @@ -1502,7 +1496,7 @@ export const pgVarcharColumn = (params: LengthParams = {}) => pgVarcharColumn satisfies ColumnHelperFor; export class PgIntDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'number' as const; + override readonly jsonType = 'number' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return expression; } @@ -1526,7 +1520,7 @@ export const pgIntColumn = () => pgIntColumn satisfies ColumnHelperFor; export class PgFloatDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'number' as const; + override readonly jsonType = 'number' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return expression; } diff --git a/packages/3-targets/postgres/1-target/src/core/date-codecs.ts b/packages/3-targets/postgres/1-target/src/core/date-codecs.ts index 8ab374203bfd..3cdeb7557f39 100644 --- a/packages/3-targets/postgres/1-target/src/core/date-codecs.ts +++ b/packages/3-targets/postgres/1-target/src/core/date-codecs.ts @@ -134,7 +134,7 @@ export class PgTimestamptzDateCodec extends CodecImpl< } export class PgTimestamptzDateDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'string' as const; + override readonly jsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return CastExpr.as(expression, 'text'); } @@ -179,7 +179,7 @@ export class PgTimestampDateCodec extends CodecImpl< } export class PgTimestampDateDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'string' as const; + override readonly jsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return CastExpr.as(expression, 'text'); } diff --git a/packages/3-targets/postgres/1-target/src/core/interval-string-codec.ts b/packages/3-targets/postgres/1-target/src/core/interval-string-codec.ts index ff2dc2d082cb..5034fc759f9a 100644 --- a/packages/3-targets/postgres/1-target/src/core/interval-string-codec.ts +++ b/packages/3-targets/postgres/1-target/src/core/interval-string-codec.ts @@ -70,7 +70,7 @@ export class PgIntervalStringCodec extends CodecImpl< } export class PgIntervalStringDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'string' as const; + override readonly jsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return CastExpr.as(expression, 'text'); } diff --git a/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-model-blocks.ts b/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-model-blocks.ts index 75015f7d288d..b46916917ea1 100644 --- a/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-model-blocks.ts +++ b/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-model-blocks.ts @@ -197,9 +197,6 @@ export function buildModel( }; } -/** Infer derives no value object check, so the check renderer reads no codec. */ -const NO_CODECS = { descriptorFor: () => undefined }; - /** * The live check names that are derived: a live check's name matches `formatWireName(composeCheckWirePrefix(table, column, kind), computeCheckContentHash(expression))` for some column of the table and some candidate `postgresRenderCheckExpressions` would render for that column. Never by comparing expressions — the live body is a Postgres reprint, never the authored text. One set serves both the element nullability of list fields and `@@check` exclusion in `buildModel`, so the two decisions cannot drift apart. * @@ -209,16 +206,13 @@ function computeDerivedCheckNames(table: SqlTableIR): ReadonlySet { const liveCheckNames = new Set((table.checks ?? []).map((check) => check.name)); const derivedCheckNames = new Set(); for (const column of Object.values(table.columns)) { - for (const candidate of postgresRenderCheckExpressions( - { - tableName: table.name, - columnName: column.name, - many: column.many === true ? { elementNullable: false } : false, - memberValues: undefined, - valueObject: undefined, - }, - NO_CODECS, - )) { + for (const candidate of postgresRenderCheckExpressions({ + tableName: table.name, + columnName: column.name, + many: column.many === true ? { elementNullable: false } : false, + memberValues: undefined, + valueObject: undefined, + })) { const derivedName = formatWireName( composeCheckWirePrefix(table.name, column.name, candidate.kind), computeCheckContentHash(candidate.expression), @@ -333,16 +327,13 @@ function enforcesElementNotNull( column: SqlColumnIR, derivedCheckNames: ReadonlySet, ): boolean { - return postgresRenderCheckExpressions( - { - tableName: table.name, - columnName: column.name, - many: { elementNullable: false }, - memberValues: undefined, - valueObject: undefined, - }, - NO_CODECS, - ).some( + return postgresRenderCheckExpressions({ + tableName: table.name, + columnName: column.name, + many: { elementNullable: false }, + memberValues: undefined, + valueObject: undefined, + }).some( (candidate) => candidate.kind === 'elementNotNull' && derivedCheckNames.has( diff --git a/packages/3-targets/postgres/1-target/src/core/psl-print/model-attributes.ts b/packages/3-targets/postgres/1-target/src/core/psl-print/model-attributes.ts index b04a6d938409..bac3e014b31e 100644 --- a/packages/3-targets/postgres/1-target/src/core/psl-print/model-attributes.ts +++ b/packages/3-targets/postgres/1-target/src/core/psl-print/model-attributes.ts @@ -1,4 +1,3 @@ -import type { CodecLookupWithDescriptors } from '@internal/framework-components/codec'; import type { PslModelAttribute } from '@internal/framework-components/psl-ast'; import type { ResolvedColumnValueObject } from '@internal/sql-contract/check-expressions'; import { escapePslString } from '@internal/sql-contract/data-type-support'; @@ -50,7 +49,6 @@ export function derivedChecks(input: { readonly memberValueSets: ReadonlyMap; /** The value object field each column that holds one stores, by column name. */ readonly columnValueObjects: ReadonlyMap; - readonly codecLookup: Pick; }): ReadonlyMap { const checks = new Map(); if (!input.managed) return checks; @@ -62,16 +60,13 @@ export function derivedChecks(input: { const memberValues = setValues?.filter( (value): value is string | number => typeof value === 'string' || typeof value === 'number', ); - for (const candidate of postgresRenderCheckExpressions( - { - tableName: input.tableName, - columnName, - many: column.many, - memberValues: memberValues?.length === setValues?.length ? memberValues : undefined, - valueObject: input.columnValueObjects.get(columnName), - }, - input.codecLookup, - )) { + for (const candidate of postgresRenderCheckExpressions({ + tableName: input.tableName, + columnName, + many: column.many, + memberValues: memberValues?.length === setValues?.length ? memberValues : undefined, + valueObject: input.columnValueObjects.get(columnName), + })) { const prefix = composeCheckWirePrefix(input.tableName, columnName, candidate.kind); checks.set(formatWireName(prefix, computeCheckContentHash(candidate.expression)), { prefix, diff --git a/packages/3-targets/postgres/1-target/src/core/psl-print/psl-contract.ts b/packages/3-targets/postgres/1-target/src/core/psl-print/psl-contract.ts index 9e4681042ac6..66ca8b326d4b 100644 --- a/packages/3-targets/postgres/1-target/src/core/psl-print/psl-contract.ts +++ b/packages/3-targets/postgres/1-target/src/core/psl-print/psl-contract.ts @@ -111,6 +111,7 @@ function nativeEnumsOf( function columnValueObjectsOf( contract: Contract, entries: readonly ModelWithTable[], + codecLookup: SqlPslBuildContext['codecLookup'], ): ReadonlyMap { const columnValueObjects = new Map(); for (const entry of entries) { @@ -122,11 +123,15 @@ function columnValueObjectsOf( } columnValueObjects.set( columnName, - resolveColumnValueObject(contract.storage, { - reference: field.type, - nullable: column.nullable, - many: field.many ?? false, - }), + resolveColumnValueObject( + contract.storage, + { + reference: field.type, + nullable: column.nullable, + many: field.many ?? false, + }, + codecLookup, + ), ); } } @@ -220,8 +225,11 @@ function buildModel( ...all.domainEnumValues, [discriminatorValueSet.name, discriminatorValueSet.values], ]), - columnValueObjects: columnValueObjectsOf(all.contract, [entry, ...singleTableVariants]), - codecLookup: all.context.codecLookup, + columnValueObjects: columnValueObjectsOf( + all.contract, + [entry, ...singleTableVariants], + all.context.codecLookup, + ), }); if (variant?.singleTable !== true) refuseUnderivedChecks(entry, checks); return { diff --git a/packages/3-targets/postgres/1-target/src/core/temporal-codecs.ts b/packages/3-targets/postgres/1-target/src/core/temporal-codecs.ts index 8b022d4044ba..a49c03d93a64 100644 --- a/packages/3-targets/postgres/1-target/src/core/temporal-codecs.ts +++ b/packages/3-targets/postgres/1-target/src/core/temporal-codecs.ts @@ -64,7 +64,7 @@ export class PgDateTemporalCodec extends CodecImpl< } export class PgDateTemporalDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'string' as const; + override readonly jsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return CastExpr.as(expression, 'text'); } @@ -112,7 +112,7 @@ export class PgTimestampTemporalCodec extends CodecImpl< } export class PgTimestampTemporalDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'string' as const; + override readonly jsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return CastExpr.as(expression, 'text'); } @@ -169,7 +169,7 @@ export class PgTimestampInstantCodec extends CodecImpl< } export class PgTimestampInstantDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'string' as const; + override readonly jsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return CastExpr.as(expression, 'text'); } @@ -223,7 +223,7 @@ export class PgTimestamptzTemporalCodec extends CodecImpl< } export class PgTimestamptzTemporalDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'string' as const; + override readonly jsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return CastExpr.as(expression, 'text'); } @@ -271,7 +271,7 @@ export class PgTimeTemporalCodec extends CodecImpl< } export class PgTimeTemporalDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'string' as const; + override readonly jsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return CastExpr.as(expression, 'text'); } diff --git a/packages/3-targets/postgres/1-target/src/core/temporal-string-codecs.ts b/packages/3-targets/postgres/1-target/src/core/temporal-string-codecs.ts index c651fa684d25..31b3664eaf81 100644 --- a/packages/3-targets/postgres/1-target/src/core/temporal-string-codecs.ts +++ b/packages/3-targets/postgres/1-target/src/core/temporal-string-codecs.ts @@ -50,7 +50,7 @@ export class PgDateStringCodec extends CodecImpl< } export class PgDateStringDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'string' as const; + override readonly jsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return CastExpr.as(expression, 'text'); } @@ -106,7 +106,7 @@ export class PgTimestampStringCodec extends CodecImpl< } export class PgTimestampStringDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'string' as const; + override readonly jsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return CastExpr.as(expression, 'text'); } @@ -151,7 +151,7 @@ export class PgTimestamptzStringCodec extends CodecImpl< } export class PgTimestamptzStringDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'string' as const; + override readonly jsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return CastExpr.as(expression, 'text'); } @@ -202,7 +202,7 @@ export class PgTimeStringCodec extends CodecImpl< } export class PgTimeStringDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'string' as const; + override readonly jsonType = 'string' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return CastExpr.as(expression, 'text'); } diff --git a/packages/3-targets/postgres/1-target/src/exports/codec-descriptor.ts b/packages/3-targets/postgres/1-target/src/exports/codec-descriptor.ts index 24bf3990b367..a775a45717a6 100644 --- a/packages/3-targets/postgres/1-target/src/exports/codec-descriptor.ts +++ b/packages/3-targets/postgres/1-target/src/exports/codec-descriptor.ts @@ -8,5 +8,4 @@ export { type PostgresCodecDescriptorRegistry, type PostgresCodecOptions, postgresCodec, - type ValueObjectJsonType, } from '../core/codec-descriptor'; diff --git a/packages/3-targets/postgres/1-target/test/check-expressions.integration.test.ts b/packages/3-targets/postgres/1-target/test/check-expressions.integration.test.ts index 0b5b7c9e7d7b..fa8b54d78ac1 100644 --- a/packages/3-targets/postgres/1-target/test/check-expressions.integration.test.ts +++ b/packages/3-targets/postgres/1-target/test/check-expressions.integration.test.ts @@ -1,7 +1,6 @@ import { timeouts, withClient, withDevDatabase } from '@repo/test-utils'; import { describe, expect, it } from 'vitest'; import { postgresRenderCheckExpressions } from '../src/core/check-expressions'; -import { postgresCodecDescriptorRegistry } from '../src/core/registry'; describe('numeric membership checks in Postgres', () => { it.each(['smallint', 'integer', 'real', 'double precision', 'numeric'])( @@ -12,16 +11,13 @@ describe('numeric membership checks in Postgres', () => { const fractional = !['smallint', 'integer'].includes(storageType); const memberValues = fractional ? [-3, 1, 2.5] : [-3, 1, 2]; for (const many of [false, true]) { - const checks = postgresRenderCheckExpressions( - { - tableName: 'members', - columnName: 'value', - many: many ? { elementNullable: false } : false, - memberValues, - valueObject: undefined, - }, - postgresCodecDescriptorRegistry, - ); + const checks = postgresRenderCheckExpressions({ + tableName: 'members', + columnName: 'value', + many: many ? { elementNullable: false } : false, + memberValues, + valueObject: undefined, + }); await client.query( `CREATE TABLE members (value ${storageType}${many ? '[]' : ''}, ${checks.map(({ expression }) => `CHECK (${expression})`).join(', ')})`, ); @@ -59,16 +55,13 @@ describe('list membership checks in Postgres', () => { labels: { type: 'varchar(20)', members: ['say "hi"', 'back\\slash', "o'brien"] }, }; const checks = Object.entries(columns).flatMap(([columnName, { members }]) => - postgresRenderCheckExpressions( - { - tableName: 'lists', - columnName, - many: { elementNullable: false }, - memberValues: members, - valueObject: undefined, - }, - postgresCodecDescriptorRegistry, - ), + postgresRenderCheckExpressions({ + tableName: 'lists', + columnName, + many: { elementNullable: false }, + memberValues: members, + valueObject: undefined, + }), ); await client.query( `CREATE TABLE lists (id int, ${Object.entries(columns) @@ -131,16 +124,13 @@ describe('list membership checks in Postgres', () => { const members = ['a,b', '{brace}', '', 'NULL', ' lead']; const nearMisses = ['a', 'brace', ' ', 'Null', 'lead']; const checks = [false, true].flatMap((many) => - postgresRenderCheckExpressions( - { - tableName: 'labels', - columnName: many ? 'list' : 'scalar', - many: many ? { elementNullable: false } : false, - memberValues: members, - valueObject: undefined, - }, - postgresCodecDescriptorRegistry, - ), + postgresRenderCheckExpressions({ + tableName: 'labels', + columnName: many ? 'list' : 'scalar', + many: many ? { elementNullable: false } : false, + memberValues: members, + valueObject: undefined, + }), ); await client.query( `CREATE TABLE labels (id serial, scalar text, list text[], ${checks.map(({ expression }) => `CHECK (${expression})`).join(', ')})`, diff --git a/packages/3-targets/postgres/1-target/test/check-expressions.test.ts b/packages/3-targets/postgres/1-target/test/check-expressions.test.ts index 08f5008bd088..c60a4e528678 100644 --- a/packages/3-targets/postgres/1-target/test/check-expressions.test.ts +++ b/packages/3-targets/postgres/1-target/test/check-expressions.test.ts @@ -1,7 +1,6 @@ import type { CheckExpressionInput } from '@internal/sql-contract/check-expressions'; import { describe, expect, it } from 'vitest'; import { postgresRenderCheckExpressions } from '../src/core/check-expressions'; -import { postgresCodecDescriptorRegistry } from '../src/core/registry'; const base: CheckExpressionInput = { tableName: 'User', @@ -13,35 +12,24 @@ const base: CheckExpressionInput = { describe('postgresRenderCheckExpressions', () => { it('renders a scalar domain enum as an IN membership predicate', () => { - expect( - postgresRenderCheckExpressions( - { ...base, memberValues: ['user', 'admin'] }, - postgresCodecDescriptorRegistry, - ), - ).toEqual([ + expect(postgresRenderCheckExpressions({ ...base, memberValues: ['user', 'admin'] })).toEqual([ { kind: 'membership', columnName: 'role', expression: `"role" IN ('user', 'admin')` }, ]); }); it('renders integer, fractional, and negative scalar members without quotes', () => { - expect( - postgresRenderCheckExpressions( - { ...base, memberValues: [1, 2.5, -3] }, - postgresCodecDescriptorRegistry, - ), - ).toEqual([{ kind: 'membership', columnName: 'role', expression: '"role" IN (1, 2.5, -3)' }]); + expect(postgresRenderCheckExpressions({ ...base, memberValues: [1, 2.5, -3] })).toEqual([ + { kind: 'membership', columnName: 'role', expression: '"role" IN (1, 2.5, -3)' }, + ]); }); it('compares numeric array members in the column type', () => { expect( - postgresRenderCheckExpressions( - { - ...base, - many: { elementNullable: false }, - memberValues: [1, 2.5, -3], - }, - postgresCodecDescriptorRegistry, - ), + postgresRenderCheckExpressions({ + ...base, + many: { elementNullable: false }, + memberValues: [1, 2.5, -3], + }), ).toEqual([ { kind: 'membership', @@ -61,10 +49,7 @@ describe('postgresRenderCheckExpressions', () => { (value) => { for (const many of [false, { elementNullable: false }] as const) { expect(() => - postgresRenderCheckExpressions( - { ...base, many, memberValues: [1, value] }, - postgresCodecDescriptorRegistry, - ), + postgresRenderCheckExpressions({ ...base, many, memberValues: [1, value] }), ).toThrow(/non-finite numeric member/); } }, @@ -72,15 +57,12 @@ describe('postgresRenderCheckExpressions', () => { it('renders array membership over null-stripped elements', () => { expect( - postgresRenderCheckExpressions( - { - ...base, - columnName: 'roles', - many: { elementNullable: false }, - memberValues: ['user', 'admin'], - }, - postgresCodecDescriptorRegistry, - ), + postgresRenderCheckExpressions({ + ...base, + columnName: 'roles', + many: { elementNullable: false }, + memberValues: ['user', 'admin'], + }), ).toEqual([ { kind: 'membership', @@ -97,14 +79,11 @@ describe('postgresRenderCheckExpressions', () => { it('renders element-non-null only for a list column with no member set', () => { expect( - postgresRenderCheckExpressions( - { - ...base, - columnName: 'tags', - many: { elementNullable: false }, - }, - postgresCodecDescriptorRegistry, - ), + postgresRenderCheckExpressions({ + ...base, + columnName: 'tags', + many: { elementNullable: false }, + }), ).toEqual([ { kind: 'elementNotNull', @@ -116,15 +95,12 @@ describe('postgresRenderCheckExpressions', () => { it('omits element-non-null for a nullable-element list while preserving membership', () => { expect( - postgresRenderCheckExpressions( - { - ...base, - columnName: 'roles', - many: { elementNullable: true }, - memberValues: ['user', 'admin'], - }, - postgresCodecDescriptorRegistry, - ), + postgresRenderCheckExpressions({ + ...base, + columnName: 'roles', + many: { elementNullable: true }, + memberValues: ['user', 'admin'], + }), ).toEqual([ { kind: 'membership', @@ -136,15 +112,12 @@ describe('postgresRenderCheckExpressions', () => { it('escapes array-literal and SQL quoting in list members', () => { expect( - postgresRenderCheckExpressions( - { - ...base, - columnName: 'roles', - many: { elementNullable: true }, - memberValues: ['say "hi"', 'back\\slash', "o'brien", '', 'NULL'], - }, - postgresCodecDescriptorRegistry, - ), + postgresRenderCheckExpressions({ + ...base, + columnName: 'roles', + many: { elementNullable: true }, + memberValues: ['say "hi"', 'back\\slash', "o'brien", '', 'NULL'], + }), ).toEqual([ { kind: 'membership', @@ -155,44 +128,35 @@ describe('postgresRenderCheckExpressions', () => { }); it('renders nothing for a plain scalar column', () => { - expect(postgresRenderCheckExpressions(base, postgresCodecDescriptorRegistry)).toEqual([]); + expect(postgresRenderCheckExpressions(base)).toEqual([]); }); it('throws on an empty member set for a scalar column', () => { - expect(() => - postgresRenderCheckExpressions( - { ...base, memberValues: [] }, - postgresCodecDescriptorRegistry, - ), - ).toThrow(/empty member set/); + expect(() => postgresRenderCheckExpressions({ ...base, memberValues: [] })).toThrow( + /empty member set/, + ); }); it('throws on an empty member set for an array column', () => { expect(() => - postgresRenderCheckExpressions( - { - ...base, - columnName: 'roles', - many: { elementNullable: false }, - memberValues: [], - }, - postgresCodecDescriptorRegistry, - ), + postgresRenderCheckExpressions({ + ...base, + columnName: 'roles', + many: { elementNullable: false }, + memberValues: [], + }), ).toThrow(/empty member set/); }); it('quotes identifiers and escapes literal quotes', () => { expect( - postgresRenderCheckExpressions( - { - tableName: 'Order', - columnName: 'sta"tus', - many: false, - memberValues: ["o'brien"], - valueObject: undefined, - }, - postgresCodecDescriptorRegistry, - ), + postgresRenderCheckExpressions({ + tableName: 'Order', + columnName: 'sta"tus', + many: false, + memberValues: ["o'brien"], + valueObject: undefined, + }), ).toEqual([ { kind: 'membership', columnName: 'sta"tus', expression: `"sta""tus" IN ('o''brien')` }, ]); diff --git a/packages/3-targets/postgres/1-target/test/check-expressions.value-object.test.ts b/packages/3-targets/postgres/1-target/test/check-expressions.value-object.test.ts index fc1263f02240..d55f8a64c8e3 100644 --- a/packages/3-targets/postgres/1-target/test/check-expressions.value-object.test.ts +++ b/packages/3-targets/postgres/1-target/test/check-expressions.value-object.test.ts @@ -4,15 +4,15 @@ import type { ResolvedValueObjectField, ResolvedValueObjectFieldType, } from '@internal/sql-contract/check-expressions'; +import type { JsonType } from '@internal/sql-contract/codec-json-type'; import { describe, expect, it } from 'vitest'; import { postgresRenderCheckExpressions } from '../src/core/check-expressions'; -import { postgresCodecDescriptorRegistry } from '../src/core/registry'; function scalar( - codecId: string, + jsonType: JsonType, values: readonly (string | number)[] | undefined = undefined, ): ResolvedValueObjectFieldType { - return { kind: 'scalar', codecId, values }; + return { kind: 'scalar', jsonType, values }; } function member( @@ -30,19 +30,16 @@ function render( valueObject: ResolvedValueObject, column: Partial> = {}, ): readonly { readonly kind: string; readonly expression: string }[] { - return postgresRenderCheckExpressions( - { - tableName: 'user', - columnName: 'address', - many: false, - memberValues: undefined, - valueObject: { nullable: false, many: false, ...column, valueObject }, - }, - postgresCodecDescriptorRegistry, - ).map(({ kind, expression }) => ({ kind, expression })); + return postgresRenderCheckExpressions({ + tableName: 'user', + columnName: 'address', + many: false, + memberValues: undefined, + valueObject: { nullable: false, many: false, ...column, valueObject }, + }).map(({ kind, expression }) => ({ kind, expression })); } -const street = member(scalar('pg/text@1')); +const street = member(scalar('string')); const flat: ResolvedValueObject = { fields: { street } }; const isObject = '$.type() == "object"'; @@ -69,7 +66,7 @@ describe('postgresRenderCheckExpressions for a column that holds a value object' }); it('lets a nullable member be absent or JSON null', () => { - const zip = member(scalar('pg/int4@1'), { nullable: true }); + const zip = member(scalar('number'), { nullable: true }); expect(render({ fields: { zip } })).toEqual([ { kind: 'shape', @@ -79,8 +76,8 @@ describe('postgresRenderCheckExpressions for a column that holds a value object' }); it('checks list members and their element nullability', () => { - const tags = member(scalar('pg/text@1'), { many: { elementNullable: false } }); - const flags = member(scalar('pg/bool@1'), { many: { elementNullable: true } }); + const tags = member(scalar('string'), { many: { elementNullable: false } }); + const flags = member(scalar('boolean'), { many: { elementNullable: true } }); expect(render({ fields: { tags, flags } })).toEqual([ { kind: 'shape', @@ -90,7 +87,7 @@ describe('postgresRenderCheckExpressions for a column that holds a value object' }); it('restricts an enum member to its value set', () => { - const kind = member(scalar('pg/text@1', ['home', "o'brien"])); + const kind = member(scalar('string', ['home', "o'brien"])); expect(render({ fields: { kind } })).toEqual([ { kind: 'shape', @@ -100,7 +97,7 @@ describe('postgresRenderCheckExpressions for a column that holds a value object' }); it('restricts a numeric enum member to its value set', () => { - const rank = member(scalar('pg/int4@1', [1, -2])); + const rank = member(scalar('number', [1, -2])); expect(render({ fields: { rank } })).toEqual([ { kind: 'shape', @@ -141,21 +138,22 @@ describe('postgresRenderCheckExpressions for a column that holds a value object' ]); }); - it('refuses JSON null for a required member whose codec declares presence only, and checks only the presence of a JSON member', () => { - const bytes = member(scalar('pg/bytea@1')); - const data = member(scalar('pg/jsonb@1')); - const notes = member(scalar('pg/jsonb@1'), { nullable: true }); - const blobs = member(scalar('pg/bytea@1'), { many: { elementNullable: false } }); - expect(render({ fields: { blobs, bytes, data, notes } })).toEqual([ + it('requires an object or array member to be of its JSON type, and lets a member of any JSON type hold any value, JSON null included', () => { + const bytes = member(scalar('object')); + const data = member(scalar('any')); + const notes = member(scalar('any'), { nullable: true }); + const tags = member(scalar('array')); + const blobs = member(scalar('object'), { many: { elementNullable: false } }); + expect(render({ fields: { blobs, bytes, data, notes, tags } })).toEqual([ { kind: 'shape', - expression: `jsonb_path_match("address", 'strict ${isObject} && exists($.keyvalue() ? (@.key == "blobs" && (@.value.type() == "array" && !exists(@.value[*] ? (@.type() == "null"))))) && exists($.keyvalue() ? (@.key == "bytes" && (@.value.type() != "null"))) && exists($.keyvalue() ? (@.key == "data")) && !exists($.keyvalue() ? (@.key != "blobs" && @.key != "bytes" && @.key != "data" && @.key != "notes"))') IS TRUE`, + expression: `jsonb_path_match("address", 'strict ${isObject} && exists($.keyvalue() ? (@.key == "blobs" && (@.value.type() == "array" && !exists(@.value[*] ? (!((@.type() == "object"))))))) && exists($.keyvalue() ? (@.key == "bytes" && (@.value.type() == "object"))) && exists($.keyvalue() ? (@.key == "data")) && exists($.keyvalue() ? (@.key == "tags" && (@.value.type() == "array"))) && !exists($.keyvalue() ? (@.key != "blobs" && @.key != "bytes" && @.key != "data" && @.key != "notes" && @.key != "tags"))') IS TRUE`, }, ]); }); - it('requires a decimal member to be a JSON string, as the ORM writes it', () => { - const amount = member(scalar('pg/numeric@1')); + it('requires a member written as a JSON string to be a string', () => { + const amount = member(scalar('string')); expect(render({ fields: { amount } })).toEqual([ { kind: 'shape', @@ -165,7 +163,7 @@ describe('postgresRenderCheckExpressions for a column that holds a value object' }); it('writes member names as jsonpath strings', () => { - const quoted = member(scalar('pg/text@1')); + const quoted = member(scalar('string')); expect(render({ fields: { 'say "hi"': quoted } })).toEqual([ { kind: 'shape', diff --git a/packages/3-targets/postgres/1-target/test/codec-value-object-json-type.test.ts b/packages/3-targets/postgres/1-target/test/codec-json-type.test.ts similarity index 51% rename from packages/3-targets/postgres/1-target/test/codec-value-object-json-type.test.ts rename to packages/3-targets/postgres/1-target/test/codec-json-type.test.ts index c9438406e2ab..614cbb28df56 100644 --- a/packages/3-targets/postgres/1-target/test/codec-value-object-json-type.test.ts +++ b/packages/3-targets/postgres/1-target/test/codec-json-type.test.ts @@ -1,25 +1,29 @@ +import { JSON_TYPES } from '@internal/sql-contract/codec-json-type'; import { describe, expect, it } from 'vitest'; import { buildPostgresCodecDescriptorRegistry } from '../src/core/codec-descriptor'; import { postgresCodecDescriptorRegistry } from '../src/core/registry'; -const DECLARATIONS = ['string', 'number', 'boolean', 'anyJson', 'presenceOnly']; - -describe('the JSON type a codec declares for a value object member', () => { +describe('the JSON type a codec declares', () => { it('is declared by every Postgres and SQL family codec', () => { const undeclared = [...postgresCodecDescriptorRegistry.values()] - .filter((descriptor) => !DECLARATIONS.includes(descriptor.valueObjectJsonType)) + .filter((descriptor) => !JSON_TYPES.includes(descriptor.jsonType)) .map((descriptor) => descriptor.codecId); expect(undeclared).toEqual([]); }); - it('is the type the SQL ORM writes, for the codecs that write one', () => { + it('is the type the codec value is written as inside a JSON document', () => { expect( Object.fromEntries( [ 'sql/text@1', 'sql/int@1', + 'sql/raw@1', 'pg/numeric@1', 'pg/bool@1', + 'pg/enum@1', + 'pg/text-array@1', + 'pg/bytea@1', + 'pg/interval@1', 'pg/jsonb@1', 'pg/int8@1', 'pg/timestamp-date@1', @@ -27,25 +31,30 @@ describe('the JSON type a codec declares for a value object member', () => { 'pg/interval-string@1', ].map((codecId) => [ codecId, - postgresCodecDescriptorRegistry.descriptorFor(codecId)?.valueObjectJsonType, + postgresCodecDescriptorRegistry.descriptorFor(codecId)?.jsonType, ]), ), ).toEqual({ 'sql/text@1': 'string', 'sql/int@1': 'number', + 'sql/raw@1': 'any', 'pg/numeric@1': 'string', 'pg/bool@1': 'boolean', - 'pg/jsonb@1': 'anyJson', - 'pg/int8@1': 'presenceOnly', + 'pg/enum@1': 'string', + 'pg/text-array@1': 'array', + 'pg/bytea@1': 'object', + 'pg/interval@1': 'object', + 'pg/jsonb@1': 'any', + 'pg/int8@1': 'number', 'pg/timestamp-date@1': 'string', 'pg/timestamp-instant@1': 'string', 'pg/interval-string@1': 'string', }); }); - it('is required of a descriptor the registry accepts', () => { + it('is not required of a descriptor the registry accepts, so a codec compiled before codecs declared one still registers', () => { const text = postgresCodecDescriptorRegistry.descriptorFor('pg/text@1'); - const { valueObjectJsonType: _declared, ...undeclared } = { + const undeclared = { descriptorKind: text?.descriptorKind, codecId: 'demo/undeclared@1', traits: text?.traits, @@ -53,9 +62,25 @@ describe('the JSON type a codec declares for a value object member', () => { isParameterized: false, factory: text?.factory.bind(text), projectJson: text?.projectJson.bind(text), - valueObjectJsonType: text?.valueObjectJsonType, }; - expect(() => buildPostgresCodecDescriptorRegistry([undeclared])).toThrow( + expect( + buildPostgresCodecDescriptorRegistry([undeclared]).descriptorFor('demo/undeclared@1'), + ).toBe(undeclared); + }); + + it('is refused when a descriptor declares one that is not a JSON type', () => { + const text = postgresCodecDescriptorRegistry.descriptorFor('pg/text@1'); + const misdeclared = { + descriptorKind: text?.descriptorKind, + codecId: 'demo/misdeclared@1', + traits: text?.traits, + paramsSchema: undefined, + isParameterized: false, + factory: text?.factory.bind(text), + projectJson: text?.projectJson.bind(text), + jsonType: 'presenceOnly', + }; + expect(() => buildPostgresCodecDescriptorRegistry([misdeclared])).toThrow( expect.objectContaining({ code: 'RUNTIME.CODEC_DESCRIPTOR_INVALID' }), ); }); diff --git a/packages/3-targets/postgres/1-target/test/postgres-codec-descriptor.test-d.ts b/packages/3-targets/postgres/1-target/test/postgres-codec-descriptor.test-d.ts index 5bd9456051ac..107de210261b 100644 --- a/packages/3-targets/postgres/1-target/test/postgres-codec-descriptor.test-d.ts +++ b/packages/3-targets/postgres/1-target/test/postgres-codec-descriptor.test-d.ts @@ -65,6 +65,7 @@ class VectorCodec extends CodecImpl< } class GenericVectorDescriptor extends CodecDescriptorImpl { + readonly jsonType = 'array' as const; override readonly dataType = dataTypeId('demo/fixture'); override readonly codecId = 'demo/vector@1' as const; override readonly traits = ['equality'] as const; @@ -83,7 +84,7 @@ class GenericVectorDescriptor extends CodecDescriptorImpl { } class DirectVectorDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'presenceOnly' as const; + override readonly jsonType = 'any' as const; override readonly dataType = dataTypeId('demo/fixture'); override readonly codecId = 'demo/direct-vector@1' as const; override readonly traits = ['equality'] as const; @@ -106,7 +107,6 @@ class DirectVectorDescriptor extends PostgresCodecDescriptor { const genericDescriptor = new GenericVectorDescriptor(); const directDescriptor = new DirectVectorDescriptor(); const adaptedDescriptor = postgresCodec(genericDescriptor, { - valueObjectJsonType: 'presenceOnly', dataType: fixtureType, factory: (descriptor, type, params) => () => new VectorCodec(descriptor, type, params.length), jsonProjection(expression, params) { @@ -154,7 +154,6 @@ test('definePostgresCodecs rejects an unadapted generic descriptor', () => { test('postgresCodec requires explicit scalar projection behavior', () => { // @ts-expect-error -- jsonProjection is mandatory postgresCodec(genericDescriptor, { - valueObjectJsonType: 'presenceOnly', dataType: fixtureType, factory: (descriptor, type, params) => () => new VectorCodec(descriptor, type, params.length), }); @@ -177,13 +176,11 @@ class TextCodec extends CodecImpl<'demo/text@1', readonly ['equality'], string, test('a factory option builds a codec of the family codec it adapts', () => { postgresCodec(genericDescriptor, { - valueObjectJsonType: 'presenceOnly', dataType: fixtureType, jsonProjection: (expression) => expression, factory: (descriptor, type, params) => () => new VectorCodec(descriptor, type, params.length), }); postgresCodec(genericDescriptor, { - valueObjectJsonType: 'presenceOnly', dataType: fixtureType, jsonProjection: (expression) => expression, // @ts-expect-error -- the adapted descriptor's factory promises the family codec, so the option cannot build another @@ -193,7 +190,7 @@ test('a factory option builds a codec of the family codec it adapts', () => { // @ts-expect-error -- direct descriptors must implement scalar JSON projection class MissingJsonProjection extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'presenceOnly' as const; + override readonly jsonType = 'any' as const; override readonly dataType = dataTypeId('demo/fixture'); override readonly codecId = 'demo/missing-json@1' as const; override readonly traits: readonly CodecTrait[] = []; diff --git a/packages/3-targets/postgres/1-target/test/postgres-codec-descriptor.test.ts b/packages/3-targets/postgres/1-target/test/postgres-codec-descriptor.test.ts index 84fc8c7e05ec..9e04ecaaeb5b 100644 --- a/packages/3-targets/postgres/1-target/test/postgres-codec-descriptor.test.ts +++ b/packages/3-targets/postgres/1-target/test/postgres-codec-descriptor.test.ts @@ -98,6 +98,7 @@ class VectorCodec extends CodecImpl< } class GenericVectorDescriptor extends CodecDescriptorImpl { + readonly jsonType = 'array' as const; override readonly dataType = dataTypeId('demo/fixture'); override readonly codecId = 'demo/vector@1' as const; override readonly traits = ['equality'] as const; @@ -130,7 +131,7 @@ class RenderingVectorDescriptor extends GenericVectorDescriptor { } class DirectVectorDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'presenceOnly' as const; + override readonly jsonType = 'any' as const; override readonly dataType = dataTypeId('demo/fixture'); override readonly codecId = 'demo/direct-vector@1' as const; override readonly traits = ['equality'] as const; @@ -281,7 +282,6 @@ describe('postgresCodec', () => { it('refuses a family template that renders literals, naming the option to use instead', () => { expect(() => postgresCodec(new RenderingVectorDescriptor(), { - valueObjectJsonType: 'presenceOnly', dataType: fixtureVectorType, jsonProjection: (expression) => expression, factory: vectorCodecFactory, @@ -293,7 +293,6 @@ describe('postgresCodec', () => { it("renders literals with the renderer the target passes, never the template's", () => { const descriptor = postgresCodec(genericVectorDescriptor, { - valueObjectJsonType: 'presenceOnly', dataType: fixtureVectorType, renderValueLiteral: (value, side) => `${side}-from-target:${String(value)}`, jsonProjection: (expression) => expression, @@ -305,7 +304,6 @@ describe('postgresCodec', () => { it('preserves the wrapped descriptor contract and builds its codec with the factory option', () => { const descriptor = postgresCodec(genericVectorDescriptor, { - valueObjectJsonType: 'presenceOnly', dataType: fixtureVectorType, factory: vectorCodecFactory, jsonProjection: (expression, params) => @@ -315,6 +313,7 @@ describe('postgresCodec', () => { expect(descriptor.descriptorKind).toBe('postgres-codec'); expect(descriptor.codecId).toBe(genericVectorDescriptor.codecId); expect(descriptor.traits).toBe(genericVectorDescriptor.traits); + expect(descriptor.jsonType).toBe('array'); expect(descriptor.dataType).toBe(fixtureVectorType.id); expect(descriptor.isParameterized).toBe(genericVectorDescriptor.isParameterized); expect(descriptor.renderOutputType?.({ length: 6 })).toBe('Vector<6>'); @@ -333,13 +332,11 @@ describe('postgresCodec', () => { it('takes its parameter schema from the data type, not the template', () => { const descriptor = postgresCodec(genericVectorDescriptor, { - valueObjectJsonType: 'presenceOnly', dataType: fixtureVectorType, factory: vectorCodecFactory, jsonProjection: (expression) => expression, }); const withoutParams = postgresCodec(genericVectorDescriptor, { - valueObjectJsonType: 'presenceOnly', dataType: dataType('demo/plain', { read: (json) => json }), factory: vectorCodecFactory, jsonProjection: (expression) => expression, @@ -358,7 +355,6 @@ describe('postgresCodec', () => { } const adapt = (wrapped: GenericVectorDescriptor) => postgresCodec(wrapped, { - valueObjectJsonType: 'presenceOnly', dataType: fixtureVectorType, factory: vectorCodecFactory, jsonProjection: (expression) => expression, @@ -376,7 +372,6 @@ describe('postgresCodec', () => { it('accepts an array override only after typed parameter validation', () => { const overrideCalls: VectorParams[] = []; const descriptor = postgresCodec(genericVectorDescriptor, { - valueObjectJsonType: 'presenceOnly', dataType: fixtureVectorType, factory: vectorCodecFactory, jsonProjection: (expression) => expression, @@ -418,7 +413,7 @@ describe('Postgres codec descriptor registry', () => { isParameterized: descriptor.isParameterized, factory: descriptor.factory.bind(descriptor), projectJson: descriptor.projectJson.bind(descriptor), - valueObjectJsonType: descriptor.valueObjectJsonType, + jsonType: descriptor.jsonType, }; expect(structuralDescriptor).not.toBeInstanceOf(PostgresCodecDescriptor); @@ -440,7 +435,7 @@ describe('Postgres codec descriptor registry', () => { isParameterized: descriptor.isParameterized, factory: descriptor.factory.bind(descriptor), projectJson: descriptor.projectJson.bind(descriptor), - valueObjectJsonType: descriptor.valueObjectJsonType, + jsonType: descriptor.jsonType, }; expect(() => buildPostgresCodecDescriptorRegistry([genericVectorDescriptor])).toThrow( diff --git a/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.check.test.ts b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.check.test.ts index 2d2aa362a580..60d5c6cb1809 100644 --- a/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.check.test.ts +++ b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.check.test.ts @@ -14,19 +14,15 @@ import { import { SqlSchemaIR } from '@internal/sql-schema-ir/types'; import { describe, expect, it } from 'vitest'; import { postgresRenderCheckExpressions } from '../../../src/core/check-expressions'; -import { postgresCodecDescriptorRegistry } from '../../../src/core/registry'; import { printPslFromFlat } from '../fixtures'; -const elementCandidate = postgresRenderCheckExpressions( - { - tableName: 'users', - columnName: 'tags', - many: { elementNullable: false }, - memberValues: undefined, - valueObject: undefined, - }, - postgresCodecDescriptorRegistry, -)[0]; +const elementCandidate = postgresRenderCheckExpressions({ + tableName: 'users', + columnName: 'tags', + many: { elementNullable: false }, + memberValues: undefined, + valueObject: undefined, +})[0]; const derivedPrefix = composeCheckWirePrefix('users', 'tags', 'elementNotNull'); const derivedHash = computeCheckContentHash(elementCandidate?.expression ?? ''); diff --git a/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.list-element-nullability.test.ts b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.list-element-nullability.test.ts index 23f012ddba83..470d859565bd 100644 --- a/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.list-element-nullability.test.ts +++ b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.list-element-nullability.test.ts @@ -12,19 +12,15 @@ import { import { SqlSchemaIR } from '@internal/sql-schema-ir/types'; import { describe, expect, it } from 'vitest'; import { postgresRenderCheckExpressions } from '../../../src/core/check-expressions'; -import { postgresCodecDescriptorRegistry } from '../../../src/core/registry'; import { printPslFromFlat } from '../fixtures'; -const elementCandidate = postgresRenderCheckExpressions( - { - tableName: 'users', - columnName: 'tags', - many: { elementNullable: false }, - memberValues: undefined, - valueObject: undefined, - }, - postgresCodecDescriptorRegistry, -)[0]; +const elementCandidate = postgresRenderCheckExpressions({ + tableName: 'users', + columnName: 'tags', + many: { elementNullable: false }, + memberValues: undefined, + valueObject: undefined, +})[0]; const derivedPrefix = composeCheckWirePrefix('users', 'tags', 'elementNotNull'); const derivedHash = computeCheckContentHash(elementCandidate?.expression ?? ''); diff --git a/packages/3-targets/postgres/1-target/test/psl-print/build-context.ts b/packages/3-targets/postgres/1-target/test/psl-print/build-context.ts index 08f23a6cce69..9ac954f431d3 100644 --- a/packages/3-targets/postgres/1-target/test/psl-print/build-context.ts +++ b/packages/3-targets/postgres/1-target/test/psl-print/build-context.ts @@ -21,7 +21,8 @@ import { postgresTargetDescriptorMeta } from '../../src/core/descriptor-meta'; import { postgresCodecDescriptorRegistry } from '../../src/core/registry'; import { postgresScalarAndNativeTypeConstructors } from '../../src/core/type-constructors'; -const citextTemplate: CodecDescriptorTemplate = { +const citextTemplate: CodecDescriptorTemplate & { readonly jsonType: 'string' } = { + jsonType: 'string', codecId: 'ext/citext@1', traits: [], paramsSchema: undefined, @@ -33,7 +34,6 @@ const citextTemplate: CodecDescriptorTemplate = { /** A codec the target does not own, as an extension would contribute it, representing text. */ export const extensionCodec: AnyPostgresCodecDescriptor = postgresCodec(citextTemplate, { - valueObjectJsonType: 'presenceOnly', dataType: pgText, jsonProjection: (expression) => expression, factory: () => () => { diff --git a/packages/3-targets/postgres/2-adapter/test/lower-to-execute-request.test.ts b/packages/3-targets/postgres/2-adapter/test/lower-to-execute-request.test.ts index 5f25b3b7c2fc..55ebcdb4ad0f 100644 --- a/packages/3-targets/postgres/2-adapter/test/lower-to-execute-request.test.ts +++ b/packages/3-targets/postgres/2-adapter/test/lower-to-execute-request.test.ts @@ -48,12 +48,14 @@ const transformingCodecDescriptor: AnyCodecDescriptor = { isParameterized: false, factory: () => () => transformingCodec, }; -const transformingDescriptor = postgresCodec(transformingCodecDescriptor, { - valueObjectJsonType: 'presenceOnly', - dataType: pgText, - jsonProjection: (expression: ProjectionExpr) => expression, - factory: () => () => transformingCodec, -}); +const transformingDescriptor = postgresCodec( + Object.assign(transformingCodecDescriptor, { jsonType: 'any' as const }), + { + dataType: pgText, + jsonProjection: (expression: ProjectionExpr) => expression, + factory: () => () => transformingCodec, + }, +); const transformingCodecRegistry = createPostgresCodecRegistryWithBuiltins([transformingDescriptor]); describe('PostgresControlAdapter.lowerToExecuteRequest — DDL literal defaults', () => { @@ -323,12 +325,14 @@ class ExtTransformDescriptor extends CodecDescriptorImpl { } const extDescriptor = new ExtTransformDescriptor(); -const extTransformDescriptor = postgresCodec(extDescriptor, { - valueObjectJsonType: 'presenceOnly', - dataType: pgText, - jsonProjection: (expression: ProjectionExpr) => expression, - factory: () => extDescriptor.factory(), -}); +const extTransformDescriptor = postgresCodec( + Object.assign(extDescriptor, { jsonType: 'any' as const }), + { + dataType: pgText, + jsonProjection: (expression: ProjectionExpr) => expression, + factory: () => extDescriptor.factory(), + }, +); function buildExtContractAndTable() { const tableColumns: StorageTableInput['columns'] = { diff --git a/packages/3-targets/postgres/2-adapter/test/migrations/alter-column-type.integration.test.ts b/packages/3-targets/postgres/2-adapter/test/migrations/alter-column-type.integration.test.ts index 639b69922fd4..5a98d8ec1d32 100644 --- a/packages/3-targets/postgres/2-adapter/test/migrations/alter-column-type.integration.test.ts +++ b/packages/3-targets/postgres/2-adapter/test/migrations/alter-column-type.integration.test.ts @@ -8,7 +8,6 @@ import { import { UNBOUND_NAMESPACE_ID } from '@internal/framework-components/ir'; import { CheckConstraint, SqlStorage } from '@internal/sql-contract/types'; import { composeCheckWirePrefix, computeCheckContentHash } from '@internal/sql-schema-ir/naming'; -import { postgresCodecDescriptorRegistry } from '@internal/target-postgres/codecs'; import { postgresDataTypes } from '@internal/target-postgres/data-types'; import { AlterColumnTypeCall } from '@internal/target-postgres/op-factory-call'; import { createPostgresMigrationPlanner } from '@internal/target-postgres/planner'; @@ -47,16 +46,13 @@ const intList = { const textList = { ...intList, dataType: 'pg/text', codecId: 'pg/text@1' }; function elementChecks(): CheckConstraint[] { - return postgresRenderCheckExpressions( - { - tableName: 'Item', - columnName: 'tags', - many: { elementNullable: false }, - memberValues: undefined, - valueObject: undefined, - }, - postgresCodecDescriptorRegistry, - ).map( + return postgresRenderCheckExpressions({ + tableName: 'Item', + columnName: 'tags', + many: { elementNullable: false }, + memberValues: undefined, + valueObject: undefined, + }).map( (candidate) => new CheckConstraint({ naming: { diff --git a/packages/3-targets/postgres/2-adapter/test/migrations/check-lifecycle-e2e.integration.test.ts b/packages/3-targets/postgres/2-adapter/test/migrations/check-lifecycle-e2e.integration.test.ts index fb74b099c044..6d3059e9b503 100644 --- a/packages/3-targets/postgres/2-adapter/test/migrations/check-lifecycle-e2e.integration.test.ts +++ b/packages/3-targets/postgres/2-adapter/test/migrations/check-lifecycle-e2e.integration.test.ts @@ -14,10 +14,7 @@ import { sql, } from '@internal/sql-contract-ts/contract-builder'; import { composeCheckWirePrefix, computeCheckContentHash } from '@internal/sql-schema-ir/naming'; -import { - createPostgresBuiltinCodecLookup, - postgresCodecDescriptorRegistry, -} from '@internal/target-postgres/codecs'; +import { createPostgresBuiltinCodecLookup } from '@internal/target-postgres/codecs'; import { postgresDataTypes } from '@internal/target-postgres/data-types'; import { PostgresDatabaseSchemaNode, @@ -106,16 +103,13 @@ function checksForColumn( readonly memberValues?: readonly string[]; }, ): CheckConstraint[] { - return postgresRenderCheckExpressions( - { - tableName, - columnName, - many: options.many, - memberValues: options.memberValues, - valueObject: undefined, - }, - postgresCodecDescriptorRegistry, - ).map( + return postgresRenderCheckExpressions({ + tableName, + columnName, + many: options.many, + memberValues: options.memberValues, + valueObject: undefined, + }).map( (candidate) => new CheckConstraint({ naming: { diff --git a/packages/3-targets/postgres/2-adapter/test/migrations/native-array-columns.integration.test.ts b/packages/3-targets/postgres/2-adapter/test/migrations/native-array-columns.integration.test.ts index 06ff1dc2c36e..a28336e11c0e 100644 --- a/packages/3-targets/postgres/2-adapter/test/migrations/native-array-columns.integration.test.ts +++ b/packages/3-targets/postgres/2-adapter/test/migrations/native-array-columns.integration.test.ts @@ -4,7 +4,6 @@ import { APP_SPACE_ID } from '@internal/framework-components/control'; import { UNBOUND_NAMESPACE_ID } from '@internal/framework-components/ir'; import { CheckConstraint, SqlStorage } from '@internal/sql-contract/types'; import { composeCheckWirePrefix, computeCheckContentHash } from '@internal/sql-schema-ir/naming'; -import { postgresCodecDescriptorRegistry } from '@internal/target-postgres/codecs'; import { parsePostgresListText } from '@internal/target-postgres/control'; import { postgresCreateNamespace, @@ -38,16 +37,13 @@ const ARRAY_COLUMNS = ['tags', 'labels', 'scores', 'tagsWithDefault'] as const; */ function declaredArrayElementChecks(): CheckConstraint[] { return ARRAY_COLUMNS.flatMap((columnName) => - postgresRenderCheckExpressions( - { - tableName: 'ArrayTest', - columnName, - many: { elementNullable: false }, - memberValues: undefined, - valueObject: undefined, - }, - postgresCodecDescriptorRegistry, - ).map( + postgresRenderCheckExpressions({ + tableName: 'ArrayTest', + columnName, + many: { elementNullable: false }, + memberValues: undefined, + valueObject: undefined, + }).map( (candidate) => new CheckConstraint({ naming: { diff --git a/packages/3-targets/postgres/2-adapter/test/postgres-codec-registry-composition.test.ts b/packages/3-targets/postgres/2-adapter/test/postgres-codec-registry-composition.test.ts index 7964219f5e43..f8461a9f768a 100644 --- a/packages/3-targets/postgres/2-adapter/test/postgres-codec-registry-composition.test.ts +++ b/packages/3-targets/postgres/2-adapter/test/postgres-codec-registry-composition.test.ts @@ -130,8 +130,7 @@ function postgresDescriptor( ): AnyPostgresCodecDescriptor { const type = fixtureSqlDataType(codecId, typeText); const template = genericDescriptor(codecId, type); - return postgresCodec(template, { - valueObjectJsonType: 'presenceOnly', + return postgresCodec(Object.assign(template, { jsonType: 'any' as const }), { dataType: type, jsonProjection(expression: ProjectionExpr): ProjectionExpr { onProjection?.(); @@ -163,8 +162,7 @@ function transformingPostgresDescriptor( return codec; }, }; - return postgresCodec(descriptor, { - valueObjectJsonType: 'presenceOnly', + return postgresCodec(Object.assign(descriptor, { jsonType: 'any' as const }), { dataType: type, jsonProjection: (expression: ProjectionExpr) => expression, factory: (_descriptor, _type, params) => descriptor.factory(params), diff --git a/packages/3-targets/postgres/2-adapter/test/sql-renderer.cast-policy.test.ts b/packages/3-targets/postgres/2-adapter/test/sql-renderer.cast-policy.test.ts index b4fb8784672b..ad75c16a6dd6 100644 --- a/packages/3-targets/postgres/2-adapter/test/sql-renderer.cast-policy.test.ts +++ b/packages/3-targets/postgres/2-adapter/test/sql-renderer.cast-policy.test.ts @@ -76,8 +76,7 @@ const dataTypes = createDataTypeLookup([ const verbatimTemplate = genericDescriptor('demo/verbatim@1', verbatimDataType); const verbatimRegistry = buildPostgresCodecDescriptorRegistry([ - postgresCodec(verbatimTemplate, { - valueObjectJsonType: 'presenceOnly', + postgresCodec(Object.assign(verbatimTemplate, { jsonType: 'any' as const }), { dataType: verbatimDataType, jsonProjection: (expression: ProjectionExpr) => expression, factory: (_descriptor, _type, params) => verbatimTemplate.factory(params), @@ -87,8 +86,7 @@ const verbatimRegistry = buildPostgresCodecDescriptorRegistry([ function descriptorFor(codecId: string, name: string): AnyPostgresCodecDescriptor { const type = fixtureDataType(name); const template = genericDescriptor(codecId, type); - return postgresCodec(template, { - valueObjectJsonType: 'presenceOnly', + return postgresCodec(Object.assign(template, { jsonType: 'any' as const }), { dataType: type, jsonProjection: (expression: ProjectionExpr) => expression, factory: (_descriptor, _type, params) => template.factory(params), diff --git a/packages/3-targets/postgres/4-codec-testkit/test/value-round-trip.integration.test.ts b/packages/3-targets/postgres/4-codec-testkit/test/value-round-trip.integration.test.ts index 8c6b39c94372..6ada9700f453 100644 --- a/packages/3-targets/postgres/4-codec-testkit/test/value-round-trip.integration.test.ts +++ b/packages/3-targets/postgres/4-codec-testkit/test/value-round-trip.integration.test.ts @@ -30,7 +30,7 @@ class MarkingTextCodec extends CodecImpl<'test/marking-text@1', readonly [], str } class MarkingTextDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'presenceOnly' as const; + override readonly jsonType = 'any' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return expression; } @@ -60,7 +60,7 @@ class TextCodec extends CodecImpl<'test/upper-casing-text@1', readonly [], strin /** A text codec whose projection changes the text, so the projected value reads to another value than the row. */ class UpperCasingTextDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'presenceOnly' as const; + override readonly jsonType = 'any' as const; protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return FunctionCallExpr.of('upper', [expression]); } diff --git a/packages/9-public/@prisma/orm-family-sql/package.json b/packages/9-public/@prisma/orm-family-sql/package.json index 0eb46a611477..857c25390f4e 100644 --- a/packages/9-public/@prisma/orm-family-sql/package.json +++ b/packages/9-public/@prisma/orm-family-sql/package.json @@ -58,6 +58,7 @@ "./contract/authored-check-naming": "./dist/contract__authored-check-naming.mjs", "./contract/canonicalization-hooks": "./dist/contract__canonicalization-hooks.mjs", "./contract/check-expressions": "./dist/contract__check-expressions.mjs", + "./contract/codec-json-type": "./dist/contract__codec-json-type.mjs", "./contract/contract-view": "./dist/contract__contract-view.mjs", "./contract/data-type": "./dist/contract__data-type.mjs", "./contract/data-type-support": "./dist/contract__data-type-support.mjs", diff --git a/packages/9-public/@prisma/orm-mysql/package.json b/packages/9-public/@prisma/orm-mysql/package.json index 3a70c10c001a..8ba0a8d2d7dc 100644 --- a/packages/9-public/@prisma/orm-mysql/package.json +++ b/packages/9-public/@prisma/orm-mysql/package.json @@ -85,6 +85,7 @@ "./family-contract/authored-check-naming": "./dist/family-contract__authored-check-naming.mjs", "./family-contract/canonicalization-hooks": "./dist/family-contract__canonicalization-hooks.mjs", "./family-contract/check-expressions": "./dist/family-contract__check-expressions.mjs", + "./family-contract/codec-json-type": "./dist/family-contract__codec-json-type.mjs", "./family-contract/contract-view": "./dist/family-contract__contract-view.mjs", "./family-contract/data-type": "./dist/family-contract__data-type.mjs", "./family-contract/data-type-support": "./dist/family-contract__data-type-support.mjs", diff --git a/packages/9-public/@prisma/orm-postgres/package.json b/packages/9-public/@prisma/orm-postgres/package.json index 623756df4f24..cf2d76739dae 100644 --- a/packages/9-public/@prisma/orm-postgres/package.json +++ b/packages/9-public/@prisma/orm-postgres/package.json @@ -100,6 +100,7 @@ "./family-contract/authored-check-naming": "./dist/family-contract__authored-check-naming.mjs", "./family-contract/canonicalization-hooks": "./dist/family-contract__canonicalization-hooks.mjs", "./family-contract/check-expressions": "./dist/family-contract__check-expressions.mjs", + "./family-contract/codec-json-type": "./dist/family-contract__codec-json-type.mjs", "./family-contract/contract-view": "./dist/family-contract__contract-view.mjs", "./family-contract/data-type": "./dist/family-contract__data-type.mjs", "./family-contract/data-type-support": "./dist/family-contract__data-type-support.mjs", diff --git a/packages/9-public/@prisma/orm-sqlite/package.json b/packages/9-public/@prisma/orm-sqlite/package.json index ef918f42df05..40f98ecc187b 100644 --- a/packages/9-public/@prisma/orm-sqlite/package.json +++ b/packages/9-public/@prisma/orm-sqlite/package.json @@ -94,6 +94,7 @@ "./family-contract/authored-check-naming": "./dist/family-contract__authored-check-naming.mjs", "./family-contract/canonicalization-hooks": "./dist/family-contract__canonicalization-hooks.mjs", "./family-contract/check-expressions": "./dist/family-contract__check-expressions.mjs", + "./family-contract/codec-json-type": "./dist/family-contract__codec-json-type.mjs", "./family-contract/contract-view": "./dist/family-contract__contract-view.mjs", "./family-contract/data-type": "./dist/family-contract__data-type.mjs", "./family-contract/data-type-support": "./dist/family-contract__data-type-support.mjs", diff --git a/projects/migration-statements/deferred.md b/projects/migration-statements/deferred.md index 0a768ea39b74..a25cb832d4b5 100644 --- a/projects/migration-statements/deferred.md +++ b/projects/migration-statements/deferred.md @@ -249,8 +249,8 @@ SQL PSL resolves a composite type by its bare name across all namespaces and wri Found in the review of the value object storage PR, 2026-10-23. Both limits are older than that PR. -- A `BigInt` member of a value object cannot be written: the `pg/jsonb@1` codec throws "Do not know how to serialize a BigInt". -- A `Bytes` member is written as an object of byte indexes, such as `{"0":1,"1":2}`, not as bytes. +- A `BigInt` member of a value object cannot be written: the `pg/jsonb@1` codec throws "Do not know how to serialize a BigInt". The same holds for a `pg/interval@1` member, whose value holds a `bigint`. Their codecs declare the JSON type their value would have: `pg/int8@1` and `pg/unboundedint@1` declare `number`, and `pg/interval@1` declares `object`. +- A `Bytes` member is written as an object of byte indexes, such as `{"0":1,"1":2}`, not as bytes. Its codec declares `object`, the type it is written as today. ## The SQL TypeScript builder has no value objects diff --git a/test/integration/test/authoring/extension-text-type.test.ts b/test/integration/test/authoring/extension-text-type.test.ts index 4c1313e2ab2f..50d3d58759cb 100644 --- a/test/integration/test/authoring/extension-text-type.test.ts +++ b/test/integration/test/authoring/extension-text-type.test.ts @@ -59,7 +59,7 @@ class CitextCodec extends CodecImpl> { - override readonly valueObjectJsonType = 'presenceOnly' as const; + override readonly jsonType = 'any' as const; override readonly dataType = testtextCitext.id; override readonly codecId = CITEXT_CODEC_ID; override readonly traits = ['equality'] as const; diff --git a/test/integration/test/sql-orm-client/include-codecs.test.ts b/test/integration/test/sql-orm-client/include-codecs.test.ts index 2c8a8da40757..a02f9c4e5f84 100644 --- a/test/integration/test/sql-orm-client/include-codecs.test.ts +++ b/test/integration/test/sql-orm-client/include-codecs.test.ts @@ -73,12 +73,14 @@ class IncludedTextDescriptor extends CodecDescriptorImpl { * column reaches JSON, since the renderer asks the descriptor rather than * assuming. This one stores and projects text unchanged. */ -const includedTextDescriptor = postgresCodec(new IncludedTextDescriptor(), { - valueObjectJsonType: 'presenceOnly', - dataType: pgText, - jsonProjection: (expression) => expression, - factory: (descriptor, dataType) => () => new IncludedTextCodec(descriptor, dataType), -}); +const includedTextDescriptor = postgresCodec( + Object.assign(new IncludedTextDescriptor(), { jsonType: 'any' as const }), + { + dataType: pgText, + jsonProjection: (expression) => expression, + factory: (descriptor, dataType) => () => new IncludedTextCodec(descriptor, dataType), + }, +); const includedTextColumn = { codecId: TEST_INCLUDED_TEXT_CODEC_ID, } as const satisfies ColumnTypeDescriptor; diff --git a/upgrade-instructions/pending/value-objects-in-storage/extension/instructions.md b/upgrade-instructions/pending/value-objects-in-storage/extension/instructions.md index 7b4512eb8e20..4521458af1a3 100644 --- a/upgrade-instructions/pending/value-objects-in-storage/extension/instructions.md +++ b/upgrade-instructions/pending/value-objects-in-storage/extension/instructions.md @@ -32,15 +32,15 @@ changes: glob: "**/*.{ts,mts,cts,tsx}" matches: - '\bValueObjectFieldType\b' - - id: postgres-codec-value-object-json-type + - id: postgres-codec-json-type summary: | - A PostgreSQL codec descriptor declares `valueObjectJsonType`: what the derived check of a value object column accepts for a member of the codec. It is `'string'`, `'number'` or `'boolean'` for the JSON type the SQL ORM writes for the codec's value, `'anyJson'` for a codec whose value is any JSON value, or `'presenceOnly'` when the check should only require the member to be present. A class that extends `PostgresCodecDescriptor` declares it as a property, and `postgresCodec(template, options)` takes it in its options. A registry refuses a descriptor without it, with `RUNTIME.CODEC_DESCRIPTOR_INVALID`. `postgresRenderCheckExpressions` takes the stack's codec lookup as a second argument. + A PostgreSQL codec descriptor declares `jsonType`: the JSON type the codec's value is written as inside a JSON document, one of `'string'`, `'number'`, `'boolean'`, `'object'`, `'array'` or `'any'`. `'any'` means any JSON value, including `null`, as for a JSON codec. The derived check of a value object column requires a member to be of its codec's JSON type, and a required member is never JSON `null` unless its codec's type is `'any'`. A class that extends `PostgresCodecDescriptor`, or an object that implements `AnyPostgresCodecDescriptor`, declares it as a property. `postgresCodec(template, options)` reads it from the template, so a family codec template declares it. A registry accepts a descriptor without it, but a contract build that derives the check of a value object whose member uses such a codec refuses it with `CONTRACT.CODEC_JSON_TYPE_MISSING`. detection: glob: "**/*.{ts,mts,cts,tsx}" matches: - '\bextends\s+PostgresCodecDescriptor\b' + - '\bAnyPostgresCodecDescriptor\b' - '\bpostgresCodec\s*\(' - - '\bpostgresRenderCheckExpressions\s*\(' --- # Value objects are storage entities @@ -117,7 +117,7 @@ const reference: ValueObjectReference = { }; ``` -## `postgres-codec-value-object-json-type` +## `postgres-codec-json-type` ```ts // before @@ -128,10 +128,10 @@ export class CitextDescriptor extends PostgresCodecDescriptor { // after export class CitextDescriptor extends PostgresCodecDescriptor { - override readonly valueObjectJsonType = 'string' as const; + override readonly jsonType = 'string' as const; override readonly codecId = 'citext/text@1'; // ... } ``` -Choose `'presenceOnly'` for a codec whose value the SQL ORM does not write as one JSON string, number or boolean, such as a vector or a geometry. A call of `postgresRenderCheckExpressions(input)` becomes `postgresRenderCheckExpressions(input, codecLookup)`, where `codecLookup` is the stack's codec lookup. +Declare the type the codec's value is written as when the application value is turned into JSON: a vector is `'array'`, a geometry and bytes are `'object'`, and a JSON codec is `'any'`. A template passed to `postgresCodec(template, options)` declares `jsonType` itself, as the `sql/*` family codecs do. From 8d8fddd13209d43476e05eda7f1b290af603f9fb Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 20:03:29 +0200 Subject: [PATCH 091/104] TML-3537: functions that read only storage take Pick, and ContractWithDomain stays on functions that read the domain Signed-off-by: willbot Signed-off-by: Will Madden --- .../0-foundation/contract/src/field-problems.ts | 4 ++-- .../contract/src/storage-value-objects.ts | 10 ++++++---- .../src/validate-storage-value-objects.ts | 6 +++--- .../framework-components/src/ir/storage.ts | 12 ++++++------ .../control-api/statements/resolve-statements.ts | 15 +++++++++++---- .../emitter/src/domain-type-generation.ts | 3 ++- .../mongo-contract/src/derive-json-schema.ts | 4 ++-- .../mongo-contract/src/recursive-groups.ts | 4 ++-- .../test/collection-validator-helpers.ts | 4 ++-- .../5-query-builders/orm/src/compile.ts | 6 +++--- .../query-builder/src/result-shape.ts | 14 +++++++------- .../1-core/contract/src/check-expressions.ts | 16 ++++++++-------- .../contract-ts/src/build-contract.ts | 4 ++-- .../2-authoring/contract-ts/src/lower-column.ts | 4 ++-- 14 files changed, 58 insertions(+), 48 deletions(-) diff --git a/packages/1-framework/0-foundation/contract/src/field-problems.ts b/packages/1-framework/0-foundation/contract/src/field-problems.ts index a2f07c264766..3e5b267fd7f4 100644 --- a/packages/1-framework/0-foundation/contract/src/field-problems.ts +++ b/packages/1-framework/0-foundation/contract/src/field-problems.ts @@ -1,6 +1,6 @@ import { blindCast } from '@internal/utils/casts'; -import type { ContractWithDomain } from './domain-envelope'; import { valueObjectAt } from './storage-value-objects'; +import type { StorageBase } from './types'; interface FieldTypeLike { readonly kind?: string; @@ -36,7 +36,7 @@ function valueObjectReferenceProblem(type: FieldTypeLike): string | undefined { /** What is wrong with the value object references of a field's type, a union's members included, each prefixed with `location`. */ export function valueObjectTypeProblems( - storage: ContractWithDomain['storage'], + storage: Pick, type: FieldTypeLike | undefined, location: string, ): readonly string[] { diff --git a/packages/1-framework/0-foundation/contract/src/storage-value-objects.ts b/packages/1-framework/0-foundation/contract/src/storage-value-objects.ts index 8ec58a2d6c7e..59431f40ff86 100644 --- a/packages/1-framework/0-foundation/contract/src/storage-value-objects.ts +++ b/packages/1-framework/0-foundation/contract/src/storage-value-objects.ts @@ -1,11 +1,11 @@ import { blindCast } from '@internal/utils/casts'; -import type { ContractWithDomain } from './domain-envelope'; import type { ContractValueObject } from './domain-types'; import type { EntityCoordinate } from './entity-coordinate'; import { isPlainRecord } from './is-plain-record'; +import type { StorageBase } from './types'; function valueObjectsOf( - storage: ContractWithDomain['storage'], + storage: Pick, namespaceId: string, ): Readonly> { const slot = Object.hasOwn(storage.namespaces, namespaceId) @@ -21,7 +21,7 @@ function valueObjectsOf( /** The storage value object a reference names, or `undefined` when storage does not have it. */ export function valueObjectAt( - storage: ContractWithDomain['storage'], + storage: Pick, reference: EntityCoordinate<'storage', 'valueObject'>, ): ContractValueObject | undefined { const valueObjects = valueObjectsOf(storage, reference.namespaceId); @@ -31,7 +31,9 @@ export function valueObjectAt( } /** Each value object in storage, in every namespace, with its coordinate. */ -export function valueObjectsWithCoordinates(storage: ContractWithDomain['storage']): ReadonlyArray<{ +export function valueObjectsWithCoordinates( + storage: Pick, +): ReadonlyArray<{ readonly coordinate: EntityCoordinate<'storage', 'valueObject'>; readonly valueObject: ContractValueObject; }> { diff --git a/packages/1-framework/0-foundation/contract/src/validate-storage-value-objects.ts b/packages/1-framework/0-foundation/contract/src/validate-storage-value-objects.ts index 412309118292..d09f9f10e445 100644 --- a/packages/1-framework/0-foundation/contract/src/validate-storage-value-objects.ts +++ b/packages/1-framework/0-foundation/contract/src/validate-storage-value-objects.ts @@ -1,5 +1,4 @@ import { ContractValidationError } from './contract-validation-error'; -import type { ContractWithDomain } from './domain-envelope'; import { type FieldLike, fieldLike, @@ -8,11 +7,12 @@ import { } from './field-problems'; import { isPlainRecord } from './is-plain-record'; import { valueObjectsWithCoordinates } from './storage-value-objects'; +import type { StorageBase } from './types'; /** * Checks the fields of the value objects in storage (ADR 309): each value object a field refers to is in storage, each value set a member names is in storage, and each field's `many` and `dict` are well formed. Both families run it beside `validateContractDomain`, which checks the references from the domain to storage. */ -export function validateStorageValueObjects(storage: ContractWithDomain['storage']): void { +export function validateStorageValueObjects(storage: Pick): void { const errors = valueObjectsWithCoordinates(storage).flatMap( ({ coordinate: { namespaceId, entityName }, valueObject }) => Object.entries(valueObject.fields).flatMap(([fieldName, field]) => { @@ -34,7 +34,7 @@ export function validateStorageValueObjects(storage: ContractWithDomain['storage } function missingValueSetProblem( - storage: ContractWithDomain['storage'], + storage: Pick, valueSet: FieldLike['valueSet'], ): string | undefined { if (valueSet === undefined) return undefined; diff --git a/packages/1-framework/1-core/framework-components/src/ir/storage.ts b/packages/1-framework/1-core/framework-components/src/ir/storage.ts index 50f771d7b926..1d17f8fb4e80 100644 --- a/packages/1-framework/1-core/framework-components/src/ir/storage.ts +++ b/packages/1-framework/1-core/framework-components/src/ir/storage.ts @@ -1,5 +1,5 @@ import { isPlainRecord } from '@internal/contract/is-plain-record'; -import type { ContractWithDomain, EntityCoordinate } from '@internal/contract/types'; +import type { EntityCoordinate, StorageBase } from '@internal/contract/types'; import { canonicalStringify } from '@internal/utils/canonical-stringify'; import { blindCast } from '@internal/utils/casts'; import type { IRNode } from './ir-node'; @@ -19,7 +19,7 @@ export { isPlainRecord }; * entity name in that map. No family-specific kind vocabulary is required. */ export function* elementCoordinates( - storage: ContractWithDomain['storage'], + storage: Pick, ): Generator { for (const [namespaceId, ns] of Object.entries(storage.namespaces)) { const entries = ns.entries; @@ -60,11 +60,11 @@ export function coordinateKey( * entity kind and entity name. With no earlier storage, every entity counts as added. */ export function changedElementCoordinates( - from: ContractWithDomain['storage'] | null, - to: ContractWithDomain['storage'], + from: Pick | null, + to: Pick, ): EntityCoordinate[] { const contentAt = ( - storage: ContractWithDomain['storage'] | null, + storage: Pick | null, coordinate: EntityCoordinate, ): string | undefined => { if (storage === null) return undefined; @@ -97,7 +97,7 @@ export function changedElementCoordinates( * and cannot verify the entity's shape. */ export function entityAt( - storage: ContractWithDomain['storage'], + storage: Pick, coord: Pick, ): T | undefined { const ns = storage.namespaces[coord.namespaceId]; diff --git a/packages/1-framework/3-tooling/cli/src/control-api/statements/resolve-statements.ts b/packages/1-framework/3-tooling/cli/src/control-api/statements/resolve-statements.ts index 3f53b47ac911..d5a8e500587e 100644 --- a/packages/1-framework/3-tooling/cli/src/control-api/statements/resolve-statements.ts +++ b/packages/1-framework/3-tooling/cli/src/control-api/statements/resolve-statements.ts @@ -7,6 +7,7 @@ import { type ContractFieldType, type ContractModelBase, type ContractWithDomain, + type StorageBase, valueObjectAt, } from '@internal/contract/types'; import type { CliStructuredError } from '@internal/errors/control'; @@ -191,9 +192,13 @@ function modelIn( return Object.hasOwn(namespace.models, name) ? namespace.models[name] : undefined; } -function hasValueObject(contract: ContractWithDomain, namespaceId: string, name: string): boolean { +function hasValueObject( + storage: Pick, + namespaceId: string, + name: string, +): boolean { return ( - valueObjectAt(contract.storage, { + valueObjectAt(storage, { plane: 'storage', namespaceId, entityKind: 'valueObject', @@ -247,7 +252,7 @@ function lookupModelInNamespace( model, }; } - if (hasValueObject(contract.contract, namespaceId, name)) { + if (hasValueObject(contract.contract.storage, namespaceId, name)) { return { kind: 'refused', ...valueObjectRefusal(displayName(namespaceId, name)) }; } return { @@ -303,7 +308,9 @@ function lookupModel( } const storageNamespaceIds = Object.keys(contract.contract.storage.namespaces); if ( - storageNamespaceIds.some((namespaceId) => hasValueObject(contract.contract, namespaceId, name)) + storageNamespaceIds.some((namespaceId) => + hasValueObject(contract.contract.storage, namespaceId, name), + ) ) { return { kind: 'refused', ...valueObjectRefusal(name) }; } diff --git a/packages/1-framework/3-tooling/emitter/src/domain-type-generation.ts b/packages/1-framework/3-tooling/emitter/src/domain-type-generation.ts index 3cf03dcb5444..fa46c107324a 100644 --- a/packages/1-framework/3-tooling/emitter/src/domain-type-generation.ts +++ b/packages/1-framework/3-tooling/emitter/src/domain-type-generation.ts @@ -8,6 +8,7 @@ import type { ContractWithDomain, CrossReference, JsonValue, + StorageBase, ValueObjectReference, } from '@internal/contract/types'; import { valueObjectsWithCoordinates } from '@internal/contract/types'; @@ -571,7 +572,7 @@ function resolveDomainValueSet( /** The values of the storage value set a value object field names, with the field's codec. */ function resolveValueObjectFieldValueSet( field: ContractValueObjectField, - storage: ContractWithDomain['storage'], + storage: Pick, ): ResolvedFieldValueSet | undefined { if (field.valueSet === undefined || field.type.kind !== 'scalar') return undefined; const valueSet = entityAt<{ readonly values: readonly JsonValue[] }>(storage, field.valueSet); diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/src/derive-json-schema.ts b/packages/2-mongo-family/1-foundation/mongo-contract/src/derive-json-schema.ts index 7da67036d24c..da52f404d9ce 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/src/derive-json-schema.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/src/derive-json-schema.ts @@ -4,8 +4,8 @@ import type { ContractModel, ContractRelation, ContractValueObject, - ContractWithDomain, JsonValue, + StorageBase, } from '@internal/contract/types'; import { valueObjectAt } from '@internal/contract/types'; import type { TypeLookups } from '@internal/framework-components/codec'; @@ -311,7 +311,7 @@ export interface ModelValidatorSources { readonly models: Record; readonly lookups: TypeLookups; /** The storage the value objects are read from, at the coordinate each field names. */ - readonly storage: ContractWithDomain['storage']; + readonly storage: Pick; readonly valueSets: FieldValueSets; } diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/src/recursive-groups.ts b/packages/2-mongo-family/1-foundation/mongo-contract/src/recursive-groups.ts index a208da47885b..6b30acc539bd 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/src/recursive-groups.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/src/recursive-groups.ts @@ -1,7 +1,7 @@ import { type ContractField, - type ContractWithDomain, type EntityCoordinate, + type StorageBase, valueObjectsWithCoordinates, } from '@internal/contract/types'; import type { ValidatorModel } from './derive-json-schema'; @@ -26,7 +26,7 @@ function referencesOf(fields: Record): string[] { * The recursive groups of value objects and embedded models: each set of types that can reach each other through value-object fields and embed relations (a strongly connected component of that graph), and each type that refers to itself. Maps the {@link valueObjectKey} or {@link modelKey} of every type in such a group to the group's number; a type in no recursive group is absent. */ export function recursiveGroupsOf( - storage: ContractWithDomain['storage'], + storage: Pick, models: Record>, ): ReadonlyMap { const edges = new Map(); diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/test/collection-validator-helpers.ts b/packages/2-mongo-family/1-foundation/mongo-contract/test/collection-validator-helpers.ts index 07cb19554e88..143d3ac2aba3 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/test/collection-validator-helpers.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/test/collection-validator-helpers.ts @@ -3,7 +3,7 @@ import type { ContractField, ContractModel, ContractValueObject, - ContractWithDomain, + StorageBase, } from '@internal/contract/types'; import { type CrossReference, crossRef } from '@internal/contract/types'; import type { MongoModelStorage } from '../src/contract-types'; @@ -56,7 +56,7 @@ export function embedMany(to: string): ContractEmbedRelation { /** Storage holding `valueObjects` in the unbound namespace, where the test helpers' value object fields point. */ export function storageWithValueObjects( valueObjects: Record, -): ContractWithDomain['storage'] { +): Pick { return { namespaces: { __unbound__: { id: '__unbound__', entries: { valueObject: valueObjects } } }, }; diff --git a/packages/2-mongo-family/5-query-builders/orm/src/compile.ts b/packages/2-mongo-family/5-query-builders/orm/src/compile.ts index d50cd1448f37..4509f8f71d6c 100644 --- a/packages/2-mongo-family/5-query-builders/orm/src/compile.ts +++ b/packages/2-mongo-family/5-query-builders/orm/src/compile.ts @@ -1,4 +1,4 @@ -import type { ContractModel, ContractWithDomain, PlanMeta } from '@internal/contract/types'; +import type { ContractModel, PlanMeta, StorageBase } from '@internal/contract/types'; import type { MongoModelStorage } from '@internal/mongo-contract'; import type { MongoFieldShape, @@ -29,7 +29,7 @@ function isToOne(include: MongoIncludeExpr): boolean { */ function includeShape( include: MongoIncludeExpr, - storage: ContractWithDomain['storage'], + storage: Pick, ): MongoFieldShape { const target = contractModelToMongoResultShape(include.targetModel, { storage }); const fields = target.kind === 'document' ? target.fields : {}; @@ -64,7 +64,7 @@ export function compileMongoQuery( state: MongoCollectionState, storageHash: string, model: ContractModel, - storage: ContractWithDomain['storage'], + storage: Pick, ): MongoQueryPlan { const stages: MongoPipelineStage[] = []; diff --git a/packages/2-mongo-family/5-query-builders/query-builder/src/result-shape.ts b/packages/2-mongo-family/5-query-builders/query-builder/src/result-shape.ts index 8a4ff7094005..304d2e188d5f 100644 --- a/packages/2-mongo-family/5-query-builders/query-builder/src/result-shape.ts +++ b/packages/2-mongo-family/5-query-builders/query-builder/src/result-shape.ts @@ -2,7 +2,7 @@ import { type ContractField, type ContractModel, type ContractValueObject, - type ContractWithDomain, + type StorageBase, valueObjectAt, } from '@internal/contract/types'; import { type MongoModelStorage, ownStoredNamesOf } from '@internal/mongo-contract'; @@ -11,11 +11,11 @@ import { freezeMongoFieldShape, freezeMongoResultShape } from '@internal/mongo-q const UNKNOWN: MongoFieldShape = Object.freeze({ kind: 'unknown' as const }); -const NO_STORAGE: ContractWithDomain['storage'] = { namespaces: {} }; +const NO_STORAGE: Pick = { namespaces: {} }; function valueObjectFields( valueObject: ContractValueObject, - storage: ContractWithDomain['storage'], + storage: Pick, enclosing: ReadonlySet, ): Record { return Object.fromEntries( @@ -28,7 +28,7 @@ function valueObjectFields( function elementShape( field: ContractField, - storage: ContractWithDomain['storage'], + storage: Pick, enclosing: ReadonlySet, ): MongoFieldShape { const { type } = field; @@ -48,7 +48,7 @@ function elementShape( function fieldShape( field: ContractField, - storage: ContractWithDomain['storage'], + storage: Pick, enclosing: ReadonlySet, ): MongoFieldShape { if (field.dict === true) return UNKNOWN; @@ -65,7 +65,7 @@ function fieldShape( */ export function contractFieldToMongoFieldShape( field: ContractField, - storage: ContractWithDomain['storage'] = NO_STORAGE, + storage: Pick = NO_STORAGE, ): MongoFieldShape { return freezeMongoFieldShape(fieldShape(field, storage, new Set())); } @@ -79,7 +79,7 @@ export function contractModelToMongoResultShape( readonly selection?: readonly string[]; /** The shape of each included relation, keyed by relation name. */ readonly includes?: Readonly>; - readonly storage?: ContractWithDomain['storage']; + readonly storage?: Pick; }, ): MongoResultShape { const fields: Record = { ...options?.includes }; diff --git a/packages/2-sql-family/1-core/contract/src/check-expressions.ts b/packages/2-sql-family/1-core/contract/src/check-expressions.ts index 59858adf6412..073d55ef9915 100644 --- a/packages/2-sql-family/1-core/contract/src/check-expressions.ts +++ b/packages/2-sql-family/1-core/contract/src/check-expressions.ts @@ -1,8 +1,8 @@ import type { ContractValueObject, ContractValueObjectField, - ContractWithDomain, EntityCoordinate, + StorageBase, } from '@internal/contract/types'; import { valueObjectAt } from '@internal/contract/types'; import type { CodecLookupWithDescriptors } from '@internal/framework-components/codec'; @@ -70,7 +70,7 @@ export type CheckExpressionRenderer = ( * What the check of a column that holds a value object reads. The contract build and `contract print` both derive it here, so print recognises exactly the check the build writes. */ export function resolveColumnValueObject( - storage: ContractWithDomain['storage'], + storage: Pick, column: { readonly reference: EntityCoordinate<'storage', 'valueObject'>; readonly nullable: boolean; @@ -100,7 +100,7 @@ export function discriminatorValueSet( * The value object a reference names, with nested value objects inlined, enum members' values read from their storage value sets, and each scalar member's JSON type read from its codec. */ export function resolveValueObject( - storage: ContractWithDomain['storage'], + storage: Pick, reference: EntityCoordinate<'storage', 'valueObject'>, codecs: CodecDescriptors, ): ResolvedValueObject { @@ -108,7 +108,7 @@ export function resolveValueObject( } function resolveEntered( - storage: ContractWithDomain['storage'], + storage: Pick, reference: EntityCoordinate<'storage', 'valueObject'>, path: readonly string[], codecs: CodecDescriptors, @@ -135,7 +135,7 @@ interface ValueObjectMember { } function resolveFieldType( - storage: ContractWithDomain['storage'], + storage: Pick, member: ValueObjectMember, path: readonly string[], codecs: CodecDescriptors, @@ -189,7 +189,7 @@ function memberJsonType( * Whether `from` refers, through its own fields or nested ones, to a value object on the path being resolved. Each value object on the path refers to `from`, so this holds exactly when `from` is in a group of value objects that contain each other which the path has already entered. */ function reachesAny( - storage: ContractWithDomain['storage'], + storage: Pick, from: EntityCoordinate<'storage', 'valueObject'>, targets: ReadonlySet, ): boolean { @@ -208,7 +208,7 @@ function reachesAny( } function requiredValueObject( - storage: ContractWithDomain['storage'], + storage: Pick, reference: EntityCoordinate<'storage', 'valueObject'>, ): ContractValueObject { const valueObject = valueObjectAt(storage, reference); @@ -220,7 +220,7 @@ function requiredValueObject( } function valueSetValues( - storage: ContractWithDomain['storage'], + storage: Pick, reference: EntityCoordinate<'storage', 'valueSet'>, ): readonly (string | number)[] { const valueSet = Object.hasOwn(storage.namespaces, reference.namespaceId) diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts b/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts index 847e83a35971..4b2a0ffd71d2 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/build-contract.ts @@ -8,12 +8,12 @@ import { type ContractEnum, type ContractModel, type ContractValueObject, - type ContractWithDomain, type CrossReference, coreHash, crossRef, type ExecutionMutationDefault, effectiveControlPolicy, + type StorageBase, type StorageHashBase, } from '@internal/contract/types'; import { encodeEnumTypeMembers } from '@internal/contract-authoring'; @@ -188,7 +188,7 @@ function storageOf( defaultNamespaceId: string, valueObjects: Record | undefined, storageValueSetsByNs: Readonly>>, -): ContractWithDomain['storage'] { +): Pick { const namespaceIds = new Set([defaultNamespaceId, ...Object.keys(storageValueSetsByNs)]); return { namespaces: Object.fromEntries( diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/lower-column.ts b/packages/2-sql-family/2-authoring/contract-ts/src/lower-column.ts index 058139108d8e..21c0616e74ef 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/lower-column.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/lower-column.ts @@ -1,4 +1,4 @@ -import type { ContractWithDomain } from '@internal/contract/types'; +import type { StorageBase } from '@internal/contract/types'; import type { CodecLookupWithDescriptors, TypeLookups } from '@internal/framework-components/codec'; import { dataTypeParams } from '@internal/framework-components/codec'; import { @@ -79,7 +79,7 @@ export interface ColumnLoweringContext { readonly qualifyColumnType: ColumnTypeQualifier | undefined; readonly renderCheckExpressions: CheckExpressionRenderer | undefined; /** The value objects and value sets storage will hold, which a value object column's check is derived from. */ - readonly storage: ContractWithDomain['storage']; + readonly storage: Pick; /** Receives the pack entity a column's type constructor refers to. */ readonly collectedColumnEntities: CollectedColumnEntities; } From ba6d0ea6e956b8fe03be4e9c5d1459cf01be4ccf Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 20:08:33 +0200 Subject: [PATCH 092/104] TML-3537: one framework schema validates a field's type for domain fields and value object members in both families; ContractValueObjectSchema is named after its type; SQL's value object schema leaves out unions and dictionaries by its structure, and its refusal names the value object and the member Signed-off-by: willbot Signed-off-by: Will Madden --- ...ema.ts => contract-value-object-schema.ts} | 18 +++-- .../contract/src/exports/types.ts | 6 +- ...s => contract-value-object-schema.test.ts} | 29 ++++++-- .../mongo-contract/src/contract-schema.ts | 33 ++------- .../mongo-contract/src/entity-kinds.ts | 4 +- .../contract/src/ir/storage-entry-schemas.ts | 72 +++---------------- .../1-core/contract/src/validators.ts | 46 +++++++++--- .../test/storage-value-object.test.ts | 30 +++++++- .../test/data-contract-json-schema.test.ts | 30 +++++++- 9 files changed, 154 insertions(+), 114 deletions(-) rename packages/1-framework/0-foundation/contract/src/{storage-value-object-schema.ts => contract-value-object-schema.ts} (52%) rename packages/1-framework/0-foundation/contract/test/{storage-value-object-schema.test.ts => contract-value-object-schema.test.ts} (64%) diff --git a/packages/1-framework/0-foundation/contract/src/storage-value-object-schema.ts b/packages/1-framework/0-foundation/contract/src/contract-value-object-schema.ts similarity index 52% rename from packages/1-framework/0-foundation/contract/src/storage-value-object-schema.ts rename to packages/1-framework/0-foundation/contract/src/contract-value-object-schema.ts index 6a84ad67c2fa..8775cc2a60ca 100644 --- a/packages/1-framework/0-foundation/contract/src/storage-value-object-schema.ts +++ b/packages/1-framework/0-foundation/contract/src/contract-value-object-schema.ts @@ -22,6 +22,13 @@ const UnionFieldTypeSchema = type({ members: ScalarFieldTypeSchema.or(ValueObjectReferenceSchema).array(), }); +/** + * The type of a domain field or of a value object's member, `ContractFieldType`: a scalar with its codec, a value object by its storage coordinate, or a union of those. A union holds no union. + */ +export const ContractFieldTypeSchema = ScalarFieldTypeSchema.or(ValueObjectReferenceSchema).or( + UnionFieldTypeSchema, +); + const StorageValueSetReferenceSchema = type({ '+': 'reject', plane: "'storage'", @@ -31,19 +38,20 @@ const StorageValueSetReferenceSchema = type({ 'spaceId?': 'string', }); -const StorageValueObjectFieldSchema = type({ +/** A value object's member, `ContractValueObjectField`. */ +export const ContractValueObjectFieldSchema = type({ '+': 'reject', nullable: 'boolean', - type: ScalarFieldTypeSchema.or(ValueObjectReferenceSchema).or(UnionFieldTypeSchema), + type: ContractFieldTypeSchema, 'many?': type('false').or({ '+': 'reject', elementNullable: 'boolean' }), 'dict?': 'true', 'valueSet?': StorageValueSetReferenceSchema, }); /** - * A value object, the storage entity under `storage.namespaces..entries.valueObject.` (ADR 309). The framework defines this entity kind, since domain fields refer to it; both families validate their entries against this schema. + * A value object, `ContractValueObject`: the storage entity under `storage.namespaces..entries.valueObject.` (ADR 309). The framework defines this entity kind, since domain fields refer to it; both families validate their entries against this schema. */ -export const StorageValueObjectSchema = type({ +export const ContractValueObjectSchema = type({ '+': 'reject', - fields: type({ '[string]': StorageValueObjectFieldSchema }), + fields: type({ '[string]': ContractValueObjectFieldSchema }), }); diff --git a/packages/1-framework/0-foundation/contract/src/exports/types.ts b/packages/1-framework/0-foundation/contract/src/exports/types.ts index 18311ac9f4e6..be3c36d95e14 100644 --- a/packages/1-framework/0-foundation/contract/src/exports/types.ts +++ b/packages/1-framework/0-foundation/contract/src/exports/types.ts @@ -4,6 +4,11 @@ export type { ContractValueObjectAt, ContractValueObjectDefinitions, } from '../contract-types'; +export { + ContractFieldTypeSchema, + ContractValueObjectFieldSchema, + ContractValueObjectSchema, +} from '../contract-value-object-schema'; export type { ControlPolicy } from '../control-policy'; export { effectiveControlPolicy, governingControlPolicy } from '../control-policy'; export type { CrossReference } from '../cross-reference'; @@ -58,7 +63,6 @@ export type { ParameterizedOutputTypesFunction, } from '../parameterized-output-types'; export { type ResolvedDomainModel, resolveDomainModel } from '../resolve-domain-model'; -export { StorageValueObjectSchema } from '../storage-value-object-schema'; export { valueObjectAt, valueObjectsWithCoordinates } from '../storage-value-objects'; export type { $, diff --git a/packages/1-framework/0-foundation/contract/test/storage-value-object-schema.test.ts b/packages/1-framework/0-foundation/contract/test/contract-value-object-schema.test.ts similarity index 64% rename from packages/1-framework/0-foundation/contract/test/storage-value-object-schema.test.ts rename to packages/1-framework/0-foundation/contract/test/contract-value-object-schema.test.ts index ce8fc9c58729..dcce99c82e4c 100644 --- a/packages/1-framework/0-foundation/contract/test/storage-value-object-schema.test.ts +++ b/packages/1-framework/0-foundation/contract/test/contract-value-object-schema.test.ts @@ -1,6 +1,9 @@ import { type } from 'arktype'; import { describe, expect, it } from 'vitest'; -import { StorageValueObjectSchema } from '../src/storage-value-object-schema'; +import { + ContractFieldTypeSchema, + ContractValueObjectSchema, +} from '../src/contract-value-object-schema'; const text = { type: { kind: 'scalar', codecId: 'pg/text@1' }, nullable: false }; const addressReference = { @@ -11,7 +14,7 @@ const addressReference = { entityName: 'Address', }; -describe('StorageValueObjectSchema', () => { +describe('ContractValueObjectSchema', () => { it('accepts scalar, value object and union members, lists, dictionaries and value sets', () => { const valueObject = { fields: { @@ -33,7 +36,7 @@ describe('StorageValueObjectSchema', () => { }, }, }; - expect(StorageValueObjectSchema(valueObject)).toEqual(valueObject); + expect(ContractValueObjectSchema(valueObject)).toEqual(valueObject); }); it.each([ @@ -59,6 +62,24 @@ describe('StorageValueObjectSchema', () => { }, ], ])('refuses %s', (_case, fields) => { - expect(StorageValueObjectSchema({ fields })).toBeInstanceOf(type.errors); + expect(ContractValueObjectSchema({ fields })).toBeInstanceOf(type.errors); + }); +}); + +describe('ContractFieldTypeSchema', () => { + it('accepts a scalar, a value object reference and a union of those', () => { + const union = { kind: 'union', members: [text.type, addressReference] }; + expect( + [text.type, addressReference, union].map((fieldType) => ContractFieldTypeSchema(fieldType)), + ).toEqual([text.type, addressReference, union]); + }); + + it('refuses a union inside a union', () => { + expect( + ContractFieldTypeSchema({ + kind: 'union', + members: [text.type, { kind: 'union', members: [text.type] }], + }), + ).toBeInstanceOf(type.errors); }); }); diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/src/contract-schema.ts b/packages/2-mongo-family/1-foundation/mongo-contract/src/contract-schema.ts index 48e03e485b41..cfae8ad555e2 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/src/contract-schema.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/src/contract-schema.ts @@ -1,39 +1,14 @@ import { ContractExecutionSectionSchema, + ContractFieldTypeSchema, + ContractValueObjectSchema, CrossReferenceSchema, - StorageValueObjectSchema, } from '@internal/contract/types'; import { type Type, type } from 'arktype'; import type { MongoJsonObject, MongoJsonPrimitive, MongoJsonValue } from './contract-types'; const ControlPolicySchema = type("'managed' | 'tolerated' | 'external' | 'observed'"); -const ScalarFieldTypeSchema = type({ - '+': 'reject', - kind: "'scalar'", - codecId: 'string', - 'typeParams?': 'Record', -}); - -const ValueObjectReferenceSchema = type({ - '+': 'reject', - kind: "'valueObject'", - plane: "'storage'", - namespaceId: 'string', - entityKind: "'valueObject'", - entityName: 'string', -}); - -const UnionFieldTypeSchema = type({ - '+': 'reject', - kind: "'union'", - members: ScalarFieldTypeSchema.or(ValueObjectReferenceSchema).array(), -}); - -const FieldTypeSchema = ScalarFieldTypeSchema.or(ValueObjectReferenceSchema).or( - UnionFieldTypeSchema, -); - const DomainEnumRefSchema = type({ plane: "'domain'", namespaceId: 'string', @@ -56,7 +31,7 @@ const ContractEnumSchema = type({ const RawFieldSchema = type({ '+': 'reject', - type: FieldTypeSchema, + type: ContractFieldTypeSchema, 'nullable?': 'boolean', 'many?': type('false').or({ elementNullable: 'boolean' }), 'dict?': 'boolean', @@ -425,7 +400,7 @@ export function createMongoNamespaceEnvelopeSchema( entries: type({ 'collection?': type({ '[string]': collectionEntrySchema(fragments) }), 'valueSet?': type({ '[string]': StorageValueSetSchema }), - 'valueObject?': type({ '[string]': StorageValueObjectSchema }), + 'valueObject?': type({ '[string]': ContractValueObjectSchema }), }), }).narrow((ns, ctx) => { if (typeof ns !== 'object' || ns === null || Array.isArray(ns)) { diff --git a/packages/2-mongo-family/1-foundation/mongo-contract/src/entity-kinds.ts b/packages/2-mongo-family/1-foundation/mongo-contract/src/entity-kinds.ts index 25271a8b7552..88b2abb94b3e 100644 --- a/packages/2-mongo-family/1-foundation/mongo-contract/src/entity-kinds.ts +++ b/packages/2-mongo-family/1-foundation/mongo-contract/src/entity-kinds.ts @@ -1,4 +1,4 @@ -import { StorageValueObjectSchema } from '@internal/contract/types'; +import { ContractValueObjectSchema } from '@internal/contract/types'; import type { AnyEntityKindDescriptor, EntityKindDescriptor, @@ -24,7 +24,7 @@ export const valueSetEntityKind: EntityKindDescriptor = { kind: 'valueObject', - schema: StorageValueObjectSchema, + schema: ContractValueObjectSchema, construct: (input) => new MongoValueObject(input), }; diff --git a/packages/2-sql-family/1-core/contract/src/ir/storage-entry-schemas.ts b/packages/2-sql-family/1-core/contract/src/ir/storage-entry-schemas.ts index c622272913ea..aad349696c7b 100644 --- a/packages/2-sql-family/1-core/contract/src/ir/storage-entry-schemas.ts +++ b/packages/2-sql-family/1-core/contract/src/ir/storage-entry-schemas.ts @@ -1,4 +1,4 @@ -import { StorageValueObjectSchema } from '@internal/contract/types'; +import { ContractFieldTypeSchema, ContractValueObjectFieldSchema } from '@internal/contract/types'; import { DATA_TYPE_ID_PATTERN } from '@internal/framework-components/codec'; import { type Type, type } from 'arktype'; import type { ForeignKeyIndex, ForeignKeyInput, ReferentialAction } from './foreign-key'; @@ -36,70 +36,16 @@ const StorageValueSetRefSchema = type({ 'spaceId?': 'string', }); -const VALUE_OBJECT_REFERENCE_KEYS = new Set([ - 'kind', - 'plane', - 'namespaceId', - 'entityKind', - 'entityName', -]); - -function isRecord(value: unknown): value is Record { - return typeof value === 'object' && value !== null && !Array.isArray(value); -} - -function isValueObjectReference(value: Record): boolean { - return ( - value['plane'] === 'storage' && - value['entityKind'] === 'valueObject' && - typeof value['namespaceId'] === 'string' && - typeof value['entityName'] === 'string' && - Object.keys(value).every((key) => VALUE_OBJECT_REFERENCE_KEYS.has(key)) - ); -} - -/** A field's type: a scalar with its codec, a value object by its storage coordinate, or a union of those. */ -export function isContractFieldType(value: unknown): boolean { - if (!isRecord(value)) return false; - const kind = value['kind']; - if (kind === 'scalar') { - if (typeof value['codecId'] !== 'string') return false; - const typeParams = value['typeParams']; - if (typeParams !== undefined && !isRecord(typeParams)) return false; - return true; - } - if (kind === 'valueObject') { - return isValueObjectReference(value); - } - if (kind === 'union') { - const members = value['members']; - if (!Array.isArray(members)) return false; - return members.every((m) => isContractFieldType(m)); - } - return false; -} - -export const ContractFieldTypeSchema = type('unknown').narrow((value, ctx) => - isContractFieldType(value) - ? true - : ctx.mustBe( - 'scalar, valueObject (by its storage coordinate: plane, namespaceId, entityKind, entityName), or union field type', - ), -); - /** - * The framework's value object schema, narrowed to what SQL stores: no member is a union or a dictionary, since the derived check cannot enforce them. + * The framework's value object schema, without what SQL does not store: no member is a union or a dictionary, since the derived check cannot enforce them. */ -export const SqlStorageValueObjectSchema = StorageValueObjectSchema.narrow((valueObject, ctx) => { - for (const [name, member] of Object.entries(valueObject.fields)) { - const kind = member.type.kind === 'union' ? 'union' : member.dict ? 'dictionary' : undefined; - if (kind !== undefined) { - return ctx.reject({ - message: `value object member "${name}" is a ${kind}; SQL value objects hold no unions or dictionaries, since their check cannot enforce them`, - }); - } - } - return true; +export const SqlStorageValueObjectSchema = type({ + '+': 'reject', + fields: type({ + '[string]': ContractValueObjectFieldSchema.omit('dict') + .merge({ type: ContractFieldTypeSchema.exclude({ kind: "'union'" }) }) + .onUndeclaredKey('reject'), + }), }); const StorageColumnSchema = type({ diff --git a/packages/2-sql-family/1-core/contract/src/validators.ts b/packages/2-sql-family/1-core/contract/src/validators.ts index e893f00151b9..087f7187268a 100644 --- a/packages/2-sql-family/1-core/contract/src/validators.ts +++ b/packages/2-sql-family/1-core/contract/src/validators.ts @@ -2,6 +2,7 @@ import { ContractValidationError } from '@internal/contract/contract-validation- import { type Contract, ContractExecutionSectionSchema, + ContractFieldTypeSchema, type ContractModel, CrossReferenceSchema, } from '@internal/contract/types'; @@ -28,7 +29,6 @@ import { type Type, type } from 'arktype'; import { contractError } from './contract-errors'; import { composeSqlEntityKinds } from './entity-kinds'; import { sameColumns, startsWithColumns } from './index-equivalence'; -import { ContractFieldTypeSchema } from './ir/storage-entry-schemas'; import { resolveSqlToOneRelationStorage } from './relation-storage'; import { sameSqlColumnType } from './sql-data-type'; @@ -290,6 +290,36 @@ function removedKeyProblems(storage: unknown): readonly string[] { return []; } +/** + * A SQL value object member that is a union or a dictionary, named by its value object and member. The storage schema refuses it too, but at the path of its namespace, so this names it first. + */ +function unenforceableValueObjectMemberProblems(storage: unknown): readonly string[] { + return entriesOf(fieldOf(storage, 'namespaces')).flatMap(([namespaceId, namespace]) => + entriesOf(fieldOf(fieldOf(namespace, 'entries'), 'valueObject')).flatMap( + ([valueObjectName, valueObject]) => + entriesOf(fieldOf(valueObject, 'fields')).flatMap(([memberName, member]) => { + const kind = + fieldOf(fieldOf(member, 'type'), 'kind') === 'union' + ? 'union' + : fieldOf(member, 'dict') === true + ? 'dictionary' + : undefined; + return kind === undefined + ? [] + : [ + `value object "${namespaceId}:${valueObjectName}" member "${memberName}" is a ${kind}; SQL value objects hold no unions or dictionaries, since their check cannot enforce them`, + ]; + }), + ), + ); +} + +/** The problems a stored contract has before its structure is validated: removed keys first, then value object members SQL cannot enforce. */ +function preStructuralStorageProblems(storage: unknown): readonly string[] { + const removedKeys = removedKeyProblems(storage); + return removedKeys.length > 0 ? removedKeys : unenforceableValueObjectMemberProblems(storage); +} + type NamespacedStorageWalk = { readonly namespaces: Readonly< Record< @@ -554,11 +584,11 @@ const SqlContractSchema = createSqlContractSchema(DEFAULT_SQL_KINDS); * @throws Error if the storage structure is invalid */ export function validateStorage(value: unknown): void { - const removedKeys = removedKeyProblems(value); + const preStructuralProblems = preStructuralStorageProblems(value); const result = StorageSchema(value); const errors = - removedKeys.length > 0 - ? [...removedKeys] + preStructuralProblems.length > 0 + ? [...preStructuralProblems] : result instanceof type.errors ? result.map((p: { message: string }) => p.message) : []; @@ -610,13 +640,13 @@ function validateSqlContractStructure>( throw new ContractValidationError(`Unsupported target family: ${targetFamily}`, 'structural'); } - const removedKeys = [ + const preStructuralProblems = [ ...removedDomainKeyProblems(value), - ...removedKeyProblems(fieldOf(value, 'storage')), + ...preStructuralStorageProblems(fieldOf(value, 'storage')), ]; - if (removedKeys.length > 0) { + if (preStructuralProblems.length > 0) { throw new ContractValidationError( - `Contract structural validation failed: ${removedKeys.join('; ')}`, + `Contract structural validation failed: ${preStructuralProblems.join('; ')}`, 'structural', ); } diff --git a/packages/2-sql-family/1-core/contract/test/storage-value-object.test.ts b/packages/2-sql-family/1-core/contract/test/storage-value-object.test.ts index 4413f4af9791..38d2bb146a57 100644 --- a/packages/2-sql-family/1-core/contract/test/storage-value-object.test.ts +++ b/packages/2-sql-family/1-core/contract/test/storage-value-object.test.ts @@ -119,6 +119,21 @@ describe('the storage valueObject slot schema', () => { ).not.toBeInstanceOf(type.errors); }); + it.each([ + [ + 'a union', + { + nullable: false, + type: { kind: 'union', members: [text.type, valueObjectType('Address')] }, + }, + ], + ['a dictionary', { ...text, dict: true }], + ])('refuses a member that is %s', (_case, member) => { + expect(validate({ Address: address, Shape: { fields: { either: member } } })).toBeInstanceOf( + type.errors, + ); + }); + it('refuses a member key it does not know', () => { expect(validate({ Address: { fields: { city: { ...text, column: 'city' } } } })).toBeInstanceOf( type.errors, @@ -180,6 +195,17 @@ describe('validateSqlContractFully with a value object in storage', () => { expect(() => validateSqlContractFully(contract)).not.toThrow(); }); + it('refuses a domain field whose type is a union inside a union, as the framework field type schema does', () => { + expect(() => + validateSqlContractFully( + contractWith({ + kind: 'union', + members: [text.type, { kind: 'union', members: [text.type] }], + }), + ), + ).toThrow(/Contract structural validation failed/); + }); + it('refuses a field type that names its value object the old way', () => { expect(() => validateSqlContractFully(contractWith({ kind: 'valueObject', name: 'Address' })), @@ -202,7 +228,9 @@ describe('validateSqlContractFully with a value object in storage', () => { Shape: { fields: { either: member } }, }); expect(() => validateSqlContractFully(contract)).toThrow( - `value object member "either" is a ${kind}; SQL value objects hold no unions or dictionaries`, + expect.objectContaining({ + message: `Contract structural validation failed: value object "__unbound__:Shape" member "either" is a ${kind}; SQL value objects hold no unions or dictionaries, since their check cannot enforce them`, + }), ); }); diff --git a/packages/2-sql-family/2-authoring/contract-ts/test/data-contract-json-schema.test.ts b/packages/2-sql-family/2-authoring/contract-ts/test/data-contract-json-schema.test.ts index bab6dae75e4f..cb0ba0454ce3 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/test/data-contract-json-schema.test.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/test/data-contract-json-schema.test.ts @@ -6,6 +6,8 @@ import { generateDataContractJsonSchema } from '../src/data-contract-json-schema import { validSqlContractJson } from './sql-contract-json-fixture'; import { storageWithNamespacedTables } from './storage-with-namespaced-tables'; +const text = { kind: 'scalar', codecId: 'pg/text@1' }; + const checkedInSchemaPath = join(import.meta.dirname, '..', 'schemas', 'data-contract-sql-v1.json'); function compileGeneratedSchema() { @@ -58,7 +60,7 @@ describe('data contract JSON schema', () => { models: { User: { storage: { namespaceId: '__unbound__', table: 'User', fields: {} }, - fields: { tags: { type: 'String', nullable: false, many } }, + fields: { tags: { type: text, nullable: false, many } }, }, }, } @@ -171,6 +173,32 @@ describe('data contract JSON schema', () => { expect(validate(contract)).toBe(false); }); + it.each([ + { kind: 'a scalar', member: { nullable: false, type: text }, valid: true }, + { + kind: 'a union', + member: { nullable: false, type: { kind: 'union', members: [text] } }, + valid: false, + }, + { kind: 'a dictionary', member: { nullable: false, type: text, dict: true }, valid: false }, + ])('validates a storage value object member that is $kind as $valid', ({ member, valid }) => { + const validate = new Ajv2020({ strict: false }).compile( + JSON.parse(readFileSync(checkedInSchemaPath, 'utf8')), + ); + const contract = validSqlContractJson({ + storage: { + storageHash: 'test', + namespaces: { + __unbound__: { + id: '__unbound__', + entries: { table: {}, valueObject: { Shape: { fields: { either: member } } } }, + }, + }, + }, + }); + expect(validate(contract)).toBe(valid); + }); + it('accepts pack-contributed entry kinds as generic entity maps', () => { const validate = compileGeneratedSchema(); const contract = validSqlContractJson({ From b7c1bf119099da0c4aebe25e5cce360faf6744ea Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 20:08:39 +0200 Subject: [PATCH 093/104] TML-3537: regenerate the SQL contract JSON schema, which now refuses union and dictionary value object members and checks a domain field's type Signed-off-by: willbot Signed-off-by: Will Madden --- .../schemas/data-contract-sql-v1.json | 177 +++++++++++------- 1 file changed, 111 insertions(+), 66 deletions(-) diff --git a/packages/2-sql-family/2-authoring/contract-ts/schemas/data-contract-sql-v1.json b/packages/2-sql-family/2-authoring/contract-ts/schemas/data-contract-sql-v1.json index e62e1866b988..0309a4adadd8 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/schemas/data-contract-sql-v1.json +++ b/packages/2-sql-family/2-authoring/contract-ts/schemas/data-contract-sql-v1.json @@ -81,7 +81,117 @@ "nullable": { "type": "boolean" }, - "type": {}, + "type": { + "anyOf": [ + { + "type": "object", + "properties": { + "codecId": { + "type": "string" + }, + "kind": { + "const": "scalar" + }, + "typeParams": { + "type": "object", + "additionalProperties": {} + } + }, + "required": ["codecId", "kind"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "entityKind": { + "const": "valueObject" + }, + "entityName": { + "type": "string" + }, + "kind": { + "const": "valueObject" + }, + "namespaceId": { + "type": "string" + }, + "plane": { + "const": "storage" + } + }, + "required": [ + "entityKind", + "entityName", + "kind", + "namespaceId", + "plane" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "kind": { + "const": "union" + }, + "members": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "codecId": { + "type": "string" + }, + "kind": { + "const": "scalar" + }, + "typeParams": { + "type": "object", + "additionalProperties": {} + } + }, + "required": ["codecId", "kind"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "entityKind": { + "const": "valueObject" + }, + "entityName": { + "type": "string" + }, + "kind": { + "const": "valueObject" + }, + "namespaceId": { + "type": "string" + }, + "plane": { + "const": "storage" + } + }, + "required": [ + "entityKind", + "entityName", + "kind", + "namespaceId", + "plane" + ], + "additionalProperties": false + } + ] + } + } + }, + "required": ["kind", "members"], + "additionalProperties": false + } + ] + }, "many": { "anyOf": [ { @@ -808,74 +918,9 @@ "plane" ], "additionalProperties": false - }, - { - "type": "object", - "properties": { - "kind": { - "const": "union" - }, - "members": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "object", - "properties": { - "codecId": { - "type": "string" - }, - "kind": { - "const": "scalar" - }, - "typeParams": { - "type": "object", - "additionalProperties": {} - } - }, - "required": ["codecId", "kind"], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "entityKind": { - "const": "valueObject" - }, - "entityName": { - "type": "string" - }, - "kind": { - "const": "valueObject" - }, - "namespaceId": { - "type": "string" - }, - "plane": { - "const": "storage" - } - }, - "required": [ - "entityKind", - "entityName", - "kind", - "namespaceId", - "plane" - ], - "additionalProperties": false - } - ] - } - } - }, - "required": ["kind", "members"], - "additionalProperties": false } ] }, - "dict": { - "const": true - }, "many": { "anyOf": [ { From 63d57883a55ee139df3c6f36d1f8aebdf2651f21 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 20:09:19 +0200 Subject: [PATCH 094/104] TML-3537: migration plan --json lists the changed storage entities as changedStorageEntities, the words its README and summary use Signed-off-by: willbot Signed-off-by: Will Madden --- .../subsystems/7. Migration System.md | 2 +- packages/1-framework/3-tooling/cli/README.md | 2 +- .../control-api/operations/migration-plan.ts | 22 +++++++++---------- .../orm/migration-plan.no-operations.test.ts | 8 +++---- .../plans-with-no-operations.e2e.test.ts | 4 ++-- ...plans-with-no-operations.mongo.e2e.test.ts | 4 ++-- ...lans-with-no-operations.sqlite.e2e.test.ts | 6 ++--- 7 files changed, 24 insertions(+), 24 deletions(-) diff --git a/docs/architecture docs/subsystems/7. Migration System.md b/docs/architecture docs/subsystems/7. Migration System.md index c69f23354fcb..71900d21cbb9 100644 --- a/docs/architecture docs/subsystems/7. Migration System.md +++ b/docs/architecture docs/subsystems/7. Migration System.md @@ -172,7 +172,7 @@ The from-contract always materialises by reading the content-addressed snapshot When `--to` is omitted, `to_contract` is the emitted contract; when `--to` is supplied, `to_contract` is the resolved destination. -**A plan with no operations.** Two different storage hashes are two different database states, so a migration between them is expected to do something. When the planner produces no operations for a changed storage hash anyway, `migration plan` still writes the package, with an empty `ops.json`, and says so: the summary, a warning in the human output, names each storage entity whose content differs between the two contracts, and `--json` lists them as `changedElementCoordinates` (entity coordinates, from `changedElementCoordinates` in `@internal/framework-components/ir`; a coordinate in the unbound namespace leaves out `namespaceId`, as `appliedStatements` does). The field is filled for every migration `migration plan` writes, and a migration written with no operations is one whose `operations` is empty. `noOp` means something else: nothing was written, because the storage hash did not change. `db migrate` and `db update` apply such a migration by moving the marker to the new hash; every runner already does this for an edge between two different hashes. This holds for a baseline with no operations and for the delta after it. Only a greenfield plan (no earlier contract) with no operations is refused, with `MIGRATION.PLANNING_FAILED` of kind `nothingToCreate`, since the contract describes nothing to create. When the storage hash does not change, nothing is written, as before. +**A plan with no operations.** Two different storage hashes are two different database states, so a migration between them is expected to do something. When the planner produces no operations for a changed storage hash anyway, `migration plan` still writes the package, with an empty `ops.json`, and says so: the summary, a warning in the human output, names each storage entity whose content differs between the two contracts, and `--json` lists them as `changedStorageEntities` (entity coordinates, from `changedElementCoordinates` in `@internal/framework-components/ir`; a coordinate in the unbound namespace leaves out `namespaceId`, as `appliedStatements` does). The field is filled for every migration `migration plan` writes, and a migration written with no operations is one whose `operations` is empty. `noOp` means something else: nothing was written, because the storage hash did not change. `db migrate` and `db update` apply such a migration by moving the marker to the new hash; every runner already does this for an edge between two different hashes. This holds for a baseline with no operations and for the delta after it. Only a greenfield plan (no earlier contract) with no operations is refused, with `MIGRATION.PLANNING_FAILED` of kind `nothingToCreate`, since the contract describes nothing to create. When the storage hash does not change, nothing is written, as before. An auto-baseline dates the delta package at the current time and the baseline package one minute earlier, so the baseline sorts first. Folder names have minute resolution, so packages planned within the same minute sort by name. diff --git a/packages/1-framework/3-tooling/cli/README.md b/packages/1-framework/3-tooling/cli/README.md index 0a542bc1db8d..8c6681bd72fd 100644 --- a/packages/1-framework/3-tooling/cli/README.md +++ b/packages/1-framework/3-tooling/cli/README.md @@ -1093,7 +1093,7 @@ prisma migration plan [--config ] [--name ] [--from ] [--t - `migrations/

/ops.json` — planned operations (empty list `[]` if placeholders blocked the planner) - `migrations/snapshots//contract.{json,d.ts}` — bookend contracts, written write-if-absent, keyed by each contract's storage hash (one entry for `from` when applicable, one for `to`) -**Rename statements:** each `--rename` names the old and the new name in contract vocabulary, never a table or a column. Each side is one of `namespace`, `Model`, `namespace.Model`, `Model.field`, `namespace.Model.field`, `Enum.value` or `namespace.Enum.value`, and both sides name the same kind of thing: a namespace, a model, a field of the same model, or a value of the same enum. Names match exactly, including case. A name without a namespace resolves when exactly one namespace declares the model. A field's model is named as the destination contract names it, so a model rename followed by a field rename on it is `--rename Profile:User --rename User.name:User.fullName`. The old name must exist in the starting contract and not in the destination; the new name must exist in the destination and not in the starting contract. Statements resolve before anything is written; a statement that is malformed fails with `MIGRATION.STATEMENT_INVALID`, and one that does not resolve fails with `MIGRATION.STATEMENT_UNRESOLVED`, naming what it searched and what it found. Each error's next step is the statement to type instead: the accepted forms, the statement written the right way round, the statements a model-and-field mix may have meant, or the order that works. A statement that has already been applied (the origin already has the new name and not the old one) says to leave it out. Statements cannot swap two names in one plan, and the error gives the three plans that do, through a temporary name. A plan from an empty database (`--from @empty`, or greenfield) has no models to rename, so every statement is unresolved. A statement whose namespace changes, such as `--rename public.Invoice:billing.Invoice`, moves the table to the new schema on Postgres, creating the schema first when the database lacks it; a move into the unbound namespace is refused. One name on each side of a namespace of the origin contract, `--rename billing:finance`, renames the namespace on Postgres; a statement that reads both as a namespace and as a model rename is refused, and `public` and a schema another contract space declares are not renamed. Namespace renames run before the other statements, whatever order they are given in. When a name is both a namespace and a model, name the model with its namespace, or give both statements. An enum value is named through its enum by its member name, `--rename Status.ARCHIVED:Status.RETIRED`, never by its stored value; on Postgres and SQLite the plan replaces the old stored value with the new member's in every column the enum types, with `this.replaceStoredValue(...)` calls in `migration.ts`, between the drop of the old check and the creation of the new one on Postgres. A member renamed with its stored value kept plans nothing, and its statement is listed with no operations. A rename into a stored value another member of the origin enum holds is refused, since it would merge the data of the two members. Without a snapshot of the contract the database is at, no enum value is known to have changed: nothing is asked about it, `--delete Status.ARCHIVED` answers nothing and is refused as unused, and on Postgres the apply fails at the new check while data holds the old value. MongoDB refuses enum value statements. Renaming value objects is not supported in this release. The planner renames the table or column, and the constraints and indexes named after it, ahead of the rest of the plan. On Postgres, a check, a row-level-security policy, or an expression or partial index whose SQL names a renamed column is dropped and created again, and adding a check back reads every row of the table under an exclusive lock, so on a large table such a rename takes time; `migration.ts` contains the same `...this.renameTable(...)` and `...this.renameColumn(...)` calls you could have written by hand. On MongoDB a field is named by its name in the contract, as on SQL (`id @map("_id")` is `User.id`), and a rename that keeps the stored name plans nothing. A model rename renames the collection, which keeps its documents, indexes and validator (`renameCollection`); a field rename renames the field in every document that holds it (`renameField`), and only in that variant's documents for a variant's field; `migration.ts` calls these factories. The statements are listed under `Statements applied` after the operations, each with its number of operations, and as `appliedStatements` in `--json` output, each with its `statement`, `description` and `operationIndexes`, the positions in `operations` of the operations it accounts for. A coordinate in a contract with no namespaces has no `namespaceId` in the JSON. A statement whose storage does not change, such as a model whose table name is kept with `@@map`, is listed with no operations. When the storage did not change at all and no statement needs an operation, no migration package is written and the output says so; when the storage changed but the planner planned nothing, the migration is written with no operations, statements or not, and the output names the storage entities that changed. In `--json` output, `changedElementCoordinates` lists the storage entities that differ between the origin and the destination for every written migration, with operations or without; a migration written with no operations has an empty `operations` and `noOp: false`. `noOp: true` means no migration was written, because the storage hash did not change, and then `changedElementCoordinates` is empty. A statement the planner cannot carry out, for example on a table whose control policy is not `managed`, fails planning with `MIGRATION.PLANNING_FAILED`, and the refused statement is carried in the error's conflicts. +**Rename statements:** each `--rename` names the old and the new name in contract vocabulary, never a table or a column. Each side is one of `namespace`, `Model`, `namespace.Model`, `Model.field`, `namespace.Model.field`, `Enum.value` or `namespace.Enum.value`, and both sides name the same kind of thing: a namespace, a model, a field of the same model, or a value of the same enum. Names match exactly, including case. A name without a namespace resolves when exactly one namespace declares the model. A field's model is named as the destination contract names it, so a model rename followed by a field rename on it is `--rename Profile:User --rename User.name:User.fullName`. The old name must exist in the starting contract and not in the destination; the new name must exist in the destination and not in the starting contract. Statements resolve before anything is written; a statement that is malformed fails with `MIGRATION.STATEMENT_INVALID`, and one that does not resolve fails with `MIGRATION.STATEMENT_UNRESOLVED`, naming what it searched and what it found. Each error's next step is the statement to type instead: the accepted forms, the statement written the right way round, the statements a model-and-field mix may have meant, or the order that works. A statement that has already been applied (the origin already has the new name and not the old one) says to leave it out. Statements cannot swap two names in one plan, and the error gives the three plans that do, through a temporary name. A plan from an empty database (`--from @empty`, or greenfield) has no models to rename, so every statement is unresolved. A statement whose namespace changes, such as `--rename public.Invoice:billing.Invoice`, moves the table to the new schema on Postgres, creating the schema first when the database lacks it; a move into the unbound namespace is refused. One name on each side of a namespace of the origin contract, `--rename billing:finance`, renames the namespace on Postgres; a statement that reads both as a namespace and as a model rename is refused, and `public` and a schema another contract space declares are not renamed. Namespace renames run before the other statements, whatever order they are given in. When a name is both a namespace and a model, name the model with its namespace, or give both statements. An enum value is named through its enum by its member name, `--rename Status.ARCHIVED:Status.RETIRED`, never by its stored value; on Postgres and SQLite the plan replaces the old stored value with the new member's in every column the enum types, with `this.replaceStoredValue(...)` calls in `migration.ts`, between the drop of the old check and the creation of the new one on Postgres. A member renamed with its stored value kept plans nothing, and its statement is listed with no operations. A rename into a stored value another member of the origin enum holds is refused, since it would merge the data of the two members. Without a snapshot of the contract the database is at, no enum value is known to have changed: nothing is asked about it, `--delete Status.ARCHIVED` answers nothing and is refused as unused, and on Postgres the apply fails at the new check while data holds the old value. MongoDB refuses enum value statements. Renaming value objects is not supported in this release. The planner renames the table or column, and the constraints and indexes named after it, ahead of the rest of the plan. On Postgres, a check, a row-level-security policy, or an expression or partial index whose SQL names a renamed column is dropped and created again, and adding a check back reads every row of the table under an exclusive lock, so on a large table such a rename takes time; `migration.ts` contains the same `...this.renameTable(...)` and `...this.renameColumn(...)` calls you could have written by hand. On MongoDB a field is named by its name in the contract, as on SQL (`id @map("_id")` is `User.id`), and a rename that keeps the stored name plans nothing. A model rename renames the collection, which keeps its documents, indexes and validator (`renameCollection`); a field rename renames the field in every document that holds it (`renameField`), and only in that variant's documents for a variant's field; `migration.ts` calls these factories. The statements are listed under `Statements applied` after the operations, each with its number of operations, and as `appliedStatements` in `--json` output, each with its `statement`, `description` and `operationIndexes`, the positions in `operations` of the operations it accounts for. A coordinate in a contract with no namespaces has no `namespaceId` in the JSON. A statement whose storage does not change, such as a model whose table name is kept with `@@map`, is listed with no operations. When the storage did not change at all and no statement needs an operation, no migration package is written and the output says so; when the storage changed but the planner planned nothing, the migration is written with no operations, statements or not, and the output names the storage entities that changed. In `--json` output, `changedStorageEntities` lists the storage entities that differ between the origin and the destination for every written migration, with operations or without; a migration written with no operations has an empty `operations` and `noOp: false`. `noOp: true` means no migration was written, because the storage hash did not change, and then `changedStorageEntities` is empty. A statement the planner cannot carry out, for example on a table whose control policy is not `managed`, fails planning with `MIGRATION.PLANNING_FAILED`, and the refused statement is carried in the error's conflicts. **Data loss:** a plan that would lose data is written only once you say what each such operation means. The command asks one question per model, field or storage name whose data an operation would lose (dropping a table or a column, or a type change that can change values), before it writes anything, including the baseline package of an auto-baseline. Each question is answered with a statement: diff --git a/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts b/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts index eca8299b0fcd..00fb3284b98e 100644 --- a/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts +++ b/packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts @@ -374,7 +374,7 @@ async function answerPlannedDataLoss(input: { } /** The coordinate of a storage entity a plan changed, without the internal unbound namespace id. */ -type ChangedElementCoordinate = Omit & { +type ChangedStorageEntity = Omit & { readonly namespaceId?: string; }; @@ -452,7 +452,7 @@ export interface MigrationPlanResult { /** * The storage entities that differ between the origin and the destination. Empty when nothing was written (`noOp`), since the storage hashes are equal. A written migration whose `operations` is empty still lists them. A coordinate in the unbound namespace has no `namespaceId`, since that id is internal. */ - readonly changedElementCoordinates: readonly ChangedElementCoordinate[]; + readonly changedStorageEntities: readonly ChangedStorageEntity[]; readonly timings: { readonly total: number; }; @@ -724,7 +724,7 @@ async function executeMigrationPlanCommandInner( ...(warnings.length > 0 ? { warnings } : {}), appliedStatements: [], temporaryValues: [], - changedElementCoordinates: [], + changedStorageEntities: [], summary: 'No changes detected between contracts', timings: { total: Date.now() - startTime }, }; @@ -907,7 +907,7 @@ async function executeMigrationPlanCommandInner( placeholderDirs: [baselineDir], appliedStatements: [...statementsWithoutOperations, ...deletes], temporaryValues: baselineLeg.value.temporaryValues, - changedElementCoordinates: changedElementCoordinates( + changedStorageEntities: changedElementCoordinates( fromContract?.storage ?? null, toContract.storage, ).map(withoutUnboundNamespaceId), @@ -936,7 +936,7 @@ async function executeMigrationPlanCommandInner( ...(warnings.length > 0 ? { warnings } : {}), appliedStatements: [...statementsWithoutOperations, ...deletes], temporaryValues: baselineLeg.value.temporaryValues, - changedElementCoordinates: changedElementCoordinates( + changedStorageEntities: changedElementCoordinates( fromContract?.storage ?? null, toContract.storage, ).map(withoutUnboundNamespaceId), @@ -978,7 +978,7 @@ async function executeMigrationPlanCommandInner( ], appliedStatements: [...deltaLeg.appliedStatements, ...deletes], temporaryValues: deltaLeg.temporaryValues, - changedElementCoordinates: changedElementCoordinates( + changedStorageEntities: changedElementCoordinates( fromContract?.storage ?? null, toContract.storage, ).map(withoutUnboundNamespaceId), @@ -1083,7 +1083,7 @@ async function executeMigrationPlanCommandInner( ...(warnings.length > 0 ? { warnings } : {}), appliedStatements: [...deltaLeg.appliedStatements, ...deletes], temporaryValues: deltaLeg.temporaryValues, - changedElementCoordinates: [], + changedStorageEntities: [], summary: 'No changes to plan: the statements need no operations', timings: { total: Date.now() - startTime }, }; @@ -1109,7 +1109,7 @@ async function executeMigrationPlanCommandInner( ...(fromDefaulted ? { fromDefaulted } : {}), appliedStatements: [...deltaLeg.appliedStatements, ...deletes], temporaryValues: deltaLeg.temporaryValues, - changedElementCoordinates: changedElementCoordinates( + changedStorageEntities: changedElementCoordinates( fromContract?.storage ?? null, toContract.storage, ).map(withoutUnboundNamespaceId), @@ -1165,9 +1165,9 @@ function withNoOperationsReport( hasNoOperations: boolean, fromContract: Contract | null, toContract: Contract, -): Pick { +): Pick { const changed = changedElementCoordinates(fromContract?.storage ?? null, toContract.storage); - const reported = { changedElementCoordinates: changed.map(withoutUnboundNamespaceId) }; + const reported = { changedStorageEntities: changed.map(withoutUnboundNamespaceId) }; if (!hasNoOperations) return { ...reported, summary }; const what = changed.length === 0 @@ -1182,7 +1182,7 @@ function withNoOperationsReport( function withoutUnboundNamespaceId({ namespaceId, ...rest -}: EntityCoordinate): ChangedElementCoordinate { +}: EntityCoordinate): ChangedStorageEntity { return namespaceId === UNBOUND_NAMESPACE_ID ? rest : { namespaceId, ...rest }; } diff --git a/packages/1-framework/3-tooling/cli/test/orm/migration-plan.no-operations.test.ts b/packages/1-framework/3-tooling/cli/test/orm/migration-plan.no-operations.test.ts index a2cf7ba90fd8..616faf21233f 100644 --- a/packages/1-framework/3-tooling/cli/test/orm/migration-plan.no-operations.test.ts +++ b/packages/1-framework/3-tooling/cli/test/orm/migration-plan.no-operations.test.ts @@ -164,7 +164,7 @@ function writtenWithNoOperations(to: string) { from: HASH_FROM, to, operations: [], - changedElementCoordinates: CHANGED_ELEMENT_COORDINATES, + changedStorageEntities: CHANGED_ELEMENT_COORDINATES, summary: NO_OPERATIONS_SUMMARY, }, }; @@ -211,7 +211,7 @@ describe('migration plan when the planner produces no operations', () => { { exitCode: 0, data: { - changedElementCoordinates: [], + changedStorageEntities: [], summary: 'Planned 0 operation(s). The storage the contract describes changed, but the database needs no operation for it, so the migration has none. No storage entity changed; what changed is storage.types.', }, @@ -240,7 +240,7 @@ describe('migration plan when the planner produces no operations', () => { data: { noOp: false, operations: [expect.objectContaining({ id: ADDITIVE_OP.id })], - changedElementCoordinates: CHANGED_ELEMENT_COORDINATES, + changedStorageEntities: CHANGED_ELEMENT_COORDINATES, summary: 'Planned 1 operation(s)', }, summary: { status: 'ok' }, @@ -278,7 +278,7 @@ describe('migration plan when the planner produces no operations', () => { from: HASH_FROM, to: HASH_TO, operations: [], - changedElementCoordinates: CHANGED_ELEMENT_COORDINATES, + changedStorageEntities: CHANGED_ELEMENT_COORDINATES, summary: `Planned baseline (1 operation(s)) + 0 operation(s). ${NO_OPERATIONS_TEXT}`, }, }); diff --git a/test/integration/test/cli-journeys/plans-with-no-operations.e2e.test.ts b/test/integration/test/cli-journeys/plans-with-no-operations.e2e.test.ts index 0fdf2232cb1a..4d804b3122f3 100644 --- a/test/integration/test/cli-journeys/plans-with-no-operations.e2e.test.ts +++ b/test/integration/test/cli-journeys/plans-with-no-operations.e2e.test.ts @@ -48,7 +48,7 @@ const TO_PSL = FROM_PSL.replace(' id Int @id\n\n', ' id Int @id\n emai interface NoOperationsPlan { readonly operations: readonly unknown[]; - readonly changedElementCoordinates: readonly unknown[]; + readonly changedStorageEntities: readonly unknown[]; readonly summary: string; } @@ -89,7 +89,7 @@ withTempDir(({ createTempDir }) => { expect(plan.exitCode, `N1.05: plan: ${plan.stderr}`).toBe(0); expect(parseJsonOutput(plan), 'N1.05: no operations').toMatchObject({ operations: [], - changedElementCoordinates: [ + changedStorageEntities: [ { plane: 'storage', namespaceId: 'public', diff --git a/test/integration/test/cli-journeys/plans-with-no-operations.mongo.e2e.test.ts b/test/integration/test/cli-journeys/plans-with-no-operations.mongo.e2e.test.ts index b3dbb23c91bd..2ce979d7942c 100644 --- a/test/integration/test/cli-journeys/plans-with-no-operations.mongo.e2e.test.ts +++ b/test/integration/test/cli-journeys/plans-with-no-operations.mongo.e2e.test.ts @@ -30,7 +30,7 @@ const FIXTURES_DIR = join(fixtureAppDir, 'fixtures/mongo-cli-journeys'); interface NoOperationsPlan { readonly operations: readonly unknown[]; - readonly changedElementCoordinates: readonly unknown[]; + readonly changedStorageEntities: readonly unknown[]; readonly summary: string; } @@ -128,7 +128,7 @@ describe('Journeys (MongoDB): a plan with no operations', { expect(plan.exitCode, `N1.05: plan: ${plan.stderr}`).toBe(0); expect(parseJsonOutput(plan), 'N1.05: no operations').toMatchObject({ operations: [], - changedElementCoordinates: [ + changedStorageEntities: [ expect.objectContaining({ entityKind: 'collection', entityName: 'auth_users' }), ], summary: expect.stringContaining('collection "'), diff --git a/test/integration/test/cli-journeys/plans-with-no-operations.sqlite.e2e.test.ts b/test/integration/test/cli-journeys/plans-with-no-operations.sqlite.e2e.test.ts index 261d548163bd..2ab0c00eba42 100644 --- a/test/integration/test/cli-journeys/plans-with-no-operations.sqlite.e2e.test.ts +++ b/test/integration/test/cli-journeys/plans-with-no-operations.sqlite.e2e.test.ts @@ -47,7 +47,7 @@ const TO_PSL = FROM_PSL.replace( interface NoOperationsPlan { readonly operations: readonly unknown[]; - readonly changedElementCoordinates: readonly unknown[]; + readonly changedStorageEntities: readonly unknown[]; readonly summary: string; } @@ -99,13 +99,13 @@ withTempDir(({ createTempDir }) => { expect( { operations: planned.operations, - changedElementCoordinates: planned.changedElementCoordinates, + changedStorageEntities: planned.changedStorageEntities, summary: planned.summary, }, 'N1.05: no operations', ).toEqual({ operations: [], - changedElementCoordinates: [ + changedStorageEntities: [ { plane: 'storage', entityKind: 'valueSet', entityName: 'Role' }, ], summary: expect.stringContaining('What changed: valueSet "Role".'), From e21fb97e702aefbe86f7ac7aece55a224af6fe94 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 20:11:27 +0200 Subject: [PATCH 095/104] TML-3537: the upgrade script writes nullable false where an old MongoDB value object member leaves it out, and drops dict false Signed-off-by: willbot Signed-off-by: Will Madden --- .../value-objects-in-storage.test.ts | 26 +++++++++++++++++++ .../value-objects-in-storage.ts | 13 +++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/value-objects-in-storage.test.ts b/test/integration/test/upgrade-instructions/value-objects-in-storage/value-objects-in-storage.test.ts index 2ba4533e0e15..59b95f99d6a4 100644 --- a/test/integration/test/upgrade-instructions/value-objects-in-storage/value-objects-in-storage.test.ts +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/value-objects-in-storage.test.ts @@ -75,6 +75,32 @@ describe('a project whose contracts keep value objects in the domain plane', () }); }); +describe('an old MongoDB contract whose value object members leave nullable out or say dict false', () => { + it('is written with nullable false and without dict, as this release reads it', () => { + const path = 'mongo/src/contract.json'; + const root = copyFixture('before'); + editJson(root, path, (json) => { + const contract = json as { + domain: { + namespaces: { + __unbound__: { + valueObjects: { + Address: { fields: Record }; + }; + }; + }; + }; + }; + const fields = contract.domain.namespaces.__unbound__.valueObjects.Address.fields; + if (fields['city'] !== undefined) delete fields['city'].nullable; + if (fields['street'] !== undefined) fields['street'].dict = false; + return contract; + }); + runScript(root); + expect(readContract(join(root, path))).toEqual(readContract(join(fixturesDir, 'after', path))); + }); +}); + describe('a contract.d.ts whose contract the script leaves unchanged', () => { it('gains the storage namespaces argument of FieldTypesFromDomain, which is now required', () => { const root = copyFixture('after'); diff --git a/upgrade-instructions/pending/value-objects-in-storage/app/scripts/value-objects-in-storage/value-objects-in-storage.ts b/upgrade-instructions/pending/value-objects-in-storage/app/scripts/value-objects-in-storage/value-objects-in-storage.ts index 636f82459917..c244028dd5f5 100644 --- a/upgrade-instructions/pending/value-objects-in-storage/app/scripts/value-objects-in-storage/value-objects-in-storage.ts +++ b/upgrade-instructions/pending/value-objects-in-storage/app/scripts/value-objects-in-storage/value-objects-in-storage.ts @@ -521,12 +521,23 @@ function valueSetsOf(storageNamespace: JsonRecord): JsonRecord { } /** A value object member, with an enum reference pointed at the enum's storage value set. */ +/** The old MongoDB schema let a member leave `nullable` out, meaning `false`, and say `dict: false`; storage requires `nullable` and keeps `dict` only when it is `true`. */ +function withStorageModifiers(member: JsonRecord): JsonRecord { + const { dict, ...rest } = member; + return { + ...rest, + nullable: typeof member['nullable'] === 'boolean' ? member['nullable'] : false, + ...(dict === undefined || dict === false ? {} : { dict }), + }; +} + function rewrittenMember( - member: JsonRecord, + stored: JsonRecord, storageNamespaces: JsonRecord, where: string, problems: string[], ): JsonRecord { + const member = withStorageModifiers(stored); const valueSet = member['valueSet']; if (!isRecord(valueSet) || valueSet['plane'] === 'storage') return member; const namespaceId = valueSet['namespaceId']; From 3f510c411230ff344a3501bfcb664c74ea00ab08 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 20:12:02 +0200 Subject: [PATCH 096/104] TML-3537: ADR 221 and ADR 309 say the framework defines valueObject and reads the valueSet slot; ADR 309 describes the codec's JSON type, and its first consequence and the rejected alternative agree with the cases that have no operations Signed-off-by: willbot Signed-off-by: Will Madden --- ...entity coordinate and pack-contributed entity kinds.md | 2 +- ...ities, and a discriminator column names a value set.md | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/architecture docs/adrs/ADR 221 - Contract IR two planes with uniform entity coordinate and pack-contributed entity kinds.md b/docs/architecture docs/adrs/ADR 221 - Contract IR two planes with uniform entity coordinate and pack-contributed entity kinds.md index 20f121e7472b..f2e73343eaf2 100644 --- a/docs/architecture docs/adrs/ADR 221 - Contract IR two planes with uniform entity coordinate and pack-contributed entity kinds.md +++ b/docs/architecture docs/adrs/ADR 221 - Contract IR two planes with uniform entity coordinate and pack-contributed entity kinds.md @@ -145,7 +145,7 @@ The built-in kinds (`tables`, `collections`) stay hardcoded inside their family The mechanism's value is that it satisfies two requirements at once: a kind is contributed at the framework level (so authoring tools — the PSL interpreter, the TS DSL, the emitter — all see it) *without* any target-specific symbol appearing in the framework or in sibling-target packages. A cheaper-looking option — keep a kind hardcoded but move the hardcode from the framework into the pack — fails the second requirement, because the slot name still leaks into the framework's authoring-tool dispatch. -**Kinds the framework defines.** Two storage kinds are not contributed but defined by the framework, because domain fields refer to them: `valueObject`, and `valueSet`, which value object members name. The framework reads them by slot key, validates a value object's fields against one schema that both families use, and types them in `contract.d.ts`. Each family still registers a descriptor and an IR class for them. See [ADR 309](ADR%20309%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md). +**Kinds the framework defines or reads.** One storage kind is not contributed but defined by the framework, because domain fields refer to it: `valueObject`. The framework reads it by slot key, validates a value object's fields against one schema that both families use, and types it in `contract.d.ts`. Where a value object member names a value set, the framework also reads the `valueSet` slot by key, to check that the reference resolves; the families define the value set entity. Each family still registers a descriptor and an IR class for both kinds. See [ADR 309](ADR%20309%20-%20Value%20objects%20are%20storage%20entities%2C%20and%20a%20discriminator%20column%20names%20a%20value%20set.md). ## The `Namespace` interface diff --git a/docs/architecture docs/adrs/ADR 309 - Value objects are storage entities, and a discriminator column names a value set.md b/docs/architecture docs/adrs/ADR 309 - Value objects are storage entities, and a discriminator column names a value set.md index 11e0ba8293a4..1904212360c7 100644 --- a/docs/architecture docs/adrs/ADR 309 - Value objects are storage entities, and a discriminator column names a value set.md +++ b/docs/architecture docs/adrs/ADR 309 - Value objects are storage entities, and a discriminator column names a value set.md @@ -94,7 +94,7 @@ Storage already holds stored facts the database does not enforce. SQLite derives **Where the principle stops.** "A fact the planner must see belongs in storage" is applied here to value objects and discriminator values. It is not applied to the names of enum members, which decide which member a stored value reads as; they stay in the domain plane. Whether they should move is open in [TML-3720](https://linear.app/prisma-company/issue/TML-3720). -**The framework defines the entity kind.** Families and packs contribute storage entity kinds ([ADR 221](ADR%20221%20-%20Contract%20IR%20two%20planes%20with%20uniform%20entity%20coordinate%20and%20pack-contributed%20entity%20kinds.md)), but `valueObject` is defined by the framework, because a domain field refers to it and the framework resolves that reference, validates it and types it. So does `valueSet` where a value object member names one. Both families validate a value object against one schema from the framework: each field has `nullable`, always written; `type`; `many` when it is a list; `dict` only as `true`; and an optional `valueSet` reference. Each family still registers its own descriptor and IR class for the kind. +**The framework defines the entity kind.** Families and packs contribute storage entity kinds ([ADR 221](ADR%20221%20-%20Contract%20IR%20two%20planes%20with%20uniform%20entity%20coordinate%20and%20pack-contributed%20entity%20kinds.md)), but the framework defines `valueObject`, because a domain field refers to it and the framework resolves that reference, validates it and types it. Where a value object member names a value set, the framework reads the `valueSet` slot by key, to check that the reference resolves; each family defines the value set entity itself. Both families validate a value object against one schema from the framework: each field has `nullable`, always written; `type`; `many` when it is a list; `dict` only as `true`; and an optional `valueSet` reference. Each family still registers its own descriptor and IR class for the kind. **Discriminators.** The value set is keyed `.`, such as `Task.type`, and holds the values sorted, so the order variants are declared in does not change the hash. A discriminator an enum types keeps the enum's value set and check: the column has one value set reference, and the enum already restricts it. Enum value sets are keyed by the enum's name, so only an enum named exactly like a discriminator's key, in the same storage namespace, clashes with it. No authoring surface writes such a name, since PSL names are identifiers and the SQL TypeScript builder has no polymorphism; the build refuses it all the same, naming both and saying to rename the enum. Enum names are not otherwise restricted. A variant stored in its own table changes nothing here: its rows are still told apart by the base table's column. The ORM writes a row only through a variant, so every row's value names a variant, and the derived check (`"type" IN ('bug', 'feature')`) rejects only a value no variant names, as an enum's check does. @@ -110,7 +110,7 @@ jsonb_path_match("address", 'strict $.type() == "object" && !exists($.keyvalue() ? (@.key != "street"))') IS TRUE ``` -The value must be an object, or for a list an array of objects. Each required member must be present; a nullable member may be absent or JSON `null`; no other key may be. A scalar member must have the JSON type its codec writes, an enum member must hold a value of its value set, and a nested value object is checked the same way, inline. A nullable column also accepts SQL `NULL` and JSON `null`. Members are read through `keyvalue()`, so an absent member is never an error, and strict mode keeps a comparison from unwrapping an array. A value object that contains itself is checked in full down to where it refers back into itself, and any object is accepted below, as the MongoDB validator does. A `Decimal` member must be a JSON string, as the SQL ORM writes it. Each PostgreSQL codec declares, on its descriptor, what the check accepts for a member of it: the JSON type the SQL ORM writes for its value; any JSON value, for a JSON codec; or presence only. A member whose codec declares presence only is checked to be present and, when it is required, not JSON `null`; a JSON member may hold any value. The check is opaque and content-named ([ADR 244](ADR%20244%20-%20Check%20constraints%20are%20opaque%20wire-named%20expressions.md)), so any change to the value object changes the check's name, and the diff drops the old check and adds the new one. +The value must be an object, or for a list an array of objects. Each required member must be present; a nullable member may be absent or JSON `null`; no other key may be. A scalar member must have the JSON type its codec writes, an enum member must hold a value of its value set, and a nested value object is checked the same way, inline. A nullable column also accepts SQL `NULL` and JSON `null`. Members are read through `keyvalue()`, so an absent member is never an error, and strict mode keeps a comparison from unwrapping an array. A value object that contains itself is checked in full down to where it refers back into itself, and any object is accepted below, as the MongoDB validator does. Each PostgreSQL codec declares, on its descriptor, the JSON type its value is written as inside a JSON document: a string, a number, a boolean, an object, an array, or any JSON value, for a JSON codec. The SQL family reads it when it resolves the value object, and the check requires each member to be of that type, so a required member is never JSON `null` unless its codec's type is any JSON value. A `Decimal` member must be a JSON string, as the SQL ORM writes it. The check is opaque and content-named ([ADR 244](ADR%20244%20-%20Check%20constraints%20are%20opaque%20wire-named%20expressions.md)), so any change to the value object changes the check's name, and the diff drops the old check and adds the new one. **Why a change is normally an operation.** The storage hash identifies a database state. Two different hashes are two different database states, so the migration between them is expected to do something to the database. On a target that derives enforcement, a change to a value object or to the discriminator values is a change to a check or a validator, which is an operation. This holds on Postgres, for its derived checks, and on MongoDB, for its collection validators. It does not hold in every case, and the known cases with no operations are: @@ -123,7 +123,7 @@ The system handles these rather than refusing them: `migration plan` writes the ## Consequences -- Any change to a value object, and any change to a variant's discriminator value, changes `storageHash` and the enforcing check or validator. The planner asks about the ones that lose data and plans their rewrite, as for any other storage change. +- Any change to a value object, and any change to a variant's discriminator value, changes `storageHash`. On a target that derives enforcement it also changes the enforcing check or validator, except in the cases listed under "Why a change is normally an operation", where the migration has no operations. The planner asks about the ones that lose data and plans their rewrite, as for any other storage change. - A removed variant that has no columns of its own is visible to the planner through the value set. - Whether the rows are rewritten, and how efficiently, is a separate decision. This one only makes the change visible. - `valueObjects` is no longer a domain namespace key. A contract that keeps it there is refused, with a message saying to re-emit it. @@ -135,7 +135,7 @@ The system handles these rather than refusing them: `migration plan` writes the - **Keep them in the domain plane and record such a change as a migration from a hash to itself.** A migration with a hand-written data operation can already do this. But snapshots are saved by storage hash, so a plan's "previous contract" can be several changes old. Fixing that means a different snapshot key, a larger change than this one, and `db update`, which runs no data operations, still could not apply it. - **Copy the definition into the storage plane and keep the domain copy.** The hash would change, but while value object fields have no stored names the two copies would hold the same fields, and they could drift apart. The domain plane can already refer to storage. See "Why one definition" above for when a second definition would be worth its cost. - **Feed the domain value objects into the storage hash without moving them.** The hash would change, but the planner compares storage, so it still could not tell what changed, and every reader of the hash would have to know that part of the domain plane feeds it. -- **Record the facts in storage without enforcing them.** The hash would change while the database stayed the same, so a change would need a migration with no operations. That contradicts what the hash means, and it leaves rows the contract no longer describes. +- **Record the facts in storage without enforcing them.** The hash would change, but no target would enforce the new facts, not even one that can. What this gives up is enforcement on Postgres and MongoDB: rows the contract no longer describes would stay without a word. Migrations with no operations are not the reason; this decision accepts them on targets that cannot enforce. ## Related From 22fd7fb6bfea3753557fdab4c65c1a186b15229a Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 20:19:56 +0200 Subject: [PATCH 097/104] TML-3537: the print refusal tests expect the value object named in the SQL refusal, and the missing jsonType error says why without guessing the codec's age Signed-off-by: willbot Signed-off-by: Will Madden --- .../2-sql-family/1-core/contract/src/check-expressions.ts | 2 +- .../postgres/1-target/test/psl-print/refusals-storage.test.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/2-sql-family/1-core/contract/src/check-expressions.ts b/packages/2-sql-family/1-core/contract/src/check-expressions.ts index 073d55ef9915..b2411eddd7fa 100644 --- a/packages/2-sql-family/1-core/contract/src/check-expressions.ts +++ b/packages/2-sql-family/1-core/contract/src/check-expressions.ts @@ -178,7 +178,7 @@ function memberJsonType( 'CONTRACT.CODEC_JSON_TYPE_MISSING', `The codec "${codecId}" of value object "${valueObject}" member "${member.name}" declares no jsonType, so the check of the value object cannot be derived.`, { - why: 'The check of a value object column requires each member to be of the JSON type its codec declares, and this codec was built against a release before codecs declared one.', + why: 'The check of a value object column requires each member to be of the JSON type its codec declares, and this codec declares none.', fix: `The author of the codec "${codecId}" adds a jsonType to its descriptor, the JSON type its value is written as inside a JSON document.`, meta: { codecId, valueObject, member: member.name, property: 'jsonType' }, }, diff --git a/packages/3-targets/postgres/1-target/test/psl-print/refusals-storage.test.ts b/packages/3-targets/postgres/1-target/test/psl-print/refusals-storage.test.ts index 00ef9d973512..4cd6016833b6 100644 --- a/packages/3-targets/postgres/1-target/test/psl-print/refusals-storage.test.ts +++ b/packages/3-targets/postgres/1-target/test/psl-print/refusals-storage.test.ts @@ -285,12 +285,12 @@ describe('value objects', () => { nullable: false, type: { kind: 'union', members: [{ kind: 'scalar', codecId: 'pg/text@1' }] }, }), - ).toThrow('value object member "street" is a union'); + ).toThrow('value object "public:Address" member "street" is a union'); }); it('never reaches a value-object member that is a dictionary, which deserializing refuses', () => { expect(() => addressContract({ ...TEXT_FIELD, dict: true })).toThrow( - 'value object member "street" is a dictionary', + 'value object "public:Address" member "street" is a dictionary', ); }); From 4d902bc2b66d59428f6ad14b5d3ec4dc19f418fc Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 20:45:47 +0200 Subject: [PATCH 098/104] TML-3537: the manual QA report for PR 3, and the brief for its fixes Signed-off-by: willbot Signed-off-by: Will Madden --- .../manual-qa-reports/2026-10-23-qa-3b-pr3.md | 86 +++++++++++++++++++ .../dispatches/20-pr3-qa-fixes.md | 72 ++++++++++++++++ 2 files changed, 158 insertions(+) create mode 100644 projects/migration-statements/manual-qa-reports/2026-10-23-qa-3b-pr3.md create mode 100644 projects/migration-statements/slices/remaining-nouns/dispatches/20-pr3-qa-fixes.md diff --git a/projects/migration-statements/manual-qa-reports/2026-10-23-qa-3b-pr3.md b/projects/migration-statements/manual-qa-reports/2026-10-23-qa-3b-pr3.md new file mode 100644 index 000000000000..72c9745ea2c6 --- /dev/null +++ b/projects/migration-statements/manual-qa-reports/2026-10-23-qa-3b-pr3.md @@ -0,0 +1,86 @@ +# Manual QA — slice 3b PR 3, value objects in storage — 2026-10-23 + +Detached checkout `wip/wt-qa` at `22fd7fb6bf`. Method: `projects/migration-statements/manual-qa.md`. Scope: PR 3 of `slices/remaining-nouns/spec.md` (value objects as storage entities, derived checks on Postgres, plans with no operations, `jsonType`, the upgrade script and instructions), read against ADR 309. + +## Summary + +| # | Scenario | Verdict | +| - | -------- | ------- | +| 1 | New Postgres project: emit, plan, migrate, verify, ORM writes, bad raw rows | ✅ | +| 2 | Change the value object with rows in the table | ⚠️ defect (F2, F3) | +| 3 | `db update` for the same changes | ⚠️ defect (F2, F3) | +| 4 | A change with no operations (`@@control(external)`) | ✅ (F5) | +| 5 | `contract print` and `contract infer` | ✅ | +| 6 | Upgrade path, `prisma-8-demo` and `mongo-demo` | ⚠️ defect (F1, F4, F6) | +| 7 | MongoDB validator follows a member change | ✅ (F7) | +| 8 | Error quality | ⚠️ unclear (F3, F5, F8) | + +Tally: 4 works, 3 defect, 1 unclear. No blocker. Every failure I caused left the database unchanged. The checks refused every bad row I tried and accepted every row the ORM wrote. + +## Defects + +1. ⚠️ **F1 (medium) — Following the upgrade instructions leaves a stale `db` ref, and the next `migration plan` writes a second migration with the same endpoints.** Step 1 runs `prisma db sign`, which writes `migrations/app/refs/db.json` (the old demo had none). Step 3's `prisma db migrate` does not advance that ref. Steps: + 1. Old `prisma-8-demo` (merge base `f543fb4173`), old CLI: `prisma db migrate`. + 2. Current CLI: run the script, `prisma db sign --db "$DATABASE_URL"` (`Advanced ref "db" → ecd77e4…`), `prisma contract emit`, `prisma migration plan --name value-object-checks`, `prisma db migrate`. + 3. `prisma migration plan` again, with no change: + ``` + ⚠ The default origin ref 'db' points at ecd77e4…, which already has a migration leading to e1916bd…. Planning from it forks the migration graph; pass --from to choose the origin explicitly. + ✔ Planned 2 operation(s) + migrations/app/20261023T1839_migration + ``` + The new migration adds the same two checks as the applied one. `migration status` then shows two edges `ecd77e4 → e1916bd`, one pending. The same happened on `mongo-demo` with the next real change. The `postgres-value-object-checks` section also says "A project with no `db` ref names the origin … `--from`", which never applies when the steps are followed in order, because step 1 just wrote a ref. The fix belongs in the instructions: `db migrate --advance-ref db` in step 3, or `db sign --no-advance-ref` in step 1 with `--from` in step 3. The fork itself is the older behaviour of `db migrate` and refs. +2. ⚠️ **F2 (medium) — Adding a required member to a value object that rows already hold has no route that keeps the data, and the advice is a dead end.** Postgres, `Address` held by `user.address` (required) and `user.previous` (nullable list), three rows. + 1. Add `country String` to `Address`. `contract emit`, `migration plan --from `: four operations (two check drops, two check adds), no question, no warning. + 2. `prisma db migrate`: + ``` + ✘ [MIGRATION.EXECUTION_FAILED] Operation checkConstraint.user.user_address_shape_5c02601b failed during execution: add check constraint "user_address_shape_5c02601b" + why: check constraint "user_address_shape_5c02601b" of relation "user" is violated by some row. The failed apply changed nothing. + → Fix the issue and re-run `prisma db migrate --to ` + ``` + Checks and marker unchanged, as promised. + 3. Fix the rows first: `UPDATE "public"."user" SET address = address || '{"country":"NO"}'` is refused by the old check `user_address_shape_9a7e984c`, which forbids the unknown key `country`. + So the user can neither fix the rows before the apply nor apply without fixing them. What worked: hand-write a raw operation (an `UPDATE` for each column) in `migration.ts` between the drops and the adds, run `node migration.ts`, `db migrate`. Nothing tells the user this. The same holds for `db update` (scenario 3), where there is no `migration.ts`: the only route was to drop the old checks by hand in SQL, update the rows, then `db update`. Renaming a member hits the same wall; the spec gives rename and delete statements to PR 4, but not adding a required member. +3. ⚠️ **F3 (low) — The failure for a check that rows break does not say which rows or how to find them.** It names the check, which is good. It does not name the column, the count of rows, or the query the upgrade instructions give. `db update` says `Inspect the reported conflict, reconcile schema drift if needed`, which reads as if the schema drifted. `db migrate` says `--to `, a placeholder the user does not need. The docs link uses `error-reference#MIGRATION.EXECUTION_FAILED`, while other errors use `error-reference/CODE`. The advice text is not new in this PR, but derived checks make this failure far more common. +4. ⚠️ **F4 (low) — The upgrade instructions never say when to upgrade the Prisma packages.** They say "Before you upgrade, apply every pending migration … with the release you are upgrading from", then run the script, then `db sign`. A reader can infer that the package upgrade goes between the two, but it is not written. I did it there and it worked. +5. ⚠️ **F6 (low, not this PR) — The old `mongo-demo` migration history does not reach its own contract.** At the merge base, old CLI, empty database: `prisma db migrate` fails with `MIGRATION.SCHEMA_VERIFY_FAILED … missing: posts/index:summary:1; missing: posts/validator` on `20260626T1916_add_posts_indexes`. I used old `db update` to bring the database to the last migration's hash before the upgrade. Not caused by this PR. + +## Other findings + +6. **F5 (minor) — The no-operation summary uses the internal kind name.** `What changed: valueObject "public.Home".` A user writes `type Home`, so `value object "public.Home"` would read better. `db update` on the same change says `✔ Database already matches contract` and `(no operations)` while it moves the marker, and does not say what changed. +7. **F7 (low, not this PR) — MongoDB: making a member required is planned and applied without a question, and existing documents that lack it stay.** `region String?` → `region String` with a document that has no `region`: one `collMod`, applied, `db verify --strict` clean. MongoDB does not check existing documents on `collMod`, so the stored document no longer fits the validator. Same as before this PR. +8. **F8 (minor) — ADR 309 names the check `
__shape`.** The built name is `
__shape_`, as the upgrade instructions say. The ADR's "Enforcement on Postgres" paragraph should say the same. +9. **F9 (not this PR) — The SQL ORM cannot create a variant through the variant's own model.** `orm.Bug.create(...)` (single-table) fails with `null value in column "type"`; `orm.Feature.create(...)` (own table) fails with `ORM.COLUMN_UNKNOWN Unknown column "title" in table "feature"`. `orm.Task.variant('bug').create(...)` works. The merge-base code gives the same two errors. +10. **F10 (not checked against main) — A `BigInt` member cannot be written through the ORM.** `RUNTIME.ENCODE_FAILED … Do not know how to serialize a BigInt`. The check expects a JSON number for `pg/int8@1`, and a plain number is accepted. A `Bytes` member is written as `{"0":1,"1":2,"2":3}` and the check expects an object, so they agree. + +## Scenario notes + +1. **New Postgres project.** PSL: enum `Kind` (`@@type("pg/text@1")`), `Geo { lat Float, lng Float }`, `Address { street, city, tags String[], kind Kind, geo Geo, note String? }`, `User.address Address`, `User.previous Address[]?`, base `Task` with `Bug` (single-table) and `Feature` (`@@map("feature")`). + - `contract emit`: `domain.namespaces.public` has only `enum` and `models`. `storage.namespaces.public.entries` has `table`, `valueObject` (`Address`, `Geo`) and `valueSet` (`Kind`, `Task.type` = `["bug","feature"]`). Domain fields and the nested `geo` member are `{ kind: 'valueObject', plane: 'storage', namespaceId: 'public', entityKind: 'valueObject', entityName }`. The enum member carries `valueSet` to `Kind`. `task.type` names `Task.type`. + - Checks: `task_type_check_cda3f5e7` = `"type" IN ('bug', 'feature')`, on `task` only. `user_address_shape_a878857d` (required, no `IS NULL`) and `user_previous_shape_61b5dc0d` (`"previous" IS NULL OR …`, array of objects). Nested `Geo` and the enum's values are inlined. + - `migration plan --name init` (4 operations, DDL preview prints each check), `db migrate`, `db verify` and `db verify --strict`: clean. A second plan: no changes. + - ORM: three users with `note` null, absent and set, `tags` empty and full, `previous` null, `[]` and two elements; read back the same. `Task.variant('bug')` and `Task.variant('feature')` creates worked (see F9). + - Raw SQL, each refused naming the check: extra key, missing key, wrong type (`street: 5`), bad enum (`office`), JSON `null` in the required column, bad nested type, extra nested key, `tags: [1]`, `tags: "a"`, `note: 3`, an array in the scalar column, a bare string; on `previous`: an object, an element missing keys, `[null]`. Accepted: `note: null` with JSON `null` in `previous`. SQL `NULL` in the required column hits `NOT NULL`. `type = 'epic'` refused by `task_type_check_cda3f5e7`. An ORM create with an extra key reaches the database and is refused by the check. +2. **Change the value object.** Adding `zip String?` with rows: two drops, two adds, applied, verify clean, rows kept. Adding `country String`: see F2. The bad-rows query from the instructions, with the expression copied from the DDL preview between `CHECK (` and `)`, returned exactly the rows lacking `country` (4). With a hand-written backfill operation in `migration.ts`, the migration applied, every row and list element holds `country`, and `db verify --strict` is clean. +3. **`db update`.** First apply, adding `zip`, and adding a variant (`Epic`, single-table: `task_type_check` dropped and re-added, `epic` accepted) all worked. `db update --dry-run` afterwards: `No operations`. Adding `country` with rows: same failure as F2, nothing changed. Removing `Bug` with a `bug` row: asks only about `Bug.severity`; with `--delete Bug.severity` the new `task_type_check` fails and nothing changes. Variant removal is PR 4. +4. **No operations.** `auth_profiles` created by hand, `@@control(external)`, holding `Home`. Adding `zip String?`: + ``` + ⚠ Planned 0 operation(s). The storage the contract describes changed, but the database needs no operation for it, so the migration has none. What changed: valueObject "public.Home". + ``` + Exit 0. `ops.json` is `[]`. `--json`: `noOp: false`, `operations: []`, `changedStorageEntities: [{ namespaceId: 'public', plane: 'storage', entityKind: 'valueObject', entityName: 'Home' }]`, the same summary. `db migrate`: `Applied 1 migration(s) (0 operation(s))`, `(no operations)`, marker at the new hash. Next plan `No changes detected`, nothing written. `db verify --strict` and `migration status` clean. No check was added to the external table; a row with any JSON was accepted. `db update` on a further change: dry run `No operations`, apply moves the marker (F5). SQLite (no check renderer): a value object change plans 0 operations with the same summary (`valueObject "Home"`, no `namespaceId` in JSON, no `__unbound__`), and `db migrate` moves the marker. +5. **Print and infer.** `contract print --output printed.prisma`, emitted in a fresh project: same `storageHash`, and `domain` and `storage` byte for byte equal. The print keeps `@@discriminator(type)`, `@@base(Task, "bug")` without `@@map`, and `@@base(Task, "feature")` with `@@map("feature")`; value objects print inside `namespace public`. `contract infer`: the derived checks appear as `@@check(expression: sql\`…\`, map: "task_type_check_cda3f5e7")` and the two `user_*_shape_*` names, with the columns as `Jsonb`, as the Data Contract docs say. The inferred PSL emitted and `db verify --schema-only` passed against the same database. +6. **Upgrade path.** Built the merge base `f543fb4173` in a second worktree and used its CLI for the "before" state. + - `prisma-8-demo`, PGlite: old `db migrate` applied all six migrations. Seeded a user with an extra address key and a task with `type = 'legacy'` by SQL. + - Script: `Rewrote 12 files and renamed 7 snapshot directories`, seven ` -> ` lines. Run again: `nothing changed`, exit 0. Snapshots hold `valueObject` and the `Task.type` value set. + - Step 1 `db sign --db`: `signed ecd77e4… (was 0ce1b6c…)`. Step 2 `contract emit`. Step 3 `migration plan`: two operations (`task_type_check_cda3f5e7`, `user_address_shape_69432dc1`), destination `e1916bd…`, the same hash as the branch's own demo. The instructions' two queries found exactly the two seeded rows. Their `UPDATE … SET address = address - 'extra'` and a `DELETE` of the legacy task fixed them. Before the fix `db migrate` failed and changed nothing; after it, applied, `db verify --strict` clean. Then F1. + - Database behind the last migration (old CLI `db migrate --to 20260917T0818_add_post_expires_at`): `db sign --db` failed with `CONTRACT.SCHEMA_VERIFICATION_FAILED`, as the instructions say. `db sign --contract c61ab00… --no-advance-ref`, `db migrate --db`, `db verify --strict --db`: all worked, ref untouched. + - Current CLI on an un-upgraded project: `CONTRACT.VALIDATION_FAILED` explaining the move, naming `value-objects-in-storage.ts`, and linking the app upgrade instructions. Clear. + - `mongo-demo`, mongodb-memory-server: see F6 for the before state. Script: 9 files, 3 snapshot directories, and `migrations/app/refs/db.json` rewritten to the new hash. `db sign`, `contract emit`, `migration plan`: `No changes detected`, as step 3 says for MongoDB. Collection validators byte for byte unchanged. `src/contract.json`, `contract.d.ts` and every snapshot equal the branch's own upgraded `mongo-demo`. +7. **MongoDB.** On the upgraded `mongo-demo`, adding `region String?` to `Address`: `Update validator on users (changed: address)`, applied, the live validator lists `region` as `["null","string"]`, and a document with `region: 5` is refused (`Document failed validation`). Making it required: one more `collMod`, applied, `required` includes `region` (F7). Verify clean, next plan empty. +8. **Error quality.** New or changed messages hit: `CONTRACT.VALIDATION_FAILED` for an old contract (clear); the 0-operation summary (F5); `MIGRATION.EXECUTION_FAILED` for a derived check (F3). No message named `__unbound__` or another internal id. `PSL_ENUM_CANNOT_INFER_TYPE` for an enum without `@@type` was clear. I could not reach the missing-`jsonType` error: every shipped codec I tried declares one (`pg/*`, pgvector `Vector` as `array`, `Json` as any value). + +## Setup + +- `mise exec -- pnpm install` and `pnpm build` in `wip/wt-qa` (Node v24.22.0 from mise). The merge base was checked out with `git worktree add --detach` at `wip/wt-qa/wip/old` and built the same way. +- CLI `packages/1-framework/3-tooling/cli/dist/bin.mjs` through a wrapper, `--no-interactive`. ORM through `@internal/postgres/runtime` with `contractFromJson`. +- Projects under `wip/wt-qa/wip/qa/`, `node_modules` linked to `test/integration/node_modules` (or each demo's own), configs from the journey fixtures. Postgres: six `@prisma/dev` (PGlite) servers from `createDevDatabase`. MongoDB: one `MongoMemoryReplSet`. SQLite: one file. +- All servers stopped afterwards. `git status --short` in `wip/wt-qa` clean at start and end. diff --git a/projects/migration-statements/slices/remaining-nouns/dispatches/20-pr3-qa-fixes.md b/projects/migration-statements/slices/remaining-nouns/dispatches/20-pr3-qa-fixes.md new file mode 100644 index 000000000000..4043a0363ef2 --- /dev/null +++ b/projects/migration-statements/slices/remaining-nouns/dispatches/20-pr3-qa-fixes.md @@ -0,0 +1,72 @@ +# Dispatch 20: fixes from manual QA and the round 2 verification of PR 3 + +**Plan:** [`../plan-pr3.md`](../plan-pr3.md) · **ADR:** ADR 309 · **Sources:** the QA report `projects/migration-statements/manual-qa-reports/2026-10-23-qa-3b-pr3.md` (F1–F10) and the verification report `wip/reviews/verify-round2.md` (three minor findings) · **Branch:** `tml-3537-value-objects-storage` + +Each item records the decision; do what it says. Write a failing test first for every behaviour change. + +## Upgrade instructions + +1. **QA F1: the steps leave a stale `db` ref.** The app instructions say to run `db sign --db`, which writes `migrations/app/refs/db.json`. Then `db migrate` does not move that ref, so the next `migration plan` forks the graph and writes a duplicate migration. Change the steps so they leave the ref correct. Work out from the CLI which form is right, for example `db migrate --advance-ref db`, or `db sign --no-advance-ref` together with `--from`. Then run the corrected steps end to end on PGlite against a copy of `examples/prisma-8-demo` from `git merge-base HEAD origin/main`: + - Use that merge base's CLI to migrate, and the current CLI to upgrade. + - The second `migration plan` with no change must write nothing. + - Do the same on `mongo-demo` with mongodb-memory-server, as far as its history allows (TML-3749 covers its broken history). + - Remove the sentence about projects with no `db` ref if the steps never reach that case. Otherwise say when it applies. +2. **QA F4: say when to upgrade the packages.** Add the step "upgrade the Prisma packages" in its place, between applying pending migrations and running the script. + +## A check that existing rows break + +3. **QA F2 and F3: the failure says what to do.** When applying a migration fails because a derived check refuses existing rows, the error must say: + - which check, table and column; + - how many rows fail, if the runner can count them cheaply with the check's own expression. If it cannot, give the query that finds them, built from the check's expression, as in the upgrade instructions; + - the route that keeps the data: plan with `migration plan`, then add a step to the `migration.ts` it writes that updates those rows, placed between the operation that drops the old check and the one that adds the new check, then apply with `db migrate`. For `db update`, say that it cannot keep this data, and give that `migration plan` route. + + Drop the advice that does not fit this case: "reconcile schema drift" from `db update`, and `--to ` from `db migrate`. + + Follow how PR 2 reported rows holding an enum value. Read `RowsHoldingValue` and the runner failure advice in `packages/1-framework/3-tooling/cli/src/utils/runner-failure-advice.ts`. Reuse that mechanism, and keep target-specific detection in the target. The framework reads only a typed interface. + + Test on PGlite: add a required member to a value object while rows lack it. Check that `db migrate` and `db update` both fail with the new advice and change nothing. Check that following the advice with a hand-written update in `migration.ts` applies cleanly and `db verify --strict` is clean. Add this as a case in `test/integration/test/cli-journeys/value-object-checks.e2e.test.ts`. + +## Messages + +4. **QA F5: the plan summary for a migration with no operations.** + - Write the entity kind in words: `value object "public.Home"`, `value set "public.Role"`, `table "public.users"`. Keep the raw kind only in `--json`. + - When `db update` moves the marker with no operations, say that, and name what changed, as `migration plan` does. Do not say "Database already matches contract" alone. +5. **QA F8: ADR 309 check names.** Give the full built name, `
__shape_`, where the ADR names the check, and match the upgrade instructions. + +## Verification minor findings + +6. **The orphaned doc comment.** Move the doc comment of `rewrittenMember` in the upgrade script back above its function (near line 523). +7. **The registry type.** The Postgres codec registry accepts a descriptor without `jsonType`, but its type still says `jsonType` is present. Make the registry's returned type say `jsonType` may be missing, or read it only through `declaredJsonType`. No code may read `descriptor.jsonType` as certainly present. +8. **The script and a malformed `nullable`.** Fill in `nullable: false` only when `nullable` is absent. Leave a malformed value as it is, so validation refuses it. + +## Not in this dispatch + +- QA F7: on MongoDB, making a member required leaves old documents. Same as before this PR, and a later slice covers MongoDB statements. +- QA F9: filed as TML-3750. +- QA F10: `BigInt` members are in `deferred.md`. +- QA F6: TML-3749. + +## Rules + +- Never run `pnpm test:integration`, `pnpm test:e2e` or `pnpm test:all`. Run only named files inside `test/integration`, with `pnpm test `, and journeys with `pnpm test:journeys `. `pnpm test:packages` is allowed. +- Use `mise exec --` for node and pnpm. Never `npx`. Do not write to `/tmp` or any system temp directory. Save slow output under `wip/` and read the file. Never use bare `git stash`, and never `git stash` at all. +- No comments where the code can say it. No bare `as` casts in production code. No `any`. No file extensions in imports. No backwards-compatibility exports. Framework packages name no family or target. +- No invented nouns. Never hard-wrap Markdown prose. +- Commit with `git commit -s --trailer "Signed-off-by: Will Madden "`. No AI attribution lines. Stage files explicitly. Commit `projects/migration-statements/slices/remaining-nouns/spec.md` too; it has an edit of mine that is not committed yet. +- Do not push. Do not open a PR. + +## Done when + +- `pnpm build` and `fixtures:check` are clean. +- `pnpm test:packages` passes, apart from the known tarball trust warning. +- Typecheck passes in every example. +- `lint:deps`, `lint:ratchets`, `check:error-reference` and `check:upgrade-coverage --mode pr --prev $(git merge-base HEAD origin/main) --head HEAD` pass. +- Typecheck, lint and `test --coverage` pass in every touched package. +- These pass: + - the journeys `value-object-checks`, `value-object-validator.mongo`, `plans-with-no-operations*` and `enum-value-statements*`; + - the upgrade script test; + - `sql-orm-client/value-object-checks`. + +## Report + +Plain English, short sentences: each item, the corrected upgrade steps and the end-to-end run, the new failure message as printed, the tests that were red first, the checks, and the commits. From 08c5d5e296a10e7a0e9650ac3863bfbd41f0f5b5 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 20:48:10 +0200 Subject: [PATCH 099/104] TML-3537: a registered Postgres codec descriptor may lack jsonType, the upgrade script keeps a malformed nullable, and ADR 309 gives the full check name Signed-off-by: willbot Signed-off-by: Will Madden --- ... discriminator column names a value set.md | 2 +- .../1-target/src/core/codec-descriptor.ts | 8 +++-- .../1-target/test/codec-json-type.test.ts | 4 +-- .../test/postgres-codec-descriptor.test-d.ts | 7 ++++ .../slices/remaining-nouns/spec.md | 2 +- .../value-objects-in-storage.test.ts | 32 +++++++++++++++++++ .../value-objects-in-storage.ts | 4 +-- 7 files changed, 50 insertions(+), 9 deletions(-) diff --git a/docs/architecture docs/adrs/ADR 309 - Value objects are storage entities, and a discriminator column names a value set.md b/docs/architecture docs/adrs/ADR 309 - Value objects are storage entities, and a discriminator column names a value set.md index 1904212360c7..c5fdcd2d5372 100644 --- a/docs/architecture docs/adrs/ADR 309 - Value objects are storage entities, and a discriminator column names a value set.md +++ b/docs/architecture docs/adrs/ADR 309 - Value objects are storage entities, and a discriminator column names a value set.md @@ -102,7 +102,7 @@ Storage already holds stored facts the database does not enforce. SQLite derives "type": { "codecId": "pg/text@1", "dataType": "pg/text", "nullable": false, "valueSet": { "entityKind": "valueSet", "entityName": "Task.type", "namespaceId": "public", "plane": "storage" } } ``` -**Enforcement on Postgres.** On a managed table, a column that holds a value object gets a derived check, as an enum column does, named `
__shape`. Its check kind is `shape`: like `membership` and `elementNotNull`, the kind names what the check enforces, here the shape of the column's value. It is one strict `jsonpath` predicate, written so that an unknown result fails, rather than the key-set operators `?&` and `-`: +**Enforcement on Postgres.** On a managed table, a column that holds a value object gets a derived check, as an enum column does, named `
__shape_`, where `` is the first eight hexadecimal digits of the hash of the check's expression, such as `user_address_shape_5c02601b`. Its check kind is `shape`: like `membership` and `elementNotNull`, the kind names what the check enforces, here the shape of the column's value. It is one strict `jsonpath` predicate, written so that an unknown result fails, rather than the key-set operators `?&` and `-`: ```sql jsonb_path_match("address", 'strict $.type() == "object" diff --git a/packages/3-targets/postgres/1-target/src/core/codec-descriptor.ts b/packages/3-targets/postgres/1-target/src/core/codec-descriptor.ts index f50debe346fd..0c08edc0e32e 100644 --- a/packages/3-targets/postgres/1-target/src/core/codec-descriptor.ts +++ b/packages/3-targets/postgres/1-target/src/core/codec-descriptor.ts @@ -42,8 +42,8 @@ const ARRAY_ORDINALITY_COLUMN = 'ordinality'; export interface AnyPostgresCodecDescriptor extends AnyCodecDescriptor { readonly descriptorKind: typeof POSTGRES_CODEC_DESCRIPTOR_KIND; - /** The JSON type the codec's value is written as inside a JSON document. Every PostgreSQL codec declares it. */ - readonly jsonType: JsonType; + /** The JSON type the codec's value is written as inside a JSON document. `PostgresCodecDescriptor` and `postgresCodec` require it; a registry may still hold a descriptor built without it. */ + readonly jsonType?: JsonType; projectJson(expression: ProjectionExpr, ref: CodecRef): ProjectionExpr; } @@ -144,7 +144,9 @@ export type AdaptedPostgresCodecDescriptor keyof CodecDescriptorTemplate> > & Pick & - Pick; + Pick & { + readonly jsonType: JsonType; + }; class PostgresCodecDescriptorAdapter< D extends AnyCodecDescriptorTemplate & { readonly jsonType: JsonType }, diff --git a/packages/3-targets/postgres/1-target/test/codec-json-type.test.ts b/packages/3-targets/postgres/1-target/test/codec-json-type.test.ts index 614cbb28df56..8f33a027a96e 100644 --- a/packages/3-targets/postgres/1-target/test/codec-json-type.test.ts +++ b/packages/3-targets/postgres/1-target/test/codec-json-type.test.ts @@ -1,4 +1,4 @@ -import { JSON_TYPES } from '@internal/sql-contract/codec-json-type'; +import { declaredJsonType } from '@internal/sql-contract/codec-json-type'; import { describe, expect, it } from 'vitest'; import { buildPostgresCodecDescriptorRegistry } from '../src/core/codec-descriptor'; import { postgresCodecDescriptorRegistry } from '../src/core/registry'; @@ -6,7 +6,7 @@ import { postgresCodecDescriptorRegistry } from '../src/core/registry'; describe('the JSON type a codec declares', () => { it('is declared by every Postgres and SQL family codec', () => { const undeclared = [...postgresCodecDescriptorRegistry.values()] - .filter((descriptor) => !JSON_TYPES.includes(descriptor.jsonType)) + .filter((descriptor) => declaredJsonType(descriptor) === undefined) .map((descriptor) => descriptor.codecId); expect(undeclared).toEqual([]); }); diff --git a/packages/3-targets/postgres/1-target/test/postgres-codec-descriptor.test-d.ts b/packages/3-targets/postgres/1-target/test/postgres-codec-descriptor.test-d.ts index 107de210261b..bce54bd7e38b 100644 --- a/packages/3-targets/postgres/1-target/test/postgres-codec-descriptor.test-d.ts +++ b/packages/3-targets/postgres/1-target/test/postgres-codec-descriptor.test-d.ts @@ -10,12 +10,14 @@ import { dataType, dataTypeId, } from '@internal/framework-components/codec'; +import type { JsonType } from '@internal/sql-contract/codec-json-type'; import { FunctionCallExpr, type ProjectionExpr } from '@internal/sql-relational-core/ast'; import type { StandardSchemaV1 } from '@standard-schema/spec'; import { expectTypeOf, test } from 'vitest'; import { definePostgresCodecs, PostgresCodecDescriptor, + type PostgresCodecDescriptorRegistry, postgresCodec, } from '../src/exports/codec-descriptor'; @@ -201,3 +203,8 @@ class MissingJsonProjection extends PostgresCodecDescriptor { } void MissingJsonProjection; + +test('a registered descriptor may lack the JSON type, which only an author must declare', () => { + type Registered = NonNullable>; + expectTypeOf().toEqualTypeOf(); +}); diff --git a/projects/migration-statements/slices/remaining-nouns/spec.md b/projects/migration-statements/slices/remaining-nouns/spec.md index e5ec3dc4bfb2..3edaced1f078 100644 --- a/projects/migration-statements/slices/remaining-nouns/spec.md +++ b/projects/migration-statements/slices/remaining-nouns/spec.md @@ -104,7 +104,7 @@ Every operation the planner writes is also something a user could write in `migr | 1 | Namespaces: move, rename, delete, and a removed namespace becomes a question. The access question wording and rename | — | | 2 | Enum values: rename and delete in scalar and list columns, on Postgres and SQLite | 1 merged | | 3 | Value objects move to the storage plane and discriminator columns name a value set, both with derived checks on Postgres (decision 1), with ADR 309. First step: confirm a Postgres check can enforce every value object shape SQL authoring produces, nesting and lists included; raise any shape it cannot. A plan whose hash changed but has no operations is written and applied, not refused. Contracts re-emitted, with an upgrade fragment. No statements yet | 2 merged | -| 4 | Value object field rename and delete, including enum values inside JSON. Variant convert and delete | 3 merged | +| 4 | Value object field rename and delete, including enum values inside JSON. A value of a new required value object member for the rows that lack it (`--backfill`), since PR 3's Postgres check refuses those rows. Variant convert and delete | 3 merged | PR 3 is separate because it changes the contract shape and every contract with value objects or variants, and needs its own review, as slice 4p's first PR did. Slice 4b reuses PR 4's value object statement support for MongoDB. diff --git a/test/integration/test/upgrade-instructions/value-objects-in-storage/value-objects-in-storage.test.ts b/test/integration/test/upgrade-instructions/value-objects-in-storage/value-objects-in-storage.test.ts index 59b95f99d6a4..bc2259028bc7 100644 --- a/test/integration/test/upgrade-instructions/value-objects-in-storage/value-objects-in-storage.test.ts +++ b/test/integration/test/upgrade-instructions/value-objects-in-storage/value-objects-in-storage.test.ts @@ -99,6 +99,38 @@ describe('an old MongoDB contract whose value object members leave nullable out runScript(root); expect(readContract(join(root, path))).toEqual(readContract(join(fixturesDir, 'after', path))); }); + + it('keeps a nullable that is not a boolean, so validation refuses it', () => { + const path = 'mongo/src/contract.json'; + const root = copyFixture('before'); + editJson(root, path, (json) => { + const contract = json as { + domain: { + namespaces: { + __unbound__: { + valueObjects: { Address: { fields: Record } }; + }; + }; + }; + }; + const city = contract.domain.namespaces.__unbound__.valueObjects.Address.fields['city']; + if (city !== undefined) city.nullable = 'yes'; + return contract; + }); + runScript(root); + const rewritten = JSON.parse(readFileSync(join(root, path), 'utf8')) as { + storage: { + namespaces: Record< + string, + { entries: { valueObject: { Address: { fields: { city: unknown } } } } } + >; + }; + }; + const city = Object.values(rewritten.storage.namespaces) + .map((namespace) => namespace.entries.valueObject?.Address?.fields.city) + .find((member) => member !== undefined); + expect(city).toMatchObject({ nullable: 'yes' }); + }); }); describe('a contract.d.ts whose contract the script leaves unchanged', () => { diff --git a/upgrade-instructions/pending/value-objects-in-storage/app/scripts/value-objects-in-storage/value-objects-in-storage.ts b/upgrade-instructions/pending/value-objects-in-storage/app/scripts/value-objects-in-storage/value-objects-in-storage.ts index c244028dd5f5..0ba2a3779f9e 100644 --- a/upgrade-instructions/pending/value-objects-in-storage/app/scripts/value-objects-in-storage/value-objects-in-storage.ts +++ b/upgrade-instructions/pending/value-objects-in-storage/app/scripts/value-objects-in-storage/value-objects-in-storage.ts @@ -520,17 +520,17 @@ function valueSetsOf(storageNamespace: JsonRecord): JsonRecord { return isRecord(entries['valueSet']) ? entries['valueSet'] : {}; } -/** A value object member, with an enum reference pointed at the enum's storage value set. */ /** The old MongoDB schema let a member leave `nullable` out, meaning `false`, and say `dict: false`; storage requires `nullable` and keeps `dict` only when it is `true`. */ function withStorageModifiers(member: JsonRecord): JsonRecord { const { dict, ...rest } = member; return { ...rest, - nullable: typeof member['nullable'] === 'boolean' ? member['nullable'] : false, + nullable: member['nullable'] === undefined ? false : member['nullable'], ...(dict === undefined || dict === false ? {} : { dict }), }; } +/** A value object member, with an enum reference pointed at the enum's storage value set. */ function rewrittenMember( stored: JsonRecord, storageNamespaces: JsonRecord, From c8c8edfb75a8852d4eb6b47572fb9aaa55912ef1 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 20:52:25 +0200 Subject: [PATCH 100/104] TML-3537: the no-operations summary writes the entity kind in words, and db update says when it signs a changed contract with no operations and names what changed Signed-off-by: willbot Signed-off-by: Will Madden --- .../operations/changed-storage-entities.ts | 59 +++++++++++++++ .../cli/src/control-api/operations/db-run.ts | 23 +++++- .../control-api/operations/migration-plan.ts | 71 ++++--------------- .../3-tooling/cli/src/control-api/types.ts | 3 + .../cli/src/orm/db/migration-blocks.ts | 1 + .../3-tooling/cli/src/orm/db/update.ts | 1 + .../cli/src/utils/formatters/migrations.ts | 3 + .../orm/migration-plan.no-operations.test.ts | 2 +- .../plans-with-no-operations.e2e.test.ts | 19 ++++- ...lans-with-no-operations.sqlite.e2e.test.ts | 7 +- .../value-object-checks.e2e.test.ts | 2 +- 11 files changed, 123 insertions(+), 68 deletions(-) create mode 100644 packages/1-framework/3-tooling/cli/src/control-api/operations/changed-storage-entities.ts diff --git a/packages/1-framework/3-tooling/cli/src/control-api/operations/changed-storage-entities.ts b/packages/1-framework/3-tooling/cli/src/control-api/operations/changed-storage-entities.ts new file mode 100644 index 000000000000..4cf323f100f5 --- /dev/null +++ b/packages/1-framework/3-tooling/cli/src/control-api/operations/changed-storage-entities.ts @@ -0,0 +1,59 @@ +import type { Contract, EntityCoordinate } from '@internal/contract/types'; +import { changedElementCoordinates, UNBOUND_NAMESPACE_ID } from '@internal/framework-components/ir'; +import { canonicalStringify } from '@internal/utils/canonical-stringify'; + +/** The coordinate of a storage entity that changed, without the internal unbound namespace id. */ +export type ChangedStorageEntity = Omit & { + readonly namespaceId?: string; +}; + +/** The storage entities that differ between two contracts, as a result reports them. */ +export function changedStorageEntities( + fromContract: Contract | null, + toContract: Contract, +): readonly ChangedStorageEntity[] { + return changedElementCoordinates(fromContract?.storage ?? null, toContract.storage).map( + withoutUnboundNamespaceId, + ); +} + +/** The sentence that names what changed in storage, such as `What changed: value object "public.Home".` */ +export function whatChangedSentence(fromContract: Contract | null, toContract: Contract): string { + const changed = changedElementCoordinates(fromContract?.storage ?? null, toContract.storage); + return changed.length === 0 + ? `No storage entity changed; what changed is ${changedStorageKeys(fromContract, toContract).join(', ') || 'outside the storage entities'}.` + : `What changed: ${changed.map(storageEntityText).join(', ')}.`; +} + +function withoutUnboundNamespaceId({ + namespaceId, + ...rest +}: EntityCoordinate): ChangedStorageEntity { + return namespaceId === UNBOUND_NAMESPACE_ID ? rest : { namespaceId, ...rest }; +} + +/** The top-level storage keys whose content differs between two contracts, such as `storage.types`, in key order. */ +function changedStorageKeys(fromContract: Contract | null, toContract: Contract): string[] { + const plain = (storage: unknown): Record => + typeof storage === 'object' && storage !== null ? JSON.parse(JSON.stringify(storage)) : {}; + const from = plain(fromContract?.storage); + const to = plain(toContract.storage); + return [...new Set([...Object.keys(from), ...Object.keys(to)])] + .filter((key) => key !== 'storageHash') + .filter((key) => canonicalStringify(from[key]) !== canonicalStringify(to[key])) + .sort() + .map((key) => `storage.${key}`); +} + +function storageEntityText(coordinate: EntityCoordinate): string { + const name = + coordinate.namespaceId === UNBOUND_NAMESPACE_ID + ? coordinate.entityName + : `${coordinate.namespaceId}.${coordinate.entityName}`; + return `${kindInWords(coordinate.entityKind)} "${name}"`; +} + +/** An entity kind as words, such as `value object` for `valueObject`. */ +function kindInWords(entityKind: string): string { + return entityKind.replace(/[A-Z]/g, (letter) => ` ${letter.toLowerCase()}`); +} diff --git a/packages/1-framework/3-tooling/cli/src/control-api/operations/db-run.ts b/packages/1-framework/3-tooling/cli/src/control-api/operations/db-run.ts index 710f7dd2aa03..5e6d247ba9e6 100644 --- a/packages/1-framework/3-tooling/cli/src/control-api/operations/db-run.ts +++ b/packages/1-framework/3-tooling/cli/src/control-api/operations/db-run.ts @@ -84,6 +84,11 @@ import type { PerSpaceExecutionEntry, PlannerWarningsListener, } from '../types'; +import { + type ChangedStorageEntity, + changedStorageEntities, + whatChangedSentence, +} from './changed-storage-entities'; import { type BuildAggregateInputs, buildContractSpaceAggregate, @@ -521,12 +526,18 @@ export async function executeRun r.entry.displayOps); + const signedWithoutOperations = + action === 'dbUpdate' && + applied.value.totalOpsExecuted === 0 && + markerRows.get(aggregate.app.spaceId)?.storageHash !== appPlan.destination.storageHash; const summary = action === 'dbInit' ? `Applied ${applied.value.totalOpsExecuted} operation(s) across ${applied.value.orderedResolutions.length} space(s), database signed` - : applied.value.totalOpsExecuted === 0 - ? `Database already matches contract across ${applied.value.orderedResolutions.length} space(s), signature updated` - : `Applied ${applied.value.totalOpsExecuted} operation(s) across ${applied.value.orderedResolutions.length} space(s), signature updated`; + : signedWithoutOperations + ? `The storage the contract describes changed, but the database needs no operation for it, so none ran and the database was signed with the new contract. ${whatChangedSentence(fromContract, contract)}` + : applied.value.totalOpsExecuted === 0 + ? `Database already matches contract across ${applied.value.orderedResolutions.length} space(s), signature updated` + : `Applied ${applied.value.totalOpsExecuted} operation(s) across ${applied.value.orderedResolutions.length} space(s), signature updated`; return wrapApplyResult({ operations: aggregateOps, @@ -538,6 +549,9 @@ export async function executeRun & { - readonly namespaceId?: string; -}; - export interface MigrationPlanResult { readonly ok: boolean; readonly noOp: boolean; @@ -907,10 +905,7 @@ async function executeMigrationPlanCommandInner( placeholderDirs: [baselineDir], appliedStatements: [...statementsWithoutOperations, ...deletes], temporaryValues: baselineLeg.value.temporaryValues, - changedStorageEntities: changedElementCoordinates( - fromContract?.storage ?? null, - toContract.storage, - ).map(withoutUnboundNamespaceId), + changedStorageEntities: changedStorageEntities(fromContract, toContract), summary: placeholderSummary('baseline', [baselinePackageDir], invocationOf(options)), timings: { total: Date.now() - startTime }, }; @@ -936,10 +931,7 @@ async function executeMigrationPlanCommandInner( ...(warnings.length > 0 ? { warnings } : {}), appliedStatements: [...statementsWithoutOperations, ...deletes], temporaryValues: baselineLeg.value.temporaryValues, - changedStorageEntities: changedElementCoordinates( - fromContract?.storage ?? null, - toContract.storage, - ).map(withoutUnboundNamespaceId), + changedStorageEntities: changedStorageEntities(fromContract, toContract), summary: buildAutoBaselinePlanSummary(baselineOps.length, 0, emittedExtensionDirs.length), timings: { total: Date.now() - startTime }, }; @@ -978,10 +970,7 @@ async function executeMigrationPlanCommandInner( ], appliedStatements: [...deltaLeg.appliedStatements, ...deletes], temporaryValues: deltaLeg.temporaryValues, - changedStorageEntities: changedElementCoordinates( - fromContract?.storage ?? null, - toContract.storage, - ).map(withoutUnboundNamespaceId), + changedStorageEntities: changedStorageEntities(fromContract, toContract), summary: placeholderSummary( 'baseline + migration', [baselinePackageDir, deltaPackageDir], @@ -1109,10 +1098,7 @@ async function executeMigrationPlanCommandInner( ...(fromDefaulted ? { fromDefaulted } : {}), appliedStatements: [...deltaLeg.appliedStatements, ...deletes], temporaryValues: deltaLeg.temporaryValues, - changedStorageEntities: changedElementCoordinates( - fromContract?.storage ?? null, - toContract.storage, - ).map(withoutUnboundNamespaceId), + changedStorageEntities: changedStorageEntities(fromContract, toContract), summary: placeholderSummary('migration', [packageDir], invocationOf(options)), timings: { total: Date.now() - startTime }, }; @@ -1166,47 +1152,14 @@ function withNoOperationsReport( fromContract: Contract | null, toContract: Contract, ): Pick { - const changed = changedElementCoordinates(fromContract?.storage ?? null, toContract.storage); - const reported = { changedStorageEntities: changed.map(withoutUnboundNamespaceId) }; + const reported = { changedStorageEntities: changedStorageEntities(fromContract, toContract) }; if (!hasNoOperations) return { ...reported, summary }; - const what = - changed.length === 0 - ? `No storage entity changed; what changed is ${changedStorageKeys(fromContract, toContract).join(', ') || 'outside the storage entities'}.` - : `What changed: ${changed.map(storageEntityText).join(', ')}.`; return { ...reported, - summary: `${summary}. The storage the contract describes changed, but the database needs no operation for it, so the migration has none. ${what}`, + summary: `${summary}. The storage the contract describes changed, but the database needs no operation for it, so the migration has none. ${whatChangedSentence(fromContract, toContract)}`, }; } -function withoutUnboundNamespaceId({ - namespaceId, - ...rest -}: EntityCoordinate): ChangedStorageEntity { - return namespaceId === UNBOUND_NAMESPACE_ID ? rest : { namespaceId, ...rest }; -} - -/** The top-level storage keys whose content differs between two contracts, such as `storage.types`, in key order. */ -function changedStorageKeys(fromContract: Contract | null, toContract: Contract): string[] { - const plain = (storage: unknown): Record => - typeof storage === 'object' && storage !== null ? JSON.parse(JSON.stringify(storage)) : {}; - const from = plain(fromContract?.storage); - const to = plain(toContract.storage); - return [...new Set([...Object.keys(from), ...Object.keys(to)])] - .filter((key) => key !== 'storageHash') - .filter((key) => canonicalStringify(from[key]) !== canonicalStringify(to[key])) - .sort() - .map((key) => `storage.${key}`); -} - -function storageEntityText(coordinate: EntityCoordinate): string { - const name = - coordinate.namespaceId === UNBOUND_NAMESPACE_ID - ? coordinate.entityName - : `${coordinate.namespaceId}.${coordinate.entityName}`; - return `${coordinate.entityKind} "${name}"`; -} - /** * Compose the success-line summary so the cross-space side effect * (extension-space migration packages materialised on disk during diff --git a/packages/1-framework/3-tooling/cli/src/control-api/types.ts b/packages/1-framework/3-tooling/cli/src/control-api/types.ts index f6ee1eab347e..2f995b67edc7 100644 --- a/packages/1-framework/3-tooling/cli/src/control-api/types.ts +++ b/packages/1-framework/3-tooling/cli/src/control-api/types.ts @@ -34,6 +34,7 @@ import type { AggregateMigrationEdgeRef } from '@internal/migration-tools/aggreg import type { Result } from '@internal/utils/result'; import type { CliStructuredError } from '../utils/cli-errors'; import type { CliInvocation } from '../utils/printed-command'; +import type { ChangedStorageEntity } from './operations/changed-storage-entities'; import type { ExecuteDbSignResult } from './operations/db-sign'; import type { ExecuteDbVerifyResult } from './operations/db-verify'; import type { RenderContractDtsOptions, RenderContractDtsResult } from './render-contract-dts'; @@ -497,6 +498,8 @@ export interface DbUpdateSuccess * graph ends at it: the origin a `migration plan` for this database starts from. */ readonly databaseStorageHash?: string; + /** The storage entities that changed, when the run signed the database with a changed contract and ran no operation. */ + readonly changedStorageEntities?: readonly ChangedStorageEntity[]; readonly summary: string; } diff --git a/packages/1-framework/3-tooling/cli/src/orm/db/migration-blocks.ts b/packages/1-framework/3-tooling/cli/src/orm/db/migration-blocks.ts index 6a5e97049d9a..8ce64431681c 100644 --- a/packages/1-framework/3-tooling/cli/src/orm/db/migration-blocks.ts +++ b/packages/1-framework/3-tooling/cli/src/orm/db/migration-blocks.ts @@ -163,6 +163,7 @@ function applySummaryText(result: MigrationCommandResult): string { const executed = result.execution?.operationsExecuted ?? 0; const spaces = result.perSpace?.length ?? 0; const across = spaces > 0 ? ` across ${spaces} contract space${spaces === 1 ? '' : 's'}` : ''; + if (result.changedStorageEntities !== undefined) return result.summary; if (executed === 0) { return `Database already matches contract${across}`; } diff --git a/packages/1-framework/3-tooling/cli/src/orm/db/update.ts b/packages/1-framework/3-tooling/cli/src/orm/db/update.ts index cb8b6e4404f4..6e4a5238b331 100644 --- a/packages/1-framework/3-tooling/cli/src/orm/db/update.ts +++ b/packages/1-framework/3-tooling/cli/src/orm/db/update.ts @@ -223,6 +223,7 @@ function updateDocument(inputs: { }, }), ...ifDefined('perSpace', value.perSpace), + ...ifDefined('changedStorageEntities', value.changedStorageEntities), appliedStatements: value.appliedStatements, temporaryValues: value.temporaryValues, ...(value.mode === 'plan' diff --git a/packages/1-framework/3-tooling/cli/src/utils/formatters/migrations.ts b/packages/1-framework/3-tooling/cli/src/utils/formatters/migrations.ts index c0b8ecf52c82..e6ebe7466dcd 100644 --- a/packages/1-framework/3-tooling/cli/src/utils/formatters/migrations.ts +++ b/packages/1-framework/3-tooling/cli/src/utils/formatters/migrations.ts @@ -3,6 +3,7 @@ import type { MigrationSubjectJson, OperationPreview, } from '@internal/framework-components/control'; +import type { ChangedStorageEntity } from '../../control-api/operations/changed-storage-entities'; import type { AppliedStatementReport } from '../../control-api/statements/report-applied-statements'; import type { TemporaryValueReport } from '../../control-api/statements/temporary-values'; import type { PerSpaceExecutionEntry } from '../../control-api/types'; @@ -104,6 +105,8 @@ export interface MigrationCommandResult { * into a single ambiguous list. See {@link PerSpaceExecutionEntry}. */ readonly perSpace?: ReadonlyArray; + /** `db update` only: the storage entities that changed, when it signed the database with a changed contract and ran no operation. */ + readonly changedStorageEntities?: readonly ChangedStorageEntity[]; /** `db update` only: the statements the plan applied, in order; empty when none were given. */ readonly appliedStatements?: readonly AppliedStatementReport[]; /** `db update` only: the new required fields whose existing rows get a temporary value. */ diff --git a/packages/1-framework/3-tooling/cli/test/orm/migration-plan.no-operations.test.ts b/packages/1-framework/3-tooling/cli/test/orm/migration-plan.no-operations.test.ts index 616faf21233f..bcebaa5c879d 100644 --- a/packages/1-framework/3-tooling/cli/test/orm/migration-plan.no-operations.test.ts +++ b/packages/1-framework/3-tooling/cli/test/orm/migration-plan.no-operations.test.ts @@ -100,7 +100,7 @@ const CHANGED_ELEMENT_COORDINATES = [ ]; const NO_OPERATIONS_TEXT = - 'The storage the contract describes changed, but the database needs no operation for it, so the migration has none. What changed: table "public.users", valueSet "public.Role", valueSet "public.Status".'; + 'The storage the contract describes changed, but the database needs no operation for it, so the migration has none. What changed: table "public.users", value set "public.Role", value set "public.Status".'; const NO_OPERATIONS_SUMMARY = `Planned 0 operation(s). ${NO_OPERATIONS_TEXT}`; /** The database sits at HASH_FROM, which a migration reaches; the emitted contract is HASH_TO. */ diff --git a/test/integration/test/cli-journeys/plans-with-no-operations.e2e.test.ts b/test/integration/test/cli-journeys/plans-with-no-operations.e2e.test.ts index 4d804b3122f3..ce09b18f98f5 100644 --- a/test/integration/test/cli-journeys/plans-with-no-operations.e2e.test.ts +++ b/test/integration/test/cli-journeys/plans-with-no-operations.e2e.test.ts @@ -120,7 +120,7 @@ withTempDir(({ createTempDir }) => { const db = useDevDatabase({ onReady: (cs) => sql(cs, AUTH_USERS_SQL) }); it( - 'moves the marker to the new hash', + 'names the changed table and moves the marker to the new hash', async () => { const ctx = setupJourney({ connectionString: db.connectionString, @@ -132,8 +132,23 @@ withTempDir(({ createTempDir }) => { expect(initial.exitCode, `N2.02: db update initial: ${initial.stderr}`).toBe(0); await emitContract(ctx, TO_PSL, 'N2.03'); - const update = await runDbUpdate(ctx); + const update = await runDbUpdate(ctx, ['--json']); expect(update.exitCode, `N2.04: db update: ${update.stderr}`).toBe(0); + expect( + parseJsonOutput>(update), + 'N2.04: names the changed table', + ).toMatchObject({ + plan: { operations: [] }, + changedStorageEntities: [ + { + plane: 'storage', + namespaceId: 'public', + entityKind: 'table', + entityName: 'auth_users', + }, + ], + summary: expect.stringContaining('What changed: table "public.auth_users".'), + }); expect(await markerHash(db.connectionString), 'N2.04: the marker').toBe( readEmittedContractStorageHash({ ...ctx, outputDir: ctx.testDir }), ); diff --git a/test/integration/test/cli-journeys/plans-with-no-operations.sqlite.e2e.test.ts b/test/integration/test/cli-journeys/plans-with-no-operations.sqlite.e2e.test.ts index 2ab0c00eba42..f07ecfc06068 100644 --- a/test/integration/test/cli-journeys/plans-with-no-operations.sqlite.e2e.test.ts +++ b/test/integration/test/cli-journeys/plans-with-no-operations.sqlite.e2e.test.ts @@ -108,7 +108,7 @@ withTempDir(({ createTempDir }) => { changedStorageEntities: [ { plane: 'storage', entityKind: 'valueSet', entityName: 'Role' }, ], - summary: expect.stringContaining('What changed: valueSet "Role".'), + summary: expect.stringContaining('What changed: value set "Role".'), }); const dir = join(ctx.testDir, 'migrations/app', latestMigrationDirName(ctx)); expect(JSON.parse(readFileSync(join(dir, 'ops.json'), 'utf-8')), 'N1.05: ops.json').toEqual( @@ -129,7 +129,7 @@ withTempDir(({ createTempDir }) => { describe('Journey N2 (SQLite): db update applies a change with no operations', () => { it( - 'moves the marker to the new hash', + 'names the changed value set and moves the marker to the new hash', async () => { const ctx = setupSqliteJourney(createTempDir); await emitContract(ctx, FROM_PSL, 'N2.01'); @@ -139,6 +139,9 @@ withTempDir(({ createTempDir }) => { const update = await runDbUpdate(ctx); expect(update.exitCode, `N2.04: db update: ${update.stderr}`).toBe(0); + expect(`${update.stdout}${update.stderr}`, 'N2.04: names the changed value set').toContain( + 'The storage the contract describes changed, but the database needs no operation for it, so none ran and the database was signed with the new contract. What changed: value set "Role".', + ); expect(markerHash(ctx.dbPath), 'N2.04: the marker').toBe( readEmittedContractStorageHash(ctx), ); diff --git a/test/integration/test/cli-journeys/value-object-checks.e2e.test.ts b/test/integration/test/cli-journeys/value-object-checks.e2e.test.ts index 61ed1f1b2d12..e67061338a24 100644 --- a/test/integration/test/cli-journeys/value-object-checks.e2e.test.ts +++ b/test/integration/test/cli-journeys/value-object-checks.e2e.test.ts @@ -467,7 +467,7 @@ withTempDir(({ createTempDir }) => { 'V5.04: no operations', ).toMatchObject({ operations: [], - summary: expect.stringContaining('What changed: valueObject "public.Home".'), + summary: expect.stringContaining('What changed: value object "public.Home".'), }); const migrate = await runMigrate(ctx); expect(migrate.exitCode, `V5.05: migrate: ${migrate.stderr}`).toBe(0); From cbf923e8423d1f7029f5099673b1be6844771660 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 21:03:47 +0200 Subject: [PATCH 101/104] TML-3537: when existing rows fail a check the apply adds, the failure names the check, table and column, gives the query that finds the rows, and the route that keeps them Signed-off-by: willbot Signed-off-by: Will Madden --- .../src/control/control-migration-types.ts | 12 ++ .../src/exports/control.ts | 1 + .../control-api/operations/run-migration.ts | 22 +++- .../3-tooling/cli/src/control-api/types.ts | 6 + .../cli/src/utils/db-update-failure.ts | 29 +++-- .../cli/src/utils/runner-failure-advice.ts | 61 +++++++++ .../utils/rows-failing-check-advice.test.ts | 91 +++++++++++++ .../core/migrations/operations/constraints.ts | 6 +- .../src/core/migrations/rows-failing-check.ts | 87 +++++++++++++ .../1-target/src/core/migrations/runner.ts | 26 +++- .../migrations/rows-failing-check.test.ts | 120 ++++++++++++++++++ .../value-object-checks.e2e.test.ts | 91 ++++++++++++- 12 files changed, 533 insertions(+), 19 deletions(-) create mode 100644 packages/1-framework/3-tooling/cli/test/utils/rows-failing-check-advice.test.ts create mode 100644 packages/3-targets/postgres/1-target/src/core/migrations/rows-failing-check.ts create mode 100644 packages/3-targets/postgres/1-target/test/migrations/rows-failing-check.test.ts diff --git a/packages/1-framework/1-core/framework-components/src/control/control-migration-types.ts b/packages/1-framework/1-core/framework-components/src/control/control-migration-types.ts index b1e96d05abc0..f30fb480203c 100644 --- a/packages/1-framework/1-core/framework-components/src/control/control-migration-types.ts +++ b/packages/1-framework/1-core/framework-components/src/control/control-migration-types.ts @@ -422,6 +422,8 @@ export interface MigrationRunnerFailureMeta { readonly rowsWithoutValue?: readonly RowsWithoutValue[]; /** The data that still holds a stored enum value whose removal its storage cannot take, when that made a check fail. */ readonly rowsHoldingValue?: RowsHoldingValue; + /** The existing rows that a check the failed operation adds refuses, when that made the operation fail. */ + readonly rowsFailingCheck?: RowsFailingCheck; /** The objects in the database that kept the failed operation from running, such as a view on a table to drop. */ readonly blockingObjects?: readonly BlockingObject[]; /** The differences the schema check after apply found, when that check failed. */ @@ -508,6 +510,16 @@ export interface RowsHoldingValue { readonly keepDataEdit?: KeepDataEdit; } +/** The existing rows that a check the failed operation adds refuses. The failure's message names where the check is and how to find the rows. */ +export interface RowsFailingCheck { + /** The check, as the database names it. */ + readonly check: string; + /** The check the plan drops before it adds this one, when the plan replaces a check. While it holds, the rows cannot be changed to fit the new check. */ + readonly replacedCheck?: string; + /** The position of the operation in the operations of the failing space's plan. */ + readonly operationIndex: number; +} + /** A field that is to become required while existing rows have no value for it. */ export interface RowsWithoutValue { /** The field, when the destination contract names the storage that lacks the value. */ diff --git a/packages/1-framework/1-core/framework-components/src/exports/control.ts b/packages/1-framework/1-core/framework-components/src/exports/control.ts index 2cf93d476a53..0bc8979eb137 100644 --- a/packages/1-framework/1-core/framework-components/src/exports/control.ts +++ b/packages/1-framework/1-core/framework-components/src/exports/control.ts @@ -71,6 +71,7 @@ export type { MigrationScaffoldContext, OpFactoryCall, PlanOrigin, + RowsFailingCheck, RowsHoldingValue, RowsWithoutValue, SchemaEntityCoordinate, diff --git a/packages/1-framework/3-tooling/cli/src/control-api/operations/run-migration.ts b/packages/1-framework/3-tooling/cli/src/control-api/operations/run-migration.ts index 31e2d8794811..2b280c8d7c8b 100644 --- a/packages/1-framework/3-tooling/cli/src/control-api/operations/run-migration.ts +++ b/packages/1-framework/3-tooling/cli/src/control-api/operations/run-migration.ts @@ -11,6 +11,7 @@ import type { MigrationOperationPolicy, MigrationRunnerFailure, MigrationSubject, + RowsFailingCheck, RowsHoldingValue, TargetMigrationsCapability, } from '@internal/framework-components/control'; @@ -34,6 +35,7 @@ import type { FailedTypeChangeReport, OnControlProgress, PerSpaceExecutionEntry, + RowsFailingCheckReport, RowsHoldingValueReport, RunnerFailureFacts, } from '../types'; @@ -85,23 +87,36 @@ function rowsHoldingValueReport( }; } +function rowsFailingCheckReport( + fact: RowsFailingCheck | undefined, +): RowsFailingCheckReport | undefined { + if (fact === undefined) return undefined; + const { operationIndex: _operationIndex, ...report } = fact; + return report; +} + /** What the runner said blocked the failed operation, named as the advice names it. */ function failureReports( failure: MigrationRunnerFailure, resolutions: readonly OrderedResolution[], ): Pick< RunnerFailureFacts, - 'failedTypeChange' | 'rowsWithoutValue' | 'rowsHoldingValue' | 'failedMigration' + | 'failedTypeChange' + | 'rowsWithoutValue' + | 'rowsHoldingValue' + | 'rowsFailingCheck' + | 'failedMigration' > { const resolution = resolutions.find(({ spaceId }) => spaceId === failure.meta.failingSpace); if (resolution === undefined) return {}; const fieldText = (field: MigrationSubject) => subjectText(field, { origin: resolution.entry.destinationContract, statements: [] }); - const { failedTypeChange, rowsWithoutValue, rowsHoldingValue } = failure.meta; + const { failedTypeChange, rowsWithoutValue, rowsHoldingValue, rowsFailingCheck } = failure.meta; const operationIndex = failedTypeChange?.operationIndex ?? rowsWithoutValue?.[0]?.operationIndex ?? - rowsHoldingValue?.operationIndex; + rowsHoldingValue?.operationIndex ?? + rowsFailingCheck?.operationIndex; const edge = operationIndex !== undefined ? edgeOfOperation(resolution.entry.migrationEdges, operationIndex) @@ -129,6 +144,7 @@ function failureReports( ? undefined : rowsHoldingValueReport(rowsHoldingValue, resolution.entry.destinationContract), ), + ...ifDefined('rowsFailingCheck', rowsFailingCheckReport(rowsFailingCheck)), ...ifDefined( 'failedMigration', edge === undefined ? undefined : { spaceId: resolution.spaceId, edge }, diff --git a/packages/1-framework/3-tooling/cli/src/control-api/types.ts b/packages/1-framework/3-tooling/cli/src/control-api/types.ts index 2f995b67edc7..237c68a512bf 100644 --- a/packages/1-framework/3-tooling/cli/src/control-api/types.ts +++ b/packages/1-framework/3-tooling/cli/src/control-api/types.ts @@ -26,6 +26,7 @@ import type { MigrationPlanOperation, MigrationPlanSubjects, OperationPreview, + RowsFailingCheck, VerifyDatabaseResult, VerifyDatabaseSchemaResult, } from '@internal/framework-components/control'; @@ -532,10 +533,15 @@ export interface RunnerFailureFacts { readonly failedPrecheck?: FailedPrecheckReport; /** The data that still holds a stored enum value whose removal its storage cannot take. */ readonly rowsHoldingValue?: RowsHoldingValueReport; + /** The existing rows that a check the failed operation adds refuses. */ + readonly rowsFailingCheck?: RowsFailingCheckReport; /** The written migration of the failed operation, when the runner named the operation, or the last one applied when the schema check after apply failed. */ readonly failedMigration?: { readonly spaceId: string; readonly edge: AggregateMigrationEdgeRef }; } +/** The existing rows that a check the failed operation adds refuses, as the advice names them. */ +export type RowsFailingCheckReport = Omit; + /** The data that still holds the stored value of an enum value the plan removes from storage that cannot do without it. */ export interface RowsHoldingValueReport { /** The enum value, as a statement names it, such as `Status.ARCHIVED`. */ diff --git a/packages/1-framework/3-tooling/cli/src/utils/db-update-failure.ts b/packages/1-framework/3-tooling/cli/src/utils/db-update-failure.ts index f7ac8f750c65..6e538c7905a7 100644 --- a/packages/1-framework/3-tooling/cli/src/utils/db-update-failure.ts +++ b/packages/1-framework/3-tooling/cli/src/utils/db-update-failure.ts @@ -6,6 +6,7 @@ import { type CliInvocation, cliCommand } from './printed-command'; import { blockingObjectsAdvice, dbUpdateFailedTypeChangeAdvice, + dbUpdateRowsFailingCheckAdvice, dbUpdateRowsHoldingValueAdvice, dbUpdateRowsWithoutValueAdvice, } from './runner-failure-advice'; @@ -44,16 +45,22 @@ export function dbUpdateRunnerFailureError( databaseStorageHash, invocation, ) - : schemaCheckIssues(failure) !== undefined - ? [ - editFileAction( - `Change the contract source to resolve the issues listed, re-run \`${cliCommand(invocation, 'contract emit')}\`, then re-run \`${update}\``, - ), - ] - : [ - chooseAction( - `Inspect the reported conflict, reconcile schema drift if needed, then re-run \`${update}\``, - ), - ]; + : facts.rowsFailingCheck !== undefined + ? dbUpdateRowsFailingCheckAdvice( + facts.rowsFailingCheck, + databaseStorageHash, + invocation, + ) + : schemaCheckIssues(failure) !== undefined + ? [ + editFileAction( + `Change the contract source to resolve the issues listed, re-run \`${cliCommand(invocation, 'contract emit')}\`, then re-run \`${update}\``, + ), + ] + : [ + chooseAction( + `Inspect the reported conflict, reconcile schema drift if needed, then re-run \`${update}\``, + ), + ]; return runnerFailureError(failure, advice); } diff --git a/packages/1-framework/3-tooling/cli/src/utils/runner-failure-advice.ts b/packages/1-framework/3-tooling/cli/src/utils/runner-failure-advice.ts index 2e06e41e58a2..ab9bb10a449c 100644 --- a/packages/1-framework/3-tooling/cli/src/utils/runner-failure-advice.ts +++ b/packages/1-framework/3-tooling/cli/src/utils/runner-failure-advice.ts @@ -9,6 +9,7 @@ import { basename, join } from 'pathe'; import { statementFlag } from '../control-api/statements/statement-flag'; import type { FailedTypeChangeReport, + RowsFailingCheckReport, RowsHoldingValueReport, RowsWithoutValueReport, RunnerFailureFacts, @@ -204,6 +205,63 @@ function migrateRowsHoldingValueAdvice( ]; } +/** Where the operation that updates the rows goes among the operations of a migration.ts that replaces a check. */ +function betweenTheChecks(report: RowsFailingCheckReport, replacedCheck: string): string { + return `after the operation that drops check "${replacedCheck}" and before the operation that adds check "${report.check}"`; +} + +function updateOperation(report: RowsFailingCheckReport): string { + return `an operation that updates those rows to fit check "${report.check}"`; +} + +function fixRowsFirst(report: RowsFailingCheckReport, rerun: string): NextAction { + return chooseAction( + `Update or delete those rows so they fit check "${report.check}", then re-run ${quoted(rerun)}`, + ); +} + +/** The way out when existing rows fail a check `db update` adds: it cannot keep the data while the check it replaces holds. */ +export function dbUpdateRowsFailingCheckAdvice( + report: RowsFailingCheckReport, + databaseStorageHash: string | undefined, + invocation: CliInvocation, +): RunnerAdvice { + const update = cliCommand(invocation, 'db update', invocation.rerunArgs ?? []); + const { replacedCheck } = report; + if (replacedCheck === undefined) return [fixRowsFirst(report, update)]; + const cannotKeep = `${quoted(cliCommand(invocation, 'db update'))} cannot keep this data`; + if (databaseStorageHash === undefined) { + return [ + chooseAction( + `${cannotKeep}, and no migration ends at the contract the database is at, so ${quoted(cliCommand(invocation, 'migration plan'))} cannot plan from it. Drop check "${replacedCheck}" in the database yourself, update those rows to fit check "${report.check}", then re-run ${quoted(update)}`, + ), + ]; + } + const plan = printedPlanCommand(invocation, databaseStorageHash, invocation.statementArgs ?? []); + return [ + chooseAction( + `${cannotKeep}. Plan the change with ${quoted(plan)}, add ${updateOperation(report)} to the migration.ts it writes, ${betweenTheChecks(report, replacedCheck)}, ${runWrittenFile(invocation)}`, + ), + ]; +} + +function migrateRowsFailingCheckAdvice( + report: RowsFailingCheckReport, + failedMigration: FailedMigrationText | undefined, + invocation: CliInvocation, +): RunnerAdvice { + const { replacedCheck } = report; + if (replacedCheck === undefined) { + return [fixRowsFirst(report, cliCommand(invocation, 'db migrate'))]; + } + const { file, run } = failedMigrationFile(failedMigration); + return [ + editFileAction( + `To keep the data, add ${updateOperation(report)} to ${file}, ${betweenTheChecks(report, replacedCheck)}, ${run}, and re-run ${dbMigrateCommand(invocation)}`, + ), + ]; +} + /** * The way out of rows without a value for `db init`: a migration plan needs an origin contract, * which a database that `db init` never initialized does not have. @@ -477,6 +535,9 @@ export function migrateAdvice( if (facts.rowsHoldingValue !== undefined) { return migrateRowsHoldingValueAdvice(facts.rowsHoldingValue, failedMigration, invocation); } + if (facts.rowsFailingCheck !== undefined) { + return migrateRowsFailingCheckAdvice(facts.rowsFailingCheck, failedMigration, invocation); + } if (schemaCheckIssues(failure) !== undefined) { return migrateSchemaCheckAdvice(failedMigration, invocation); } diff --git a/packages/1-framework/3-tooling/cli/test/utils/rows-failing-check-advice.test.ts b/packages/1-framework/3-tooling/cli/test/utils/rows-failing-check-advice.test.ts new file mode 100644 index 000000000000..1e51b73f8938 --- /dev/null +++ b/packages/1-framework/3-tooling/cli/test/utils/rows-failing-check-advice.test.ts @@ -0,0 +1,91 @@ +import { migrationRunnerFailure } from '@internal/framework-components/control'; +import { describe, expect, it } from 'vitest'; +import { dbUpdateRunnerFailureError } from '../../src/utils/db-update-failure'; +import { migrateRunnerFailureError } from '../../src/utils/migrate-failure'; + +const PROJECT = { cwd: '/work/app', configPath: undefined, dbGiven: false }; +const FROM = 'sha256:0123'; +const QUERY = `SELECT * FROM "public"."user" WHERE NOT ("address" IS NULL)`; + +const failure = migrationRunnerFailure( + 'MIGRATION.EXECUTION_FAILED', + 'Rows of table "user" fail check "user_address_shape_5c02601b" on column "address", so the check cannot be added.', + { why: `Find them with \`${QUERY}\`.`, meta: { databaseUnchanged: true } }, +); + +const replacing = { + check: 'user_address_shape_5c02601b', + replacedCheck: 'user_address_shape_9a7e984c', +} as const; + +const { replacedCheck: _replaced, ...adding } = replacing; + +function labels(error: { readonly nextActions?: readonly { readonly label: string }[] }) { + return (error.nextActions ?? []).map(({ label }) => label); +} + +describe('the advice when existing rows fail a check the apply adds', () => { + it('on db migrate, says to update the rows in the failed migration between the drop of the old check and the add of the new one', () => { + const error = migrateRunnerFailureError( + failure, + { rowsFailingCheck: replacing }, + { directory: '/work/app/migrations/app/20260101T0000_country', from: FROM }, + PROJECT, + ); + + expect(labels(error)).toEqual([ + 'To keep the data, add an operation that updates those rows to fit check "user_address_shape_5c02601b" to `migrations/app/20260101T0000_country/migration.ts`, after the operation that drops check "user_address_shape_9a7e984c" and before the operation that adds check "user_address_shape_5c02601b", run `node migrations/app/20260101T0000_country/migration.ts`, and re-run `{bin} db migrate`', + ]); + expect(JSON.stringify(error.nextActions)).not.toContain('--to'); + }); + + it('on db migrate, says to fix the rows first when the check replaces none', () => { + const error = migrateRunnerFailureError( + failure, + { rowsFailingCheck: adding }, + { directory: '/work/app/migrations/app/20260101T0000_country', from: FROM }, + PROJECT, + ); + + expect(labels(error)).toEqual([ + 'Update or delete those rows so they fit check "user_address_shape_5c02601b", then re-run `{bin} db migrate`', + ]); + }); + + it('on db update, says it cannot keep the data, and to plan the change with migration plan instead', () => { + const error = dbUpdateRunnerFailureError( + failure, + { rowsFailingCheck: replacing }, + { databaseStorageHash: FROM, invocation: PROJECT }, + ); + + expect(labels(error)).toEqual([ + '`{bin} db update` cannot keep this data. Plan the change with `{bin} migration plan --from sha256:0123`, add an operation that updates those rows to fit check "user_address_shape_5c02601b" to the migration.ts it writes, after the operation that drops check "user_address_shape_9a7e984c" and before the operation that adds check "user_address_shape_5c02601b", run `node /migration.ts` for the directory it writes, and apply it with `{bin} db migrate`', + ]); + expect(JSON.stringify(error.nextActions)).not.toContain('drift'); + }); + + it('on db update with no migration at the database contract, says to drop the old check and fix the rows in the database', () => { + const error = dbUpdateRunnerFailureError( + failure, + { rowsFailingCheck: replacing }, + { databaseStorageHash: undefined, invocation: PROJECT }, + ); + + expect(labels(error)).toEqual([ + '`{bin} db update` cannot keep this data, and no migration ends at the contract the database is at, so `{bin} migration plan` cannot plan from it. Drop check "user_address_shape_9a7e984c" in the database yourself, update those rows to fit check "user_address_shape_5c02601b", then re-run `{bin} db update`', + ]); + }); + + it('on db update, says to fix the rows first when the check replaces none', () => { + const error = dbUpdateRunnerFailureError( + failure, + { rowsFailingCheck: adding }, + { databaseStorageHash: FROM, invocation: PROJECT }, + ); + + expect(labels(error)).toEqual([ + 'Update or delete those rows so they fit check "user_address_shape_5c02601b", then re-run `{bin} db update`', + ]); + }); +}); diff --git a/packages/3-targets/postgres/1-target/src/core/migrations/operations/constraints.ts b/packages/3-targets/postgres/1-target/src/core/migrations/operations/constraints.ts index b29753329bb5..20a34d1bd135 100644 --- a/packages/3-targets/postgres/1-target/src/core/migrations/operations/constraints.ts +++ b/packages/3-targets/postgres/1-target/src/core/migrations/operations/constraints.ts @@ -343,6 +343,10 @@ export async function renameConstraint( }; } +export function dropCheckConstraintId(tableName: string, constraintName: string): string { + return `dropCheckConstraint.${tableName}.${constraintName}`; +} + export async function dropCheckConstraint( schemaName: string, tableName: string, @@ -356,7 +360,7 @@ export async function dropCheckConstraint( table: tableName, }); return { - id: `dropCheckConstraint.${tableName}.${constraintName}`, + id: dropCheckConstraintId(tableName, constraintName), label: `Drop check constraint "${constraintName}" on "${tableName}"`, operationClass: 'widening', target: targetDetails('checkConstraint', constraintName, schemaName, tableName), diff --git a/packages/3-targets/postgres/1-target/src/core/migrations/rows-failing-check.ts b/packages/3-targets/postgres/1-target/src/core/migrations/rows-failing-check.ts new file mode 100644 index 000000000000..9f38bceef55c --- /dev/null +++ b/packages/3-targets/postgres/1-target/src/core/migrations/rows-failing-check.ts @@ -0,0 +1,87 @@ +import type { SqlMigrationPlanOperation } from '@internal/family-sql/control'; +import type { RowsFailingCheck } from '@internal/framework-components/control'; +import type { SqlStorage } from '@internal/sql-contract/types'; +import { CHECK_KINDS, composeCheckWirePrefix, parseWireName } from '@internal/sql-schema-ir/naming'; +import { ifDefined } from '@internal/utils/defined'; +import { dropCheckConstraintId } from './operations/constraints'; +import { qualifyTableName } from './planner-sql-checks'; +import type { PostgresPlanTargetDetails } from './planner-target-details'; + +/** SQLSTATE `check_violation`: a row fails a check. */ +export const CHECK_VIOLATION = '23514'; + +type Operation = SqlMigrationPlanOperation; + +/** + * The existing rows that the check `operation` adds refuses, when `constraint`, the check the + * database names in its `check_violation`, is that check and the contract declares it. + */ +export function rowsFailingCheckOf(input: { + readonly operation: Operation; + readonly constraint: string | undefined; + readonly storage: SqlStorage; + readonly operations: readonly Operation[]; + readonly operationIndex: number; +}): + | { readonly summary: string; readonly why: string; readonly fact: RowsFailingCheck } + | undefined { + const details = input.operation.target.details; + const table = details?.table; + if (details?.objectType !== 'checkConstraint' || table === undefined) return undefined; + if (input.constraint !== details.name) return undefined; + const declared = declaredCheck(input.storage, table, details.name); + if (declared === undefined) return undefined; + const { prefix, expression } = declared.check; + const column = + prefix === undefined + ? undefined + : declared.columns.find((name) => + CHECK_KINDS.some((kind) => composeCheckWirePrefix(table, name, kind) === prefix), + ); + const replacedCheck = + prefix === undefined + ? undefined + : input.operations + .slice(0, input.operationIndex) + .reverse() + .map(({ id, target }) => + target.details === undefined ? undefined : { id, ...target.details }, + ) + .find( + (dropped) => + dropped !== undefined && + dropped.id === dropCheckConstraintId(table, dropped.name) && + parseWireName(dropped.name)?.prefix === prefix, + )?.name; + const query = `SELECT * FROM ${qualifyTableName(details.schema, table)} WHERE NOT (${expression})`; + const onColumn = column === undefined ? '' : ` on column "${column}"`; + return { + summary: `Rows of table "${table}" fail check "${details.name}"${onColumn}, so the check cannot be added.`, + why: `Find them with \`${query}\`.`, + fact: { + check: details.name, + ...ifDefined('replacedCheck', replacedCheck), + operationIndex: input.operationIndex, + }, + }; +} + +function declaredCheck( + storage: SqlStorage, + table: string, + name: string, +): + | { + readonly check: { readonly prefix?: string; readonly expression: string }; + readonly columns: readonly string[]; + } + | undefined { + for (const namespace of Object.values(storage.namespaces)) { + const declared = namespace.entries.table?.[table]; + const check = declared?.checks?.find((candidate) => candidate.name === name); + if (declared !== undefined && check !== undefined) { + return { check, columns: Object.keys(declared.columns) }; + } + } + return undefined; +} diff --git a/packages/3-targets/postgres/1-target/src/core/migrations/runner.ts b/packages/3-targets/postgres/1-target/src/core/migrations/runner.ts index 4b30fc3315c6..66001a0b1b7b 100644 --- a/packages/3-targets/postgres/1-target/src/core/migrations/runner.ts +++ b/packages/3-targets/postgres/1-target/src/core/migrations/runner.ts @@ -55,6 +55,7 @@ import { import { MARKER_LOCK_KEY, MARKER_LOCK_SQL } from './marker-lock'; import { qualifyTableName } from './planner-sql-checks'; import type { PostgresPlanTargetDetails } from './planner-target-details'; +import { CHECK_VIOLATION, rowsFailingCheckOf } from './rows-failing-check'; import { SCHEMA_OBJECTS_SQL, schemaBlockingObjects, @@ -540,7 +541,7 @@ class PostgresMigrationRunner implements SqlMigrationRunner['driver'], operation: SqlMigrationPlanOperation, - plan: { readonly contract: Contract; readonly operationIndex: number }, + plan: PlanPosition, ): Promise> { for (const step of operation.execute) { try { @@ -564,6 +565,29 @@ class PostgresMigrationRunner implements SqlMigrationRunner { + it('names the check, the table and the column, gives the query, and the check the plan drops before it', () => { + expect( + rowsFailingCheckOf({ + operation: add, + constraint: NEW, + storage: storageWith(wireCheck), + operations: [drop, add], + operationIndex: 1, + }), + ).toEqual({ + summary: `Rows of table "user" fail check "${NEW}" on column "address", so the check cannot be added.`, + why: `Find them with \`SELECT * FROM "public"."user" WHERE NOT ("address" IS NOT NULL)\`.`, + fact: { check: NEW, replacedCheck: OLD, operationIndex: 1 }, + }); + }); + + it('names no column and no replaced check for a check named by hand', () => { + const exact = new CheckConstraint({ + naming: { kind: 'exact', name: 'address_present' }, + expression: NEW_EXPRESSION, + }); + const addExact = checkOperation('checkConstraint.user.address_present', 'address_present'); + expect( + rowsFailingCheckOf({ + operation: addExact, + constraint: 'address_present', + storage: storageWith(exact), + operations: [drop, addExact], + operationIndex: 1, + }), + ).toEqual({ + summary: 'Rows of table "user" fail check "address_present", so the check cannot be added.', + why: 'Find them with `SELECT * FROM "public"."user" WHERE NOT ("address" IS NOT NULL)`.', + fact: { check: 'address_present', operationIndex: 1 }, + }); + }); + + it('says nothing when the violated check is not the one the operation adds, or the contract does not declare it', () => { + const storage = storageWith(wireCheck); + expect([ + rowsFailingCheckOf({ + operation: add, + constraint: 'other', + storage, + operations: [add], + operationIndex: 0, + }), + rowsFailingCheckOf({ + operation: checkOperation('checkConstraint.user.undeclared', 'undeclared'), + constraint: 'undeclared', + storage, + operations: [], + operationIndex: 0, + }), + ]).toEqual([undefined, undefined]); + }); +}); diff --git a/test/integration/test/cli-journeys/value-object-checks.e2e.test.ts b/test/integration/test/cli-journeys/value-object-checks.e2e.test.ts index e67061338a24..9a47ab2f6d85 100644 --- a/test/integration/test/cli-journeys/value-object-checks.e2e.test.ts +++ b/test/integration/test/cli-journeys/value-object-checks.e2e.test.ts @@ -7,7 +7,7 @@ * * - V1: the first migration creates the value object check. A row the ORM writes is accepted; a row with a key `Address` does not declare is refused, naming the check. * - V2: adding a member, and then making a member nullable, each drop the old check and add the new one, on a table whose rows fit the new shape. - * - V3: adding a required member to a table that holds a row without it fails at apply, names the check, and changes nothing. + * - V3: adding a required member to a table that holds a row without it fails at apply, names the check, the table and the column, gives the query that finds the rows and the route that keeps them, and changes nothing. Following that route applies the change and keeps the row. * - V4: adding a variant replaces the discriminator check, and the new variant's value is accepted. * - V5: changing a value object that only an external table's column holds changes the storage hash and needs no operation: `migration plan` writes a migration with no operations, and `migrate` moves the marker. */ @@ -33,6 +33,7 @@ import { runDbVerify, runMigrate, runMigrationPlan, + selfEmitMigration, setupJourney, sql, timeouts, @@ -187,6 +188,54 @@ async function expectSettled(ctx: JourneyContext, via: Via, label: string): Prom ).toEqual([]); } +const GIVE_EVERY_ADDRESS_A_COUNTRY = `rawSql({ + id: 'data.user.address.country', + label: 'Give every address a country', + operationClass: 'data', + target: { id: 'postgres' }, + precheck: [], + execute: [ + { + description: 'give every address a country', + sql: \`UPDATE "public"."user" SET address = address || '{"country": "NO"}' WHERE address IS NOT NULL\`, + }, + ], + postcheck: [], + }), + `; + +/** Keeps the rows as the failure's advice says: in the failed migration.ts, or for `db update` in the database by hand. */ +async function followTheAdvice( + ctx: JourneyContext, + via: Via, + oldCheck: string | undefined, + connectionString: string, +): Promise { + if (via === 'db update') { + await sql(connectionString, `ALTER TABLE "public"."user" DROP CONSTRAINT "${oldCheck}"`); + await sql( + connectionString, + `UPDATE "public"."user" SET address = address || '{"country": "NO"}' WHERE address IS NOT NULL`, + ); + const update = await runDbUpdate(ctx, ['--json']); + expect(update.exitCode, `V3.06: db update: ${JSON.stringify(engineError(update))}`).toBe(0); + return; + } + const dir = join(ctx.testDir, 'migrations/app', latestMigrationDirName(ctx)); + const path = join(dir, 'migration.ts'); + const written = readFileSync(path, 'utf-8'); + const filled = written + .replace(/import \{ ([^}]*) \} from '([^']+\/migration)';/, "import { $1, rawSql } from '$2';") + .replace('this.addCheckConstraint(', `${GIVE_EVERY_ADDRESS_A_COUNTRY}this.addCheckConstraint(`); + expect(filled, 'V3.06: the update sits before the new check').toContain('rawSql({'); + expect(filled, 'V3.06: rawSql is imported').toContain('rawSql }'); + writeFileSync(path, filled); + const emitted = await selfEmitMigration(ctx, ['--dir', dir, '--config', ctx.configPath]); + expect(emitted.exitCode, `V3.06: run migration.ts: ${emitted.stdout}${emitted.stderr}`).toBe(0); + const migrate = await runMigrate(ctx, ['--json']); + expect(migrate.exitCode, `V3.06: migrate: ${JSON.stringify(engineError(migrate))}`).toBe(0); +} + async function checkNames(connectionString: string, table: string): Promise { const { rows } = await sql( connectionString, @@ -376,8 +425,36 @@ withTempDir(({ createTempDir }) => { failed = await runDbUpdate(ctx, ['--json']); } expect(failed.exitCode, 'V3.04: the apply fails').not.toBe(0); - expect(JSON.stringify(engineError(failed)), 'V3.04: the failure names the check').toMatch( - /user_address_shape_[0-9a-f]{8}/, + const [oldCheck] = before; + const error = engineError(failed); + expect( + error, + 'V3.04: the failure names the check, the table, the column and the route', + ).toMatchObject({ + code: 'MIGRATION.EXECUTION_FAILED', + summary: expect.stringMatching( + /^Rows of table "user" fail check "user_address_shape_[0-9a-f]{8}" on column "address", so the check cannot be added\.$/, + ), + why: expect.stringContaining('Find them with `SELECT * FROM "public"."user" WHERE NOT ('), + nextActions: [ + { + label: + via === 'migrate' + ? expect.stringMatching( + new RegExp( + `^To keep the data, add an operation that updates those rows to fit check "user_address_shape_[0-9a-f]{8}" to \`.+/migration\\.ts\`, after the operation that drops check "${oldCheck}" and before the operation that adds check`, + ), + ) + : expect.stringMatching( + new RegExp( + `cannot keep this data, and no migration ends at the contract the database is at, so .+ cannot plan from it\\. Drop check "${oldCheck}" in the database yourself`, + ), + ), + }, + ], + }); + expect(JSON.stringify(error), 'V3.04: no advice that does not fit').not.toMatch( + /drift|/, ); expect(await checkNames(db.connectionString, 'user'), 'V3.05: the old check stays').toEqual( @@ -388,6 +465,14 @@ withTempDir(({ createTempDir }) => { (await sql(db.connectionString, 'SELECT id, address FROM "user"')).rows, 'V3.05: the row stays', ).toEqual([{ id: 1, address: { street: 'Main St', city: 'Oslo' } }]); + + await followTheAdvice(ctx, via, oldCheck, db.connectionString); + expect( + (await sql(db.connectionString, 'SELECT id, address FROM "user"')).rows, + 'V3.06: the row is kept and fits', + ).toEqual([{ id: 1, address: { street: 'Main St', city: 'Oslo', country: 'NO' } }]); + const verify = await runDbVerify(ctx, ['--strict']); + expect(verify.exitCode, `V3.07: db verify --strict: ${verify.stderr}`).toBe(0); }, timeouts.spinUpPpgDev, ); From 7db112679066b7dded50186123cda342382c8a60 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 21:07:53 +0200 Subject: [PATCH 102/104] TML-3537: the upgrade steps upgrade the packages before the script and apply with --advance-ref db, so the next plan writes nothing; the advice to fix rows re-runs db migrate with its flags Signed-off-by: willbot Signed-off-by: Will Madden --- .../3-tooling/cli/src/utils/runner-failure-advice.ts | 2 +- .../cli/test/utils/rows-failing-check-advice.test.ts | 6 +++--- .../value-objects-in-storage/app/instructions.md | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/1-framework/3-tooling/cli/src/utils/runner-failure-advice.ts b/packages/1-framework/3-tooling/cli/src/utils/runner-failure-advice.ts index ab9bb10a449c..db7a3dc3143c 100644 --- a/packages/1-framework/3-tooling/cli/src/utils/runner-failure-advice.ts +++ b/packages/1-framework/3-tooling/cli/src/utils/runner-failure-advice.ts @@ -252,7 +252,7 @@ function migrateRowsFailingCheckAdvice( ): RunnerAdvice { const { replacedCheck } = report; if (replacedCheck === undefined) { - return [fixRowsFirst(report, cliCommand(invocation, 'db migrate'))]; + return [fixRowsFirst(report, cliCommand(invocation, 'db migrate', invocation.rerunArgs ?? []))]; } const { file, run } = failedMigrationFile(failedMigration); return [ diff --git a/packages/1-framework/3-tooling/cli/test/utils/rows-failing-check-advice.test.ts b/packages/1-framework/3-tooling/cli/test/utils/rows-failing-check-advice.test.ts index 1e51b73f8938..3164cf8ca935 100644 --- a/packages/1-framework/3-tooling/cli/test/utils/rows-failing-check-advice.test.ts +++ b/packages/1-framework/3-tooling/cli/test/utils/rows-failing-check-advice.test.ts @@ -39,16 +39,16 @@ describe('the advice when existing rows fail a check the apply adds', () => { expect(JSON.stringify(error.nextActions)).not.toContain('--to'); }); - it('on db migrate, says to fix the rows first when the check replaces none', () => { + it('on db migrate, says to fix the rows first when the check replaces none, and to re-run with the same flags', () => { const error = migrateRunnerFailureError( failure, { rowsFailingCheck: adding }, { directory: '/work/app/migrations/app/20260101T0000_country', from: FROM }, - PROJECT, + { ...PROJECT, rerunArgs: ['--advance-ref', 'db'] }, ); expect(labels(error)).toEqual([ - 'Update or delete those rows so they fit check "user_address_shape_5c02601b", then re-run `{bin} db migrate`', + 'Update or delete those rows so they fit check "user_address_shape_5c02601b", then re-run `{bin} db migrate --advance-ref db`', ]); }); diff --git a/upgrade-instructions/pending/value-objects-in-storage/app/instructions.md b/upgrade-instructions/pending/value-objects-in-storage/app/instructions.md index 6aa1543d9fbf..6ee3840965cd 100644 --- a/upgrade-instructions/pending/value-objects-in-storage/app/instructions.md +++ b/upgrade-instructions/pending/value-objects-in-storage/app/instructions.md @@ -27,7 +27,7 @@ changes: ## `value-objects-in-storage` -Before you upgrade, apply every pending migration to each database (development, staging, production) with the release you are upgrading from, so each database is at the last migration. Then commit your work, so the script's changes can be reviewed and undone with git. Run the script before you re-emit the contract: `prisma db sign` must see the contract the database was created from before a re-emit adds the new checks. Run it from the project root: +Before you upgrade, apply every pending migration to each database (development, staging, production) with the release you are upgrading from, so each database is at the last migration. Then commit your work, so the script's changes can be reviewed and undone with git. Then upgrade the Prisma packages to this release. Run the script before you re-emit the contract: `prisma db sign` must see the contract the database was created from before a re-emit adds the new checks. Run it from the project root: ```sh node /scripts/value-objects-in-storage/value-objects-in-storage.ts @@ -48,9 +48,9 @@ The script rewrites a snapshot's `contract.d.ts` for the moved value objects. In Then: -1. Sign each database (development, staging, production) with the rewritten contract, before you re-emit: `prisma db sign --db "$DATABASE_URL"`. Its marker still holds the old storage hash, so until it is signed `prisma db migrate` refuses with `MIGRATION.MARKER_MISMATCH` and `prisma db verify` with `CONTRACT.MARKER_MISMATCH`. This works for a database at the last migration. +1. Sign each database (development, staging, production) with the rewritten contract, before you re-emit: `prisma db sign --db "$DATABASE_URL"`. Its marker still holds the old storage hash, so until it is signed `prisma db migrate` refuses with `MIGRATION.MARKER_MISMATCH` and `prisma db verify` with `CONTRACT.MARKER_MISMATCH`. This works for a database at the last migration. It also points your local `db` ref at the rewritten contract, writing the ref if the project had none; `prisma migration plan` starts from that ref. 2. Re-emit the contract: `prisma contract emit`. -3. On PostgreSQL, plan and apply the migration that adds the new checks, as `postgres-value-object-checks` describes. On MongoDB there is nothing to plan: the collection validators do not change. +3. On PostgreSQL, plan the migration that adds the new checks with `prisma migration plan`, and apply it to each database with `prisma db migrate --advance-ref db --db "$DATABASE_URL"`, as `postgres-value-object-checks` describes. `--advance-ref db` moves the `db` ref to the new contract. Without it the ref stays at the contract of step 1, and the next `prisma migration plan` starts from there and writes a second migration that adds the same checks. On MongoDB there is nothing to plan: the collection validators do not change. A database you could not migrate before the upgrade is behind the last migration, and `prisma db sign --db "$DATABASE_URL"` fails on it with `CONTRACT.SCHEMA_VERIFICATION_FAILED`, because it signs against the last contract. Do not follow its advice to run `prisma db update`: that skips the pending migrations. Instead, sign the database at the new hash of the hash its marker holds, then migrate it: @@ -78,7 +78,7 @@ type Outputs = ValueObjectTypesFromStorage__shape_`. It accepts a JSON object with every required member, each of its JSON type, no other key, and for a list column an array of such objects; a nullable column also accepts SQL `NULL` and JSON `null`. For each discriminator column it derives `
__check_`, which accepts only the variants' values. A table whose control policy is not managed gets neither check. -After you re-emit the contract, `prisma migration plan` writes a migration that adds the checks. A project with no `db` ref names the origin, the last migration: `prisma migration plan --from `; without it the command refuses with `MIGRATION.PLAN_ORIGIN_UNKNOWN`, as it does for any plan in such a project. Its DDL preview prints each check's expression. Before you run `prisma db migrate`, find the rows that would fail by selecting the rows where the expression is not true, for example: +After you re-emit the contract, `prisma migration plan` writes a migration that adds the checks. It starts from the `db` ref that `prisma db sign` wrote. Its DDL preview prints each check's expression. Before you run `prisma db migrate`, find the rows that would fail by selecting the rows where the expression is not true, for example: ```sql SELECT id, address FROM "public"."user" @@ -89,7 +89,7 @@ SELECT id, type FROM "public"."task" WHERE NOT ("type" IN ('bug', 'feature')); Adding a check takes a lock on the table while Postgres reads every row to check it. Any later change to a value object renames the check, so the check is dropped and added again, and the rows are read again. Apply the migration when the table can wait for that read. -Copy the expression from the preview between `CHECK (` and the closing `)`. Fix or delete each row the query returns, for example by removing the extra keys with `UPDATE "public"."user" SET address = address - 'extra' WHERE address ? 'extra'`, then apply the migration. A migration that fails on a row changes nothing, and the error names the check; fix the rows and run `prisma db migrate` again. +Copy the expression from the preview between `CHECK (` and the closing `)`. Fix or delete each row the query returns, for example by removing the extra keys with `UPDATE "public"."user" SET address = address - 'extra' WHERE address ? 'extra'`, then apply the migration with `prisma db migrate --advance-ref db`. A migration that fails on a row changes nothing, and the error names the check, its table and column, and the query that finds the rows; fix the rows and run `prisma db migrate --advance-ref db` again. ## `discriminator-typed-by-variant-values` From d846707047ea957f4e48bb9df8712d63ea92f9ca Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 21:30:23 +0200 Subject: [PATCH 103/104] TML-3537: the demo migrations sort their snapshot imports Signed-off-by: willbot Signed-off-by: Will Madden --- .../app/20260922T1218_add_post_title_search/migration.ts | 8 ++++---- .../app/20261009T1354_add_post_body_search/migration.ts | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/prisma-8-demo/migrations/app/20260922T1218_add_post_title_search/migration.ts b/examples/prisma-8-demo/migrations/app/20260922T1218_add_post_title_search/migration.ts index 43660ecfa642..08c9514102d9 100755 --- a/examples/prisma-8-demo/migrations/app/20260922T1218_add_post_title_search/migration.ts +++ b/examples/prisma-8-demo/migrations/app/20260922T1218_add_post_title_search/migration.ts @@ -1,13 +1,13 @@ #!/usr/bin/env -S node import { Migration, MigrationCLI } from '@prisma/orm-postgres/migration'; -import type { Contract as Start } from '../../snapshots/705398c87a4f2c7e2ac0a49002b9061963f726b8875ffe53da1feac8df00b040/contract'; -import startContract from '../../snapshots/705398c87a4f2c7e2ac0a49002b9061963f726b8875ffe53da1feac8df00b040/contract.json' with { - type: 'json', -}; import type { Contract as End } from '../../snapshots/53cf82d46a1c1c79995d8ea54e981886bbdce3d4cd9944506960643877c474f5/contract'; import endContract from '../../snapshots/53cf82d46a1c1c79995d8ea54e981886bbdce3d4cd9944506960643877c474f5/contract.json' with { type: 'json', }; +import type { Contract as Start } from '../../snapshots/705398c87a4f2c7e2ac0a49002b9061963f726b8875ffe53da1feac8df00b040/contract'; +import startContract from '../../snapshots/705398c87a4f2c7e2ac0a49002b9061963f726b8875ffe53da1feac8df00b040/contract.json' with { + type: 'json', +}; export default class M extends Migration { override readonly startContractJson = startContract; diff --git a/examples/prisma-8-demo/migrations/app/20261009T1354_add_post_body_search/migration.ts b/examples/prisma-8-demo/migrations/app/20261009T1354_add_post_body_search/migration.ts index 074d9344b649..26afa740e0fc 100755 --- a/examples/prisma-8-demo/migrations/app/20261009T1354_add_post_body_search/migration.ts +++ b/examples/prisma-8-demo/migrations/app/20261009T1354_add_post_body_search/migration.ts @@ -1,13 +1,13 @@ #!/usr/bin/env -S node import { col, Migration, MigrationCLI } from '@prisma/orm-postgres/migration'; -import type { Contract as End } from '../../snapshots/e1916bd73bb2388302d81a1eaa17125c0cedb6ce47b489a7adeeb41c2ef50ebf/contract'; -import endContract from '../../snapshots/e1916bd73bb2388302d81a1eaa17125c0cedb6ce47b489a7adeeb41c2ef50ebf/contract.json' with { - type: 'json', -}; import type { Contract as Start } from '../../snapshots/53cf82d46a1c1c79995d8ea54e981886bbdce3d4cd9944506960643877c474f5/contract'; import startContract from '../../snapshots/53cf82d46a1c1c79995d8ea54e981886bbdce3d4cd9944506960643877c474f5/contract.json' with { type: 'json', }; +import type { Contract as End } from '../../snapshots/e1916bd73bb2388302d81a1eaa17125c0cedb6ce47b489a7adeeb41c2ef50ebf/contract'; +import endContract from '../../snapshots/e1916bd73bb2388302d81a1eaa17125c0cedb6ce47b489a7adeeb41c2ef50ebf/contract.json' with { + type: 'json', +}; export default class M extends Migration { override readonly startContractJson = startContract; From b8bc67751212ed4e65418c243a68836d5a9a5557 Mon Sep 17 00:00:00 2001 From: willbot Date: Wed, 28 Oct 2026 15:12:36 +0100 Subject: [PATCH 104/104] TML-3537: the value object journeys load only workspace packages, so the CLI they run in-process sees one copy of Prisma ORM Signed-off-by: willbot Signed-off-by: Will Madden --- .../value-object-checks.e2e.test.ts | 28 ++++------------ .../value-object-validator.mongo.e2e.test.ts | 32 +++---------------- .../test/utils/journey-workspace-helpers.ts | 28 ++++++++++++++++ 3 files changed, 39 insertions(+), 49 deletions(-) diff --git a/test/integration/test/cli-journeys/value-object-checks.e2e.test.ts b/test/integration/test/cli-journeys/value-object-checks.e2e.test.ts index 9a47ab2f6d85..2b18055a3530 100644 --- a/test/integration/test/cli-journeys/value-object-checks.e2e.test.ts +++ b/test/integration/test/cli-journeys/value-object-checks.e2e.test.ts @@ -13,10 +13,6 @@ */ import { readFileSync, writeFileSync } from 'node:fs'; -import type { Contract } from '@prisma/orm-postgres/contract/types'; -import type { SqlStorage } from '@prisma/orm-postgres/family-contract/types'; -import postgres from '@prisma/orm-postgres/runtime'; -import { contractFromJson } from '@prisma/orm-postgres/static'; import { join } from 'pathe'; import { describe, expect, it } from 'vitest'; import { withTempDir } from '../utils/cli-test-helpers'; @@ -39,6 +35,7 @@ import { timeouts, useDevDatabase, } from '../utils/journey-test-helpers'; +import { createRowThroughOrm } from '../utils/journey-workspace-helpers'; type Via = 'migrate' | 'db update'; const VIAS: readonly Via[] = ['migrate', 'db update']; @@ -262,29 +259,18 @@ async function insertError(connectionString: string, query: string): Promise): Promise }; - }; -} - /** Writes a user through the ORM of the emitted contract. */ -async function createUserThroughOrm( +function createUserThroughOrm( ctx: JourneyContext, connectionString: string, row: Record, ): Promise { - const contractJson = JSON.parse(readFileSync(join(ctx.testDir, 'contract.json'), 'utf-8')); - const client = postgres({ - contract: contractFromJson>(contractJson), - url: connectionString, + return createRowThroughOrm({ + contractJson: JSON.parse(readFileSync(join(ctx.testDir, 'contract.json'), 'utf-8')), + connectionString, + model: 'User', + row, }); - const runtime = await client.connect(); - try { - return await (client.orm as unknown as UserOrm).public.User.create(row); - } finally { - await runtime.close(); - } } withTempDir(({ createTempDir }) => { diff --git a/test/integration/test/cli-journeys/value-object-validator.mongo.e2e.test.ts b/test/integration/test/cli-journeys/value-object-validator.mongo.e2e.test.ts index 070c1b71617a..05abd8a9f910 100644 --- a/test/integration/test/cli-journeys/value-object-validator.mongo.e2e.test.ts +++ b/test/integration/test/cli-journeys/value-object-validator.mongo.e2e.test.ts @@ -11,7 +11,7 @@ import { timeouts } from '@repo/test-utils'; import { type Db, MongoClient } from 'mongodb'; import { MongoMemoryReplSet } from 'mongodb-memory-server'; import { afterAll, afterEach, beforeAll, describe, expect, it } from 'vitest'; -import { fixtureAppDir } from '../utils/cli-test-helpers'; +import { fixtureAppDir, writeProjectManifest } from '../utils/cli-test-helpers'; import { getMigrationDirs, type JourneyContext, @@ -24,6 +24,7 @@ import { runMigrate, runMigrationPlan, } from '../utils/journey-test-helpers'; +import { writeMongoPslConfig } from '../utils/journey-workspace-helpers'; function contractWith(addressMembers: string): string { return `// use prisma-8 @@ -56,33 +57,8 @@ function setupProject(connectionString: string): JourneyContext { const outputDir = join(testDir, 'output'); mkdirSync(outputDir, { recursive: true }); mkdirSync(join(testDir, 'migrations'), { recursive: true }); - writeFileSync( - join(testDir, 'package.json'), - JSON.stringify({ - name: 'mongo-value-object-journey', - private: true, - type: 'module', - dependencies: { '@prisma/orm-mongo': 'workspace:0.16.0' }, - }), - 'utf-8', - ); - const configPath = join(testDir, 'prisma.config.ts'); - writeFileSync( - configPath, - [ - "import { defineConfig as ormConfig } from '@prisma/orm-mongo/config';", - "import { definePrismaConfig } from '@prisma/cli-engine';", - '', - 'export default definePrismaConfig({', - ' orm: ormConfig({', - " contract: './contract.prisma',", - " output: 'output',", - ` db: { connection: ${JSON.stringify(connectionString)} },`, - ' }),', - '});', - '', - ].join('\n'), - ); + writeProjectManifest(testDir); + const configPath = writeMongoPslConfig(testDir, connectionString); return { testDir, configPath, outputDir }; } diff --git a/test/integration/test/utils/journey-workspace-helpers.ts b/test/integration/test/utils/journey-workspace-helpers.ts index c1f9b8b81a01..0771b1eaedcc 100644 --- a/test/integration/test/utils/journey-workspace-helpers.ts +++ b/test/integration/test/utils/journey-workspace-helpers.ts @@ -155,6 +155,34 @@ export async function selectRows(options: { } } +/** Creates a row of `model` in the `public` namespace through a Postgres client built from the emitted contract. */ +export async function createRowThroughOrm(options: { + readonly contractJson: unknown; + readonly connectionString: string; + readonly model: string; + readonly row: Record; +}): Promise { + const client = postgres({ + contract: contractFromJson>(options.contractJson), + url: options.connectionString, + }); + const runtime = await client.connect(); + try { + const models = ( + client.orm as unknown as { + readonly public: Record): Promise }>; + } + ).public; + const collection = models[options.model]; + if (collection === undefined) { + throw new Error(`the ORM exposes no ${options.model}; it has ${Object.keys(models)}`); + } + return await collection.create(options.row); + } finally { + await runtime.close(); + } +} + /** * The PSL `contract infer` would write for the Postgres schema `namespace`. *