| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
TML-3709: PostgreSQL's explicit date and time types name their values, read ISO strings on every server, and get the design for convenience types (#30834) ## Linked issue Refs TML-3709 (Linear project: Convenience date and time types). Fixes TML-3418, TML-3711 and TML-3719, which were reviewed as prisma/orm#30851, prisma/orm#30844 and prisma/orm#30839 and squash-merged into this branch so they reach `main` together. ## Summary ```prisma model Event { id Int @id at TimestamptzTemporal(3) // was Timestamptz(3): timestamptz(3), read as a Temporal.Instant legacyAt TimestampJsDate(3) // new: timestamp(3), read as a JavaScript Date, stored as UTC day DateTemporal // new name: date, read as a Temporal.PlainDate period IntervalString // new: interval, read as "P1Y2M" label TimestamptzString // reads "2024-01-01T00:00:00Z" on every server } ``` **The decision: PostgreSQL's explicit date and time types become a complete, predictable layer, and ADR 279 records how convenience types `DateTime`, `Date`, `Time` and `Duration` will be built on top of it.** This PR lands the design and the three changes the convenience types need first. The convenience types themselves and the tools that write them follow in prisma/orm#30847 and prisma/orm#30858. ### Why Prisma 7 had one date type, `DateTime`, always read as a JavaScript `Date`. That is easy to learn, but a `Date` is a moment in time, so it is the wrong value for a calendar date: a birthday created at local midnight east of UTC is stored as the day before. Prisma 8 replaced it with explicit types, each naming the column type and the value the application reads: `TimestamptzJsDate` is a `timestamptz` read as a `Date`. Every choice is visible, but a column type with three kinds of value has three names, and nothing says which to start with. The research behind this project also found three defects in the explicit layer: the string types returned text that depends on the server's settings, `timestamp` and `interval` lacked codecs a sensible default needs, and an adopted Prisma 7 schema stored local time where Prisma 7 stored UTC. ### 1. The design: ADR 279 ADR 279, "Convenience types choose an explicit type from their arguments", adds a second kind of type declaration. A convenience type is named for what a field means, and its optional arguments `as:` (the value the application reads) and `storage:` (the database type) pick exactly one explicit type from a table: ```prisma paidAt DateTime // exactly TimestamptzJsDate(3) settledAt DateTime(as: temporal) // exactly TimestamptzTemporal legacyAt DateTime(storage: timestamp) // exactly TimestampJsDate(3) shipBy Date // exactly DateString ``` The contract records only the explicit type, so nothing below PSL and the TypeScript builder changes. Defaults need no setup: `DateTime` reads a `Date`, and `Date`, `Time` and `Duration` read ISO 8601 strings, so no default needs a `Temporal` polyfill. The project's spec, plan and per-PR specs are in `projects/datetime-convenience-types/`, which is deleted when the project closes. ### 2. Every explicit type names its kind of value (TML-3711) Every type for a column type with more than one codec now carries a suffix, so no explicit type looks like the neutral default and the bare names are free for the convenience types: - `Timestamptz` becomes `TimestamptzTemporal`, `Timestamp` becomes `TimestampTemporal`, and `Interval` becomes `IntervalParts`; - `DateTemporal` and `TimeTemporal` are added beside `Date` and `Time`, which the convenience types take over in prisma/orm#30847. The old names are removed outright; there are no deprecated aliases before the release candidate. Three codecs are added: `pg/timestamp-date@1` (`TimestampJsDate`) and `pg/timestamp-instant@1` (`TimestampInstant`) read a `timestamp` column as UTC, and `pg/interval-string@1` (`IntervalString`) reads an interval as an ISO 8601 duration. Prisma 7's `DateTime` is a `timestamp(3)` read as a `Date`, so an adopted schema needs the first. Temporal fields take their types from `temporal-spec`, a package of declarations with no runtime code. Before, they named the global `Temporal` type and were typed `any` in a project without a polyfill's global declarations. ### 3. String types read ISO 8601 on every server (TML-3418) | `TimestamptzString` holding `2024-01-01T00:00:00Z` | Before | After | | --- | --- | --- | | on a server in UTC | `2024-01-01 00:00:00+00` | `2024-01-01T00:00:00Z` | | on a server set to `Europe/Berlin` | `2024-01-01 01:00:00+01` | `2024-01-01T00:00:00Z` | | holding 44 BC | `0044-03-15 12:00:00+00 BC` | `-000043-03-15T12:00:00Z` | The string codecs return the ISO 8601 form the contract already stores (ADR 254). Codec ids and contracts do not change. Their `now()` generators write the same form and round down to the column's precision. PostgreSQL prints dates in the session's `DateStyle`, so the PostgreSQL target now declares `sessionSettings: { DateStyle: 'ISO' }`, and the driver ensures it on every connection it hands out. A connection Prisma opens gets it as a startup option. A `pg.Pool` or `pg.Client` the application passes in gets `set_config` before first use and again after a connection held for a transaction is released. If a connection still prints another style, every date and time codec fails with `RUNTIME.DATE_STYLE_NOT_ISO` instead of misreading the value. ADR 159 records the rule: a target declares session settings, and the driver ensures them. ### 4. An adopted Prisma 7 schema fills `@default(now())` in UTC (TML-3719) Prisma 7's client filled `@default(now())` itself on create, in UTC, and the database default covered writes from outside Prisma. The Prisma 7 schema reader kept only the database default, so on a server whose `TimeZone` is not UTC every row Prisma 8 created stored local time. The reader now reads `@default(now())` as `@default(now()) @onCreate(now())`. The storage hash is unchanged; the execution hash changes, so adopted projects re-emit. The reader also refuses two schemas Prisma 7 refuses: `now()` on a field that is not a date or time, and a function default other than `dbgenerated` on a list field. ## Testing performed Each part was tested and reviewed in its own PR (see the links above); the details are there. On this branch after combining them: - `pnpm build`, `pnpm typecheck`, `pnpm fixtures:check`, `pnpm lint:deps`, `pnpm lint:ratchets`, `pnpm lint:docs`, `pnpm check:upgrade-coverage --mode pr`. - Package tests and lint for target-postgres, adapter-postgres, driver-postgres, the codec testkit and contract-prisma7, and the date and time integration files and journeys. ## Skill update `skills/prisma-8/references/contract.md` and `debug.md` use the new names, describe ISO values and the `DateStyle` error. Upgrade instructions: `explicit-date-time-types` (`app` and `extension`), the ISO strings instructions (`app` and `extension`), and `prisma7-now-default-on-create` (`app`). ## Checklist - [x] All commits are signed off. - [x] Scoped to one logical concern: the explicit date and time layer the convenience types are built on. - [x] Tests are updated. - [x] The PR title is in `TML-NNNN: <sentence-case title>` form. - [x] The Skill update section is filled in. ## Notes for the reviewer - The three PRs were merged here to send one entry through the merge queue instead of four. Their diffs were approved as they were; the only new work on this branch is the merge of the ISO strings changes with the explicit types changes, which both changed `decodeDate` in `date-codecs.ts`. The shared `decodeDate` keeps the `DateStyle` check, so `TimestampJsDate` and `TimestampInstant` get it too, and `date-style-every-codec.test.ts` checks every registered date and timestamp codec. - Hosted connection poolers (Supavisor, Neon, Hyperdrive, Prisma Postgres, PgBouncer) are not tested. Where a pooler refuses or drops the startup option, the failure is a connection error or `RUNTIME.DATE_STYLE_NOT_ISO`, never a misread value. - SQLite, MySQL and MongoDB convenience types are a later decision (TML-3714), and so are convenience types for other scalars (TML-3715). **Alternatives considered:** - **Name every type after its application meaning, with no explicit layer.** That reverses ADR 257 and leaves arguments as the only exact way to state a column type. - **One codec for `DateTime`, with the column type as a parameter.** A codec represents exactly one data type (ADR 254). - **Keep `Timestamptz` and `Timestamp` unsuffixed.** The explicit types would follow two naming patterns. - **The codecs set `DateStyle`, or the runtime sends `SET` before each query.** A codec has no connection, and a statement per query costs a round trip on every query. - **Parse every `DateStyle`.** The `SQL` and `German` styles are ambiguous for some dates. - **Keep only the database default for Prisma 7's `now()`.** Every adopted project on a server not in UTC keeps a regression from Prisma 7. Agent: sauron-95 🤖 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.5 <noreply@anthropic.com> | 8 天前 | |
TML-3709: PostgreSQL's explicit date and time types name their values, read ISO strings on every server, and get the design for convenience types (#30834) ## Linked issue Refs TML-3709 (Linear project: Convenience date and time types). Fixes TML-3418, TML-3711 and TML-3719, which were reviewed as prisma/orm#30851, prisma/orm#30844 and prisma/orm#30839 and squash-merged into this branch so they reach `main` together. ## Summary ```prisma model Event { id Int @id at TimestamptzTemporal(3) // was Timestamptz(3): timestamptz(3), read as a Temporal.Instant legacyAt TimestampJsDate(3) // new: timestamp(3), read as a JavaScript Date, stored as UTC day DateTemporal // new name: date, read as a Temporal.PlainDate period IntervalString // new: interval, read as "P1Y2M" label TimestamptzString // reads "2024-01-01T00:00:00Z" on every server } ``` **The decision: PostgreSQL's explicit date and time types become a complete, predictable layer, and ADR 279 records how convenience types `DateTime`, `Date`, `Time` and `Duration` will be built on top of it.** This PR lands the design and the three changes the convenience types need first. The convenience types themselves and the tools that write them follow in prisma/orm#30847 and prisma/orm#30858. ### Why Prisma 7 had one date type, `DateTime`, always read as a JavaScript `Date`. That is easy to learn, but a `Date` is a moment in time, so it is the wrong value for a calendar date: a birthday created at local midnight east of UTC is stored as the day before. Prisma 8 replaced it with explicit types, each naming the column type and the value the application reads: `TimestamptzJsDate` is a `timestamptz` read as a `Date`. Every choice is visible, but a column type with three kinds of value has three names, and nothing says which to start with. The research behind this project also found three defects in the explicit layer: the string types returned text that depends on the server's settings, `timestamp` and `interval` lacked codecs a sensible default needs, and an adopted Prisma 7 schema stored local time where Prisma 7 stored UTC. ### 1. The design: ADR 279 ADR 279, "Convenience types choose an explicit type from their arguments", adds a second kind of type declaration. A convenience type is named for what a field means, and its optional arguments `as:` (the value the application reads) and `storage:` (the database type) pick exactly one explicit type from a table: ```prisma paidAt DateTime // exactly TimestamptzJsDate(3) settledAt DateTime(as: temporal) // exactly TimestamptzTemporal legacyAt DateTime(storage: timestamp) // exactly TimestampJsDate(3) shipBy Date // exactly DateString ``` The contract records only the explicit type, so nothing below PSL and the TypeScript builder changes. Defaults need no setup: `DateTime` reads a `Date`, and `Date`, `Time` and `Duration` read ISO 8601 strings, so no default needs a `Temporal` polyfill. The project's spec, plan and per-PR specs are in `projects/datetime-convenience-types/`, which is deleted when the project closes. ### 2. Every explicit type names its kind of value (TML-3711) Every type for a column type with more than one codec now carries a suffix, so no explicit type looks like the neutral default and the bare names are free for the convenience types: - `Timestamptz` becomes `TimestamptzTemporal`, `Timestamp` becomes `TimestampTemporal`, and `Interval` becomes `IntervalParts`; - `DateTemporal` and `TimeTemporal` are added beside `Date` and `Time`, which the convenience types take over in prisma/orm#30847. The old names are removed outright; there are no deprecated aliases before the release candidate. Three codecs are added: `pg/timestamp-date@1` (`TimestampJsDate`) and `pg/timestamp-instant@1` (`TimestampInstant`) read a `timestamp` column as UTC, and `pg/interval-string@1` (`IntervalString`) reads an interval as an ISO 8601 duration. Prisma 7's `DateTime` is a `timestamp(3)` read as a `Date`, so an adopted schema needs the first. Temporal fields take their types from `temporal-spec`, a package of declarations with no runtime code. Before, they named the global `Temporal` type and were typed `any` in a project without a polyfill's global declarations. ### 3. String types read ISO 8601 on every server (TML-3418) | `TimestamptzString` holding `2024-01-01T00:00:00Z` | Before | After | | --- | --- | --- | | on a server in UTC | `2024-01-01 00:00:00+00` | `2024-01-01T00:00:00Z` | | on a server set to `Europe/Berlin` | `2024-01-01 01:00:00+01` | `2024-01-01T00:00:00Z` | | holding 44 BC | `0044-03-15 12:00:00+00 BC` | `-000043-03-15T12:00:00Z` | The string codecs return the ISO 8601 form the contract already stores (ADR 254). Codec ids and contracts do not change. Their `now()` generators write the same form and round down to the column's precision. PostgreSQL prints dates in the session's `DateStyle`, so the PostgreSQL target now declares `sessionSettings: { DateStyle: 'ISO' }`, and the driver ensures it on every connection it hands out. A connection Prisma opens gets it as a startup option. A `pg.Pool` or `pg.Client` the application passes in gets `set_config` before first use and again after a connection held for a transaction is released. If a connection still prints another style, every date and time codec fails with `RUNTIME.DATE_STYLE_NOT_ISO` instead of misreading the value. ADR 159 records the rule: a target declares session settings, and the driver ensures them. ### 4. An adopted Prisma 7 schema fills `@default(now())` in UTC (TML-3719) Prisma 7's client filled `@default(now())` itself on create, in UTC, and the database default covered writes from outside Prisma. The Prisma 7 schema reader kept only the database default, so on a server whose `TimeZone` is not UTC every row Prisma 8 created stored local time. The reader now reads `@default(now())` as `@default(now()) @onCreate(now())`. The storage hash is unchanged; the execution hash changes, so adopted projects re-emit. The reader also refuses two schemas Prisma 7 refuses: `now()` on a field that is not a date or time, and a function default other than `dbgenerated` on a list field. ## Testing performed Each part was tested and reviewed in its own PR (see the links above); the details are there. On this branch after combining them: - `pnpm build`, `pnpm typecheck`, `pnpm fixtures:check`, `pnpm lint:deps`, `pnpm lint:ratchets`, `pnpm lint:docs`, `pnpm check:upgrade-coverage --mode pr`. - Package tests and lint for target-postgres, adapter-postgres, driver-postgres, the codec testkit and contract-prisma7, and the date and time integration files and journeys. ## Skill update `skills/prisma-8/references/contract.md` and `debug.md` use the new names, describe ISO values and the `DateStyle` error. Upgrade instructions: `explicit-date-time-types` (`app` and `extension`), the ISO strings instructions (`app` and `extension`), and `prisma7-now-default-on-create` (`app`). ## Checklist - [x] All commits are signed off. - [x] Scoped to one logical concern: the explicit date and time layer the convenience types are built on. - [x] Tests are updated. - [x] The PR title is in `TML-NNNN: <sentence-case title>` form. - [x] The Skill update section is filled in. ## Notes for the reviewer - The three PRs were merged here to send one entry through the merge queue instead of four. Their diffs were approved as they were; the only new work on this branch is the merge of the ISO strings changes with the explicit types changes, which both changed `decodeDate` in `date-codecs.ts`. The shared `decodeDate` keeps the `DateStyle` check, so `TimestampJsDate` and `TimestampInstant` get it too, and `date-style-every-codec.test.ts` checks every registered date and timestamp codec. - Hosted connection poolers (Supavisor, Neon, Hyperdrive, Prisma Postgres, PgBouncer) are not tested. Where a pooler refuses or drops the startup option, the failure is a connection error or `RUNTIME.DATE_STYLE_NOT_ISO`, never a misread value. - SQLite, MySQL and MongoDB convenience types are a later decision (TML-3714), and so are convenience types for other scalars (TML-3715). **Alternatives considered:** - **Name every type after its application meaning, with no explicit layer.** That reverses ADR 257 and leaves arguments as the only exact way to state a column type. - **One codec for `DateTime`, with the column type as a parameter.** A codec represents exactly one data type (ADR 254). - **Keep `Timestamptz` and `Timestamp` unsuffixed.** The explicit types would follow two naming patterns. - **The codecs set `DateStyle`, or the runtime sends `SET` before each query.** A codec has no connection, and a statement per query costs a round trip on every query. - **Parse every `DateStyle`.** The `SQL` and `German` styles are ambiguous for some dates. - **Keep only the database default for Prisma 7's `now()`.** Every adopted project on a server not in UTC keeps a regression from Prisma 7. Agent: sauron-95 🤖 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.5 <noreply@anthropic.com> | 8 天前 | |
chore(release): bump to 8.0.0-rc.21 (#30864) `8.0.0-rc.20` → `8.0.0-rc.21`. See [versioning](https://github.com/prisma/orm/blob/main/docs/oss/versioning.md). **Merging this PR ships the release.** The push to `main` carries the new root version. The `Publish to npm` workflow then publishes `8.0.0-rc.21` under the `latest` dist-tag and creates a pre-release GitHub Release. ## What to review - **Release notes:** [`docs/releases/v8.0.0-rc.21.md`](https://github.com/prisma/orm/blob/release/8.0.0-rc.21/docs/releases/v8.0.0-rc.21.md), mirrored in `CHANGELOG.md`. Eleven breaking changes: client members reject instead of throwing, the enum value question, MongoDB names and options, one set of MongoDB contract rules, `close()` with your own pool, `markerLocation`, the renamed PostgreSQL date and time types, ISO 8601 text date and time values with `DateStyle=ISO` required, and a Prisma 7 `@default(now())` filled on create (from #30834), the control API checking a database's marker against the migration graph itself (from #30866), and field presets removed in favour of type constructors and type aliases (from #30845). #30834, #30866 and #30845 merged after this branch was cut; `main` is merged in. It also covers the MySQL preview packages and the corrected rc.19 and rc.20 upgrade guides (#30855). The notes leave out #30848, #30854 (docs-sync tooling) and #30852 (docs only). - **Upgrade guides:** `skills/prisma-8/upgrading/{app,extension}/upgrades/8.0.0-rc.20-to-8.0.0-rc.21/instructions.md`, assembled from the fourteen pending fragments. The app guide has 31 changes plus the Supabase restamp, and the extension guide has 29. None was dropped. The originals are archived unchanged under `upgrade-instructions/releases/8.0.0-rc.20-to-8.0.0-rc.21/sources/`. While assembling, I fixed a wrong import path (`@prisma/orm-target-mongo/pack` does not exist), a detection that matched only code that was already upgraded, and a miscount of optional fields. - **Version bump:** every `package.json`, `pnpm-lock.yaml` (workspace specifiers only), the skill's `library_version`, and the Supabase contract version stamps. ## Checks run locally - `check:upgrade-coverage --mode publish --prev v8.0.0-rc.20 --head HEAD`: pass - `check:release-notes --mode pr`: pass - `lint:skills`: pass - `test/upgrade-instructions`: 86 files, 1650 tests pass The docs PR in prisma/web follows once this is published. Agent: rochester-20 --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> | 4 天前 | |
TML-3688, TML-3411, TML-3685, TML-3687: A Supabase client shuts down like a Postgres client, because all role work goes through one role scope (#30828) ```ts const db = await supabase({ contract, url, jwtSecret }); const user = await db.asUser(jwt); const pending = user.transaction(async (tx) => { /* ... */ }); await db.close(); // waits for the transaction above to commit, then shuts down await user.orm.public.Post.all(); // before: fails inside pg-pool with "Cannot use a pool after calling end on the pool" // after: rejects with DRIVER.NOT_CONNECTED "Runtime is closed" ``` A Supabase client now shuts down the way a Postgres client does. `close()` waits for work that has started, then refuses new work with one clear error. To get there, every query a Supabase client runs under a database role now goes through one object instead of four separate methods. ## Background A Supabase client runs queries on behalf of a Postgres role. `db.asUser(jwt)`, `db.asAnon()` and `db.asServiceRole()` each return a **role-bound client**. Every statement it sends runs on a connection where the role has been set with `set_config`. That connection is a **role session**. The client also has a second, internal runtime for Supabase's own tables (`auth.*`, `storage.*`), reached through `db.asServiceRole().supabase`. This is the **extension runtime**. Both runtimes share one database driver. ## The decision ### One role scope replaces four role methods `SupabaseRuntime.forRole(binding)` returns a `SupabaseRoleScope`, the one object that runs work under a role: `query`, `execute`, the two prepared-statement operations, and `connection()` for a role session. It replaces `queryWithRole`, `executeWithRole`, `preparedQueryWithRole` and `preparedExecuteWithRole`. Role-bound clients, their transactions, the ORM's role routing and the service-role `supabase` member are all built from it. Every role transaction starts the same way: `withTransaction` on a session from `connection()`. ### Every connection follows one close rule `close()` waits for work that has started, meaning statements that are running and transactions that are open. A connection that is merely held between statements does not keep it waiting. Once the runtime starts refusing work, opening any connection, a role session included, fails with `DRIVER.NOT_CONNECTED` "Runtime is closed". This is the rule from prisma/orm#30533. Role sessions used to skip it because they took their connections through `acquireRawConnection`, which had no check. ### Both Supabase runtimes close together The application runtime and the extension runtime are built with a new runtime option, `ownsDriver: false`. Their `close()` refuses new work and waits for running work, but leaves the shared driver open. `db.close()` closes both at the same time, then closes the driver once. It ends the pool only when the client created it; a pool you passed in stays open. ### Two smaller changes in the same code - **`pg` takes a `pg.Pool` only.** It used to accept a `pg.Client` and then ignore it. Passing anything else fails at `supabase()` with `SUPABASE.CONFIG_INVALID`. - **Role-bound clients and their transactions offer `enums` and `nativeEnums`,** as a Postgres transaction does. They are built once and shared. ## Why Each `*WithRole` method was implemented and wired separately, and the ORM's role routing repeated the same operations a second time. A role-bound client therefore lacked whatever someone forgot to wire: it had no `enums`, and its prepared statements needed their own bridge. The one that mattered most was shutdown. Role sessions skipped the close check, so after `db.close()` a role query failed inside pg-pool with an error that had no code. The extension runtime was never closed at all, so its queries got the driver's "Call connect(binding) to reconnect", which a Supabase user cannot do. With one role scope, the close rule applies to role work in one place, and the gaps cannot reopen when a new operation is added. ## What changes for users These changes are breaking. Upgrade instructions are in `upgrade-instructions/pending/supabase-role-runtime/`. - **Code that called a `*WithRole` method** calls `forRole(binding)` and uses the returned scope. - **Code that passed `pg: new Client(...)`** passes a `pg.Pool`, or `url`. A pool you pass is not ended by `db.close()`, so end it yourself at shutdown. - **Test doubles of `RoleBoundDb`, `SupabaseTransactionContext` and `SupabaseRuntime`** add `enums`, `nativeEnums` and `forRole`. - **A query on a closed Supabase client rejects with "Runtime is closed".** It used to throw at the call or fail inside pg-pool. ## Changes in the shared runtime The close rule lives in `SqlRuntimeBase` (`packages/2-sql/5-runtime`), so these changes reach every SQL client: - `connection()` is built on `acquireRawConnection`, so there is one implementation of the rule. - An `execute` on a raw connection counts as running work until it settles. A query stream counts until the driver answers its first read, so a stream that is partly read and then dropped never holds `close()`. - A transaction counts as open work from before its start hooks run until its commit or rollback settles. It stops counting if its commit fails or its connection is released or destroyed, so a transaction that can never settle does not leave `close()` waiting forever. - The close warning `runtime.close.waiting-for-activities` also reports `openTransactions`: the transactions still open while close waits. - Transactions on Postgres, the serverless connection and Supabase reuse the client's `sql` and `raw` instead of building new ones. ## Docs The Supabase, Postgres and SQLite READMEs, the runtime subsystem doc, ADR 230, ADR 237, the error reference and the `prisma-8` skill describe the single close rule and `SupabaseRoleScope`. A new internal entry, `@internal/postgres/internal/static-context`, shares the Postgres enum builder with Supabase. ## How it was checked - **Tests, each failing before its fix:** - the close rule in `SqlRuntimeBase`: raw connections, a raw `execute` in flight, a transaction still starting, a failed commit, and a transaction whose connection is released or destroyed; - on Supabase: a role query, role transaction, ORM call and extension query refused after close; close waiting for an open role transaction and for an extension query; a caller's pool left open; a session released after close still sending `RESET ALL`; - `pg: new Client()` rejected; `enums` and `nativeEnums` on role-bound clients and transactions; - a transaction's `sql`, `raw` and `toSql` are the client's own. - **Review:** three rounds of `/drive-code-review` (system design and code), then CodeRabbit. Its two code findings, a raw `execute` not counted and a transaction still starting not counted, are fixed with tests. - **Repository checks:** every CI lint and check step, `check:upgrade-coverage --mode pr`, `fixtures:check`, the SQL runtime, Postgres, SQLite and Supabase package tests, the upgrade-instruction tests and the integration files that close runtimes, after merging `main`. - **Manual check** on PGlite with the Supabase mock schema: a close started during an open service-role transaction finished only after the transaction committed. Afterwards a role query and a service-role query on `auth.users` both rejected with "Runtime is closed". `pg: new Client()` failed with `SUPABASE.CONFIG_INVALID`. The real-Supabase acceptance suite runs in CI. ## Alternatives considered - **Make a `pg.Client` work, as `postgres()` does.** A single connection cannot serve Supabase. An ordinary statement error destroys the role session's connection, which would end the whole client. A role query inside another role's transaction would wait forever for the one connection. And the check that the database matches the contract would run under whatever role happened to be bound. - **A public `drain()` on the runtime** to close the extension runtime without closing the driver. It sat on no interface, so the Supabase client had to depend on the implementation class, and the name suggested the runtime could start again. Driver ownership as a construction option keeps `close()` the only shutdown method. - **Keep a transaction counted when its connection's release fails.** A failed release is not always followed by a destroy, so `close()` could wait forever. Closes TML-3688, TML-3411, TML-3685, TML-3687. Agent: raskolnikov-51 --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> | 10 天前 | |
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> | 2 个月前 | |
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> | 2 个月前 | |
chore(release): bump to 8.0.0-rc.21 (#30864) `8.0.0-rc.20` → `8.0.0-rc.21`. See [versioning](https://github.com/prisma/orm/blob/main/docs/oss/versioning.md). **Merging this PR ships the release.** The push to `main` carries the new root version. The `Publish to npm` workflow then publishes `8.0.0-rc.21` under the `latest` dist-tag and creates a pre-release GitHub Release. ## What to review - **Release notes:** [`docs/releases/v8.0.0-rc.21.md`](https://github.com/prisma/orm/blob/release/8.0.0-rc.21/docs/releases/v8.0.0-rc.21.md), mirrored in `CHANGELOG.md`. Eleven breaking changes: client members reject instead of throwing, the enum value question, MongoDB names and options, one set of MongoDB contract rules, `close()` with your own pool, `markerLocation`, the renamed PostgreSQL date and time types, ISO 8601 text date and time values with `DateStyle=ISO` required, and a Prisma 7 `@default(now())` filled on create (from #30834), the control API checking a database's marker against the migration graph itself (from #30866), and field presets removed in favour of type constructors and type aliases (from #30845). #30834, #30866 and #30845 merged after this branch was cut; `main` is merged in. It also covers the MySQL preview packages and the corrected rc.19 and rc.20 upgrade guides (#30855). The notes leave out #30848, #30854 (docs-sync tooling) and #30852 (docs only). - **Upgrade guides:** `skills/prisma-8/upgrading/{app,extension}/upgrades/8.0.0-rc.20-to-8.0.0-rc.21/instructions.md`, assembled from the fourteen pending fragments. The app guide has 31 changes plus the Supabase restamp, and the extension guide has 29. None was dropped. The originals are archived unchanged under `upgrade-instructions/releases/8.0.0-rc.20-to-8.0.0-rc.21/sources/`. While assembling, I fixed a wrong import path (`@prisma/orm-target-mongo/pack` does not exist), a detection that matched only code that was already upgraded, and a miscount of optional fields. - **Version bump:** every `package.json`, `pnpm-lock.yaml` (workspace specifiers only), the skill's `library_version`, and the Supabase contract version stamps. ## Checks run locally - `check:upgrade-coverage --mode publish --prev v8.0.0-rc.20 --head HEAD`: pass - `check:release-notes --mode pr`: pass - `lint:skills`: pass - `test/upgrade-instructions`: 86 files, 1650 tests pass The docs PR in prisma/web follows once this is published. Agent: rochester-20 --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> | 4 天前 | |
Config files import definePrismaConfig, the engine's current name for the marker (#30129) ## Why now `@prisma/cli-engine` has exported the config marker as `definePrismaConfig` since 0.2.0, keeping `defineConfig` as a deprecated alias. Engine 0.3.0 drops the alias before launch. Every `prisma.config.ts` in this repo still imports the old name, so on 0.3.0 they all fail at evaluation with `defineConfig is not a function`. Doing the rename now, on the current 0.2.3 pin, costs nothing. The two names are literally the same function object there: ```js import('@prisma/cli-engine').then((m) => m.definePrismaConfig === m.defineConfig); // true ``` So this PR is mergeable today and changes no behaviour. It clears the last blocker on moving the engine pin to 0.3.0, which #30128 is waiting for. ## What changed **306 TypeScript files** import the marker from `@prisma/cli-engine`. All move to `definePrismaConfig`, at the import and at the call. Almost all are `prisma.config.ts` fixtures under `test/`, plus the example and app configs. Two things that share the name are deliberately untouched: - The **framework's own `defineConfig`**, exported by the target facades and `@internal/cli/config-types`. It is always imported aliased (`defineConfig as ormConfig`), so it never collided with the sweep. - **`defineConfigSection`**, a separate and current engine export. Replacements were anchored on the exact import line and on `defineConfig(`, never on a bare word match. After the sweep: - no `defineConfig` reference to `@prisma/cli-engine` remains in any `.ts`, `.mts`, `.mjs` or `.js` file - both `defineConfigSection` occurrences are intact, and no `definePrismaConfigSection` exists anywhere - the aliased framework imports are unchanged - the diff is 643 insertions against 643 deletions — line for line, nothing added or dropped **21 more files** spell the old name out rather than importing it: - `scripts/regen-example-migrations.mjs` generates a temporary config file. Its `engineDefineConfig` alias existed only because the engine marker and the framework builder shared a name, so the alias goes with the rename. - The `CONFIG.VERSION_MARKER_MISSING` diagnostic told the reader to create the config with `defineConfig`. Its summary, explanation and fix now name `definePrismaConfig`. So does its entry in `docs/reference/error-reference.md`, which additionally pointed at the target package's `/config` entrypoint — the wrong import for the marker. - Doc comments in the config loader, the ORM loader and its config types, `init` and its package resolution, and the publish-surface import roots. - Four test names, and an `init` assertion that only checked for the substring `defineConfig` and so passed by accident against the scaffold's `definePrismaConfig`. It now asserts the real name. The CHANGELOG, the rc.2 release notes and the rc.1-to-rc.2 upgrade recipes still say `defineConfig`, deliberately: they record releases where that was the name. ## Verification All on the current 0.2.3 pin — no override, no tarball. | Check | Result | | --- | --- | | `pnpm build` | 85/85 tasks pass | | `turbo run typecheck --force` | 166/166 tasks pass, nothing cached. Covers `integration-tests`, whose tsconfig includes `test/**/*`, so all 262 fixture configs are typechecked. | | `pnpm test:packages` | 15630 tests — the same count as `main` before the change, so nothing was lost or silently skipped | | `pnpm test:integration` | 2061 pass across 370 of 371 files. These evaluate the renamed config files for real. | | `@internal/cli` | 1436 tests pass | | `@internal/config-loader` | 46 tests pass | | `@internal/errors` | 124 tests pass | | `@internal/config` | 23 tests pass | | `@internal/language-server` | 273 tests pass | | `pnpm lint` | 99/99 tasks pass | | `pnpm lint:deps`, `lint:docs`, `lint:manifests` | pass | | `pnpm check:conformance` | pass | | `pnpm check:error-reference` | pass, all 288 codes listed | Three test files fail somewhere in those runs. Each was checked and none is caused by this change: - `test/ports/.../driver-adapters-error-forwarding` fails identically on unmodified `origin/main` — I checked out `main` in this working copy and reproduced it. - `@prisma/orm-framework`'s `module-identity` also fails on unmodified `main` here: `pnpm pack` trips over a leftover gitignored `skills/prisma-8` directory. - `@internal/adapter-postgres`'s `order-by-enum` PGlite test fails only under parallel load; it passes on its own (7/7). ## Related - prisma/prisma-cli#233 — the engine change that removes the alias - #30128 — the ORM config-path change that needs engine 0.3.0; this PR is its prerequisite 🤖 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 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> | 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> | 2 个月前 | |
feat(extension-supabase): scaffold package (D1) Stand up packages/3-extensions/supabase/ with the full build config (package.json, tsdown, tsconfigs, biome, prisma-next.config) modelled on pgvector. Exports ./pack (compiling placeholder stub for D3) and ./runtime (M2 stub). Wires prismaContract('src/contract/contract.prisma', { target: postgres, defaultControlPolicy: 'external' }) in prisma-next.config.ts. Registers the package in architecture.config.json so pnpm lint:deps is green. All D1 gates pass: build, typecheck, lint (--error-on-warnings), lint:deps. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Will Madden <madden@prisma.io> | 4 个月前 | |
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> | 2 个月前 | |
db init, db update and migrate no longer strand the database when contract.d.ts is missing (#30293) ## Linked issue n/a — no Linear ticket; follows up the `db sign` preflight that landed in #30251 (after `db sign`, `migration plan` proposes only the change). ## Skill update n/a — no skill told users to keep `contract.d.ts` next to `contract.json` for the database commands, and the new error code carries its own fix (`prisma contract emit`). ## At a glance Before, with `contract.d.ts` missing, `db init` migrated the database, wrote the marker, then died on the snapshot write: ```text $ prisma db init … Database initialized ENOENT: no such file or directory, open '/app/prisma/contract.d.ts' $ echo $? 2 ``` No ref, no snapshot, and the next `migration plan` started from the wrong place. Now the command refuses before it connects: ```text $ prisma db init ✖ Failed to render contract types (CONTRACT.TYPES_RENDER_FAILED) The types for the contract at /app/prisma/contract.json could not be rendered: … Run prisma contract emit to see why the contract does not emit, fix it, then advance the ref again. ``` The snapshot's `contract.d.ts` is no longer read from disk at all. It is rendered from the `contract.json` being snapshotted: ```ts // packages/1-framework/3-tooling/cli/src/control-api/operations/ref-advancement.ts const rendered = await args.client.renderContractDts({ contract: args.contractJson, resolveImportSpecifier, }); ``` ## Decision This PR ships three things: 1. **`ControlClient.renderContractDts`.** Given a parsed `contract.json`, the client deserializes it through the family and renders `contract.d.ts` through the same `emit()` call `emit` uses, with the import specifiers the caller asks for. A snapshot is that JSON plus these declarations, so whoever writes a snapshot renders them from the JSON it is storing. 2. **Ref advancement renders before the database write.** `preflightRefAdvancement` validates the ref name, builds the project's import-specifier resolver, and renders the declarations through the client. `db sign`, `db init`, `db update`, and `migrate --advance-ref` all run it before anything is applied. The sibling-file read (`readContractIR`) and the after-the-fact `resolveRefAdvancementFields` are gone. 3. **A two-facade project manifest is a structured refusal.** Resolving the import root threw a bare `ImportRootError` when a `package.json` depends on both `@prisma/orm-postgres` and `@prisma/orm-mongo`. It now maps to `CLI.PROJECT_MANIFEST_INVALID`, naming the manifest. ## Reviewer notes - **Why render at all, given `migration.ts` imports the snapshot `.d.ts` by path.** That import is exactly why the file must exist on disk, and why it should be the declarations of that snapshot's JSON. Reading a sibling file only guaranteed that by convention. Rendering guarantees it by construction. - **`migrate` orders differently from `init` and `update`.** In [migrate.ts](packages/1-framework/3-tooling/cli/src/orm/migrate.ts) the preflight sits after `connect` and the marker read, right before `client.migrate`. A first cut hoisted it above `connect`, which made a missing `--to` snapshot beat the invariant pre-check that an existing test rightly expects to win. Reads before, the write after, is the property that matters. - **Environment drift is a deliberate behaviour change.** The rendered declarations reflect the packages installed now, not at emit time. If an extension was upgraded between `contract emit` and `db update`, the snapshot `.d.ts` differs from the project's `contract.d.ts`. The snapshot is then consistent with its JSON under the current install, which is the honest answer. - **Cost.** Each ref advance now runs the type render plus prettier, on a command that already connects to a database. - **The CLI test fixture package declares both facades.** That is why the two-facade error surfaced, and why the command tests now write their own single-facade manifest with `writeProjectManifest`. - **Deferred on purpose.** `migration plan` and `migration new` still read the sibling `.d.ts` when writing the destination snapshot ([migration-plan.ts](packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts), [migration-new.ts](packages/1-framework/3-tooling/cli/src/control-api/operations/migration-new.ts)). They touch no database, and their offline tests use fake descriptors that cannot drive the real emitter. The `contractDts` field on the `contractAt` path stays for the same reason. ## How it fits together 1. **The client renders.** [client.ts](packages/1-framework/3-tooling/cli/src/control-api/client.ts) adds `renderContractDts` next to `emit`, both calling one private `emitArtifacts` so they cannot drift. Failures come back as `CONTRACT_VALIDATION_FAILED` (the family rejected the JSON) or `RENDER_FAILED` (the emitter refused it). The fixture client in [fixture-client.ts](packages/1-framework/3-tooling/cli/src/control-api/testing/fixture-client.ts) gets a matching default. 2. **The preflight maps.** [ref-advancement.ts](packages/1-framework/3-tooling/cli/src/control-api/operations/ref-advancement.ts) turns those into `CONTRACT.VALIDATION_FAILED` and the new `CONTRACT.TYPES_RENDER_FAILED`, both locating the contract JSON. `buildRefAdvancementFields` becomes the pure after-the-write tail for `init` and `update`. 3. **The commands split around the write.** [init.ts](packages/1-framework/3-tooling/cli/src/orm/db/init.ts) and [update.ts](packages/1-framework/3-tooling/cli/src/orm/db/update.ts) compute the ref name and preflight before `connect`, then build the fields after the apply. [sign.ts](packages/1-framework/3-tooling/cli/src/orm/db/sign.ts) creates the client before its existing preflight so it can render. `migrate` preflights after resolving the apply contract and before applying. 4. **The manifest refusal.** [project-import-root.ts](packages/1-framework/3-tooling/cli/src/utils/project-import-root.ts) catches `ImportRootError` where it reads the manifest, so every caller of the resolver gets a structured error. ## Behavior changes & evidence - **`db init`, `db update`, and `migrate --advance-ref` refuse before any database write when the snapshot cannot be rendered**, with a structured code and a fix, instead of migrating and then exiting on a bare ENOENT. Implementation: [init.ts](packages/1-framework/3-tooling/cli/src/orm/db/init.ts), [update.ts](packages/1-framework/3-tooling/cli/src/orm/db/update.ts), [migrate.ts](packages/1-framework/3-tooling/cli/src/orm/migrate.ts). Evidence: [db-init.test.ts](packages/1-framework/3-tooling/cli/test/orm/db-init.test.ts), [migrate.test.ts](packages/1-framework/3-tooling/cli/test/orm/migrate.test.ts). - **Every ref advance stores declarations rendered from the snapshotted JSON**, and none of the four commands needs `contract.d.ts` on disk any more. `db sign` without the file now succeeds. Implementation: [ref-advancement.ts](packages/1-framework/3-tooling/cli/src/control-api/operations/ref-advancement.ts). Evidence: [db-sign.ref-advancement.test.ts](packages/1-framework/3-tooling/cli/test/orm/db-sign.ref-advancement.test.ts), [ref-advancement.test.ts](packages/1-framework/3-tooling/cli/test/control-api/ref-advancement.test.ts). - **`renderContractDts` produces the text `emit` produces** for the same contract, and rewrites imports through the resolver it is given. Implementation: [client.ts](packages/1-framework/3-tooling/cli/src/control-api/client.ts). Evidence: [client.test.ts](packages/1-framework/3-tooling/cli/test/control-api/client.test.ts). - **A project depending on two database facades gets `CLI.PROJECT_MANIFEST_INVALID`** naming the manifest, instead of an uncaught error. Implementation: [project-import-root.ts](packages/1-framework/3-tooling/cli/src/utils/project-import-root.ts). Evidence: [project-import-root.test.ts](packages/1-framework/3-tooling/cli/test/utils/project-import-root.test.ts). - **Docs.** The migration-system subsystem doc says where a ref's snapshot declarations come from, and [error-reference.md](docs/reference/error-reference.md) documents `CONTRACT.TYPES_RENDER_FAILED`. ## Testing performed - `pnpm typecheck` in `@internal/cli` and `@internal/extension-sqlite` (against the rebuilt CLI dist) - `pnpm test` in `@internal/cli`: 116 files, 1472 tests - `pnpm test test/migrations/db-init-update.cli.test.ts` in the sqlite adapter (real stack) - `pnpm check:error-reference` - Review pass by a second agent; its findings (error-reference entry, two prose inaccuracies, a test path) are folded into the last commit ## Alternatives considered - **Keep reading the sibling `.d.ts`, just earlier.** This was the original proposal: give `init`, `update`, and `migrate` the same read-before-write preflight `db sign` got. It removes the stranded-database bug but keeps the snapshot's declarations coupled to whichever file happens to sit next to `contract.json`. Rendering makes the snapshot a pure function of its JSON and the installed stack. - **Stop storing `contract.d.ts` in the snapshot and render on read.** `migration.ts` imports `../../snapshots/<hash>/contract` by path and `tsc` has to find it without Prisma running, so the file must be materialised. Rendering at write time keeps that. - **A standalone renderer keyed off the config instead of a client method.** The CLI command tests mount fake control clients, and the fake families in those fixtures cannot drive the real emitter. Putting the renderer on the client gives those tests their seam and keeps the real path one function. - **Also switching `migration plan` and `migration new` to render.** Deferred; see reviewer notes. ## 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 exists for this change; the prefix will be added if one is assigned. - [x] The **Skill update** section above is filled in. ## Notes for the reviewer See the reviewer notes above. 🤖 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** - Ref-advancing commands now generate and store contract type declarations from the contract snapshot before migrations or reference updates. - Planning modes continue to show the proposed reference without applying changes. - **Bug Fixes** - Validation or type-generation failures now stop execution before database, migration, reference, or snapshot changes occur. - Invalid project manifests now produce structured, actionable CLI errors. - **Documentation** - Added reference documentation for contract type-generation failures and their effects. <!-- 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.1 <noreply@anthropic.com> | 1 个月前 |
@internal/extension-supabase
Supabase extension pack for Prisma 8.
Overview
This extension pack ships a complete, faithful contract of everything Supabase owns in the database — every auth (23) and storage (10) table of the reference platform version, their native enum types, and the three platform roles (anon, authenticated, service_role) — all external: an application contract composes them via extensions: [supabasePack], the migration planner emits no DDL for them (they're Supabase-managed), and db verify confirms they exist in the live database while tolerating the Supabase-internal schemas the pack doesn't declare (realtime, vault, …).
It also ships the role-binding runtime: supabase({...}) returns a db whose asUser(jwt) / asAnon() / asServiceRole() bind the Postgres role + JWT claims per session, and asServiceRole().supabase.{sql,orm} exposes the pack's own auth/storage tables as a secondary root for admin reads. The secondary-root design is ADR 237.
Contract generation
The contract is introspected, not hand-authored: pnpm contract:generate restores the checked-in reference fixture (test/fixtures/supabase-reference/, captured from supabase/postgres:17.6.1.106 — PostgreSQL 17.6, gotrue v2.188.1, storage-api v1.54.1, supabase CLI 2.95.4, 2026-07-12) into a fresh PGlite database, introspects auth + storage, writes src/contract/contract.prisma, and emits. Reruns are byte-identical. The honest boundary of "faithful" — the handful of columns/defaults/indexes deliberately not declared, and why that's verify-safe under external — is recorded in src/contract/CONTRACT-FIDELITY.md. To track a newer Supabase platform version, re-capture the fixture from a newer stack (record the new version pins in the fixture header) and rerun contract:generate.
Responsibilities
- Supabase contract: the complete introspection-generated contract described above,
defaultControlPolicy: 'external', roles contributed as first-classroleentities during emit (prisma.config.ts). /packsubpath: anExtensionPackvalue (supabasePackdefault +supabasePackWith(options)factory) that an app composes into its config viaextensions. Tree-shaking-clean —/packimports no runtime code./runtimesubpath: theSupabaseRuntimerole-binding runtime andsupabase({...})facade (session-coupledset_configrole + claims binding, per ADR 230), plus theservice_role-only.supabasesecondary root./contractsubpath: branded model handles for the commonly-referenced models (AuthUser,AuthIdentity,AuthSession,StorageBucket,StorageObject) used for cross-space FK references from app contracts. The handle set is deliberately curated, not one-per-table.- Internal test substrate (not published):
test/fixtures/supabase-reference/set-up-mock-schema.tsexportssetUpSupabaseMockSchema(client), restoring the reference fixture (all Supabase schemas, tables, roles, and the platform's real default privileges) into a test database. Used only by this package's tests — including the hermetic PGlite coverage of the example app's flows (test/fixtures/example-app/);examples/supabaseitself ships only the real-Supabase acceptance suite.
Installation
pnpm add @internal/extension-supabase
Configuration
Compose the pack into your application contract via extensions. The pack's contract space (the auth and storage namespaces) joins the app's aggregate at emit time:
// prisma.config.ts
import { defineConfig } from '@internal/cli/config-types';
import postgresAdapter from '@internal/adapter-postgres/control';
import sql from '@internal/family-sql/control';
import postgres from '@internal/target-postgres/control';
import postgresPackRef from '@internal/target-postgres/pack';
import { prismaContract } from '@internal/sql-contract-psl/provider';
import supabasePack from '@internal/extension-supabase/pack';
export default defineConfig({
family: sql,
target: postgres,
adapter: postgresAdapter,
contract: prismaContract('./src/contract.prisma', { target: postgresPackRef }),
extensions: [supabasePack],
});
See examples/supabase for the full runnable walking-skeleton app.
Runtime usage
The /runtime subpath exports the supabase(...) factory. It returns a SupabaseDb that has no top-level query surface — you bind a role first. That is deliberate: a Supabase app has no meaningful "no role" execution context, and defaulting to the connection's login role is exactly the silent-RLS-bypass footgun the design removes.
// db.ts
import { supabase } from '@internal/extension-supabase/runtime';
import { contractFromJson } from '@internal/postgres/static';
import type { Contract } from './contract';
import contractJson from './contract.json' with { type: 'json' };
export const contract = contractFromJson<Contract>(contractJson);
export const db = await supabase({
contract,
url: process.env['DATABASE_URL']!, // direct Postgres connection
jwksUrl: process.env['SUPABASE_JWKS_URL']!, // xor jwtSecret (legacy HS256) — see "JWT validation"
});
Bind each request to the role that should run it. asUser is async (it verifies the JWT); asAnon / asServiceRole are sync:
// A signed-in user — RLS scopes rows to auth.uid().
const userDb = await db.asUser(jwt); // rejects with code SUPABASE.JWT_INVALID on a bad token
const mine = await userDb.orm.public.Profile.select('id', 'username').all().toArray();
// The anon role — sees only what anon RLS policies permit.
const publicRows = await db.asAnon().orm.public.Profile.select('id', 'username').all().toArray();
// service_role — BYPASSRLS. Its .sql / .orm stay app-only; .supabase reaches auth.*/storage.*.
const admin = db.asServiceRole();
const users = await admin.supabase
.query(admin.supabase.sql.auth.users.select('id', 'email').build())
.toArray();
A role-bound client offers sql, orm, raw, prepare, query, execute, toSql, enums, nativeEnums and transaction. enums and nativeEnums are the application contract's, as on a postgres() client, and every role-bound client and transaction shares the same objects. query(plan) and execute(plan) have the types they have on a postgres() client and on tx, and run under the bound role. For an ORM plan, query yields the database rows, not the result the ORM builds from them. toSql(plan) returns { sql, params } with no database: params are the plan's values before codecs encode them, middleware rewrites (beforeCompile) are not applied, a plan with prepare bind sites throws RUNTIME.PREPARE_BIND_ON_ADHOC, and a plan built for another contract throws PLAN.HASH_MISMATCH. A plan from db.asServiceRole().supabase belongs to the Supabase contract, so render it with db.asServiceRole().supabase.toSql(plan). Creating the client has no side effects beyond checking jwtSecret or jwksUrl and pg: the pool is created on the first query, so db.asAnon().toSql(plan) works with no database. Inside transaction(async (tx) => ...), tx offers the same query members with the same types: sql, orm, raw, prepare, query, execute, toSql, enums and nativeEnums. The transaction runs every statement under the bound role. tx has no transaction. The type is SupabaseTransactionContext<Contract>, which has the members of a Postgres transaction.
const userDb = await db.asUser(jwt);
const byName = await userDb.prepare({ username: 'pg/text@1' }, (params) =>
userDb.sql.public.profile.select('id').where((f, fns) => fns.eq(f.username, params.username)).build(),
);
await byName.query(userDb, { username: 'ada' }); // runs under the user's role
await userDb.transaction(async (tx) => {
await tx.execute(tx.raw.sql`UPDATE profile SET username = 'ada' WHERE username = 'ada_l'`.affectedCount().build());
await byName.query(tx, { username: 'ada' }); // runs inside the transaction, under the user's role
});
A prepared statement runs on what it is given. ps.query(roleBound, params) runs under that client's role, and ps.query(tx, params) runs inside the transaction. Preparing sends nothing to the database.
Pass collections to give models custom collection classes: supabase({ contract, url, jwksUrl, collections: { Profile: ProfileCollection } }), where ProfileCollection extends Collection<Contract, 'Profile'> from @prisma/orm-postgres/orm-client. Every role-bound client's .orm (from asUser, asAnon and asServiceRole) then returns a ProfileCollection for Profile. So does tx.orm inside a role-bound client's transaction(...). The classes do not reach the .supabase ORM over auth.* and storage.*.
Pass log (an object with info, warn, error and an optional debug method, each taking one event object; console works, and the type is Log from @prisma/orm-family-sql/runtime) to receive what the runtime logs: the marker-verification warnings CONTRACT.MARKER_MISSING and CONTRACT.MARKER_MISMATCH from the first query, the warnings lints() emits for rules whose severity is warn, and the errors a middleware's activityEnded or activityFinalized hook throws, which the runtime logs and swallows. It also receives the warning runtime.close.waiting-for-activities when close() is waiting only for activities whose handles were never ended or for transactions still open, with the counts openActivities and openTransactions; close() waits for each such transaction until it commits or rolls back, and the error runtime.connection.release.error when the connection taken for an activity's own transaction fails to release after the transaction settles. The runtime for auth.* and storage.* reads logs to it too. If log has a debug method, it also receives debug events, for example when a middleware intercepts a statement. budgets() violations reach log only in mode: 'permissive'; see the mode paragraph below. Without log, the runtime drops these events.
mode is 'strict' by default, and every budgets() violation throws. Pass mode: 'permissive' and a violation whose severity is 'warn' goes to log.warn instead, and the query runs; a violation whose severity is 'error' still throws. budgets() gives latency 'warn' and the row count 'error' by default, so a row-count limit warns only with severities: { rowCount: 'warn' }.
Instead of url, pass pg with a pg.Pool you created. A pg.Client cannot serve Supabase, because each role-bound query and transaction needs a connection of its own, so anything other than a pg.Pool fails at supabase() with SUPABASE.CONFIG_INVALID.
db.close() refuses new work at once, on the role-bound clients and on .supabase alike, with DRIVER.NOT_CONNECTED "Runtime is closed". It waits for the work already running to finish, open role transactions included, then ends the pool the client created from url. A pg.Pool you pass in stays open, as with postgres(); you own its lifecycle.
Role binding is structurally unbypassable. Each role-bound query runs on a connection that has had set_config('role', …, false) and set_config('request.jwt.claims', …, false) applied below the user-middleware chain, with RESET ALL on release — user middleware can neither observe nor suppress it, and no role leaks across pool checkouts. See ADR 230.
JWT validation
asUser(jwt) verifies the token with jose before any connection is acquired, then derives the Postgres role from the token's role claim (defaulting to authenticated). Configure exactly one key source:
jwksUrl— the project's JWKS endpoint (https://<project-ref>.supabase.co/auth/v1/.well-known/jwks.json). Current Supabase projects sign ES256 with asymmetric keys — this is the default choice.jwtSecret— the symmetric HS256 secret, for legacy projects only.supabase statusprints aJWT_SECRETeven on ES256 projects; its presence does not mean the project uses it.
Supplying both, or neither, throws a structured error with code SUPABASE.CONFIG_INVALID. A malformed / expired / mis-signed token throws one with code SUPABASE.JWT_INVALID and the underlying reason in meta.reason — including an algorithm/key-source mismatch (an ES256 token against a jwtSecret client, or an HS256 token against a jwksUrl client) with a message naming the fix. Match these errors on their code, never a class:
import { isStructuredError } from '@internal/utils/structured-error';
try {
await db.asUser(jwt);
} catch (e) {
if (isStructuredError(e) && e.code === 'SUPABASE.JWT_INVALID') {
// e.message, e.meta?.reason
}
}
Admin reads of auth.* / storage.*
The .supabase secondary root exists only on asServiceRole() — asUser / asAnon have no .supabase. It is the extension's own contract surface, never merged into the app contract. Note that a real Supabase project grants service_role no table privileges on auth.* / storage.* (only schema USAGE); before using the admin root, run the narrow grant once for what you read, e.g. GRANT USAGE ON SCHEMA auth TO service_role; GRANT SELECT ON TABLE auth.users TO service_role;. Prefer the GoTrue Admin API for user management; direct service_role SQL is for ad-hoc admin reads (Supabase-internal schemas can drift across platform upgrades). See ADR 237.
Authoring RLS in TypeScript
The example authors its RLS policies in PSL (policy_select / policy_update + @@rls). The same policies can be authored in TypeScript via the model builder's .rls([...]) stage; the emitted wire policy names are identical to the PSL form (TML-2883). See ADR 234 for the content-addressed wire-name scheme and the Adapters & Targets subsystem doc for the RLS surface.
Not supported (out of v0.1 scope)
- Direct merged cross-space queries —
db.sql.auth.usersoff the app db does not exist by design (cross-space querying was not built; only FK references cross the boundary). Usedb.asServiceRole().supabase.sql.auth.usersfor Supabase-internal tables. - Supabase Realtime — the WebSocket change feed is a separate subsystem.
- Storage uploads —
storage.*tables are declared for reference/reads; file upload/download helpers are out of scope (use@supabase/storage-js). - PostgREST /
@supabase/supabase-jsinterop — Prisma 8 connects directly to Postgres; there is noserviceRoleKey/ PostgREST path. - Edge runtimes — the runtime needs a Postgres driver; Cloudflare Workers / Deno / Vercel Edge are out of scope (Node.js + Bun for v0.1).
- Triggers & functions as first-class IR — the "create a profile on signup" trigger is a documented raw-SQL recipe, not contract-authored (functions are not contract elements).
auth.uid()etc. live inside opaque RLS predicate strings.
Known gaps (deferred to post-launch)
- Performance benchmarks (role-bound-query overhead, JWT-validation timing) are not yet published.
- Per-subpath bundle-size thresholds (
/pack,/contract,/runtime) are not yet enforced in CI.
References
- ADR 237 — why Supabase-internal access is a service_role-only secondary root.
- ADR 230 — Runtime target layer — the role-binding model.
- ADR 212 — Contract spaces — the package layout this extension follows.
- ADR 224 — Control Policy —
externaldispatch.