| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
The caller builds the PSL binder, and an unknown name gets one diagnostic (#30563) ## Linked issue n/a: no Linear ticket (omitted at the operator's request). This is the first of two PRs for go-to-definition in the PSL language server. The second PR adds `textDocument/definition` on top of the binder this one gives the language server. ## At a glance The caller builds the binder once, next to the symbol table, and passes it to `interpret`. This is the CLI path in `packages/2-sql/2-authoring/contract-psl/src/provider.ts`: ```ts const { symbolTable, diagnostics: symbolTableDiagnostics } = buildSymbolTable({ documents, sources }); const { binder, diagnostics: binderDiagnostics } = createProjectBinder({ symbolTable, sources, context }); const interpreted = interpretPslDocumentToSqlContract({ documents, symbolTable, sources, binder, /* … */ }); ``` The language server's `ProjectArtifacts` makes the same `createProjectBinder` call, caches the result per snapshot, and uses that binder both for interpretation and, in the next PR, for navigation. An unknown or misspelled name now gets one diagnostic, which describes what was written: ``` schema: vector pgvectr.Vector(3) (pgvector not in prisma.config.ts) before: PSL_EXTENSION_NAMESPACE_NOT_COMPOSED Type constructor "pgvectr.Vector" uses unrecognized namespace "pgvectr". Add extension pack "pgvectr" to extensions in prisma.config.ts. after: PSL_UNRESOLVED_REFERENCE Cannot find type "pgvectr.Vector" ``` ## Decision This PR delivers five things: 1. **One binder factory for every caller.** `createProjectBinder({ symbolTable, sources, context })` in `@internal/psl-parser` builds the binder from a `ContractSourceContext`. - Both family providers, the language server and tests use it. - `PslInterpretInput` and both interpreter inputs require `binder`. - The interpreters no longer build or report a binder. `createSqlBinder` and `createMongoBinder` are deleted. 2. **The family's wording comes from the family descriptor.** - `describeUnsupportedAttribute` (SQL and Mongo) and `describeUnresolvedType` (Mongo) sit on the family descriptor's `pslDiagnostics`. - The CLI and the language server copy them into `ContractSourceContext.pslDiagnostics`, and the binder uses them from there, so both produce identical diagnostics without either knowing the family. 3. **No guessing and no filtering.** - Removed: the extension-pack guess (`checkUncomposedNamespace`, `PSL_EXTENSION_NAMESPACE_NOT_COMPOSED`); SQL's and Mongo's filters on binder diagnostics; and `PSL_UNKNOWN_FIELD_PRESET`, which could no longer fire once the binder resolves presets from the same contribution tree. - An interpreter's own unsupported-type diagnostic now fires only for a type the binder resolved but the family cannot store. 4. **A field preset written without a call is rejected.** `createdAt temporal.createdAt` reports `PSL_PRESET_NOT_CALLED` in both families (`Field "Post.createdAt" uses field preset "temporal.createdAt" without calling it. Write temporal.createdAt().`). The interpreters report it; the binder only resolves the name. 5. **Interpreters take field types from the binder.** SQL's `resolveFieldTypeDescriptor` and Mongo's `resolveNonRelationField` switch on the binder's resolution for the field's type instead of looking the written name up in descriptor tables or the contribution tree. The binder's contributed-type symbol carries the original descriptor, so a preset and a type constructor are told apart by `kind` (`createBinder`'s `typeConstructors` option is now `contributedTypes`). An unresolved name gets no interpreter diagnostic; a type constructor that needs arguments, written bare, reports `PSL_TYPE_CONSTRUCTOR_NOT_CALLED`. `contract-prisma7` instantiates its mapped constructors directly. ## Reviewer notes - **Largest diff:** test callers now build the binder explicitly through each family's `./test` fixture `bindPslSchema(schema, { context })` and pass it to `interpretPslDocumentTo*Contract`. It is mechanical; no test expectation changed except the ones listed under behaviour changes. - **`contract-prisma7`** does not use the binder; it instantiates the type constructors its native-type table maps to through `instantiateFieldTypeConstructor`. - **Summary headline:** binder diagnostics now arrive as seed diagnostics. A schema whose only errors come from the binder gets the summary `withSeedDiagnostics` writes, `Schema has N errors`, instead of `PSL to SQL contract interpretation failed`. The result is still `ok: false` with the same diagnostics. - **Origin of the removed filters:** both came from #30349. SQL dropped binder diagnostics whenever the guess fired. Mongo dropped every binder diagnostic about a type reference, which also hid that the Mongo binder never knew about field presets. That gap is fixed here, by `createProjectBinder` reading presets from `contributions.field`. - **#30521's Mongo wording is kept unchanged.** - `BigInt` → `use "Int64" (stored as BSON long)` and the scalar-type list for other unknown names now come from Mongo's `describeUnresolvedType`, with the earlier-name table keyed by name in `contract-psl/src/describe-unresolved-type.ts`. - The `formerScalarCodecIds` provider option is removed. - **Test fixtures** are exported as `./test` subpaths of the two contract-psl packages, which `excludedSubpaths` keeps out of the published shells. Nothing under `packages/9-public` or `publish-surface` changes. - **Project files:** `projects/lsp-go-to-definition/` holds the spec and plans and is removed at project close-out. ## Behavior changes & evidence - **Unknown type names:** one `PSL_UNRESOLVED_REFERENCE` from the binder, instead of `PSL_UNSUPPORTED_FIELD_TYPE` or the extension-pack message. A binding the binder resolves but Mongo cannot store, e.g. a `types {}` binding, keeps `PSL_UNSUPPORTED_FIELD_TYPE`. - Implementation: [`psl-column-resolution.ts`](packages/2-sql/2-authoring/contract-psl/src/psl-column-resolution.ts), [`mongo-family/.../interpreter.ts`](packages/2-mongo-family/2-authoring/contract-psl/src/interpreter.ts). - Evidence: [`interpreter.diagnostics.test.ts`](packages/2-sql/2-authoring/contract-psl/test/interpreter.diagnostics.test.ts), [`namespace-without-pack/expected-diagnostics.json`](test/integration/test/authoring/diagnostics/namespace-without-pack/expected-diagnostics.json). - **Attributes from an extension that isn't composed:** `@@pgvector.index` without pgvector reports `PSL_UNSUPPORTED_MODEL_ATTRIBUTE`. - Implementation: [`psl-field-resolution.ts`](packages/2-sql/2-authoring/contract-psl/src/psl-field-resolution.ts). - Evidence: [`interpreter.diagnostics.test.ts`](packages/2-sql/2-authoring/contract-psl/test/interpreter.diagnostics.test.ts). - **Misspelled field preset:** `temporal.createdAtt()` reports one `Cannot find type "temporal.createdAtt"`. - Evidence: [`interpreter.temporal-presets.test.ts`](packages/2-mongo-family/2-authoring/contract-psl/test/interpreter.temporal-presets.test.ts), [`interpreter.defaults.preset-misuse.test.ts`](packages/2-sql/2-authoring/contract-psl/test/interpreter.defaults.preset-misuse.test.ts). - **Language server:** same diagnostics as the CLI, from one cached binder per snapshot. The binder is exposed as `ProjectArtifacts.binder()`. - Implementation: [`project-artifacts.ts`](packages/1-framework/3-tooling/language-server/src/project-artifacts.ts). - Evidence: [`project-artifacts.test.ts`](packages/1-framework/3-tooling/language-server/test/project-artifacts.test.ts), [`lsp-emit-parity.integration.test.ts`](test/integration/test/authoring/lsp-emit-parity.integration.test.ts), [`mongo-unresolved-type-lsp-parity.integration.test.ts`](test/integration/test/authoring/mongo-unresolved-type-lsp-parity.integration.test.ts). ## Skill update n/a: the user-visible delta is diagnostic wording for invalid schemas. No CLI command, config field or public `@prisma/*` API changes. ## Testing performed - `pnpm build`, `pnpm typecheck`, `pnpm lint`, `pnpm lint:deps` - `pnpm fixtures:check`: no changes. - `pnpm test:packages`: 20,178 passed. The only failures are the 3 tarball tests that fail on `origin/main` with `ERR_PNPM_TRUST_DOWNGRADE` for `@vercel/detect-agent`. - `pnpm test:integration`: 5,333 passed. The only failures are the `extension-tarball` / `facade-tarball` trust-downgrade tests and `contract-imports.test.ts` (a pnpm warning on stderr), which fail the same way on `origin/main`. ## Follow-ups - Next PR: binder resolution for namespace qualifiers, and `textDocument/definition` in the language server. ## Alternatives considered - **The language server builds its own binder from framework parts only.** Rejected: `fieldRef` / `referencedFieldRef` are bound only through family attribute specs, so such a binder would resolve type positions but not `@relation` / `@@index` references. - **`interpret()` returns the binder it built.** Rejected: it changes the result type every caller uses, and it still binds the schema inside a family call that the language server cannot reuse. - **Keeping the filters and teaching the language server to filter too.** Rejected: a family-blind caller cannot filter by family, and filtering hid a real gap (the Mongo binder not knowing presets). - **A narrow filter for Mongo's earlier scalar names (`BigInt`, `Bytes`, `Decimal`).** Rejected in favour of `describeUnresolvedType`, which keeps one diagnostic, from the binder. - **Contributing the describers through `authoring`, like `attributeSpecs`.** Rejected: any pack can contribute there, which needs a single-contributor check. A stack has exactly one family descriptor, so putting them there needs no check. ## Checklist - [x] All commits are signed off (`git commit -s`) per the [DCO](../CONTRIBUTING.md#developer-certificate-of-origin-dco). - [x] I read [CONTRIBUTING.md](../CONTRIBUTING.md) and the change is scoped to one logical concern. - [x] Tests are updated. - [ ] The PR title is in `TML-NNNN: <sentence-case title>` form. No Linear ticket, at the operator's request. - [x] The **Skill update** section above is filled in. ## Notes for the reviewer See **Reviewer notes** above. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Signed-off-by: Steven McClankerton <tatarintsev@prisma.io> Co-authored-by: Steven McClankerton <tatarintsev@prisma.io> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 9 小时前 | |
The caller builds the PSL binder, and an unknown name gets one diagnostic (#30563) ## Linked issue n/a: no Linear ticket (omitted at the operator's request). This is the first of two PRs for go-to-definition in the PSL language server. The second PR adds `textDocument/definition` on top of the binder this one gives the language server. ## At a glance The caller builds the binder once, next to the symbol table, and passes it to `interpret`. This is the CLI path in `packages/2-sql/2-authoring/contract-psl/src/provider.ts`: ```ts const { symbolTable, diagnostics: symbolTableDiagnostics } = buildSymbolTable({ documents, sources }); const { binder, diagnostics: binderDiagnostics } = createProjectBinder({ symbolTable, sources, context }); const interpreted = interpretPslDocumentToSqlContract({ documents, symbolTable, sources, binder, /* … */ }); ``` The language server's `ProjectArtifacts` makes the same `createProjectBinder` call, caches the result per snapshot, and uses that binder both for interpretation and, in the next PR, for navigation. An unknown or misspelled name now gets one diagnostic, which describes what was written: ``` schema: vector pgvectr.Vector(3) (pgvector not in prisma.config.ts) before: PSL_EXTENSION_NAMESPACE_NOT_COMPOSED Type constructor "pgvectr.Vector" uses unrecognized namespace "pgvectr". Add extension pack "pgvectr" to extensions in prisma.config.ts. after: PSL_UNRESOLVED_REFERENCE Cannot find type "pgvectr.Vector" ``` ## Decision This PR delivers five things: 1. **One binder factory for every caller.** `createProjectBinder({ symbolTable, sources, context })` in `@internal/psl-parser` builds the binder from a `ContractSourceContext`. - Both family providers, the language server and tests use it. - `PslInterpretInput` and both interpreter inputs require `binder`. - The interpreters no longer build or report a binder. `createSqlBinder` and `createMongoBinder` are deleted. 2. **The family's wording comes from the family descriptor.** - `describeUnsupportedAttribute` (SQL and Mongo) and `describeUnresolvedType` (Mongo) sit on the family descriptor's `pslDiagnostics`. - The CLI and the language server copy them into `ContractSourceContext.pslDiagnostics`, and the binder uses them from there, so both produce identical diagnostics without either knowing the family. 3. **No guessing and no filtering.** - Removed: the extension-pack guess (`checkUncomposedNamespace`, `PSL_EXTENSION_NAMESPACE_NOT_COMPOSED`); SQL's and Mongo's filters on binder diagnostics; and `PSL_UNKNOWN_FIELD_PRESET`, which could no longer fire once the binder resolves presets from the same contribution tree. - An interpreter's own unsupported-type diagnostic now fires only for a type the binder resolved but the family cannot store. 4. **A field preset written without a call is rejected.** `createdAt temporal.createdAt` reports `PSL_PRESET_NOT_CALLED` in both families (`Field "Post.createdAt" uses field preset "temporal.createdAt" without calling it. Write temporal.createdAt().`). The interpreters report it; the binder only resolves the name. 5. **Interpreters take field types from the binder.** SQL's `resolveFieldTypeDescriptor` and Mongo's `resolveNonRelationField` switch on the binder's resolution for the field's type instead of looking the written name up in descriptor tables or the contribution tree. The binder's contributed-type symbol carries the original descriptor, so a preset and a type constructor are told apart by `kind` (`createBinder`'s `typeConstructors` option is now `contributedTypes`). An unresolved name gets no interpreter diagnostic; a type constructor that needs arguments, written bare, reports `PSL_TYPE_CONSTRUCTOR_NOT_CALLED`. `contract-prisma7` instantiates its mapped constructors directly. ## Reviewer notes - **Largest diff:** test callers now build the binder explicitly through each family's `./test` fixture `bindPslSchema(schema, { context })` and pass it to `interpretPslDocumentTo*Contract`. It is mechanical; no test expectation changed except the ones listed under behaviour changes. - **`contract-prisma7`** does not use the binder; it instantiates the type constructors its native-type table maps to through `instantiateFieldTypeConstructor`. - **Summary headline:** binder diagnostics now arrive as seed diagnostics. A schema whose only errors come from the binder gets the summary `withSeedDiagnostics` writes, `Schema has N errors`, instead of `PSL to SQL contract interpretation failed`. The result is still `ok: false` with the same diagnostics. - **Origin of the removed filters:** both came from #30349. SQL dropped binder diagnostics whenever the guess fired. Mongo dropped every binder diagnostic about a type reference, which also hid that the Mongo binder never knew about field presets. That gap is fixed here, by `createProjectBinder` reading presets from `contributions.field`. - **#30521's Mongo wording is kept unchanged.** - `BigInt` → `use "Int64" (stored as BSON long)` and the scalar-type list for other unknown names now come from Mongo's `describeUnresolvedType`, with the earlier-name table keyed by name in `contract-psl/src/describe-unresolved-type.ts`. - The `formerScalarCodecIds` provider option is removed. - **Test fixtures** are exported as `./test` subpaths of the two contract-psl packages, which `excludedSubpaths` keeps out of the published shells. Nothing under `packages/9-public` or `publish-surface` changes. - **Project files:** `projects/lsp-go-to-definition/` holds the spec and plans and is removed at project close-out. ## Behavior changes & evidence - **Unknown type names:** one `PSL_UNRESOLVED_REFERENCE` from the binder, instead of `PSL_UNSUPPORTED_FIELD_TYPE` or the extension-pack message. A binding the binder resolves but Mongo cannot store, e.g. a `types {}` binding, keeps `PSL_UNSUPPORTED_FIELD_TYPE`. - Implementation: [`psl-column-resolution.ts`](packages/2-sql/2-authoring/contract-psl/src/psl-column-resolution.ts), [`mongo-family/.../interpreter.ts`](packages/2-mongo-family/2-authoring/contract-psl/src/interpreter.ts). - Evidence: [`interpreter.diagnostics.test.ts`](packages/2-sql/2-authoring/contract-psl/test/interpreter.diagnostics.test.ts), [`namespace-without-pack/expected-diagnostics.json`](test/integration/test/authoring/diagnostics/namespace-without-pack/expected-diagnostics.json). - **Attributes from an extension that isn't composed:** `@@pgvector.index` without pgvector reports `PSL_UNSUPPORTED_MODEL_ATTRIBUTE`. - Implementation: [`psl-field-resolution.ts`](packages/2-sql/2-authoring/contract-psl/src/psl-field-resolution.ts). - Evidence: [`interpreter.diagnostics.test.ts`](packages/2-sql/2-authoring/contract-psl/test/interpreter.diagnostics.test.ts). - **Misspelled field preset:** `temporal.createdAtt()` reports one `Cannot find type "temporal.createdAtt"`. - Evidence: [`interpreter.temporal-presets.test.ts`](packages/2-mongo-family/2-authoring/contract-psl/test/interpreter.temporal-presets.test.ts), [`interpreter.defaults.preset-misuse.test.ts`](packages/2-sql/2-authoring/contract-psl/test/interpreter.defaults.preset-misuse.test.ts). - **Language server:** same diagnostics as the CLI, from one cached binder per snapshot. The binder is exposed as `ProjectArtifacts.binder()`. - Implementation: [`project-artifacts.ts`](packages/1-framework/3-tooling/language-server/src/project-artifacts.ts). - Evidence: [`project-artifacts.test.ts`](packages/1-framework/3-tooling/language-server/test/project-artifacts.test.ts), [`lsp-emit-parity.integration.test.ts`](test/integration/test/authoring/lsp-emit-parity.integration.test.ts), [`mongo-unresolved-type-lsp-parity.integration.test.ts`](test/integration/test/authoring/mongo-unresolved-type-lsp-parity.integration.test.ts). ## Skill update n/a: the user-visible delta is diagnostic wording for invalid schemas. No CLI command, config field or public `@prisma/*` API changes. ## Testing performed - `pnpm build`, `pnpm typecheck`, `pnpm lint`, `pnpm lint:deps` - `pnpm fixtures:check`: no changes. - `pnpm test:packages`: 20,178 passed. The only failures are the 3 tarball tests that fail on `origin/main` with `ERR_PNPM_TRUST_DOWNGRADE` for `@vercel/detect-agent`. - `pnpm test:integration`: 5,333 passed. The only failures are the `extension-tarball` / `facade-tarball` trust-downgrade tests and `contract-imports.test.ts` (a pnpm warning on stderr), which fail the same way on `origin/main`. ## Follow-ups - Next PR: binder resolution for namespace qualifiers, and `textDocument/definition` in the language server. ## Alternatives considered - **The language server builds its own binder from framework parts only.** Rejected: `fieldRef` / `referencedFieldRef` are bound only through family attribute specs, so such a binder would resolve type positions but not `@relation` / `@@index` references. - **`interpret()` returns the binder it built.** Rejected: it changes the result type every caller uses, and it still binds the schema inside a family call that the language server cannot reuse. - **Keeping the filters and teaching the language server to filter too.** Rejected: a family-blind caller cannot filter by family, and filtering hid a real gap (the Mongo binder not knowing presets). - **A narrow filter for Mongo's earlier scalar names (`BigInt`, `Bytes`, `Decimal`).** Rejected in favour of `describeUnresolvedType`, which keeps one diagnostic, from the binder. - **Contributing the describers through `authoring`, like `attributeSpecs`.** Rejected: any pack can contribute there, which needs a single-contributor check. A stack has exactly one family descriptor, so putting them there needs no check. ## Checklist - [x] All commits are signed off (`git commit -s`) per the [DCO](../CONTRIBUTING.md#developer-certificate-of-origin-dco). - [x] I read [CONTRIBUTING.md](../CONTRIBUTING.md) and the change is scoped to one logical concern. - [x] Tests are updated. - [ ] The PR title is in `TML-NNNN: <sentence-case title>` form. No Linear ticket, at the operator's request. - [x] The **Skill update** section above is filled in. ## Notes for the reviewer See **Reviewer notes** above. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Signed-off-by: Steven McClankerton <tatarintsev@prisma.io> Co-authored-by: Steven McClankerton <tatarintsev@prisma.io> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 9 小时前 | |
TML-3302: A date or time default is stored in its type's canonical form, however it is written (#30532) ## At a glance ```prisma model Event { id Int @id a DateTime @default("2024-01-01T00:00:00Z") b DateTime @default("2024-01-01T00:00:00.000Z") c DateTime @default("2024-01-01T01:00:00+01:00") } ``` These three defaults are one instant. Before this change `contract.json` stored each as typed, so the three columns had three texts, and the same schema written in TypeScript gave a fourth. After this change all of them are stored as `"2024-01-01T00:00:00Z"`, and so is the TypeScript version. ```prisma model Event { id Int @id localAt Timestamp @default("2024-01-01T00:00:00Z") } ``` This one is refused, because a `Timestamp` column holds no offset: ``` PSL_INVALID_DEFAULT_LITERAL: Field "Event.localAt": pg/timestamp holds no UTC offset, but "2024-01-01T00:00:00Z" has one. Leave it out, as in "2024-01-01T12:34:56". ``` Refs: TML-3302, TML-3371. Fixes TML-3395. ## The decision Each date and time data type owns a function that turns text into the type's canonical form, the one text the type stores for a value. That function is the type's cast from text, as ADR 254 requires. The canonical form is the text `Temporal` prints. Text the type does not hold is refused with a message that shows text it takes. Everything that reads, compares, writes or prints a date or time default goes through that one function: the PSL default check, `encodeJson` of every codec of the type, schema verification, the migration planner, DDL rendering on Postgres and SQLite, `contract infer` and `contract print`. ## Why one text A storage hash is a hash of `contract.json`. When one value had several texts, the same schema had several hashes, depending on how the user typed a default and on whether they wrote PSL or TypeScript. Schema verification and the planner then compared the texts through JavaScript `Date` and checks on native type names, which read a zone-less text as UTC and could not tell a `Timestamp` from a `Timestamptz`. The cause was that the cast from text into each date and time type returned its input unchanged. ADR 254 defines a cast as producing the receiving type's canonical form, so the fix is to make the casts do what the ADR says. ## How it works 1. **The data type declares the function.** `DataType` in the framework gains an optional `toCanonicalForm` member. Each Postgres date and time type (`pg/date`, `pg/time`, `pg/timetz`, `pg/timestamp`, `pg/timestamptz`, `pg/interval`) and `sqlite/datetime` declares one. The function uses no `Temporal` and no `Date`, so it gives the same answer on every runtime. 2. **One shared reader.** The functions share `canonicalDateTime` in `@internal/sql-relational-core`, which reads ISO 8601 and returns the canonical text for a shape (date, time, time with offset, date and time, instant) and a range. The Postgres target reads PostgreSQL's own forms first: a space for `T`, an offset of `+HH`, a ` BC` suffix and `infinity`. 3. **What is refused.** An offset on a type that holds none, and no offset on a type that needs one. A date on a time type or a time on a date type. More than six fraction digits, or more than three on `sqlite/datetime`, which holds milliseconds. A date or time that does not exist. A year outside what PostgreSQL and every codec of the type can hold. 4. **Every path finds the function through the column's data type.** The SQL family builds a resolver from the codec descriptors of every component in the stack, so a codec from an extension pack is covered. Each column in the schema IR carries its data type. Verification and the planner compare defaults through it. Postgres DDL writes the canonical form, with PostgreSQL's year text for years outside 1 to 9999. SQLite DDL writes the text the codec writes for every row, so a row that takes the default equals a row the application wrote; before this change they differed, which is TML-3395. 5. **Codecs write the canonical form.** `encodeJson` of every codec of these types passes its output through the type's function, so a TypeScript contract stores the same text as PSL. A `Temporal` value with digits below one microsecond is refused. 6. **A contract that is not re-emitted.** A contract emitted by an earlier version can hold a default the type now refuses, such as a `Timestamp` default written with `Z`. Verification then reports the refusal and says to re-emit the contract, and the planner refuses to write that default into DDL, instead of writing the same `SET DEFAULT` on every run. ## What this means for users A default that was not already in canonical form gets a new storage hash when the contract is re-emitted. The database does not change. The upgrade instructions in `upgrade-instructions/pending/date-time-default-canonical-form/` say how to find such defaults, how to sign the database or record an empty migration, that a refused text which is corrected to a different value needs a real migration, that a SQLite table created before the upgrade keeps its old default text and how to fix its rows, and to sign or migrate before deploying the re-emitted contract. ## How it is tested - Parity fixtures under `test/integration/test/authoring/parity/` emit the same schema from PSL and TypeScript for every date and time type, `Timetz` and SQLite included, and check the contracts are equal. - `packages/3-targets/3-targets/postgres/test/date-time-codecs.canonical-form.test.ts` walks the codec registry, so a new codec of a date and time type cannot be missed, and includes a nanosecond `Temporal` value. - `packages/2-sql/4-lanes/relational-core/test/ast/date-time-canonical-form.test.ts` and the Postgres `date-time-canonical-form.dates` and `.times` tests cover the shared reader and the Postgres functions: every shape, every refusal message, the ranges and the year text. - `test/integration/test/cli-journeys/date-time-default-canonical-form.e2e.test.ts` runs the built CLI with no `Temporal` preloaded: a contract emitted before this change still verifies with no schema change planned, and `contract infer` prints a default that emits to the same text. - `test/integration/test/date-time-defaults/` cover Postgres and SQLite verification, both SQLite DDL paths, and a contract that holds a refused default. - The codec conformance suites compare the database's JSON projection with `encodeJson` through the canonical form. ## Also in this pull request - ADR 254 states the canonical form of each date and time type, its range and its reading rules, in one place. ADR 184 points to it. - The project directory `projects/date-time-default-text/` is deleted; the spec and plan are done. ## Testing performed - `pnpm build`, `pnpm typecheck`, `pnpm lint`, `pnpm lint:deps`, `pnpm lint:throws`, `pnpm fixtures:check`, `pnpm check:publish-deps`, `pnpm check:upgrade-coverage --mode pr`: pass. - `pnpm test:packages`: pass, except three packaging tests that fail locally in `pnpm install` on a trust check for `@vercel/detect-agent`, unrelated to this change. CI runs them. - `pnpm test:integration` and `pnpm test:e2e`: pass. The packaging tarball tests fail locally on the same trust check, and one Mongo file failed on a mongod internal error under load and passes on rerun. - Manual QA with the built CLI on Node 24, which has no `Temporal`, against a PGlite dev database: `contract emit` on the three-instant schema above stores `"2024-01-01T00:00:00Z"` three times; `db init` creates the table with `'2024-01-01 00:00:00+00'::timestamp with time zone` on each column and a row that takes the defaults reads `2024-01-01 00:00:00` UTC three times; `db verify` reports that the marker and schema match; `contract infer` prints `@default("2024-01-01T00:00:00Z")` and emitting the printed schema stores the same text. On SQLite, `DateTime @default("2024-01-01T01:00:00+01:00")` is stored as `"2024-01-01T00:00:00Z"`, DDL writes `DEFAULT '2024-01-01T00:00:00.000Z'`, a row that takes the default equals a row the application writes, and verification passes. ## Follow-ups - TML-3393: `migration plan` refuses a change with no DDL, so a re-emitted contract needs `migration new`. - TML-3394: the Prisma 7 schema reader computes its own date text. - TML-3396: PostgreSQL's year text is read and written in six places in the Postgres target. - To file: the "re-emit the contract" wording for a refused default lives in schema IR and both planners repeat the throw, so one SQL family function should own it; the codec testkits look up data types in the target's own list, not the stack, so an extension pack's own data type would be compared on raw text; SQLite planner DDL picks the datetime text by the fixed codec id `sqlite/datetime@1`, so an extension codec of `sqlite/datetime` is not covered there; a TypeScript default goes only through `codec.encodeJson`, so nothing applies the type's function to an extension codec that breaks the rule; `pg/int8` and `pg/numeric` meet ADR 254's rule for a canonical-form function but still compare through native type names; SQLite `now()` stores `datetime('now')` text that differs from the codec's. ## Alternatives considered - **A per-target table from codec id to canonical-form function**, passed to verification as an option. Rejected: it left extension-pack codecs out and gave the canonical form a second owner beside the data type. - **The canonical form as whatever `Temporal` prints, applied by the Temporal codecs directly.** Rejected: the Temporal codecs then wrote nanoseconds, which the types do not hold, and the form had two definitions. - **Reading PostgreSQL's ` BC` and `infinity` forms in the shared reader.** Rejected: the shared reader is used by SQLite, which has neither; the Postgres target reads them first. - **Keeping the JavaScript `Date` comparison in verification for a contract that is not re-emitted.** Rejected: it read a zone-less text as UTC and made a `Timestamp` default written with `Z` equal to one without, which hides a real difference. Agent: cadmus-43 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Date and time values now use type-specific canonical forms across PostgreSQL and SQLite, including when encoded, stored as defaults, rendered in SQL, and inferred from schemas. * Schema verification explains when a default cannot be represented and recommends re-emitting the contract. * **Bug Fixes** * Unsupported or invalid date/time defaults are rejected with actionable errors. * CLI database commands preserve structured contract-error details, and equivalent date/time defaults are compared consistently. * **Documentation** * Added upgrade guidance for canonicalized defaults, including when database updates or signing may be needed. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> | 2 天前 | |
chore(deps-dev)(deps-dev): bump the dev-deps group across 1 directory with 10 updates (#30037) Bumps the dev-deps group with 10 updates in the / directory: | Package | From | To | | --- | --- | --- | | [@biomejs/biome](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome) | `2.5.7` | `2.5.8` | | [dependency-cruiser](https://github.com/sverweij/dependency-cruiser) | `18.1.0` | `18.1.1` | | [pkg-pr-new](https://github.com/stackblitz-labs/pkg.pr.new/tree/HEAD/packages/cli) | `0.0.86` | `0.0.87` | | [skills](https://github.com/vercel-labs/skills) | `1.5.21` | `1.5.22` | | [turbo](https://github.com/vercel/turborepo) | `2.10.8` | `2.10.9` | | [wrangler](https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/wrangler) | `4.118.0` | `4.119.0` | | [@cloudflare/vitest-pool-workers](https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/vitest-pool-workers) | `0.20.1` | `0.20.3` | | [@cloudflare/workers-types](https://github.com/cloudflare/workerd) | `5.20260714.1` | `5.20260804.1` | | [@prisma/compute-sdk](https://github.com/prisma/project-compute) | `0.38.0` | `0.39.0` | | [@prisma/management-api-sdk](https://github.com/prisma/pdp-control-plane/tree/HEAD/packages/management-api-sdk) | `1.53.0` | `1.56.0` | Updates `@biomejs/biome` from 2.5.7 to 2.5.8 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/biomejs/biome/releases">@biomejs/biome's releases</a>.</em></p> <blockquote> <h2>Biome CLI v2.5.8</h2> <h2>2.5.8</h2> <h3>Patch Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/biomejs/biome/pull/10710">#10710</a> <a href="https://github.com/biomejs/biome/commit/0a0fbc15d67c410c80dfae398903f845544fcd65"><code>0a0fbc1</code></a> Thanks <a href="https://github.com/dyc3"><code>@dyc3</code></a>! - Added a new nursery rule <a href="https://biomejs.dev/linter/rules/use-react-compiler/"><code>useReactCompiler</code></a>, which reports diagnostics from React Compiler lint mode.</p> </li> <li> <p><a href="https://redirect.github.com/biomejs/biome/pull/11251">#11251</a> <a href="https://github.com/biomejs/biome/commit/ea9dd8a93e65f849840415e8e26cd668aa1af913"><code>ea9dd8a</code></a> Thanks <a href="https://github.com/dyc3"><code>@dyc3</code></a>! - Improved performance of <a href="https://biomejs.dev/linter/rules/no-import-cycles/"><code>noImportCycles</code></a>.</p> </li> <li> <p><a href="https://redirect.github.com/biomejs/biome/pull/11247">#11247</a> <a href="https://github.com/biomejs/biome/commit/52b44d6795741d051bf703bd69c6cb447af8fd1d"><code>52b44d6</code></a> Thanks <a href="https://github.com/dyc3"><code>@dyc3</code></a>! - Added the nursery rule <a href="https://biomejs.dev/linter/rules/no-svelte-legacy-const/"><code>noSvelteLegacyConst</code></a>, which disallows legacy Svelte <code>{@const}</code> tags and recommends declaration tags with <code>$derived()</code>.</p> <p>Invalid:</p> <pre lang="svelte"><code>{#each boxes as box} {@const area = box.width * box.height} <p>{area}</p> {/each} </code></pre> <p>Valid:</p> <pre lang="svelte"><code>{#each boxes as box} {const area = $derived(box.width * box.height)} <p>{area}</p> {/each} </code></pre> </li> <li> <p><a href="https://redirect.github.com/biomejs/biome/pull/11252">#11252</a> <a href="https://github.com/biomejs/biome/commit/d5f570414fdcdddf62372e35c05f6dababad9287"><code>d5f5704</code></a> Thanks <a href="https://github.com/Turtle-Hwan"><code>@Turtle-Hwan</code></a>! - Fixed <a href="https://redirect.github.com/biomejs/biome/issues/11250">#11250</a>: <a href="https://biomejs.dev/linter/rules/use-await/"><code>useAwait</code></a> no longer reports async functions that contain an <code>await using</code> declaration.</p> </li> <li> <p><a href="https://redirect.github.com/biomejs/biome/pull/11143">#11143</a> <a href="https://github.com/biomejs/biome/commit/6be7be1b147d7b4352ff5625a78bd54a48958950"><code>6be7be1</code></a> Thanks <a href="https://github.com/vznh"><code>@vznh</code></a>! - Fixed <a href="https://redirect.github.com/biomejs/biome/issues/11017">#11017</a>: <a href="https://biomejs.dev/linter/rules/no-useless-undefined/"><code>noUselessUndefined</code></a> no longer reports <code>return undefined</code> when the enclosing function has a return type annotation other than <code>undefined</code> or <code>void</code>.</p> </li> <li> <p><a href="https://redirect.github.com/biomejs/biome/pull/11234">#11234</a> <a href="https://github.com/biomejs/biome/commit/caefe393c66340914c481f7ccfc82979cf76b61b"><code>caefe39</code></a> Thanks <a href="https://github.com/subotac"><code>@subotac</code></a>! - Fixed <a href="https://redirect.github.com/biomejs/biome/issues/11228">#11228</a>: CSS block comments between a declaration colon and value now preserve their source indentation.</p> <pre lang="diff"><code> :root { --font-stack: -/* comment */ + /* comment */ system-ui; } </code></pre> </li> <li> <p><a href="https://redirect.github.com/biomejs/biome/pull/11285">#11285</a> <a href="https://github.com/biomejs/biome/commit/bca1f73d939423056337bfd0a42cbdcb66bb1e3f"><code>bca1f73</code></a> Thanks <a href="https://github.com/denbezrukov"><code>@denbezrukov</code></a>! - Fixed <a href="https://redirect.github.com/biomejs/biome/issues/11280">#11280</a>: CSS formatting keeps comments inside functional pseudo-classes and pseudo-elements instead of moving them before the function name.</p> <pre lang="diff"><code>-:/* comment */ where(div) {} +:where(/* comment */ div) {} </code></pre> </li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/biomejs/biome/blob/main/packages/@biomejs/biome/CHANGELOG.md">@biomejs/biome's changelog</a>.</em></p> <blockquote> <h2>2.5.8</h2> <h3>Patch Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/biomejs/biome/pull/10710">#10710</a> <a href="https://github.com/biomejs/biome/commit/0a0fbc15d67c410c80dfae398903f845544fcd65"><code>0a0fbc1</code></a> Thanks <a href="https://github.com/dyc3"><code>@dyc3</code></a>! - Added a new nursery rule <a href="https://biomejs.dev/linter/rules/use-react-compiler/"><code>useReactCompiler</code></a>, which reports diagnostics from React Compiler lint mode.</p> </li> <li> <p><a href="https://redirect.github.com/biomejs/biome/pull/11251">#11251</a> <a href="https://github.com/biomejs/biome/commit/ea9dd8a93e65f849840415e8e26cd668aa1af913"><code>ea9dd8a</code></a> Thanks <a href="https://github.com/dyc3"><code>@dyc3</code></a>! - Improved performance of <a href="https://biomejs.dev/linter/rules/no-import-cycles/"><code>noImportCycles</code></a>.</p> </li> <li> <p><a href="https://redirect.github.com/biomejs/biome/pull/11247">#11247</a> <a href="https://github.com/biomejs/biome/commit/52b44d6795741d051bf703bd69c6cb447af8fd1d"><code>52b44d6</code></a> Thanks <a href="https://github.com/dyc3"><code>@dyc3</code></a>! - Added the nursery rule <a href="https://biomejs.dev/linter/rules/no-svelte-legacy-const/"><code>noSvelteLegacyConst</code></a>, which disallows legacy Svelte <code>{@const}</code> tags and recommends declaration tags with <code>$derived()</code>.</p> <p>Invalid:</p> <pre lang="svelte"><code>{#each boxes as box} {@const area = box.width * box.height} <p>{area}</p> {/each} </code></pre> <p>Valid:</p> <pre lang="svelte"><code>{#each boxes as box} {const area = $derived(box.width * box.height)} <p>{area}</p> {/each} </code></pre> </li> <li> <p><a href="https://redirect.github.com/biomejs/biome/pull/11252">#11252</a> <a href="https://github.com/biomejs/biome/commit/d5f570414fdcdddf62372e35c05f6dababad9287"><code>d5f5704</code></a> Thanks <a href="https://github.com/Turtle-Hwan"><code>@Turtle-Hwan</code></a>! - Fixed <a href="https://redirect.github.com/biomejs/biome/issues/11250">#11250</a>: <a href="https://biomejs.dev/linter/rules/use-await/"><code>useAwait</code></a> no longer reports async functions that contain an <code>await using</code> declaration.</p> </li> <li> <p><a href="https://redirect.github.com/biomejs/biome/pull/11143">#11143</a> <a href="https://github.com/biomejs/biome/commit/6be7be1b147d7b4352ff5625a78bd54a48958950"><code>6be7be1</code></a> Thanks <a href="https://github.com/vznh"><code>@vznh</code></a>! - Fixed <a href="https://redirect.github.com/biomejs/biome/issues/11017">#11017</a>: <a href="https://biomejs.dev/linter/rules/no-useless-undefined/"><code>noUselessUndefined</code></a> no longer reports <code>return undefined</code> when the enclosing function has a return type annotation other than <code>undefined</code> or <code>void</code>.</p> </li> <li> <p><a href="https://redirect.github.com/biomejs/biome/pull/11234">#11234</a> <a href="https://github.com/biomejs/biome/commit/caefe393c66340914c481f7ccfc82979cf76b61b"><code>caefe39</code></a> Thanks <a href="https://github.com/subotac"><code>@subotac</code></a>! - Fixed <a href="https://redirect.github.com/biomejs/biome/issues/11228">#11228</a>: CSS block comments between a declaration colon and value now preserve their source indentation.</p> <pre lang="diff"><code> :root { --font-stack: -/* comment */ + /* comment */ system-ui; } </code></pre> </li> <li> <p><a href="https://redirect.github.com/biomejs/biome/pull/11285">#11285</a> <a href="https://github.com/biomejs/biome/commit/bca1f73d939423056337bfd0a42cbdcb66bb1e3f"><code>bca1f73</code></a> Thanks <a href="https://github.com/denbezrukov"><code>@denbezrukov</code></a>! - Fixed <a href="https://redirect.github.com/biomejs/biome/issues/11280">#11280</a>: CSS formatting keeps comments inside functional pseudo-classes and pseudo-elements instead of moving them before the function name.</p> <pre lang="diff"><code>-:/* comment */ where(div) {} +:where(/* comment */ div) {} </code></pre> </li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/biomejs/biome/commit/6b8f09c04394f2a9f72b89f9381724681169641a"><code>6b8f09c</code></a> ci: release (<a href="https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome/issues/11236">#11236</a>)</li> <li><a href="https://github.com/biomejs/biome/commit/23c0369c43b59284ca68c65883d6ede4228b6fb8"><code>23c0369</code></a> feat(lint): nursery noInvalidPropertyInitValue (<a href="https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome/issues/11187">#11187</a>)</li> <li><a href="https://github.com/biomejs/biome/commit/52b44d6795741d051bf703bd69c6cb447af8fd1d"><code>52b44d6</code></a> feat(lint/html): add <code>noSvelteLegacyConst</code> (<a href="https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome/issues/11247">#11247</a>)</li> <li><a href="https://github.com/biomejs/biome/commit/0a0fbc15d67c410c80dfae398903f845544fcd65"><code>0a0fbc1</code></a> feat(lint/js): add <code>useReactCompiler</code> (<a href="https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome/issues/10710">#10710</a>)</li> <li>See full diff in <a href="https://github.com/biomejs/biome/commits/@biomejs/biome@2.5.8/packages/@biomejs/biome">compare view</a></li> </ul> </details> <br /> Updates `dependency-cruiser` from 18.1.0 to 18.1.1 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/sverweij/dependency-cruiser/releases">dependency-cruiser's releases</a>.</em></p> <blockquote> <h2>v18.1.1</h2> <h2>👷 maintenance</h2> <ul> <li>1ee565bb/ 942cf969 build(npm): updates external dependencies</li> <li>f0061d15 fix: removes all unused catch parameters</li> <li>cbe062ae/ c0250f8d chore(tools): uses node permission model</li> <li>01c47439 fix(build): re-adds esbuild to the devDependencies</li> <li>e57d9fc2 chore: replaces eslint with oxlint (<a href="https://redirect.github.com/sverweij/dependency-cruiser/issues/1074">#1074</a>)</li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/sverweij/dependency-cruiser/commit/da355e4cc82383f7b9744e0beb2f64d3c20f2e25"><code>da355e4</code></a> 18.1.1</li> <li><a href="https://github.com/sverweij/dependency-cruiser/commit/c0250f8dd06f622fba990fcd9b1d5da8c4bfaa1b"><code>c0250f8</code></a> chore(tools): makes the tools work again on node 22</li> <li><a href="https://github.com/sverweij/dependency-cruiser/commit/942cf969aa6174f1a4bfa91542dc6da37e5cdbcd"><code>942cf96</code></a> build(npm): updates external dependencies</li> <li><a href="https://github.com/sverweij/dependency-cruiser/commit/cbe062ae2b1994a7ce38bec9939562a33b193b84"><code>cbe062a</code></a> chore(tools): uses node permission model</li> <li><a href="https://github.com/sverweij/dependency-cruiser/commit/01c474397603d0a3db8288793c6bf362c18c7784"><code>01c4743</code></a> fix(build): re-adds esbuild to the devDependencies</li> <li><a href="https://github.com/sverweij/dependency-cruiser/commit/e57d9fc2f06a860eee3d7bf3667da05d52145a3e"><code>e57d9fc</code></a> chore: replaces eslint with oxlint (<a href="https://redirect.github.com/sverweij/dependency-cruiser/issues/1074">#1074</a>)</li> <li><a href="https://github.com/sverweij/dependency-cruiser/commit/f0061d1545e2c16a8ee0212f36306e2f41bad056"><code>f0061d1</code></a> fix: removes all unused catch parameters</li> <li><a href="https://github.com/sverweij/dependency-cruiser/commit/1ee565bb8ee935385b4288891849740125829f99"><code>1ee565b</code></a> build(npm): updates external dependencies</li> <li>See full diff in <a href="https://github.com/sverweij/dependency-cruiser/compare/v18.1.0...v18.1.1">compare view</a></li> </ul> </details> <br /> Updates `pkg-pr-new` from 0.0.86 to 0.0.87 <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/stackblitz-labs/pkg.pr.new/commit/d293ab292f1e71640630a6e4e4683235b92cdef7"><code>d293ab2</code></a> release: v0.0.87</li> <li>See full diff in <a href="https://github.com/stackblitz-labs/pkg.pr.new/commits/v0.0.87/packages/cli">compare view</a></li> </ul> </details> <br /> Updates `skills` from 1.5.21 to 1.5.22 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/vercel-labs/skills/releases">skills's releases</a>.</em></p> <blockquote> <h2>v1.5.22</h2> <h2>Changelog</h2> <ul> <li>fix: discover skills nested under two categories (<a href="https://redirect.github.com/vercel-labs/skills/issues/1866">#1866</a>)</li> <li>fix(update): normalize GitHub shorthand for project deletion checks (<a href="https://redirect.github.com/vercel-labs/skills/issues/1865">#1865</a>)</li> <li>fix: preserve locked GitHub host during updates (<a href="https://redirect.github.com/vercel-labs/skills/issues/1837">#1837</a>)</li> <li>Surface skills added upstream to well-known sources during update (<a href="https://redirect.github.com/vercel-labs/skills/issues/1824">#1824</a>)</li> <li>Make skills update work for well-known installs (incl. skills.sh packs) (<a href="https://redirect.github.com/vercel-labs/skills/issues/1821">#1821</a>)</li> <li>Preselect all skills when installing a skills.sh pack (<a href="https://redirect.github.com/vercel-labs/skills/issues/1820">#1820</a>)</li> <li>Add MiniMax Code agent support (<a href="https://redirect.github.com/vercel-labs/skills/issues/1814">#1814</a>)</li> <li>fix(remove): keep the lock entry while another agent still uses the skill (<a href="https://redirect.github.com/vercel-labs/skills/issues/1786">#1786</a>)</li> <li>fix(find): show all registry results in non-interactive search (<a href="https://redirect.github.com/vercel-labs/skills/issues/1748">#1748</a>)</li> <li>fix: store local path sources in lockfile using portable source (<a href="https://redirect.github.com/vercel-labs/skills/issues/1743">#1743</a>)</li> </ul> <h2>Contributors</h2> <p><a href="https://github.com/AndreaCovelli"><code>@AndreaCovelli</code></a>,<a href="https://github.com/IsmaelMartinez"><code>@IsmaelMartinez</code></a> <a href="https://github.com/SenseiMarv"><code>@SenseiMarv</code></a>,<a href="https://github.com/Ygilany"><code>@Ygilany</code></a> <a href="https://github.com/byapparov"><code>@byapparov</code></a>,<a href="https://github.com/hetaoBackend"><code>@hetaoBackend</code></a> <a href="https://github.com/mlekhi"><code>@mlekhi</code></a>,<a href="https://github.com/quuu"><code>@quuu</code></a></p> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/vercel-labs/skills/commit/a4d243c3d4f86cdf9385dd1b6a0733f6937e70b5"><code>a4d243c</code></a> v1.5.22</li> <li><a href="https://github.com/vercel-labs/skills/commit/ab4fc49265c443279a5deae20297e631470da68c"><code>ab4fc49</code></a> fix(find): show all returned search results (<a href="https://redirect.github.com/vercel-labs/skills/issues/1748">#1748</a>)</li> <li><a href="https://github.com/vercel-labs/skills/commit/644686b10ba6a6b563a1518e1a124a52e84460af"><code>644686b</code></a> Merge pull request <a href="https://redirect.github.com/vercel-labs/skills/issues/1743">#1743</a> from SenseiMarv/fix/store-local-path-sources-using-p...</li> <li><a href="https://github.com/vercel-labs/skills/commit/7533583f24a9a9be6fd5f783912f55c15a08b31e"><code>7533583</code></a> Merge branch 'main' into fix/store-local-path-sources-using-portable-source</li> <li><a href="https://github.com/vercel-labs/skills/commit/50d3b75443c24d2e224f5961cdd75e793c99d912"><code>50d3b75</code></a> fix project update GitHub shorthand clone (<a href="https://redirect.github.com/vercel-labs/skills/issues/1865">#1865</a>)</li> <li><a href="https://github.com/vercel-labs/skills/commit/653739a1ed1316cc2492ea81fbfcee14a45e4802"><code>653739a</code></a> fix: preserve locked GitHub host during updates (<a href="https://redirect.github.com/vercel-labs/skills/issues/1837">#1837</a>)</li> <li><a href="https://github.com/vercel-labs/skills/commit/65658a84d05961bbd0e2ea1afedb976946131315"><code>65658a8</code></a> Merge pull request <a href="https://redirect.github.com/vercel-labs/skills/issues/1786">#1786</a> from IsmaelMartinez/fix/remove-agent-subset-keeps-lock</li> <li><a href="https://github.com/vercel-labs/skills/commit/375f497e5c0e2d9ef743de372ce53545b1d77620"><code>375f497</code></a> Merge pull request <a href="https://redirect.github.com/vercel-labs/skills/issues/1866">#1866</a> from vercel-labs/fix/deeper-nested-skill-discovery</li> <li><a href="https://github.com/vercel-labs/skills/commit/dc045b90613a7c4d9c0feebbb96a27abedadf86b"><code>dc045b9</code></a> docs: update nested discovery depth</li> <li><a href="https://github.com/vercel-labs/skills/commit/6eeafb76573a798e330687c10fd83592bd619f8e"><code>6eeafb7</code></a> fix: discover skills nested under two categories</li> <li>Additional commits viewable in <a href="https://github.com/vercel-labs/skills/compare/v1.5.21...v1.5.22">compare view</a></li> </ul> </details> <br /> Updates `turbo` from 2.10.8 to 2.10.9 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/vercel/turborepo/releases">turbo's releases</a>.</em></p> <blockquote> <h2>Turborepo v2.10.9</h2> <!-- raw HTML omitted --> <h2>What's Changed</h2> <h3>Changelog</h3> <ul> <li>chore: Release Turborepo 2.10.8 by <a href="https://github.com/github-actions"><code>@github-actions</code></a>[bot] in <a href="https://redirect.github.com/vercel/turborepo/pull/13626">vercel/turborepo#13626</a></li> <li>perf: Walk literal-prefix tree globs without wax compilation by <a href="https://github.com/charpeni"><code>@charpeni</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13522">vercel/turborepo#13522</a></li> <li>fix: Accept semver ranges in devEngines.packageManager.version by <a href="https://github.com/bangseongbeom"><code>@bangseongbeom</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13623">vercel/turborepo#13623</a></li> <li>docs: Explain affected package invalidation reasons by <a href="https://github.com/ghoullier"><code>@ghoullier</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13594">vercel/turborepo#13594</a></li> <li>perf(lockfiles): Borrow field-name scalars in the pnpm fast parser by <a href="https://github.com/anthonyshew"><code>@anthonyshew</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13648">vercel/turborepo#13648</a></li> <li>perf(repository): Avoid discarded alias allocation in Relationship by <a href="https://github.com/anthonyshew"><code>@anthonyshew</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13650">vercel/turborepo#13650</a></li> <li>perf(lockfiles): Drop redundant human_name clone for pnpm v7/v9 by <a href="https://github.com/anthonyshew"><code>@anthonyshew</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13649">vercel/turborepo#13649</a></li> <li>perf: Index workspace nodes by name in project_relationships by <a href="https://github.com/anthonyshew"><code>@anthonyshew</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13647">vercel/turborepo#13647</a></li> <li>perf: Share resolution identity lists across identical workspace closures by <a href="https://github.com/anthonyshew"><code>@anthonyshew</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13641">vercel/turborepo#13641</a></li> <li>docs: Fix duplicated word in runtime dependencies guide summary by <a href="https://github.com/anthonyshew"><code>@anthonyshew</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13630">vercel/turborepo#13630</a></li> <li>refactor: Remove turborepo-lsp dependency on turborepo-lib by <a href="https://github.com/anthonyshew"><code>@anthonyshew</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13631">vercel/turborepo#13631</a></li> <li>perf: Index Bun nested lockfile entries by name for fallback resolution by <a href="https://github.com/anthonyshew"><code>@anthonyshew</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13633">vercel/turborepo#13633</a></li> <li>perf: Memoize framework inference per package during task hashing by <a href="https://github.com/anthonyshew"><code>@anthonyshew</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13634">vercel/turborepo#13634</a></li> <li>perf: Avoid materializing transient declarations in external_dependencies by <a href="https://github.com/anthonyshew"><code>@anthonyshew</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13646">vercel/turborepo#13646</a></li> <li>perf: Enable shared closure DP for npm and yarn1 lockfiles by <a href="https://github.com/anthonyshew"><code>@anthonyshew</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13635">vercel/turborepo#13635</a></li> <li>perf: Parse pnpm explicit-key entries in the lockfile fast path by <a href="https://github.com/anthonyshew"><code>@anthonyshew</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13640">vercel/turborepo#13640</a></li> <li>perf: Parallelize resolution fingerprint hashing by <a href="https://github.com/anthonyshew"><code>@anthonyshew</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13642">vercel/turborepo#13642</a></li> <li>perf: Build resolution identity lists in parallel by <a href="https://github.com/anthonyshew"><code>@anthonyshew</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13643">vercel/turborepo#13643</a></li> <li>perf: Intern resolution identities as Arc<str> across closures by <a href="https://github.com/anthonyshew"><code>@anthonyshew</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13645">vercel/turborepo#13645</a></li> <li>fix: Compose affected tasks with package filters by <a href="https://github.com/anthonyshew"><code>@anthonyshew</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13656">vercel/turborepo#13656</a></li> <li>docs: Explain worktree cache path isolation by <a href="https://github.com/anthonyshew"><code>@anthonyshew</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13657">vercel/turborepo#13657</a></li> <li>fix: Upgrade brace-expansion to 5.0.9 by <a href="https://github.com/anthonyshew"><code>@anthonyshew</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13658">vercel/turborepo#13658</a></li> <li>docs: Correct verified inaccuracies in the Turborepo Agent Skill by <a href="https://github.com/charpeni"><code>@charpeni</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13644">vercel/turborepo#13644</a></li> <li>chore: Update Next.js to 16.3.0 by <a href="https://github.com/anthonyshew"><code>@anthonyshew</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13659">vercel/turborepo#13659</a></li> <li>fix: Don't use <code>eprintln!</code> in the panic hook by <a href="https://github.com/molofsky"><code>@molofsky</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13637">vercel/turborepo#13637</a></li> <li>fix: Invalidate only when Git ignore sources change by <a href="https://github.com/smasato"><code>@smasato</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13632">vercel/turborepo#13632</a></li> <li>docs: Update Geistdocs to 1.19.4 by <a href="https://github.com/christopherkindl"><code>@christopherkindl</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13680">vercel/turborepo#13680</a></li> <li>docs: Exclude Turborepo from its own OSS products menu by <a href="https://github.com/christopherkindl"><code>@christopherkindl</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13681">vercel/turborepo#13681</a></li> <li>docs: Use the geistdocs Turborepo logo in the navbar by <a href="https://github.com/christopherkindl"><code>@christopherkindl</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13682">vercel/turborepo#13682</a></li> <li>docs: Update redirected vercel.com/nextjs.org links to current targets by <a href="https://github.com/molebox"><code>@molebox</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13685">vercel/turborepo#13685</a></li> <li>refactor: Generalize native command arguments by <a href="https://github.com/anthonyshew"><code>@anthonyshew</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13664">vercel/turborepo#13664</a></li> <li>refactor: Move native contracts to tasks by <a href="https://github.com/anthonyshew"><code>@anthonyshew</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13665">vercel/turborepo#13665</a></li> <li>docs: Fix loadTransformers reference in turbo-codemod README by <a href="https://github.com/latent-9"><code>@latent-9</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13683">vercel/turborepo#13683</a></li> <li>refactor: Model native task execution explicitly by <a href="https://github.com/anthonyshew"><code>@anthonyshew</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13666">vercel/turborepo#13666</a></li> <li>feat: Compose aggregate native task dependencies by <a href="https://github.com/anthonyshew"><code>@anthonyshew</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13667">vercel/turborepo#13667</a></li> <li>fix: Respect aggregate task overrides by <a href="https://github.com/anthonyshew"><code>@anthonyshew</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13668">vercel/turborepo#13668</a></li> <li>test: Stabilize watch task inputs regression test by <a href="https://github.com/anthonyshew"><code>@anthonyshew</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13686">vercel/turborepo#13686</a></li> <li>feat: Parse Python quality tool declarations by <a href="https://github.com/anthonyshew"><code>@anthonyshew</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13669">vercel/turborepo#13669</a></li> <li>feat: Resolve Python quality plans by <a href="https://github.com/anthonyshew"><code>@anthonyshew</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13670">vercel/turborepo#13670</a></li> <li>refactor: Extract uv native task specs by <a href="https://github.com/anthonyshew"><code>@anthonyshew</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13671">vercel/turborepo#13671</a></li> <li>feat: Synthesize Python quality tasks by <a href="https://github.com/anthonyshew"><code>@anthonyshew</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13672">vercel/turborepo#13672</a></li> <li>test: Cover Python quality task commands by <a href="https://github.com/anthonyshew"><code>@anthonyshew</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13673">vercel/turborepo#13673</a></li> <li>feat: Hash Python quality task inputs by <a href="https://github.com/anthonyshew"><code>@anthonyshew</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13674">vercel/turborepo#13674</a></li> <li>test: Cover Python quality task graph by <a href="https://github.com/anthonyshew"><code>@anthonyshew</code></a> in <a href="https://redirect.github.com/vercel/turborepo/pull/13675">vercel/turborepo#13675</a></li> <li>chore: Release Turborepo 2.10.9-canary.1 by <a href="https://github.com/github-actions"><code>@github-actions</code></a>[bot] in <a href="https://redirect.github.com/vercel/turborepo/pull/13687">vercel/turborepo#13687</a></li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/vercel/turborepo/commit/33237d4be13d7b74768c2cf3353b19cfa8d1af7c"><code>33237d4</code></a> publish 2.10.9 to registry</li> <li><a href="https://github.com/vercel/turborepo/commit/3b0e57f1289b2a6b3d6dd402bce928469d3b25fa"><code>3b0e57f</code></a> fix: Prevent Windows process cleanup PID reuse (<a href="https://redirect.github.com/vercel/turborepo/issues/13695">#13695</a>)</li> <li><a href="https://github.com/vercel/turborepo/commit/efe4e1bdf665f2950cf89d7968907de36c2f0737"><code>efe4e1b</code></a> fix: Prune Bun wildcard workspace dev dependencies (<a href="https://redirect.github.com/vercel/turborepo/issues/13694">#13694</a>)</li> <li><a href="https://github.com/vercel/turborepo/commit/a98e5cde97796088c6107684a64a40a967cd1ef0"><code>a98e5cd</code></a> docs: Document dependency-driven Python tasks (<a href="https://redirect.github.com/vercel/turborepo/issues/13676">#13676</a>)</li> <li><a href="https://github.com/vercel/turborepo/commit/c09a92f526b6dca9ea0243922f680803779759cd"><code>c09a92f</code></a> chore: Release Turborepo 2.10.9-canary.1 (<a href="https://redirect.github.com/vercel/turborepo/issues/13687">#13687</a>)</li> <li><a href="https://github.com/vercel/turborepo/commit/09bd548dddbff2a29086bdef7cb07b02d5e5458a"><code>09bd548</code></a> test: Cover Python quality task graph (<a href="https://redirect.github.com/vercel/turborepo/issues/13675">#13675</a>)</li> <li><a href="https://github.com/vercel/turborepo/commit/3584a5fb8edac9efc826fdea57e92088505fc76a"><code>3584a5f</code></a> feat: Hash Python quality task inputs (<a href="https://redirect.github.com/vercel/turborepo/issues/13674">#13674</a>)</li> <li><a href="https://github.com/vercel/turborepo/commit/0d43ff3cbf5ac8873c646a84b2fd7ae53097e08d"><code>0d43ff3</code></a> test: Cover Python quality task commands (<a href="https://redirect.github.com/vercel/turborepo/issues/13673">#13673</a>)</li> <li><a href="https://github.com/vercel/turborepo/commit/94708adc6bc19b41805741cc5a15ac5467a481cf"><code>94708ad</code></a> feat: Synthesize Python quality tasks (<a href="https://redirect.github.com/vercel/turborepo/issues/13672">#13672</a>)</li> <li><a href="https://github.com/vercel/turborepo/commit/e14f04ec6c2dc2791b0a3beb32df7515b31b3d4b"><code>e14f04e</code></a> refactor: Extract uv native task specs (<a href="https://redirect.github.com/vercel/turborepo/issues/13671">#13671</a>)</li> <li>Additional commits viewable in <a href="https://github.com/vercel/turborepo/compare/v2.10.8...v2.10.9">compare view</a></li> </ul> </details> <br /> Updates `wrangler` from 4.118.0 to 4.119.0 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/cloudflare/workers-sdk/releases">wrangler's releases</a>.</em></p> <blockquote> <h2>wrangler@4.119.0</h2> <h3>Minor Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/cloudflare/workers-sdk/pull/14952">#14952</a> <a href="https://github.com/cloudflare/workers-sdk/commit/20470fa8b09761c50b5c2c1d6a5f2652b61bd271"><code>20470fa</code></a> Thanks <a href="https://github.com/nelsonjsduarte"><code>@nelsonjsduarte</code></a>! - Add <code>--parse-type</code> flag to <code>wrangler ai-search create</code></p> <p><code>wrangler ai-search create</code> now accepts <code>--parse-type</code> to control how a website data source discovers URLs. <code>sitemap</code> (the default) reads XML sitemaps; <code>discover</code> follows links recursively.</p> <p>Previously the parse type could only be chosen through the interactive wizard, which was skipped whenever <code>--source</code> was supplied — so it was impossible to create a <code>discover</code> instance from a script.</p> <pre lang="sh"><code>wrangler ai-search create my-instance \ --type web-crawler \ --source https://example.com \ --parse-type discover </code></pre> <p>The interactive wizard now offers <code>Discover</code> alongside <code>Sitemap</code>. <code>--parse-type</code> is only valid with <code>--type web-crawler</code>; passing it with <code>--type builtin</code> or <code>--type r2</code> is rejected, since the API stores the value for those source types but never reads it. When the flag is omitted in non-interactive mode the field is left unset and the API default (<code>sitemap</code>) applies.</p> </li> <li> <p><a href="https://redirect.github.com/cloudflare/workers-sdk/pull/14941">#14941</a> <a href="https://github.com/cloudflare/workers-sdk/commit/266172b98c27770e6d48d3fd42790e2125115e5e"><code>266172b</code></a> Thanks <a href="https://github.com/nickpatt"><code>@nickpatt</code></a>! - Improve the Local Explorer's Observability views</p> <p><code>console.log</code> messages now render the way the console would (JSON-encoded strings are unwrapped and multi-argument logs are joined), traces and events can be looked up by trace or span id from the search bar, and an event's "View trace" button jumps to the exact invocation that emitted it — even when a trace_id spans several invocations (e.g. a subrequest or self fetch).</p> </li> <li> <p><a href="https://redirect.github.com/cloudflare/workers-sdk/pull/14064">#14064</a> <a href="https://github.com/cloudflare/workers-sdk/commit/a9e5abb8c0c2e7895b0bb09c6c8e8ffd3dbc3bc0"><code>a9e5abb</code></a> Thanks <a href="https://github.com/petebacondarwin"><code>@petebacondarwin</code></a>! - Add support for OAuth 2.0 Device Authorization Grant to <code>wrangler login</code></p> <p>Run <code>wrangler login --device</code> to authenticate without a local callback server. Useful in containers, remote SSH sessions, Codespaces, and any other environment where <code>localhost:8976</code> is unreachable from your browser.</p> <p>The new flow:</p> <ul> <li>prints the verification URL and user code to the terminal,</li> <li>attempts to open the verification URL in your default browser automatically (suppressed via <code>--browser=false</code>),</li> <li>and polls the token endpoint until you approve the request (with a 5-minute hard cap).</li> </ul> <p>The verification URL is supplied by the authorization server, so it is rejected unless it is an <code>https</code> URL on the same auth domain the device code was requested from — it is never printed or opened otherwise.</p> <p><code>--callback-host</code> and <code>--callback-port</code> cannot be combined with <code>--device</code>, since this flow does not start a local callback server.</p> </li> </ul> <h3>Patch Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/cloudflare/workers-sdk/pull/14984">#14984</a> <a href="https://github.com/cloudflare/workers-sdk/commit/9c7453837e3293787c0cb1778520f630aea7e5ca"><code>9c74538</code></a> Thanks <a href="https://github.com/apps/dependabot"><code>@dependabot</code></a>! - Update dependencies of "miniflare", "wrangler"</p> <p>The following dependency versions have been updated:</p> <table> <thead> <tr> <th>Dependency</th> <th>From</th> <th>To</th> </tr> </thead> <tbody> <tr> <td><code>@cloudflare/workers-types</code></td> <td>^5.20260730.1</td> <td>^5.20260731.1</td> </tr> <tr> <td>workerd</td> <td>1.20260730.1</td> <td>1.20260731.1</td> </tr> </tbody> </table> </li> <li> <p><a href="https://redirect.github.com/cloudflare/workers-sdk/pull/15012">#15012</a> <a href="https://github.com/cloudflare/workers-sdk/commit/0d33cb8dfb1d6289cb180f16e0e60cd7073a1b1b"><code>0d33cb8</code></a> Thanks <a href="https://github.com/apps/dependabot"><code>@dependabot</code></a>! - Update dependencies of "miniflare", "wrangler"</p> <p>The following dependency versions have been updated:</p> </li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/cloudflare/workers-sdk/commit/2938c01a9da2424a3f2d3c73bd870c7b75b39753"><code>2938c01</code></a> Version Packages (<a href="https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/wrangler/issues/15021">#15021</a>)</li> <li><a href="https://github.com/cloudflare/workers-sdk/commit/b6a862966aaaa4d2bc7845a349636a6af65313fe"><code>b6a8629</code></a> Revert "Version Packages (<a href="https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/wrangler/issues/14977">#14977</a>)" (<a href="https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/wrangler/issues/15033">#15033</a>)</li> <li><a href="https://github.com/cloudflare/workers-sdk/commit/42c4227798c21cfde8dbfb087be1bb9078dab185"><code>42c4227</code></a> Version Packages (<a href="https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/wrangler/issues/14977">#14977</a>)</li> <li><a href="https://github.com/cloudflare/workers-sdk/commit/511635c70821d33c64bb377e2c4a6be27683801f"><code>511635c</code></a> [wrangler] Skip the CAA half of the unenv-preset testDns E2E (<a href="https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/wrangler/issues/15016">#15016</a>)</li> <li><a href="https://github.com/cloudflare/workers-sdk/commit/ebd1dfd3778dd3fdb9a63a5596852287eb4029b1"><code>ebd1dfd</code></a> [vite-plugin] Surface Local Explorer API to headless agents, matching wrangle...</li> <li><a href="https://github.com/cloudflare/workers-sdk/commit/a9e5abb8c0c2e7895b0bb09c6c8e8ffd3dbc3bc0"><code>a9e5abb</code></a> [wrangler] Add OAuth 2.0 Device Authorization Grant support (<a href="https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/wrangler/issues/14064">#14064</a>)</li> <li><a href="https://github.com/cloudflare/workers-sdk/commit/20470fa8b09761c50b5c2c1d6a5f2652b61bd271"><code>20470fa</code></a> [wrangler] Add --parse-type to ai-search create (<a href="https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/wrangler/issues/14952">#14952</a>)</li> <li><a href="https://github.com/cloudflare/workers-sdk/commit/5fd61271cdb7c661eace968ae4cbae40d2fbdc37"><code>5fd6127</code></a> [miniflare] De-flake rate limit tests at bucket boundaries (<a href="https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/wrangler/issues/14961">#14961</a>)</li> <li>See full diff in <a href="https://github.com/cloudflare/workers-sdk/commits/wrangler@4.119.0/packages/wrangler">compare view</a></li> </ul> </details> <br /> Updates `@cloudflare/vitest-pool-workers` from 0.20.1 to 0.20.3 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/cloudflare/workers-sdk/releases">@cloudflare/vitest-pool-workers's releases</a>.</em></p> <blockquote> <h2><code>@cloudflare/vitest-pool-workers</code><a href="https://github.com/0"><code>@0</code></a>.20.3</h2> <h3>Patch Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/cloudflare/workers-sdk/pull/15013">#15013</a> <a href="https://github.com/cloudflare/workers-sdk/commit/8cf78c83cb4c64be8b458d7bd618b47e7c6e7d25"><code>8cf78c8</code></a> Thanks <a href="https://github.com/dario-piotrowicz"><code>@dario-piotrowicz</code></a>! - Update undici from 7.28.0 to 7.29.0</p> </li> <li> <p>Updated dependencies [<a href="https://github.com/cloudflare/workers-sdk/commit/35c87e97199fb4548d4d9aaac024c3e07be5734e"><code>35c87e9</code></a>, <a href="https://github.com/cloudflare/workers-sdk/commit/b4f0c9760bcab1e04cf1a9c8859feed8b4fc6487"><code>b4f0c97</code></a>, <a href="https://github.com/cloudflare/workers-sdk/commit/8cf78c83cb4c64be8b458d7bd618b47e7c6e7d25"><code>8cf78c8</code></a>, <a href="https://github.com/cloudflare/workers-sdk/commit/a60ff4dea0bbae8775726d9cf885655b56460a30"><code>a60ff4d</code></a>, <a href="https://github.com/cloudflare/workers-sdk/commit/99eb50ce1d3420a50ae0e95958bf49d65874706e"><code>99eb50c</code></a>, <a href="https://github.com/cloudflare/workers-sdk/commit/35c87e97199fb4548d4d9aaac024c3e07be5734e"><code>35c87e9</code></a>]:</p> <ul> <li>wrangler@4.120.0</li> <li><a href="mailto:miniflare@5.20260801.1-alpha">miniflare@5.20260801.1-alpha</a></li> </ul> </li> </ul> <h2><code>@cloudflare/vitest-pool-workers</code><a href="https://github.com/0"><code>@0</code></a>.20.2</h2> <h3>Patch Changes</h3> <ul> <li>Updated dependencies [<a href="https://github.com/cloudflare/workers-sdk/commit/20470fa8b09761c50b5c2c1d6a5f2652b61bd271"><code>20470fa</code></a>, <a href="https://github.com/cloudflare/workers-sdk/commit/9c7453837e3293787c0cb1778520f630aea7e5ca"><code>9c74538</code></a>, <a href="https://github.com/cloudflare/workers-sdk/commit/266172b98c27770e6d48d3fd42790e2125115e5e"><code>266172b</code></a>, <a href="https://github.com/cloudflare/workers-sdk/commit/a88d1691d57bf44616ad15556a51b7f8ca17375c"><code>a88d169</code></a>, <a href="https://github.com/cloudflare/workers-sdk/commit/a88d1691d57bf44616ad15556a51b7f8ca17375c"><code>a88d169</code></a>, <a href="https://github.com/cloudflare/workers-sdk/commit/daf65f28cecf35e251dc6e476d5bbd82972d68de"><code>daf65f2</code></a>, <a href="https://github.com/cloudflare/workers-sdk/commit/a9e5abb8c0c2e7895b0bb09c6c8e8ffd3dbc3bc0"><code>a9e5abb</code></a>]: <ul> <li>wrangler@4.119.0</li> <li><a href="mailto:miniflare@5.20260801.0-alpha">miniflare@5.20260801.0-alpha</a></li> </ul> </li> </ul> </blockquote> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/cloudflare/workers-sdk/blob/main/packages/vitest-pool-workers/CHANGELOG.md">@cloudflare/vitest-pool-workers's changelog</a>.</em></p> <blockquote> <h2>0.20.3</h2> <h3>Patch Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/cloudflare/workers-sdk/pull/15013">#15013</a> <a href="https://github.com/cloudflare/workers-sdk/commit/8cf78c83cb4c64be8b458d7bd618b47e7c6e7d25"><code>8cf78c8</code></a> Thanks <a href="https://github.com/dario-piotrowicz"><code>@dario-piotrowicz</code></a>! - Update undici from 7.28.0 to 7.29.0</p> </li> <li> <p>Updated dependencies [<a href="https://github.com/cloudflare/workers-sdk/commit/35c87e97199fb4548d4d9aaac024c3e07be5734e"><code>35c87e9</code></a>, <a href="https://github.com/cloudflare/workers-sdk/commit/b4f0c9760bcab1e04cf1a9c8859feed8b4fc6487"><code>b4f0c97</code></a>, <a href="https://github.com/cloudflare/workers-sdk/commit/8cf78c83cb4c64be8b458d7bd618b47e7c6e7d25"><code>8cf78c8</code></a>, <a href="https://github.com/cloudflare/workers-sdk/commit/a60ff4dea0bbae8775726d9cf885655b56460a30"><code>a60ff4d</code></a>, <a href="https://github.com/cloudflare/workers-sdk/commit/99eb50ce1d3420a50ae0e95958bf49d65874706e"><code>99eb50c</code></a>, <a href="https://github.com/cloudflare/workers-sdk/commit/35c87e97199fb4548d4d9aaac024c3e07be5734e"><code>35c87e9</code></a>]:</p> <ul> <li>wrangler@4.120.0</li> <li><a href="mailto:miniflare@5.20260801.1-alpha">miniflare@5.20260801.1-alpha</a></li> </ul> </li> </ul> <h2>0.20.2</h2> <h3>Patch Changes</h3> <ul> <li>Updated dependencies [<a href="https://github.com/cloudflare/workers-sdk/commit/20470fa8b09761c50b5c2c1d6a5f2652b61bd271"><code>20470fa</code></a>, <a href="https://github.com/cloudflare/workers-sdk/commit/9c7453837e3293787c0cb1778520f630aea7e5ca"><code>9c74538</code></a>, <a href="https://github.com/cloudflare/workers-sdk/commit/266172b98c27770e6d48d3fd42790e2125115e5e"><code>266172b</code></a>, <a href="https://github.com/cloudflare/workers-sdk/commit/a88d1691d57bf44616ad15556a51b7f8ca17375c"><code>a88d169</code></a>, <a href="https://github.com/cloudflare/workers-sdk/commit/a88d1691d57bf44616ad15556a51b7f8ca17375c"><code>a88d169</code></a>, <a href="https://github.com/cloudflare/workers-sdk/commit/daf65f28cecf35e251dc6e476d5bbd82972d68de"><code>daf65f2</code></a>, <a href="https://github.com/cloudflare/workers-sdk/commit/a9e5abb8c0c2e7895b0bb09c6c8e8ffd3dbc3bc0"><code>a9e5abb</code></a>]: <ul> <li>wrangler@4.119.0</li> <li><a href="mailto:miniflare@5.20260801.0-alpha">miniflare@5.20260801.0-alpha</a></li> </ul> </li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/cloudflare/workers-sdk/commit/b0aea76e0a7862b4ecfbe44232fb0a56ba3a2525"><code>b0aea76</code></a> Version Packages (<a href="https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/vitest-pool-workers/issues/15036">#15036</a>)</li> <li><a href="https://github.com/cloudflare/workers-sdk/commit/2938c01a9da2424a3f2d3c73bd870c7b75b39753"><code>2938c01</code></a> Version Packages (<a href="https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/vitest-pool-workers/issues/15021">#15021</a>)</li> <li><a href="https://github.com/cloudflare/workers-sdk/commit/b6a862966aaaa4d2bc7845a349636a6af65313fe"><code>b6a8629</code></a> Revert "Version Packages (<a href="https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/vitest-pool-workers/issues/14977">#14977</a>)" (<a href="https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/vitest-pool-workers/issues/15033">#15033</a>)</li> <li><a href="https://github.com/cloudflare/workers-sdk/commit/42c4227798c21cfde8dbfb087be1bb9078dab185"><code>42c4227</code></a> Version Packages (<a href="https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/vitest-pool-workers/issues/14977">#14977</a>)</li> <li>See full diff in <a href="https://github.com/cloudflare/workers-sdk/commits/@cloudflare/vitest-pool-workers@0.20.3/packages/vitest-pool-workers">compare view</a></li> </ul> </details> <br /> Updates `@cloudflare/workers-types` from 5.20260714.1 to 5.20260804.1 <details> <summary>Commits</summary> <ul> <li>See full diff in <a href="https://github.com/cloudflare/workerd/commits">compare view</a></li> </ul> </details> <br /> Updates `@prisma/compute-sdk` from 0.38.0 to 0.39.0 <details> <summary>Commits</summary> <ul> <li>See full diff in <a href="https://github.com/prisma/project-compute/commits">compare view</a></li> </ul> </details> <br /> Updates `@prisma/management-api-sdk` from 1.53.0 to 1.56.0 <details> <summary>Commits</summary> <ul> <li>See full diff in <a href="https://github.com/prisma/pdp-control-plane/commits/HEAD/packages/management-api-sdk">compare view</a></li> </ul> </details> <br /> Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore <dependency name> major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore <dependency name> minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore <dependency name>` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore <dependency name>` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore <dependency name> <ignore condition>` will remove the ignore condition of the specified dependency and ignore conditions </details> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-authored-by: willbot <w.a.madden+machine@gmail.com> | 1 个月前 | |
ci: combine package tests and coverage (#30082) ## Linked issue n/a — this infrastructure migration has no Linear ticket. ## At a glance ```json "coverage:packages": "turbo run build --filter='!./examples/**' --filter='!./test/**' && vitest run --coverage", "coverage:report": "node scripts/coverage-report.mjs" ``` One root Vitest invocation now runs package tests and collects coverage, replacing the duplicated package-test and coverage CI jobs. ## Decision This PR ships three related changes: 1. Package tests and package coverage run together in one root Vitest multi-project invocation on Vitest `5.0.0-rc.2`. 2. Each package owns its complete coverage policy in an adjacent `coverage.config.json`, while root composition and post-processing preserve package thresholds and time-limited warning-only exceptions. 3. The obsolete, type-test-only SQL lane query-builder package and its public facade export are removed instead of retaining a permanently unmeasurable 95% runtime-coverage policy. ## Reviewer notes - The broad config diff is mostly moving existing coverage include/exclude/threshold blocks from `vitest.config.ts` into adjacent JSON policies and removing now-redundant package coverage scripts. - Vitest 5 removes `describe.sequential`; affected suites now use `{ concurrent: false }`. Compile-only `.test-d.ts` suites also declare compile-time test cases so Vitest 5 recognizes them. - `examples/prisma-8-cloudflare-worker` intentionally remains on Vitest 4 because `@cloudflare/vitest-pool-workers@0.20.3` requires Vitest 4 peers. - Eight existing package coverage deficits remain visible as active, non-blocking warning-only entries. Expired warnings and ordinary threshold failures still block CI. ## How it fits together 1. `scripts/coverage-config.js` discovers and validates package policies deterministically, rebases package globs to the repository root, and composes process-wide V8 collection settings. 2. The root `vitest.config.ts` references every package project and applies the composed coverage settings to a single test process. 3. `scripts/coverage-report.mjs` reads the root `coverage/coverage-final.json`, attributes files to their owning package, calculates all four metrics, and enforces each package's policy and warning expiry. 4. `.github/workflows/ci.yml` runs `pnpm coverage:packages` in the test job, reports package coverage even when collection finds a test failure, and removes the standalone coverage job. Test failures remain blocking. 5. Vitest 5 compatibility updates keep type tests, sequential suites, and CLI module mocks deterministic under the new runner behavior. ## Behavior changes & evidence - **Package tests execute once in CI while still producing coverage.** The combined command and workflow live in [`package.json`](package.json) and [`.github/workflows/ci.yml`](.github/workflows/ci.yml); [`scripts/coverage-config.test.mjs`](scripts/coverage-config.test.mjs) guards the single-run workflow shape. - **Coverage ownership remains package-local and threshold enforcement remains package-aware.** Composition is implemented in [`scripts/coverage-config.js`](scripts/coverage-config.js), reporting in [`scripts/coverage-report.mjs`](scripts/coverage-report.mjs), and exercised by [`scripts/coverage-report.test.mjs`](scripts/coverage-report.test.mjs). - **Vitest 5 runs the workspace without the previous V8 merge bottleneck.** The workspace pins are in [`package.json`](package.json) and [`pnpm-lock.yaml`](pnpm-lock.yaml); representative compatibility fixes are covered by [`packages/1-framework/3-tooling/cli/test/migration-cli.test.ts`](packages/1-framework/3-tooling/cli/test/migration-cli.test.ts) and the migrated type-test suites. - **The obsolete SQL lane query-builder is no longer published.** Its package is removed, along with the facade dependency/export in [`packages/9-public/@prisma/orm-family-sql/package.json`](packages/9-public/@prisma/orm-family-sql/package.json) and publish-surface mapping in [`packages/0-shared/publish-surface/src/shells.ts`](packages/0-shared/publish-surface/src/shells.ts). ## Compatibility / migration / risk This is a pre-1.0 breaking cleanup: `@internal/sql-lane-query-builder` and `@prisma/orm-family-sql/lane-query-builder` are removed. Repository references and generated facade wiring were removed together, and the public SQL family shell rebuilds without them. Coverage semantics remain package-specific; only orchestration and report aggregation change. ## Testing performed - `CI=true TEST_TIMEOUT_MULTIPLIER=2 pnpm coverage:packages` — 1,155 files passed; 15,311 tests passed, 3 expected failures, no type errors - `pnpm coverage:report` — 69 package policies, 0 blocking failures, 8 active warnings, 0 expired warnings - `pnpm test:scripts` — 476 tests passed - `pnpm lint:deps` - `pnpm lint:manifests` - `pnpm build --filter=@prisma/orm-family-sql...` - Publish-surface tests and typecheck — 56 tests passed - Focused package tests/typechecks for CLI, Mongo runtime, SQL ORM client, SQLite codec testkit, integration tests, examples, and shell tarballs - `pnpm install --frozen-lockfile --ignore-scripts` - Targeted Biome checks and `git diff --check` ## Skill update n/a — the removed prototype query-builder export was not referenced by any user-facing skill; its package, public README, architecture docs, and publish surface were updated directly. ## Alternatives considered - **Keep Vitest 4 and optimize around it:** the single V8 run remained CPU-bound for more than 37 minutes because the relevant V8 merge optimization is only available in Vitest 5; the Vitest 4 backport was not merged. - **Switch to Istanbul coverage:** benchmarking was slower and introduced CLI language-server instrumentation timeouts, so V8 remains the provider. - **Run packages sequentially:** this preserves policy isolation but repeats runner startup and cannot eliminate duplicate test execution in CI; root collection plus package-aware post-processing keeps policy ownership without that cost. ## Checklist - [x] All commits are signed off (`git commit -s`) per the DCO. - [x] I read `CONTRIBUTING.md` and the change is scoped to one logical concern. - [x] Tests are updated. - [ ] The PR title is in `TML-NNNN: <sentence-case title>` form — no Linear ticket exists, so this uses the conventional commit title required by `CONTRIBUTING.md`. - [x] The **Skill update** section is filled in. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Breaking Changes** - Removed the SQL lane query-builder package and its public package export. - Updated SQL documentation and package entrypoint references. - **Testing & Quality** - Centralized package coverage reporting with package-specific thresholds, exclusions, and warning policies. - Improved coverage validation, threshold reporting, and CI integration. - Updated serialized integration-test execution for compatibility with the current test runner. - **Documentation** - Expanded testing guidance for package coverage workflows and CI behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Steven McClankerton <tatarintsev@prisma.io> Co-authored-by: Steven McClankerton <tatarintsev@prisma.io> | 1 个月前 | |
chore(release): bump to 8.0.0-rc.14 (#30541) ## Release `8.0.0-rc.13` → `8.0.0-rc.14` See [`docs/oss/versioning.md`](https://github.com/prisma/orm/blob/main/docs/oss/versioning.md) for the versioning policy and the release procedure. ## What to review - **Release notes:** [`docs/releases/v8.0.0-rc.14.md`](https://github.com/prisma/orm/blob/release/8.0.0-rc.14/docs/releases/v8.0.0-rc.14.md). This file becomes the body of the GitHub Release. `CHANGELOG.md` carries the same text. The notes cover the 15 pull requests merged since `v8.0.0-rc.13`; internal refactors, process notes and documentation-only changes are left out. - **Upgrade guides:** the [app guide](https://github.com/prisma/orm/blob/release/8.0.0-rc.14/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.13-to-8.0.0-rc.14/instructions.md) and the [extension guide](https://github.com/prisma/orm/blob/release/8.0.0-rc.14/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.13-to-8.0.0-rc.14/instructions.md). They are assembled from the 5 pending fragments, which moved unchanged to `upgrade-instructions/releases/8.0.0-rc.13-to-8.0.0-rc.14/sources/`. ## What changed - The `version` of every workspace `package.json`, and the `workspace:` specifiers that name it, in the package files and in `pnpm-lock.yaml`. - The `library_version` stamp in `skills/prisma-8/SKILL.md`. - The extension version stamp in the Supabase `contract.json` and `contract.d.ts` artefacts. - The upgrade guides, the archived fragments, the release notes and the changelog. ## How the guides were assembled - Order: package changes first (`@prisma/cli-engine` pin, `temporal-polyfill` peer), then the serverless client, date and time defaults, and the Mongo ORM. - Every line of every fragment is in a guide; I checked this by script. - The `temporal-polyfill-peer` fragments had their own `#` title. The guides drop it, so each change sits under a heading that names its change id, as every other entry does. - The app guide ends with the `contract-artifacts-restamp` entry, as the previous app guides do. - The fragments had no scripts or assets. `serverless-connect-returns-connection/extension` declared `changes: []` and adds nothing. ## Checks run locally - `pnpm check:upgrade-coverage --mode publish --prev v8.0.0-rc.13 --head HEAD` passes. - `pnpm check:release-notes --mode pr --prev origin/main` passes. - `pnpm lint:docs` passes. ## Pull requests that may merge first Four approved pull requests add pending fragments: #30521, #30530, #30512 and #30331. If one merges before this one, I merge `main` into this branch, add its fragment to the guides, archive it, and add its change to the notes. CI checks the merged tree, so a leftover fragment fails the merge queue. ## Merging this pull request ships the release The push to `main` carries the new root `version`. The `Publish to npm` workflow detects the change, publishes `8.0.0-rc.14` under the dist-tag `latest`, and creates a matching GitHub Release, marked as a pre-release. ## Docs site The matching docs-site pull request follows in prisma/web. It merges after this release is published. Agent: pasteur-43 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Postgres serverless connections now expose ORM and transaction helpers. Optional cursor batching and multi-file schema support in the language server are also available. * **Bug Fixes** * Improved MongoDB encoding, decoding, query, and upsert behavior; added Node 24 date/time support and made SQLite date defaults consistent. * Improved namespace-aware relation resolution, policy placement, database-sign guidance, and upgrade instructions. * **Breaking Changes** * Updated Postgres connection and cursor behavior, canonical date/time defaults and validation, MongoDB result shapes and codec requirements, and extension and contract diagnostics. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> | 2 天前 | |
feat: an application depends on one Prisma package (ADR 242) (#29864) ## What changes for someone using Prisma Today, an application that talks to Postgres installs a long list of our packages: ```jsonc { "dependencies": { "@prisma-next/postgres": "...", "@prisma-next/sql-runtime": "...", "@prisma-next/sql-orm-client": "...", "@prisma-next/target-postgres": "...", "@prisma-next/adapter-postgres": "...", "@prisma-next/sql-contract": "..." // ...a dozen more } } ``` After this PR, it installs one: ```jsonc { "dependencies": { "@prisma/orm-postgres": "0.16.0" } } ``` Everything else arrives as that package's own dependencies. Three of our example apps are converted in this PR to prove it — one per database — and each has exactly one Prisma package in its `dependencies`. This implements [ADR 242](https://github.com/prisma/prisma/pull/29852), which is already merged. ## What gets published 17 packages, all under the `@prisma` scope: - **3 database packages** — `@prisma/orm-postgres`, `orm-sqlite`, `orm-mongo`. An application installs exactly one. We call these *facades*: each is a small package that wires its database together and re-exports everything an application needs. - **6 extension packs** — PostGIS, pgvector, ParadeDB, Supabase, arktype-json, middleware-cache. Optional, installed alongside a database package. - **7 platform packages** — the framework, the toolchain, one per database family, one per database target. Applications never install these directly; they arrive as dependencies. Extension authors do install them. - **the `prisma` command**, as a bin-only package. Every other workspace package — around 50 of them — stops being published. They still exist in the repo as the unit we organise code in; they just stop having a life on the registry. **This PR does not make that switch yet.** It builds and proves the new surface while leaving today's publish list exactly as it is. Flipping it is a separate change. ## The problem this design has to avoid A published package can't depend on packages that won't exist on the registry. So each published package *contains a compiled copy* of the internal packages it covers. That creates a trap. If one application ends up with the same code twice — once inside a published package, once as its own package — then classes, registries, and anything compared by reference exist twice too. An `instanceof` check quietly returns false. Nothing crashes, nothing fails to compile, and both copies behave identically in isolation. You find out much later, somewhere unrelated. So the rule the whole design follows is: **every piece of internal code is published from exactly one package.** Concretely, that means: - Each published package is built in one pass, so code shared between its own entry points exists once. Verified from the build's source maps: no module appears in more than one chunk, in any published package. - When one published package needs code from another, it imports it as a real dependency rather than compiling in a second copy. - A facade re-exports from the platform packages; it never carries its own copy. `@prisma/orm-postgres/orm-client` and `@prisma/orm-family-sql/orm-client` are two names for the same object, and there's a test that asserts exactly that from installed tarballs. - One table in `packages/0-shared/publish-surface` maps every internal package to where it's published. The build, the code generator, and the lint checks all read it, so there's one answer to "where does this live" rather than three that can drift. ## Generated code follows the application Prisma writes imports into your project — contract types and migration files. Those imports have to name packages your project actually depends on, or they won't resolve. So the generator now reads the `package.json` next to the config it's generating for. A project that depends on `@prisma/orm-postgres` gets imports from that package. A project on today's names keeps today's names. Nothing to configure, because the manifest already says which it is. Contract hashes are unaffected, and that isn't an assumption — hashes are computed from a structure that import text never enters, and there's a test asserting the hash is identical across naming schemes *while* the emitted imports demonstrably differ. ## What stops the trap coming back Two checks, because the failure is silent and won't show up in a test suite: - Every example app and test project must use one naming scheme, not a mix. `lint-single-import-root` scans them and fails the build if any project imports from both, since that's the situation that loads code twice. - `lint-consumer-internal-imports` counts how many internal-package imports remain in those projects and compares against a committed number. It fails if the number goes up (someone added one) and also if it goes down without the number being updated (so improvements get locked in). Target is zero. The build itself also refuses to proceed if the published-package map would put one module in two places, or if a published package's `package.json` no longer matches what its code actually needs. ## Reading this PR It's large — 257 files — because it's a migration. The commits are grouped and meant to be read in order: 1. **Platform packages** — the build mechanism, and the seven platform packages it produces. 2. **Database packages, extension packs, the `prisma` command** — completes the set of 17. 3. **Generated imports become configurable** — one place decides which names get written, with today's names still the default. 4. **Database-family symmetry, publishing the map, the identity checks.** 5. **One package per application** — the three converted examples, the re-exports they proved necessary, and the counting check. 6. **Migration files follow the project too.** One thing worth knowing while reading: re-exporting a package republishes all of its sub-paths, not just the one that was needed. This PR adds 115 published sub-paths across the three database packages. Two candidates were dropped for exactly that reason — see below. ## Alternatives considered **Let an application install platform packages alongside its facade.** Nothing would need re-exporting and the facades would stay thinner. Rejected: an application would again juggle several Prisma dependencies whose correct combination it maintains by hand, and getting it wrong — upgrading one and not the other — produces the silent two-copies failure above. Re-exporting costs a generated line and nothing at runtime. **Re-export everything an application might plausibly want.** Rejected in review: because re-exporting brings a package's entire sub-path surface, generosity is expensive and hard to undo. Migration tooling (54 sub-paths) was dropped because its only users are extension packs, which install platform packages anyway; the SQL driver re-export was dropped because nothing imported it at all. What remains is what a converted example actually needed. **Flip the publish list in this same PR.** Rejected: it would mix "does the new surface work" with "is it safe to stop publishing 50 packages" in one review. The switch is mechanical once this lands, and gets its own change. ## Verification `build`, `typecheck` (156 tasks), `test:packages` (1077 files / 14087 tests), `test:e2e`, `lint`, `lint:deps`, `lint:docs`, `lint:manifests`, `check:publish-deps`, `check:clean-tree`, `lint:casts` and `lint:throws` (no new instances), `test:scripts`, coverage, the tarball-install suites, and regenerating every committed artifact leaves the tree unchanged. Known-unstable and unrelated to this change: the `relation-mode-gh-*` port suites (TML-3140), and several test timeouts that are too tight under load. ## Follow-ups TML-3124 switch the publish list · TML-3127 build cache can validate a stale published package on CI · TML-3140 unstable port suites · TML-3141 a test-helper sub-path reaches a package that is never published. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added consolidated public ORM packages for PostgreSQL, MongoDB, SQLite, framework tooling, database targets, and extensions. - Generated contracts, migrations, and scaffolds now adapt imports to the consuming project’s package surface. - Added facade-provided `prisma-next` CLI access and consolidated migration entrypoints. - **Documentation** - Updated installation, package naming, public entrypoint, and migration scaffolding guidance. - **Tests** - Added coverage for package installation, exports, CLI behavior, module identity, and import compatibility. - **Chores** - Added checks preventing incompatible internal and public package imports. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> | 1 个月前 | |
feat: an application depends on one Prisma package (ADR 242) (#29864) ## What changes for someone using Prisma Today, an application that talks to Postgres installs a long list of our packages: ```jsonc { "dependencies": { "@prisma-next/postgres": "...", "@prisma-next/sql-runtime": "...", "@prisma-next/sql-orm-client": "...", "@prisma-next/target-postgres": "...", "@prisma-next/adapter-postgres": "...", "@prisma-next/sql-contract": "..." // ...a dozen more } } ``` After this PR, it installs one: ```jsonc { "dependencies": { "@prisma/orm-postgres": "0.16.0" } } ``` Everything else arrives as that package's own dependencies. Three of our example apps are converted in this PR to prove it — one per database — and each has exactly one Prisma package in its `dependencies`. This implements [ADR 242](https://github.com/prisma/prisma/pull/29852), which is already merged. ## What gets published 17 packages, all under the `@prisma` scope: - **3 database packages** — `@prisma/orm-postgres`, `orm-sqlite`, `orm-mongo`. An application installs exactly one. We call these *facades*: each is a small package that wires its database together and re-exports everything an application needs. - **6 extension packs** — PostGIS, pgvector, ParadeDB, Supabase, arktype-json, middleware-cache. Optional, installed alongside a database package. - **7 platform packages** — the framework, the toolchain, one per database family, one per database target. Applications never install these directly; they arrive as dependencies. Extension authors do install them. - **the `prisma` command**, as a bin-only package. Every other workspace package — around 50 of them — stops being published. They still exist in the repo as the unit we organise code in; they just stop having a life on the registry. **This PR does not make that switch yet.** It builds and proves the new surface while leaving today's publish list exactly as it is. Flipping it is a separate change. ## The problem this design has to avoid A published package can't depend on packages that won't exist on the registry. So each published package *contains a compiled copy* of the internal packages it covers. That creates a trap. If one application ends up with the same code twice — once inside a published package, once as its own package — then classes, registries, and anything compared by reference exist twice too. An `instanceof` check quietly returns false. Nothing crashes, nothing fails to compile, and both copies behave identically in isolation. You find out much later, somewhere unrelated. So the rule the whole design follows is: **every piece of internal code is published from exactly one package.** Concretely, that means: - Each published package is built in one pass, so code shared between its own entry points exists once. Verified from the build's source maps: no module appears in more than one chunk, in any published package. - When one published package needs code from another, it imports it as a real dependency rather than compiling in a second copy. - A facade re-exports from the platform packages; it never carries its own copy. `@prisma/orm-postgres/orm-client` and `@prisma/orm-family-sql/orm-client` are two names for the same object, and there's a test that asserts exactly that from installed tarballs. - One table in `packages/0-shared/publish-surface` maps every internal package to where it's published. The build, the code generator, and the lint checks all read it, so there's one answer to "where does this live" rather than three that can drift. ## Generated code follows the application Prisma writes imports into your project — contract types and migration files. Those imports have to name packages your project actually depends on, or they won't resolve. So the generator now reads the `package.json` next to the config it's generating for. A project that depends on `@prisma/orm-postgres` gets imports from that package. A project on today's names keeps today's names. Nothing to configure, because the manifest already says which it is. Contract hashes are unaffected, and that isn't an assumption — hashes are computed from a structure that import text never enters, and there's a test asserting the hash is identical across naming schemes *while* the emitted imports demonstrably differ. ## What stops the trap coming back Two checks, because the failure is silent and won't show up in a test suite: - Every example app and test project must use one naming scheme, not a mix. `lint-single-import-root` scans them and fails the build if any project imports from both, since that's the situation that loads code twice. - `lint-consumer-internal-imports` counts how many internal-package imports remain in those projects and compares against a committed number. It fails if the number goes up (someone added one) and also if it goes down without the number being updated (so improvements get locked in). Target is zero. The build itself also refuses to proceed if the published-package map would put one module in two places, or if a published package's `package.json` no longer matches what its code actually needs. ## Reading this PR It's large — 257 files — because it's a migration. The commits are grouped and meant to be read in order: 1. **Platform packages** — the build mechanism, and the seven platform packages it produces. 2. **Database packages, extension packs, the `prisma` command** — completes the set of 17. 3. **Generated imports become configurable** — one place decides which names get written, with today's names still the default. 4. **Database-family symmetry, publishing the map, the identity checks.** 5. **One package per application** — the three converted examples, the re-exports they proved necessary, and the counting check. 6. **Migration files follow the project too.** One thing worth knowing while reading: re-exporting a package republishes all of its sub-paths, not just the one that was needed. This PR adds 115 published sub-paths across the three database packages. Two candidates were dropped for exactly that reason — see below. ## Alternatives considered **Let an application install platform packages alongside its facade.** Nothing would need re-exporting and the facades would stay thinner. Rejected: an application would again juggle several Prisma dependencies whose correct combination it maintains by hand, and getting it wrong — upgrading one and not the other — produces the silent two-copies failure above. Re-exporting costs a generated line and nothing at runtime. **Re-export everything an application might plausibly want.** Rejected in review: because re-exporting brings a package's entire sub-path surface, generosity is expensive and hard to undo. Migration tooling (54 sub-paths) was dropped because its only users are extension packs, which install platform packages anyway; the SQL driver re-export was dropped because nothing imported it at all. What remains is what a converted example actually needed. **Flip the publish list in this same PR.** Rejected: it would mix "does the new surface work" with "is it safe to stop publishing 50 packages" in one review. The switch is mechanical once this lands, and gets its own change. ## Verification `build`, `typecheck` (156 tasks), `test:packages` (1077 files / 14087 tests), `test:e2e`, `lint`, `lint:deps`, `lint:docs`, `lint:manifests`, `check:publish-deps`, `check:clean-tree`, `lint:casts` and `lint:throws` (no new instances), `test:scripts`, coverage, the tarball-install suites, and regenerating every committed artifact leaves the tree unchanged. Known-unstable and unrelated to this change: the `relation-mode-gh-*` port suites (TML-3140), and several test timeouts that are too tight under load. ## Follow-ups TML-3124 switch the publish list · TML-3127 build cache can validate a stale published package on CI · TML-3140 unstable port suites · TML-3141 a test-helper sub-path reaches a package that is never published. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added consolidated public ORM packages for PostgreSQL, MongoDB, SQLite, framework tooling, database targets, and extensions. - Generated contracts, migrations, and scaffolds now adapt imports to the consuming project’s package surface. - Added facade-provided `prisma-next` CLI access and consolidated migration entrypoints. - **Documentation** - Updated installation, package naming, public entrypoint, and migration scaffolding guidance. - **Tests** - Added coverage for package installation, exports, CLI behavior, module identity, and import compatibility. - **Chores** - Added checks preventing incompatible internal and public package imports. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> | 1 个月前 | |
feat(cli): `prisma contract print` writes the configured contract as Prisma 8 PSL that reads back as the same contract (#30315) ## At a glance `prisma contract print` takes the contract your config already loads and writes it as a Prisma 8 PSL contract. Given this `prisma.config.ts`: ```ts contract: prisma7Schema('./prisma/schema.prisma'), ``` and this Prisma 7 schema: ```prisma enum Priority { LOW @map("low") HIGH @map("high") } model Post { id Int @id @default(autoincrement()) tags String[] meta Json @default("{\"draft\":true}") priority Priority @default(LOW) author User @relation(fields: [authorId], references: [id]) authorId Int } ``` running `prisma contract print --output prisma/contract.prisma` writes (the `User` model is left out here): ```prisma // use prisma-8 // Printed from prisma/schema.prisma by `prisma contract print`. namespace public { model Post { id Int @id @default(autoincrement()) tags String[]? @noCheck(elementNotNull) meta Jsonb @default(json`{"draft":true}`) priority pg.enum(Priority) @default("low") authorId Int author User @relation(fields: [authorId], references: [id], onDelete: Restrict, onUpdate: Cascade, index: false) } native_enum Priority { low = "low" high = "high" } } ``` Point `contract` at the written file, run `contract emit`, and the emitted contract is the one the Prisma 7 schema produced: the same serialized contract, including its hashes. Without `--output`, the command prints the PSL and writes no file: on screen in a terminal, to standard output with `--format human` (`prisma contract print --format human > printed.prisma`), or as `psl.text` in the JSON result. ## The decision The printer follows one rule: **the file it writes must read back as the same contract.** It writes each part of the contract as the PSL that reads back the same. A part with no such PSL is refused by name, and nothing is written. It never drops or changes part of the contract without saying so. This applies to any contract source, not only Prisma 7. The command loads whatever `contract` names in the config (a Prisma 7 schema, a TypeScript contract, or a PSL contract) and prints the loaded contract. The Prisma 7 cutover is the first use, and the reason the command exists, but nothing in the command is specific to Prisma 7. The rule is proven before release, not checked by the command at run time. An integration test prints every emitted Postgres contract tracked in the repo and requires each one to read back as the same contract, or to be refused with the reason the test expects. A new fixture is covered as soon as it is committed. ## How it works The command loads the contract the same way `contract emit` does, validates it the way `contract emit` does, hands it to the family's `buildPslContract`, and prints the document it returns. With `--output`, it writes the text with the same staged publish `contract infer` uses. One control stack serves the whole run: the source is loaded against it, the family instance is created from it, and its block descriptors and codecs render the text. The hook is named for what it returns, a PSL document; printing is one consumer of it. The SQL family passes the target's `buildPslContract` hook a `SqlPslBuildContext`: the stack's authoring types, codec lookup and data type lookup. The printer asks the stack which PSL type reads back as a column's codec, native type and parameters, so a column carried by an extension codec prints as that extension's type, such as `pgvector.Vector(3)`. Value-object field types and literal defaults are resolved the same way. The Postgres hook lives in `packages/3-targets/3-targets/postgres/src/core/psl-print/`. It shares its literal, index, enum-block and default-mapping builders with `contract infer` through `psl-build/`. It writes: - **Models and fields**, with `@@map` and `@map` only where the name the PSL reader would derive differs from the name in the contract. - **Types**: value objects as `type` blocks (lists of them as lists), named types as a `types` block, domain enums as `enum` blocks, native enums as `native_enum` blocks. - **Keys and indexes**: primary keys with their names, `@@unique`, and `@@index` with every argument the language has. - **Checks**, by their `name:` prefix when the wire name derives from it and by `map:` otherwise, minus the checks the reader derives for list and enum columns. - **Relations**, with the referential actions and constraint name their foreign key carries, and explicit junction models for many-to-many. - **Polymorphism**: `@@discriminator` on the base and `@@base` on each variant, for single-table and multi-table variants. - **Control policies** as `@@control`, and **row-level security** as `@@rls`, `policy_<operation>` blocks and `role` blocks in `namespace unbound`. - **Defaults**: literals through the same `mapDefault` as `contract infer`, keyed by the data type of the column's codec, so a Json object prints as a `json` tagged literal; `now()` and `autoincrement()` by name; id generators as `uuid()` and the rest; `@updatedAt` pairs as the `temporal.*` presets; every other database expression as a `sql` tagged literal. Every refusal lives in one module, `refusals.ts`, and covers a valid contract that PSL cannot express; the printer takes a validated contract, so it does not re-check structure. The module matches the `CONTRACT.PRINT_UNSUPPORTED` list in `docs/reference/error-reference.md` one to one; a test fails if the two lists differ in length. Each refusal names the model, field, column or entity. A PSL file cannot carry the contract's default control policy; the config sets it on the PSL source. When the contract has one, the command warns, names it in the next step and in the JSON result (`sourceSettings.defaultControlPolicy`), and the CLI and Postgres READMEs show a config that sets it. With `--output`, the command refuses to write over a file the project needs: any file the contract source reads (compared as real files, through symbolic links, case-insensitively on a volume that ignores case, and including every file a glob input matches or would match once written), `prisma.config.ts`, or the emitted `contract.json` and `contract.d.ts`. ## What is proven The rule is an equality. Every round-trip test prints, reads the text back through the PSL source with the same stack, and compares the serialized contracts, which carry the hashes. The comparison leaves out `capabilities` and `extensions`, which the composed stack reports rather than the source. - **Every Postgres contract in the repo** (`test/integration/test/psl-print/every-postgres-contract-roundtrip.integration.test.ts`): 272 emitted contracts. 249 print and read back as the same contract. The other 23 are refused, and the test lists each one with the reason its refusal must give. - **Every Prisma 7 fixture** (`test/integration/test/psl-print/prisma7-fixture-roundtrip.integration.test.ts`): 33 of the 35 fixtures round-trip. The other 2 declare one model name in two namespaces, and the test asserts their refusal. - **TypeScript-authored contracts** (`typescript-contract-roundtrip.integration.test.ts`): five contracts built with the TypeScript builder round-trip. - **PSL-authored cases and extension types** (`authored-contract-roundtrip.integration.test.ts`, `extension-types-roundtrip.integration.test.ts`): control policies, every index argument, domain enums, primary key names, non-default codecs, checks named by prefix, lists of value objects, row-level security with roles and policies, a model in the unbound namespace, and a `pgvector.Vector(3)` column. - **Every refusal** has a unit test that asserts its code and meta. Journeys run the command end to end. The `relations` and `supported-verify` Prisma 7 fixtures print, emit with the same storage hash the Prisma 7 source emitted, sign, and `db verify` with zero findings against the database built from the SQL Prisma 7.10.0 generated. A PSL source prints. A TypeScript contract with a default control policy prints with a warning, and the config the README shows emits the printed file with that policy. A schema with a `view`, and an `--output` path that is the schema being read, exit 2 and write nothing. ## Changes outside the printer - **Contract source format.** Every contract source states its `format`, `'psl'` or `'typescript'`, and the `orm` config schema rejects any other value or a missing one. A Prisma 7 schema is PSL text, so the Prisma 7 source declares `'psl'`, and `contract format` formats it. Upgrade instructions are in `upgrade-instructions/pending/contract-print/`. - **One PSL grammar.** The parser has no grammar option. A `view` body parses as fields in every document, and an `enum` member may carry `@` attributes in every document; each reader decides what it accepts. The SQL and Mongo readers report `PSL_UNSUPPORTED_ENUM_MEMBER_ATTRIBUTE`. That check and the unknown top-level block check live once in `@internal/psl-parser`, and both readers call them. - **Formatter.** It keeps a `//` comment written between a block's name and its `{`, moving it after the `{`. It writes a space before a list value after `:`, `,` or `=` (`fields: [authorId]`). Bare entries that shared a line are now written one per line. - **PSL printer.** It prints value-object `type` blocks, which it used to drop. It always writes the `// use prisma-8` marker, and each caller passes one description line. - **PSL reader.** - A scalar list field keeps its type parameters, so `Decimal @db.Numeric(65,30)[]` reads back with its type. One emitted fixture changes: an enum list field gains `typeParams.typeName`. Storage is unchanged. - A unique index over plain columns with no `where` makes a back-relation singular, as a unique constraint does. - It exports its naming rules (`pslModelMapName`, `pslFieldMapName`), so the printer writes `@@map` and `@map` by the same rules. - A policy expression decodes every JSON string escape. A PSL contract that wrote `\t` in a policy expression now reads a tab there; an upgrade note says so. - **CLI.** `contract emit`, `contract print`, `ControlClient.emit` and `orm init` load a contract source through one loader, which expands glob inputs. `contract print` validates the loaded contract the way `contract emit` does. - **`contract format`** formats every `.prisma` file under a source input that is a directory, as a Prisma 7 or Prisma 6 source may name one. - **Default emitted path.** The rule for where `contract emit` writes when the config sets no `output` lives once in `@internal/config`; the three facades and the CLI call it. - **Published surface.** `@prisma/orm-family-sql` gains `./contract-psl/map-names` (the two naming rules the printer shares with the reader) and `./family/psl-build`. - **`check-upgrade-coverage`** lists only the directories it reads. It listed the whole repository tree, which passed the 1 MiB child-process output limit. - **Framework vocabulary lint.** It flags the name of any Prisma version before 8 in `packages/1-framework`, except in the CLI, which names Prisma 7 when `orm init` sets Prisma 8 up beside a Prisma 7 project. ## What it cannot write yet The full list is under `CONTRACT.PRINT_UNSUPPORTED` in the error reference, and `projects/prisma7-contract-source/spec.md` records what would lift each. The ones a user is most likely to meet: - A relation into another contract space, such as a Supabase app's relation to `supabase:auth.AuthUser`. PSL can write it; the printer would need the composed extension contracts. - One model name in two namespaces. The PSL reader groups relations by bare model name. - A domain enum or value object outside the default namespace, and a value-object field with type parameters or a value set. The PSL reader does not carry them back. - A foreign key no relation travels, a to-one relation with no foreign key, a back-relation with no owning relation on the other model, and a relation with no `on` part. - A namespace whose name is not a PSL identifier or is `unbound`, and any name `__proto__`, which the PSL reader loses. - A union or dictionary field, a column with its own control policy, a model with an owner, and an entity kind a pack contributes. None has PSL syntax the printer can write. ## Alternatives considered - **Write a file by default, as `contract infer` does.** Rejected: the name `print` would be wrong about what the command does, and in a PSL project the default path would be the contract source itself, so the command would refuse whenever it ran without flags. Printing by default can never overwrite a file. - **Restrict the command to a Prisma 7 source.** That would hide the parts a Prisma 7 schema never produces (value objects, polymorphism, named types, control policies) instead of printing them. Rejected: a printer that drops what it does not understand is not safe behind any check, and the restriction would make the command's name wrong about what it does. - **Read the written file back inside the command and refuse on a mismatch.** Rejected: a published command that refuses its own output is not useful to users. Gaps must be found before release, which is what the test over every contract in the repo does. - **Name the Prisma 7 source's format `'prisma7'` in the framework, or give the parser a `prisma7` grammar.** Rejected: the framework supports PSL and TypeScript, and a Prisma 7 schema is PSL. The grammar is general; only the readers differ. - **Pick a column's PSL type from a table the target keeps.** Rejected: such a table cannot see extension types, and the stack already knows every type it can read back. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> | 5 天前 | |
move SQL family package from 3-tooling to layer 9 packages/2-sql/3-tooling/family/ → packages/2-sql/9-family/ The SQL family is an entry point at the top of the import hierarchy, like the Mongo family at packages/2-mongo-family/9-family/. Architecture config updated to add "family" to the SQL layerOrder. | 5 个月前 |
@internal/family-sql
SQL family descriptor for Prisma 8.
Purpose
Provides the SQL family descriptor (ControlFamilyDescriptor) that includes:
- The SQL target family hook (
sqlEmission) - Factory method (
create()) to create family instances
Responsibilities
- Family Descriptor Export: Exports the SQL
ControlFamilyDescriptorfor use in CLI configuration files - Family Instance Creation: Creates
SqlFamilyInstanceobjects that implement control-plane domain actions (verify,schemaVerify,introspect,emitContract,deserializeContract) - Planner & Runner SPI: Owns the
MigrationPlanner/MigrationRunnerinterfaces plus theSqlControlTargetDescriptorhelper so targets can expose planners and runners (e.g., Postgres init planner/runner) - Family Hook Integration: Integrates the SQL target family hook (
sqlEmission) from@internal/sql-contract-emitter - Control Plane Entry Point: Serves as the control plane entry point for the SQL family, enabling the CLI to select the family hook and create family instances
- Contract-to-SchemaIR Conversion: Converts
SqlStoragefrom a contract intoSqlSchemaIRfor offline migration planning, enablingmigration planto work without a database connection - Destructive Change Detection: Compares two
SqlStoragevalues and identifies destructive changes (dropped tables/columns) for migration policy enforcement - Storage Type Control Hooks: Extracts codec-owned control hooks for planning/verification/introspection of
storage.typeswithout adding enum-specific fields to shared IR - Codec Ownership: Enforces a single owner per
codecIdfor parameterized renderers and control-plane hooks to prevent ambiguous conflicts during assembly - Authoring Contribution Assembly: Assembles authoring contributions (type constructors, field presets) from composed components for PSL interpretation
- Parameterized Type Verification: Expands contract
typeParamsinto expected native type strings during schema verification and flags missing parameters as type mismatches - Schema Defaults Policy: Ignores execution mutation defaults during schema verification since they are applied before DB writes
- Foreign Key Config Awareness: Schema verification respects the contract's
foreignKeysconfiguration — whenforeignKeys.constraintsisfalse, FK constraint checks are skipped during verification (see ADR 161) - Referential Action Verification: When a contract FK specifies
onDeleteoronUpdate, the verifier compares them against the introspected schema and reportsforeign_key_mismatchon mismatch (see ADR 166)
Usage
import sql from '@internal/family-sql/control';
import { createControlStack } from '@internal/framework-components/control';
// sql is a ControlFamilyDescriptor with:
// - kind: 'family'
// - id: 'sql'
// - familyId: 'sql'
// - hook: TargetFamilyHook
// - create: (stack) => SqlFamilyInstance
// Build a control stack (assembles all contributions from components)
const stack = createControlStack({
family: sql,
target: postgresTargetDescriptor,
adapter: postgresAdapterDescriptor,
driver: postgresDriverDescriptor,
extensions: [pgVectorExtensionDescriptor],
});
// Create a family instance for control-plane operations
const familyInstance = sql.create(stack);
// Use instance methods for domain actions
const contract = familyInstance.deserializeContract(contractJson);
const verifyResult = await familyInstance.verify({ driver, contract, ... });
// Targets that implement SqlControlTargetDescriptor can build planners
const planner = postgresTargetDescriptor.migrations.createPlanner(familyInstance);
const planResult = planner.plan({
contract: sqlContract,
schema,
policy,
frameworkComponents: [postgresTargetDescriptor, postgresAdapterDescriptor, pgVectorExtensionDescriptor],
});
// Targets also provide runners for executing plans
const runner = postgresTargetDescriptor.migrations.createRunner(familyInstance);
const executeResult = await runner.execute({
plan: planResult.plan,
driver,
destinationContract: sqlContract,
frameworkComponents: [postgresTargetDescriptor, postgresAdapterDescriptor, pgVectorExtensionDescriptor],
});
// PSL contribution assembly (scalar type descriptors, mutation defaults, authoring
// contributions, codec lookup) is handled at the framework level by createControlStack.
// The CLI passes assembled contributions via ContractSourceContext when calling
// contract source providers — no manual assembly needed in user configs.
// executeResult is a Result<MigrationRunnerSuccessValue, MigrationRunnerFailure>
if (executeResult.ok) {
console.log(`Executed ${executeResult.value.operationsExecuted} operations`);
} else {
console.error(`Migration failed: ${executeResult.failure.code} - ${executeResult.failure.summary}`);
}
Architecture
This package is the control plane entry point for the SQL family. It composes:
@internal/sql-contract-emitter- Provides the SQL family hook@internal/sql-operations- SQL operation signature types@internal/sql-contract- SQL contract types and validation
The framework CLI uses this descriptor to:
- Create family instances for control-plane operations (via
create())
Family instances implement domain actions:
-
deserializeContract(contractJson): Validates and normalizes contract, returnsContractwithout mappings -
verify(): Verifies database marker against contract (compares target, storageHash, profileHash) -
schemaVerify(): Verifies database schema against contract (compares contract requirements vs live schema) -
introspect(): Introspects database schema and returnsSqlSchemaIR -
toSchemaView(schema): ProjectsSqlSchemaIRintoCoreSchemaViewfor human-readable display. Always displays native database types (e.g.,int4,text) rather than mapped codec IDs (e.g.,pg/int4@1) to reflect actual database state. -
emitContract({ contract }): Emits contract JSON and DTS as strings. Handles stripping mappings and validation internally. Uses preassembled state (operation registry, type imports, extension IDs). -
inferPslContract(schemaIR): Infers a PSL contract AST from an introspected schema, forcontract infer. Delegates to the target descriptor's optionalinferPslContracthook; throwsCONTRACT.INFER_UNSUPPORTEDwhen the target has none. -
buildPslContract(contract): Builds the PSL document AST that reads back as the same contract, forcontract print. Delegates to the target descriptor's optionalbuildPslContracthook, and passes it the stack's authoring contributions, codecs and data types (SqlPslBuildContext), the parts the PSL source reads the file back with. Returns the document withsourceSettings: the settings the config must set on the new PSL source because a PSL file cannot carry them. Today that is only the contract'sdefaultControlPolicy, present when the contract has one. ThrowsCONTRACT.PRINT_UNSUPPORTEDwhen the target has no hook, or when the contract holds something PSL cannot express.contractmust be one the target's contract serializer accepted.
The descriptor is "pure data + factory" - it only provides the hook and factory method. All family-specific logic lives on the instance.
Package Structure
src/core/control-descriptor.ts:SqlFamilyDescriptorclass implementingControlFamilyDescriptorinterface (pure data + factory)src/core/control-instance.ts:createSqlFamilyInstancefunction that createsSqlFamilyInstancewith domain action methods (deserializeContract,verify,schemaVerify,introspect,toSchemaView,emitContract). ContainsconvertOperationManifestfunction used internally by instance creation and test utilities in the same package.src/core/assembly.ts: Assembly helpers for extracting type imports, collecting codec-owned storage type control hooks, and composing mutation-default registries with duplicate detection.src/core/verify.ts: Verification helpers (parseContractMarkerRow,collectSupportedCodecTypeIds)src/core/control-adapter.ts: SQL control adapter interface (SqlControlAdapter) for control-plane operationssrc/core/migrations/: Migration IR helpers plus planner and runner SPI types (MigrationPlanner,MigrationRunner,SqlControlTargetDescriptor). Runners returnMigrationRunnerResultwhich is a union of success/failure.src/core/migrations/contract-to-schema-ir.ts:contractToSchemaIR(contract, { annotationNamespace, ... })converts a contract toSqlSchemaIRfor offline migration planning (used bymigration planto synthesize the "from" schema without a database connection). A target may passdataTypeOf, built bybuildDataTypeResolver(frameworkComponents)indata-type-resolver.ts, which returns the data type a codec's descriptor names among the data types every component registers. Each column carries its data type, and a default of a type that declares a canonical form compares through it, so two texts of one date or time value are equal. Also exportsdetectDestructiveChanges(from, to)which compares twoSqlStoragevalues and returns a list of destructive changes (dropped tables, dropped columns) for migration policy enforcement.
Migration Runner Error Codes
The runner returns structured errors with the following codes:
DESTINATION_CONTRACT_MISMATCH: Plan destination hash doesn't match provided contract hashMARKER_ORIGIN_MISMATCH: Existing marker doesn't match plan's expected originPOLICY_VIOLATION: Operation class is not allowed by the plan's policyPRECHECK_FAILED: Operation precheck returned falsePOSTCHECK_FAILED: Operation postcheck returned false after executionSCHEMA_VERIFY_FAILED: Resulting schema doesn't satisfy the destination contractEXECUTION_FAILED: SQL execution error during operation executionsrc/exports/control.ts: Control plane entry point (exportsSqlFamilyDescriptorinstance)src/exports/runtime.ts: Runtime plane entry point
Entrypoints
./control: Control plane entry point for CLI/config usage (exportsSqlFamilyDescriptor)./control-adapter: SQL control adapter interface (SqlControlAdapter,SqlControlAdapterDescriptor) for target-specific adapters./psl-build: PSL building blocks bothcontract inferandcontract printuse, with no dialect knowledge:mapDefault(a stored default as the PSL attribute that reads back as it), thePslTypeMaptypes, andtoEnumMemberName./psl-infer: Database-to-PSL inference utilities forcontract infer: name transforms, relation inference, and the printer-config types./runtime: Runtime plane identity exports only (family ID, types, descriptor identity). Does not export runtime creation helpers—useinstantiateExecutionStackfrom@internal/framework-components/executionandcreateExecutionContext,createRuntime,createSqlExecutionStackfrom@internal/sql-runtime. See ADR 152../verify: Marker row parsing helper (parseContractMarkerRow). Marker reads are owned by eachSqlControlAdapter(e.g.PostgresControlAdapter.readMarker) so dialect-specific SQL stays target-local.
How to debug db init
- CLI orchestration:
packages/1-framework/3-tooling/cli/src/commands/db-init.ts - Planner/runner SPI types:
packages/2-sql/3-tooling/family/src/core/migrations/types.ts - Pure schema verifier (used by planner + runner):
@internal/family-sql/schema-verify(source:packages/2-sql/3-tooling/family/src/core/schema-verify/) - Postgres implementation:
- Planner:
packages/3-targets/3-targets/postgres/src/core/migrations/planner.ts - Runner:
packages/3-targets/3-targets/postgres/src/core/migrations/runner.ts
- Planner:
- Tests:
- CLI integration:
test/integration/test/cli.db-init.e2e.test.ts - Target unit/integration:
packages/3-targets/3-targets/postgres/test/migrations/*
- CLI integration: