@prisma/orm-mongo 8.0.0-rc.4-dev.17 → 8.0.0-rc.5-dev.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/package.json +11 -10
  2. package/skills/prisma-8/SKILL.md +84 -0
  3. package/skills/prisma-8/references/build.md +142 -0
  4. package/skills/prisma-8/references/contract.md +417 -0
  5. package/skills/prisma-8/references/debug.md +141 -0
  6. package/skills/prisma-8/references/feedback.md +251 -0
  7. package/skills/prisma-8/references/migration-review.md +224 -0
  8. package/skills/prisma-8/references/migrations.md +519 -0
  9. package/skills/prisma-8/references/queries-mongo.md +236 -0
  10. package/skills/prisma-8/references/queries-postgres.md +415 -0
  11. package/skills/prisma-8/references/queries.md +168 -0
  12. package/skills/prisma-8/references/quickstart.md +326 -0
  13. package/skills/prisma-8/references/runtime.md +344 -0
  14. package/skills/prisma-8/references/supabase.md +244 -0
  15. package/skills/prisma-8/references/upgrade-app.md +101 -0
  16. package/skills/prisma-8/references/upgrade-extension.md +105 -0
  17. package/skills/prisma-8/upgrading/app/upgrades/0.10-to-0.11/instructions.md +56 -0
  18. package/skills/prisma-8/upgrading/app/upgrades/0.11-to-0.12/instructions.md +381 -0
  19. package/skills/prisma-8/upgrading/app/upgrades/0.11-to-0.12/re-emit-closed-mongo-contracts.ts +202 -0
  20. package/skills/prisma-8/upgrading/app/upgrades/0.11-to-0.12/re-emit-domain-namespaced-contracts.ts +201 -0
  21. package/skills/prisma-8/upgrading/app/upgrades/0.11-to-0.12/re-emit-postgres-public-default.ts +198 -0
  22. package/skills/prisma-8/upgrading/app/upgrades/0.11-to-0.12/strip-migration-labels-hints.ts +340 -0
  23. package/skills/prisma-8/upgrading/app/upgrades/0.12-to-0.13/instructions.md +339 -0
  24. package/skills/prisma-8/upgrading/app/upgrades/0.12-to-0.13/re-emit-mti-variant-link-columns.ts +229 -0
  25. package/skills/prisma-8/upgrading/app/upgrades/0.13-to-0.14/instructions.md +543 -0
  26. package/skills/prisma-8/upgrading/app/upgrades/0.13-to-0.14/migration-op-factories-to-methods.ts +290 -0
  27. package/skills/prisma-8/upgrading/app/upgrades/0.13-to-0.14/uuid-preset-rename.ts +43 -0
  28. package/skills/prisma-8/upgrading/app/upgrades/0.14-to-0.15/instructions.md +359 -0
  29. package/skills/prisma-8/upgrading/app/upgrades/0.15-to-0.16/instructions.md +173 -0
  30. package/skills/prisma-8/upgrading/app/upgrades/0.16-to-0.17/instructions.md +805 -0
  31. package/skills/prisma-8/upgrading/app/upgrades/0.16-to-0.17/strip-sha256-hash-prefixes.ts +382 -0
  32. package/skills/prisma-8/upgrading/app/upgrades/0.17-to-8.0.0-rc.1/instructions.md +72 -0
  33. package/skills/prisma-8/upgrading/app/upgrades/0.7-to-0.8/instructions.md +8 -0
  34. package/skills/prisma-8/upgrading/app/upgrades/0.8-to-0.9/instructions.md +36 -0
  35. package/skills/prisma-8/upgrading/app/upgrades/0.8-to-0.9/strip-inline-contracts.ts +226 -0
  36. package/skills/prisma-8/upgrading/app/upgrades/0.9-to-0.10/instructions.md +86 -0
  37. package/skills/prisma-8/upgrading/app/upgrades/0.9-to-0.10/stamp-storage-types-kind.ts +360 -0
  38. package/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.1-to-8.0.0-rc.2/instructions.md +588 -0
  39. package/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.2-to-8.0.0-rc.3/instructions.md +5 -0
  40. package/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.3-to-8.0.0-rc.4/instructions.md +158 -0
  41. package/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.4-to-8.0.0-rc.5/instructions.md +42 -0
  42. package/skills/prisma-8/upgrading/extension/upgrades/0.10-to-0.11/instructions.md +276 -0
  43. package/skills/prisma-8/upgrading/extension/upgrades/0.11-to-0.12/instructions.md +738 -0
  44. package/skills/prisma-8/upgrading/extension/upgrades/0.11-to-0.12/migrate-contract-testing-imports.ts +97 -0
  45. package/skills/prisma-8/upgrading/extension/upgrades/0.11-to-0.12/regenerate-extension-public-baseline.ts +223 -0
  46. package/skills/prisma-8/upgrading/extension/upgrades/0.11-to-0.12/strip-migration-labels-hints.ts +340 -0
  47. package/skills/prisma-8/upgrading/extension/upgrades/0.12-to-0.13/instructions.md +266 -0
  48. package/skills/prisma-8/upgrading/extension/upgrades/0.13-to-0.14/instructions.md +522 -0
  49. package/skills/prisma-8/upgrading/extension/upgrades/0.13-to-0.14/migration-op-factories-to-methods.ts +290 -0
  50. package/skills/prisma-8/upgrading/extension/upgrades/0.13-to-0.14/uuid-preset-rename.ts +43 -0
  51. package/skills/prisma-8/upgrading/extension/upgrades/0.14-to-0.15/instructions.md +803 -0
  52. package/skills/prisma-8/upgrading/extension/upgrades/0.15-to-0.16/instructions.md +219 -0
  53. package/skills/prisma-8/upgrading/extension/upgrades/0.16-to-0.17/instructions.md +731 -0
  54. package/skills/prisma-8/upgrading/extension/upgrades/0.16-to-0.17/strip-sha256-hash-prefixes.ts +382 -0
  55. package/skills/prisma-8/upgrading/extension/upgrades/0.17-to-8.0.0-rc.1/instructions.md +194 -0
  56. package/skills/prisma-8/upgrading/extension/upgrades/0.7-to-0.8/instructions.md +8 -0
  57. package/skills/prisma-8/upgrading/extension/upgrades/0.8-to-0.9/instructions.md +57 -0
  58. package/skills/prisma-8/upgrading/extension/upgrades/0.8-to-0.9/strip-inline-contracts.ts +226 -0
  59. package/skills/prisma-8/upgrading/extension/upgrades/0.9-to-0.10/instructions.md +150 -0
  60. package/skills/prisma-8/upgrading/extension/upgrades/0.9-to-0.10/stamp-storage-types-kind.ts +360 -0
  61. package/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.1-to-8.0.0-rc.2/instructions.md +746 -0
  62. package/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.2-to-8.0.0-rc.3/instructions.md +5 -0
  63. package/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.3-to-8.0.0-rc.4/instructions.md +137 -0
  64. package/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.4-to-8.0.0-rc.5/instructions.md +129 -0
@@ -0,0 +1,746 @@
1
+ ---
2
+ from: "8.0.0-rc.1"
3
+ to: "8.0.0-rc.2"
4
+ changes:
5
+ - id: check-constraint-ir-carries-an-opaque-expression
6
+ summary: |
7
+ `CheckConstraint` (contract IR) and `SqlCheckConstraintIR` (schema IR) changed from
8
+ `{ name, column, valueSet }` / `{ name, column, permittedValues }` to
9
+ `{ name, prefix?, expression }`. Both are constructed from an `SqlObjectNaming` rather
10
+ than a bare name, exactly like `Index` / `SqlIndexIR`: pass
11
+ `{ naming: { kind: 'wire', prefix, hash }, expression }` for a managed check, or
12
+ `{ naming: { kind: 'exact', name }, expression }` for one adopted verbatim. Compute the
13
+ hash with `computeCheckContentHash(expression)` from `@internal/sql-schema-ir/naming`.
14
+ Reading a check off a built node is unchanged (`check.name`), and `check.prefix` tells
15
+ you whether it is wire-named. Contract JSON hydrates through
16
+ `checkConstraintInputFromSerialized`, which rejects a `prefix` that does not parse out of
17
+ the `name`. `resolveValueSetValues` is gone — a check no longer references a value set,
18
+ so there is nothing to resolve; the members are already baked into the predicate text.
19
+ detection:
20
+ glob: "**/*.{ts,mts,cts}"
21
+ contains:
22
+ - 'CheckConstraint'
23
+ - 'permittedValues'
24
+ - 'resolveValueSetValues'
25
+ anyMatch: true
26
+ - id: add-check-constraint-call-takes-an-expression
27
+ summary: |
28
+ `AddCheckConstraintCall` is now constructed as
29
+ `(schemaName, tableName, constraintName, expression)` — the `column` and `values`
30
+ parameters are gone, and the rendered DDL is
31
+ `ALTER TABLE … ADD CONSTRAINT "x" CHECK (<expression>)` with the predicate emitted
32
+ verbatim. The matching migration-class method takes
33
+ `{ schema, table, constraint, expression }`. `DropCheckConstraintCall` is unchanged.
34
+ There is no compatibility overload — update every construction site.
35
+ detection:
36
+ glob: "**/*.{ts,mts,cts}"
37
+ contains:
38
+ - 'AddCheckConstraintCall'
39
+ - 'addCheckConstraint'
40
+ anyMatch: true
41
+ - id: specifier-default-control-policy-requires-create-namespace
42
+ summary: |
43
+ The options bag on `typescriptContract` / `typescriptContractFromPath` now requires
44
+ `createNamespace` alongside `defaultControlPolicy`. Stamping a specifier default carries
45
+ a consequence — derived CHECK constraints are stripped from tables the stamped policy
46
+ leaves non-managed — and the strip rebuilds storage namespaces through the target's
47
+ factory, so the two options travel together. Pass the same factory the PSL specifier
48
+ already takes:
49
+ `typescriptContract(contract, output, { defaultControlPolicy: 'external' })` becomes
50
+ `typescriptContract(contract, output, { defaultControlPolicy: 'external',
51
+ createNamespace: postgresCreateNamespace })`, with `postgresCreateNamespace` imported
52
+ from the Postgres target's types entrypoint (`@internal/target-postgres/types`).
53
+ Calls without an options bag are unchanged, and `emptyContract` already took
54
+ `createNamespace`.
55
+ detection:
56
+ glob: "**/*.{ts,mts,cts}"
57
+ contains:
58
+ - 'typescriptContract'
59
+ - 'defaultControlPolicy'
60
+ anyMatch: false
61
+ - id: re-emit-extension-contract-spaces
62
+ summary: |
63
+ Run your extension's `contract emit` (the `build:contract-space` script, if you have one)
64
+ to regenerate its committed `contract.json` / `contract.d.ts`. Two things change: any
65
+ enum CHECK is re-serialized into the new shape with a wire name, and every list (`many`)
66
+ column gains a declared element-non-null CHECK that the Postgres planner used to
67
+ synthesize without ever declaring. Prefixes derived from long table and column names are
68
+ truncated to 54 UTF-8 bytes so the wire name fits Postgres's 63-byte identifier limit;
69
+ identity lives in the hash, so truncated prefixes still yield distinct names.
70
+ Postgres introspection also stopped parsing predicates and now captures every CHECK
71
+ constraint verbatim, so any hand-written or platform-installed check on a table your
72
+ extension manages is visible for the first time: it verifies as an undeclared extra under
73
+ `--strict` and becomes a `dropCheckConstraint` under a policy that allows `destructive`.
74
+ If your extension installs checks out of band — through a raw-SQL migration step rather
75
+ than through the contract — declare them instead: `@@check(expression: "…", map: "<physical
76
+ name>")` adopts a constraint under the name it already carries. Until they are declared,
77
+ keep the tables carrying them under an additive-only policy — the checks survive, and only
78
+ `--strict` verify reports them — or expect the first destructive plan against an upgraded
79
+ database to offer to drop them. Declaring them is the durable fix; see
80
+ `authored-check-constraints` in this transition.
81
+ detection:
82
+ glob: "**/contract.json"
83
+ contains:
84
+ - '"many": true'
85
+ - '"valueSet"'
86
+ anyMatch: true
87
+ - id: stub-execution-contexts-must-carry-an-aggregate-registry
88
+ summary: |
89
+ A pack that hand-rolls an `ExecutionContext` in its tests must give it an
90
+ `aggregateDescriptors` registry whose `values()` yields the descriptors the test exercises.
91
+ `orm(...)` and every `Collection` construction enumerate that registry to build their method
92
+ sets, so a stub context without the field raises
93
+ `TypeError: Cannot read properties of undefined (reading 'values')` at construction, and a
94
+ registry whose `values()` yields nothing produces a collection and a `sql()` function bag
95
+ with no aggregate methods at all — `TypeError: aggregate.count is not a function` at the
96
+ first call. A `resolve()` that answers `count` is no longer sufficient on its own: `resolve`
97
+ settles one `(operation, input)` pair, `values` declares the vocabulary. Prefer
98
+ `buildSqlAggregateDescriptorRegistry(descriptors, codecDescriptors)` from
99
+ `@internal/sql-relational-core/aggregate-descriptor-registry` over a hand-written object —
100
+ it settles the same registry the runtime does.
101
+ detection:
102
+ glob: "**/*.{ts,tsx,mts,cts}"
103
+ contains:
104
+ - "aggregateDescriptors"
105
+ - "ExecutionContext"
106
+ - "SqlAggregateDescriptorRegistry"
107
+ anyMatch: true
108
+ - id: aggregate-surfaces-derive-from-the-contract-s-operation-map
109
+ summary: |
110
+ The ORM's `aggregate()` / `groupBy().aggregate()` / `groupBy().having()` builders, the
111
+ include reducers on a collection, and the SQL builder's aggregate functions no longer
112
+ declare `count` / `sum` / `avg` / `min` / `max` outright. Each surface is a mapped type over
113
+ the operation names in the contract's emitted `AggregateTypes` block. Deriving the surface
114
+ neither adds nor removes a method by itself, but the block a re-emit produces is not the
115
+ list it was: PostgreSQL now declares eight operations and SQLite seven, and the bare results
116
+ over integer columns moved — `count`, `sum`, and `avg`. What stayed: `min` / `max`, `sum`
117
+ and `avg` over a float, `numeric`, `interval`, or `time` column, and `sum` over an
118
+ `UnboundedInt` column, each still in its own family — see
119
+ `count-over-a-field-counts-that-field` and `aggregate-defaults-are-js-native-numbers`.
120
+ A contract whose block is unknown — an in-code `defineContract(...)` value, or a
121
+ contract emitted before `AggregateTypes` existed — resolves every one of those surfaces to
122
+ `AggregateOperationsUnavailable`, an empty type, so the call fails with
123
+ `Property 'count' does not exist` rather than offering selector types the declaration
124
+ cannot supply. This is a compile-time change only: the runtime builds its methods from the
125
+ composed registry and dispatches exactly as before. Either emit the contract and type the
126
+ client from the emitted `Contract`, or cast the builder to a dynamic record — see the body
127
+ for the shape.
128
+ detection:
129
+ glob: "**/*.{ts,tsx,mts,cts}"
130
+ contains:
131
+ - "defineContract"
132
+ - ".aggregate("
133
+ - "AggregateBuilder"
134
+ - "HavingBuilder"
135
+ anyMatch: true
136
+ - id: count-over-a-field-counts-that-field
137
+ summary: |
138
+ `count(field)` renders `COUNT(<column>)` in the ORM. It used to drop the argument and render
139
+ `COUNT(*)`. PostgreSQL declares `count` with `input: { kind: 'any' }`, which settles into
140
+ both a `withoutInput` row and an `anyInput` row, so the derived method carries both arities
141
+ honestly: `count()` counts rows, `count(field)` counts that field's non-null values. Typed
142
+ call sites are unaffected, because `count` took no argument before and no such call
143
+ compiled. Reachable through a call that bypassed the types — a `@ts-expect-error`, an
144
+ `as never` argument, or dynamic invocation. Those call sites change result whenever the
145
+ column holds NULLs. Sweep them and drop the argument wherever `COUNT(*)` was what you meant.
146
+ detection:
147
+ glob: "**/*.{ts,tsx,mts,cts}"
148
+ contains:
149
+ - "count("
150
+ - ".aggregate("
151
+ anyMatch: true
152
+ - id: contributed-aggregate-operations-carry-a-lowering-hook
153
+ summary: |
154
+ An aggregate descriptor whose `operation` is not one of `count`, `sum`, `avg`, `min`, `max`
155
+ must declare a `lower` hook. `operation` has been a `string` for a release, but a name
156
+ outside that alphabet was inert — no consumer surface offered it. It is live now: a
157
+ contributed operation surfaces as a method on the ORM and SQL-builder aggregate surfaces
158
+ under its own name, so it must be able to build an expression, and the closed AST alphabet
159
+ gives it none. Registry assembly rejects a hook-less descriptor with
160
+ `RUNTIME.AGGREGATE_LOWERING_MISSING` while the execution context composes. Two further
161
+ rules follow. An out-of-alphabet operation is projection-only: HAVING, ORDER BY, and
162
+ comparison operands refuse it with `ORM.AGGREGATE_PROJECTION_ONLY`, and its typed HAVING
163
+ method does not exist. And its name may not shadow an ORM collection member (`select`,
164
+ `where`, `include`, `combine`, `state`, …), because reducers install into that same
165
+ namespace; `orm(...)` rejects a collision with `ORM.AGGREGATE_OPERATION_RESERVED`.
166
+ detection:
167
+ glob: "**/*.{ts,tsx,mts,cts}"
168
+ contains:
169
+ - "aggregateDescriptors"
170
+ - "SqlAggregateDescriptor"
171
+ anyMatch: true
172
+ - id: aggregate-defaults-are-js-native-numbers
173
+ summary: |
174
+ Both built-in targets split their aggregate vocabulary. `count()`, `sum()` over an integer
175
+ input, and `avg()` over an integer input read as `number` — where they read as a `bigint`,
176
+ a `bigint`-or-decimal-string, and a decimal string. Three new operations carry the lossless
177
+ results: `countBigInt` → `bigint`, `sumBigInt` → `bigint` (over `pg/int8@1`,
178
+ `pg/int8number@1`, and `pg/unboundedint@1` it resolves to `pg/unboundedint@1`, exact past
179
+ 2^63), and `avgDecimal` → `pg/numeric@1`, PostgreSQL only. `count`'s empty-input answer is
180
+ `0`, not `0n`. `count`, and `sum` over an integer input, raise `RUNTIME.DECODE_FAILED` past
181
+ ±(2^53 − 1) rather than rounding, on the JSON/include path as well as the wire path; no
182
+ other result carries that guard, `avg` included. Unchanged: `min`/`max`,
183
+ `sum`/`avg` over float codecs, `sum` over `pg/numeric@1` and `pg/unboundedint@1`, and the
184
+ ORM's `having(...)` operands, which its typed surface fixes at `number`. The SQL builder's
185
+ comparison operands do move: `fns.gt` types both sides from one codec, so
186
+ `fns.gt(fns.count(), 1n)` becomes `fns.gt(fns.count(), 1)`.
187
+ Two things to do. Re-run your contract space's `contract emit` —
188
+ the `AggregateTypes` block gains the three operations and the changed result codecs. Then
189
+ fix pack tests that assert aggregate values or rendered SQL: `2n` and decimal-string
190
+ expectations become plain numbers, and PostgreSQL's integer `avg` renders
191
+ `CAST(avg(…) AS float8)` where it rendered a plain `avg(…)`. SQLite's transport cast to
192
+ text is unchanged, but `sqlite/bigintnumber@1` now carries a JSON projection
193
+ (`CAST(… AS INTEGER)`), so a SQLite include aggregate arrives inside `json_object` as a
194
+ JSON number rather than a JSON string.
195
+ detection:
196
+ glob: "**/*.{ts,tsx,mts,cts}"
197
+ contains:
198
+ - ".aggregate("
199
+ - "aggregateDescriptors"
200
+ - "AggregateTypes"
201
+ anyMatch: true
202
+ - id: non-nullable-aggregate-descriptors-declare-an-empty-result
203
+ summary: |
204
+ A descriptor with `nullable: false` must also declare `emptyResultJson` — the value the
205
+ operation answers with when no result row reaches the client at all. `AggregateResultNullability`
206
+ (exported from `@internal/framework-components/components`) is a discriminated union, so
207
+ `{ nullable: false }` on its own does not compile, and registry assembly rejects it at
208
+ runtime with `RUNTIME.AGGREGATE_DESCRIPTOR_INVALID` (or `CONTRACT.AGGREGATE_DESCRIPTOR_INVALID`
209
+ during emit). State the value in the **result codec's canonical JSON**, not as an
210
+ application value: `emptyResultJson: 0` under `pg/int8number@1`, `emptyResultJson: '0'`
211
+ under `pg/int8@1`. The empty-input answer belongs to the operation rather than to the codec
212
+ — `count`'s identity element is zero, an `every()`'s would be `true` — which is why it sits
213
+ on the descriptor. `ResolvedSqlAggregate` (`@internal/sql-relational-core/query-lane-context`)
214
+ follows the same union, so a consumer that constructs one spreads the nullability rather
215
+ than assigning `nullable: boolean`; reading `resolved.nullable` still narrows as before.
216
+ Nullable descriptors are unchanged.
217
+ detection:
218
+ glob: "**/*.{ts,tsx,mts,cts}"
219
+ contains:
220
+ - "nullable: false"
221
+ - "aggregateDescriptors"
222
+ - "ResolvedSqlAggregate"
223
+ anyMatch: true
224
+ - id: integer-codecs-check-the-js-type-they-are-given
225
+ summary: |
226
+ `pg/int8@1`, `pg/unboundedint@1`, and `sqlite/bigint@1` refuse a JS `number` on `encode`,
227
+ and `pg/int8number@1` and `sqlite/bigintnumber@1` refuse a `bigint`, with
228
+ `RUNTIME.ENCODE_FAILED` and a message naming the type that arrived
229
+ (`pg/int8@1 value must be a bigint, got number 9`) plus `meta.received`. The bigint codecs
230
+ used to accept a number and stringify it, which let a fractional value through to an
231
+ integer column. `encodeJson` is deliberately wider on the exact codecs: it accepts a
232
+ safe-integer `number`, because a schema language writes no `bigint` literal and
233
+ `BigInt @default(0)` arrives as the JSON number `0`; a non-integral or unsafe number raises
234
+ `<codec> number literal must be an integer within the safe integer range`. Two consequences
235
+ for a pack. Any place you hand a codec a value read out of a contract, JSON, or PSL must
236
+ pick the matching method — `encode` takes the application value, `encodeJson` takes and
237
+ returns canonical JSON. And if your pack renders DDL literal defaults itself, read the
238
+ stored value back first: `await codec.encode(codec.decodeJson(stored), {})`, which is the
239
+ two declared conversions in their declared order, rather than passing canonical JSON
240
+ straight to `encode`.
241
+ detection:
242
+ glob: "**/*.{ts,tsx,mts,cts}"
243
+ contains:
244
+ - "encodeJson"
245
+ - "decodeJson"
246
+ - "codec.encode"
247
+ anyMatch: true
248
+ - id: authored-check-constraints
249
+ summary: |
250
+ Two things change for packs. `sql.checkConstraint` is a new adapter-reported capability: an
251
+ adapter whose target implements CHECK constraint DDL reports it, and the `@@check` authoring
252
+ surface is gated on it. And a check is no longer "derived" merely by being wire-named —
253
+ user-authored checks are wire-named too. Derivation is now decided by whether the wire prefix
254
+ is one derivation would produce for a column of that table. A pack that read
255
+ `check.prefix !== undefined` to mean "Prisma Next generated this" must use the same
256
+ prefix-shape test, `derivedCheckPrefixes` from `@internal/sql-schema-ir/naming`. An authored
257
+ name that collides with a derived prefix shape is rejected at authoring with
258
+ `CONTRACT.CHECK_NAME_RESERVED`.
259
+ detection:
260
+ glob: "**/*.{ts,tsx}"
261
+ contains:
262
+ - 'checkConstraint'
263
+ - 'derivedCheckPrefixes'
264
+ anyMatch: true
265
+ - id: runtime-query-execute-hard-cut
266
+ summary: |
267
+ Runtime and scope implementations expose `query()` for rows, while rc.1 prepared rows use `target.queryPrepared(prepared, params, options?)` and rc.2 uses `prepared.query(target, params, options?)`; statistics-returning `execute()` remains for non-returning statements. Classify callers and helpers by the result they consume; do not globally rename `execute`. Route row plans to `query`, prepared row plans to `prepared.query(target, params, options?)`, and non-returning writes to `execute`, reading `stats.affectedRows` when a count is needed. Preserve the bound connection, row-result laziness, and eager statistics result. Middleware uses `beforeQuery` → `interceptQuery` → driver query → `onRow` → `afterQuery` for rows and `beforeExecute` → `interceptExecute` → driver execute → `afterExecute` for statistics. Query interception returns `{ rows }`; execute interception returns `{ stats }`. There is no operation discriminator, compatibility alias, or generic fallback hook. The Mongo facade keeps static `db.query` and removes row-execution `db.execute`; execute a built row plan through `(await db.runtime()).query(plan)`.
268
+ detection:
269
+ glob: "**/*.{ts,tsx,mts,cts}"
270
+ contains:
271
+ - "beforeQuery"
272
+ - "interceptExecute"
273
+ - ".queryPrepared("
274
+ - ".execute("
275
+ anyMatch: true
276
+ - id: config-file-is-prisma-config-with-an-orm-section
277
+ summary: |
278
+ The CLI config file is `prisma.config.ts` — the `prisma-next.config.ts` name is
279
+ deprecated — and the config value is engine-shaped: `defineConfig` from
280
+ `@prisma/cli-engine` wraps the whole ORM config as its `orm` section. Rename the file,
281
+ then wrap the existing export: alias the current `defineConfig` import (from the target
282
+ facade or CLI config-types) as `ormConfig` and write
283
+ `export default defineConfig({ orm: ormConfig({ ...existing config... }) })`, adding
284
+ `@prisma/cli-engine` to devDependencies. Both the deprecated filename and the flat shape
285
+ still load, each printing a deprecation warning on stderr, so the two steps can land
286
+ separately; anything asserting clean stderr around CLI invocations sees the warning
287
+ until both are done.
288
+ detection:
289
+ glob: "**/prisma*.config.*"
290
+ contains:
291
+ - "defineConfig"
292
+ anyMatch: true
293
+ - id: published-prisma-next-bin-retired
294
+ summary: |
295
+ Nothing published ships a `prisma-next` bin anymore: `@prisma/orm-toolchain` publishes
296
+ the `orm` command family at `@prisma/orm-toolchain/cli` and no bin, and the database
297
+ facades forward no launcher. The only user-facing binary is the unified `prisma` CLI
298
+ (the prisma-cli distribution), which mounts the same commands. Replace
299
+ `prisma-next <command>` invocations in package scripts and CI with the unified CLI's
300
+ equivalent, and drop any dependency that was taken only to put the bin on PATH.
301
+ detection:
302
+ glob: "**/package.json"
303
+ contains:
304
+ - "prisma-next"
305
+ anyMatch: true
306
+ - id: prepared-statements-split-by-declared-result
307
+ summary: |
308
+ `runtime.prepare()` now returns one of two handles, chosen from the plan the callback
309
+ builds: a rows plan gives the `PreparedStatement<Params, Row>` you already have,
310
+ consumed with `.query(target, params)`; a plan whose declared result is an affected-row
311
+ count gives a `PreparedExecution<Params>`, consumed with `.execute(target, params)` and
312
+ resolving `SqlStatementStats`. Two things follow for an extension. A facade that
313
+ redeclares `prepare()` changes its return type from
314
+ `Promise<PreparedStatement<ParamsFromDeclaration<D, CT>, Row>>` to
315
+ `Promise<PreparedFor<ParamsFromDeclaration<D, CT>, Row>>`, importing `PreparedFor` from
316
+ `@internal/sql-runtime` — no logic changes. A scope of your own that installs the
317
+ prepared-query bridge (`preparedStatementQuery`) must also install the execute bridge
318
+ (`preparedStatementExecute`, from `@internal/sql-runtime/internal/prepared-query`),
319
+ routing it to your bound queryable exactly as the query bridge does; without it,
320
+ `prepared.execute(yourScope, ...)` throws on the bridge invariant.
321
+ detection:
322
+ glob: "**/*.{ts,mts,cts}"
323
+ contains:
324
+ - "preparedStatementQuery"
325
+ - "PreparedStatement<ParamsFromDeclaration"
326
+ anyMatch: true
327
+ - id: prepare-defaults-to-the-contract-codec-map
328
+ summary: |
329
+ A facade that redeclares `prepare()` should default its codec-map parameter to
330
+ `ExtractCodecTypes<TContract>` alone. Intersecting it with `CodecTypesBase` — the shape
331
+ the in-tree facades used — collapses `keyof CT` to `string` through that type's index
332
+ signature, which costs a caller every codec-id completion in the declaration and lets an
333
+ id no registry carries typecheck. Drop the intersection; the `CT extends CodecTypesBase`
334
+ constraint stays and nothing else in the signature changes. If your facade exposes a
335
+ contract-bound raw tag, its `.returns()` now narrows to the contract's ids the same way,
336
+ with no change on your side; a contract-free tag keeps accepting any string.
337
+ detection:
338
+ glob: "**/*.{ts,mts,cts}"
339
+ contains:
340
+ - "ExtractCodecTypes<TContract> & CodecTypesBase"
341
+ - "CodecTypesBase ="
342
+ anyMatch: true
343
+ ---
344
+
345
+ # 8.0.0-rc.1 → 8.0.0-rc.2 — Extension-author upgrade instructions
346
+
347
+ ## `stub-execution-contexts-must-carry-an-aggregate-registry`
348
+
349
+ The aggregate method sets are built by enumerating the composed registry, once per `Collection` construction and once at `orm(...)`. A test that fabricates an `ExecutionContext` therefore has to supply one:
350
+
351
+ ```ts
352
+ import { buildSqlAggregateDescriptorRegistry } from '@internal/sql-relational-core/aggregate-descriptor-registry';
353
+
354
+ const context = {
355
+ contract,
356
+ codecDescriptors,
357
+ aggregateDescriptors: buildSqlAggregateDescriptorRegistry(descriptors, codecDescriptors),
358
+ // …
359
+ };
360
+ ```
361
+
362
+ Two distinct failures tell you which half is missing:
363
+
364
+ - **`TypeError: Cannot read properties of undefined (reading 'values')`**, thrown from `new Collection(...)` or `orm(...)` — the stub has no `aggregateDescriptors` field at all.
365
+ - **`TypeError: aggregate.count is not a function`** (or `posts.count is not a function`, or `fns.sum is not a function`) — the stub has a registry, but its `values()` yields nothing, so no method was installed. `resolve()` alone no longer describes the surface: `resolve` settles one `(operation, input)` pair on demand, `values` declares which operations exist.
366
+
367
+ A minimal hand-written stub that keeps `count` available:
368
+
369
+ ```ts
370
+ const aggregateDescriptors = {
371
+ resolve: (operation: string) =>
372
+ operation === 'count'
373
+ ? {
374
+ operation,
375
+ output: { codecId: 'pg/int8@1' },
376
+ nullable: false as const,
377
+ emptyResultJson: '0',
378
+ lower: undefined,
379
+ }
380
+ : undefined,
381
+ values: function* () {
382
+ yield {
383
+ operation: 'count',
384
+ input: { kind: 'any' as const },
385
+ output: { kind: 'codec' as const, codecId: 'pg/int8@1' },
386
+ nullable: false as const,
387
+ emptyResultJson: '0',
388
+ };
389
+ },
390
+ };
391
+ ```
392
+
393
+ `emptyResultJson` is required on any non-nullable row — see [`non-nullable-aggregate-descriptors-declare-an-empty-result`](#non-nullable-aggregate-descriptors-declare-an-empty-result) for what the value means and which form to state it in.
394
+
395
+ Prefer the real builder where the test can afford it — it applies the same validation the runtime does, including the lowering rule below.
396
+
397
+ ## `aggregate-surfaces-derive-from-the-contract-s-operation-map`
398
+
399
+ Every aggregate surface is now a mapped type keyed by the operation names in the contract's emitted `AggregateTypes` block:
400
+
401
+ | Surface | Type |
402
+ | --- | --- |
403
+ | `collection.aggregate(fn)` | `AggregateBuilder<TContract, ModelName, NsId>` |
404
+ | `groupBy(...).aggregate(fn)` | the same |
405
+ | `groupBy(...).having(fn)` | `HavingBuilder<…>` — keyed by the map *intersected with* the SQL alphabet |
406
+ | `include('rel', (rel) => …)` | `AggregateIncludeReducers<…>` on the collection |
407
+ | `sql().select((f, fns) => …)` | `AggregateOnlyFunctions<QC>` |
408
+
409
+ For a contract emitted by `prisma-next contract emit` on 8.0.0-rc.1 or later, the derivation itself takes nothing away — the block names whatever the composed stack declares. It does not name the same list it did, though: PostgreSQL now declares eight operations and SQLite seven, and the bare results over integer columns moved — `count`, `sum`, and `avg`. `min` / `max` did not, nor did `sum` and `avg` over a float, `numeric`, `interval`, or `time` column, nor `sum` over an `UnboundedInt` column; each of those stays in its own family. Re-emit, then work the two entries that carry those changes — [`count-over-a-field-counts-that-field`](#count-over-a-field-counts-that-field) and [`aggregate-defaults-are-js-native-numbers`](#aggregate-defaults-are-js-native-numbers).
410
+
411
+ For a contract whose block is unknown, all five surfaces resolve to `AggregateOperationsUnavailable` — an empty interface carrying one optional symbol-keyed brand that names the reason on hover. Two populations reach it:
412
+
413
+ - a contract built in code with `defineContract(...)` and used without emission (fixtures, integration tests, the no-emit authoring flow);
414
+ - a contract emitted before `AggregateTypes` existed, i.e. before 8.0.0-rc.1.
415
+
416
+ The symptom is a compile error at the call site:
417
+
418
+ ```text
419
+ Property 'count' does not exist on type 'AggregateOperationsUnavailable'.
420
+ ```
421
+
422
+ Runtime behaviour is unchanged — the methods are installed from the composed registry either way — so a cast is a legitimate fix where the contract is deliberately un-emitted:
423
+
424
+ ```ts
425
+ import type { AggregateSpec } from '@internal/sql-orm-client';
426
+
427
+ type DynamicAggregates = Record<string, (field?: string) => AggregateSpec[string]>;
428
+
429
+ const stats = await readings.aggregate((aggregate) => {
430
+ const dynamic = aggregate as DynamicAggregates;
431
+ return { total: dynamic['sum']!('counter'), peak: dynamic['max']!('counter') };
432
+ });
433
+ ```
434
+
435
+ The same shape works for an include reducer, whose callback receives the refinement collection:
436
+
437
+ ```ts
438
+ const reduceToTotal = (related: unknown): unknown =>
439
+ (related as DynamicAggregates)['sum']!('counter');
440
+
441
+ await readings.select('id').include('samples', (samples) => reduceToTotal(samples) as never).all();
442
+ ```
443
+
444
+ Where an argument was previously widened past the types — `aggregate.sum('counter' as never)` compiled because `AggregateFieldNames` was already `never` for such a contract — the cast moves from the argument to the builder, and the field name goes back to being a plain string.
445
+
446
+ The better fix, wherever the pack can emit, is to emit: run `prisma-next contract emit` and type the client from the emitted `Contract`. That restores full typing — arities, admitted field names, and per-codec result types — instead of erasing it.
447
+
448
+ ## `count-over-a-field-counts-that-field`
449
+
450
+ `count(field)` counts that field:
451
+
452
+ ```ts
453
+ await db.orm.User.aggregate((aggregate) => ({ all: aggregate.count() }));
454
+ // SELECT COUNT(*) …
455
+
456
+ await db.orm.User.aggregate((aggregate) => ({ named: aggregate.count('email') }));
457
+ // SELECT COUNT("email") … — rows whose email is NULL are not counted
458
+ ```
459
+
460
+ The second call used to render `COUNT(*)`: the argument was accepted and discarded. It is honoured now, because both arities are read off `count`'s rows rather than special-cased — PostgreSQL declares `count` with `input: { kind: 'any' }`, which settles into a `withoutInput` row (the zero-argument call) and an `anyInput` row (the field-taking call).
461
+
462
+ No typed call site changes meaning, because the field-taking overload did not exist before. What to sweep for is a call that got past the types:
463
+
464
+ - `// @ts-expect-error` immediately above a `count(...)` call. Where the argument is a field the contract admits, the suppression is now unused and TypeScript reports the unused directive. Where it is not — a relation name, say — the directive still holds and the call still fails.
465
+ - `count(x as never)` or `count(x as any)`.
466
+ - dynamic dispatch through a `Record<string, …>` cast.
467
+
468
+ For each, decide which count you meant. `COUNT(*)` is `count()`; `COUNT(col)` skips NULLs.
469
+
470
+ ## `contributed-aggregate-operations-carry-a-lowering-hook`
471
+
472
+ Aggregate operation names are an open vocabulary, and they now reach the consumer surfaces. The SQL alphabet holds exactly five names (`count`, `sum`, `avg`, `min`, `max`), and a pack that contributes a descriptor named outside it — `bitOr`, say — gets a method under that name on the ORM's `aggregate()`, on `groupBy().aggregate()`, and on the include reducers, plus a matching function on the SQL builder. Three rules come with that.
473
+
474
+ **Declare a `lower` hook.** The AST's `AggregateExpr` carries only alphabet names, so a novel operation has no default form; the hook builds its whole expression from existing nodes:
475
+
476
+ ```ts
477
+ import type { SqlAggregateDescriptor } from '@internal/sql-relational-core/aggregate-descriptor-registry';
478
+ import { FunctionCallExpr } from '@internal/sql-relational-core/ast';
479
+
480
+ const bitOr: SqlAggregateDescriptor = {
481
+ operation: 'bitOr',
482
+ input: { kind: 'codec', codecId: 'pg/int8@1' },
483
+ output: { kind: 'codec', codecId: 'pg/int8@1' },
484
+ nullable: true,
485
+ lower: ({ expr }) => FunctionCallExpr.of('bit_or', expr === undefined ? [] : [expr]),
486
+ };
487
+ ```
488
+
489
+ Without it, the execution context refuses to compose:
490
+
491
+ ```text
492
+ RUNTIME.AGGREGATE_LOWERING_MISSING: Aggregate descriptor 'bitOr:codec:pg/int8@1' declares
493
+ operation 'bitOr', which is outside the SQL aggregate alphabet (count, sum, avg, min, max)
494
+ and carries no lowering hook.
495
+ ```
496
+
497
+ **Use it in projections only.** The lowered form is a rendering for the driver boundary. HAVING, ORDER BY, and comparison operands compare inside the database, where that rendering would change what the comparison means, so both consumers refuse those positions at authoring time with `ORM.AGGREGATE_PROJECTION_ONLY`. The typed surface agrees: `HavingBuilder` is keyed by the map intersected with the alphabet, so a contributed operation has no HAVING method to call. Project it in a select and filter or order on the projected value.
498
+
499
+ **Pick a name no collection member owns.** Include reducers install into the ORM collection's own namespace, beside `select`, `where`, `include`, `combine`, `aggregate`, and the collection's instance fields. `orm(...)` rejects a collision with `ORM.AGGREGATE_OPERATION_RESERVED` and names the operation in `meta.operation`; rename the operation.
500
+
501
+ Reference: [the aggregate descriptor guide](https://github.com/prisma/prisma/blob/main/docs/reference/aggregate-descriptor-guide.md).
502
+
503
+ ## `aggregate-defaults-are-js-native-numbers`
504
+
505
+ Both built-in targets now split their aggregate vocabulary: the bare operations answer in the JS-native type, three new suffixed operations answer losslessly.
506
+
507
+ | Operation | Input | Result codec | Was |
508
+ | --- | --- | --- | --- |
509
+ | `count` | none or any | `pg/int8number@1` / `sqlite/bigintnumber@1` | `pg/int8@1` / `sqlite/bigint@1` |
510
+ | `countBigInt` | none or any | `pg/int8@1` / `sqlite/bigint@1` | — (new) |
511
+ | `sum` | `pg/int2@1`, `pg/int4@1`, `pg/int@1`, `sql/int@1` | `pg/int8number@1` | `pg/int8@1` |
512
+ | `sum` | `pg/int8@1`, `pg/int8number@1` | `pg/int8number@1` | `pg/numeric@1` |
513
+ | `sum` | SQLite's integer codecs | `sqlite/bigintnumber@1` | `sqlite/bigint@1` |
514
+ | `sumBigInt` | `pg/int2@1`, `pg/int4@1`, `pg/int@1`, `sql/int@1` | `pg/int8@1` | — (new) |
515
+ | `sumBigInt` | `pg/int8@1`, `pg/int8number@1`, `pg/unboundedint@1` | `pg/unboundedint@1` | — (new) |
516
+ | `sumBigInt` | SQLite's integer codecs | `sqlite/bigint@1` | — (new) |
517
+ | `avg` | every PostgreSQL integer codec | `pg/float8@1`, through a result cast | `pg/numeric@1` |
518
+ | `avgDecimal` | every PostgreSQL integer codec, plus `pg/numeric@1` | `pg/numeric@1` | — (new) |
519
+
520
+ Everything else keeps its row: `min` / `max`, `sum` and `avg` over the float codecs, `sum` over `pg/numeric@1` and `pg/unboundedint@1`, `avg` over `pg/numeric@1` and `pg/interval@1`, and SQLite's `avg`, which was already `sqlite/real@1`.
521
+
522
+ `sumBigInt` over a 64-bit input resolves to `pg/unboundedint@1` rather than `pg/int8@1`, deliberately: PostgreSQL computes that total as a `numeric`, and casting it back to `int8` would raise `bigint out of range` past 2^63. On SQLite, `sumBigInt` is offered inside SQLite's own bound — a 64-bit `SUM` overflow raises `integer overflow` in the database rather than promoting to a float.
523
+
524
+ ### What to do
525
+
526
+ 1. **Re-run your contract space's `contract emit`.** The `AggregateTypes` block in the committed `contract.d.ts` gains `countBigInt`, `sumBigInt`, and `avgDecimal`, and the changed result codecs on `count` / `sum` / `avg`.
527
+ 2. **Fix value assertions in pack tests.** `expect(stats.total).toBe(2n)` becomes `toBe(2)`; a decimal-string average expectation becomes a number. Where the test was proving exactness, change the *method* to the suffixed variant rather than the expectation.
528
+ 3. **Fix rendered-SQL assertions on PostgreSQL `avg`.** An integer `avg` renders `CAST(avg("t"."c") AS float8)` where it rendered a plain `avg("t"."c")`. The cast is on the **result**, so the exact `numeric` mean is computed first and rounded once.
529
+ 4. **Expect a JSON number from a SQLite include aggregate.** `sqlite/bigintnumber@1` carries a JSON projection (`CAST(… AS INTEGER)`), so an included `count` or `sum` arrives inside `json_object` as a JSON number rather than a JSON string. The transport cast to text on the flat path is unchanged.
530
+ 5. **Retype SQL-builder comparison literals against an aggregate.** `fns.gt(a, b)` types both operands from one codec, so a literal compared against `fns.count()` or an integer `fns.sum(...)` follows the aggregate's new result codec: `fns.gt(fns.count(), 1n)` becomes `fns.gt(fns.count(), 1)`. The ORM's `having(...)` is not this case — its comparand is typed `number` outright.
531
+
532
+ `count`, and `sum` over an integer input, raise `RUNTIME.DECODE_FAILED` past ±(2^53 − 1) rather than rounding, on the JSON path as well as the wire path — the guard runs after `JSON.parse`, and rounding is monotone, so a value outside the range cannot parse back inside it. They are the two results a guarded integer codec produces; `sum` over a float, `numeric`, or unbounded-integer input keeps that input's own family, and `avg` resolves to a float codec that rounds as any double does.
533
+
534
+ ## `non-nullable-aggregate-descriptors-declare-an-empty-result`
535
+
536
+ A descriptor that declares `nullable: false` must declare `emptyResultJson` beside it:
537
+
538
+ ```ts
539
+ import type { SqlAggregateDescriptor } from '@internal/sql-relational-core/aggregate-descriptor-registry';
540
+
541
+ const count: SqlAggregateDescriptor = {
542
+ operation: 'count',
543
+ input: { kind: 'any' },
544
+ output: { kind: 'codec', codecId: 'pg/int8number@1' },
545
+ nullable: false,
546
+ emptyResultJson: 0,
547
+ };
548
+ ```
549
+
550
+ `AggregateResultNullability` — exported from `@internal/framework-components/components` — is a discriminated union (`{ nullable: true } | { nullable: false; emptyResultJson: JsonValue }`), so `{ nullable: false }` alone is a type error. The runtime check agrees: registry assembly raises `RUNTIME.AGGREGATE_DESCRIPTOR_INVALID`, and `contract emit` raises `CONTRACT.AGGREGATE_DESCRIPTOR_INVALID`.
551
+
552
+ **State the value in the result codec's canonical JSON, not as an application value.** The client decodes it through the codec the same row declares, so the two must agree — and the wrong form only fails at the one moment a populated table never reaches. `count`'s zero is `0` under `pg/int8number@1` and `'0'` under `pg/int8@1`.
553
+
554
+ **Why it lives on the descriptor.** The empty-input answer is a property of the operation, not of the type its result carries: `count`'s identity element is zero, an `every()`'s would be `true`, a `product()`'s would be one. A codec has no way to know which.
555
+
556
+ The value is read only where no result row reaches the client at all — an absent aggregate alias, or an include whose envelope never arrived. SQL answers an ordinary empty input set itself.
557
+
558
+ If your pack consumes a resolution rather than contributing one, `ResolvedSqlAggregate` (`@internal/sql-relational-core/query-lane-context`) follows the same union. Reading `resolved.nullable` narrows as it always did; constructing one spreads the nullability instead of assigning a boolean:
559
+
560
+ ```ts
561
+ const nullability = descriptor.nullable
562
+ ? ({ nullable: true } as const)
563
+ : ({ nullable: false, emptyResultJson: descriptor.emptyResultJson } as const);
564
+
565
+ return { operation, output, ...nullability, lower };
566
+ ```
567
+
568
+ ## `integer-codecs-check-the-js-type-they-are-given`
569
+
570
+ The integer codecs answer for the JS type before the range, so a wrong type reads as a wrong type:
571
+
572
+ ```text
573
+ RUNTIME.ENCODE_FAILED: pg/int8@1 value must be a bigint, got number 9
574
+ RUNTIME.ENCODE_FAILED: pg/int8number@1 value must be a number, got bigint 9
575
+ ```
576
+
577
+ `meta.received` names the type that arrived. `pg/int8@1`, `pg/unboundedint@1`, and `sqlite/bigint@1` read a `bigint`; `pg/int8number@1` and `sqlite/bigintnumber@1` read a `number`. The bigint codecs used to accept a number and stringify it, which meant `1.5` reached an integer column as valid decimal text.
578
+
579
+ **`encodeJson` is wider on the exact codecs, and only there.** It also accepts a safe-integer `number`, because a schema language writes no `bigint` literal — `BigInt @default(0)` arrives as the JSON number `0`. A non-integral or unsafe number is refused:
580
+
581
+ ```text
582
+ RUNTIME.ENCODE_FAILED: pg/int8@1 number literal must be an integer within
583
+ the safe integer range, got 9007199254740992
584
+ ```
585
+
586
+ Past that range the literal was already rounded before the codec saw it, so its digits no longer name the value that was written.
587
+
588
+ Two consequences for a pack:
589
+
590
+ - **Pick the method that matches the value you hold.** `encode` takes the application value and produces a wire value; `encodeJson` takes and returns canonical JSON. Handing canonical JSON to `encode` used to work for codecs whose two forms coincide and now fails loudly for the ones whose forms differ.
591
+ - **If your pack renders DDL literal defaults itself, read the stored value back first.** A contract stores a default in the codec's canonical JSON, so the pair is `decodeJson` then `encode`, in that order:
592
+
593
+ ```ts
594
+ const value = stored instanceof Date ? stored : codec.decodeJson(stored);
595
+ const wire = await codec.encode(value, {});
596
+ ```
597
+
598
+ A `Date` is the one authored value JSON has no notation for, so it is the one that arrives as itself.
599
+
600
+ <!--
601
+ PR #30049: `changes: []`. The 8.0.0-rc.2 release cut. The extension-package diff is the lockstep version bump, the `@prisma/cli-engine` peer moving to 0.1.1, and the supabase contract fixtures re-emitted so their embedded extension version matches. The engine peer is a devDependency in every extension package here, so it reaches no downstream extension author; the consumer-facing changes this release ships are the entries above, recorded by the PRs that made them.
602
+
603
+ PR #29920: `changes: []`. Adds prepared-statement test coverage to the Supabase runtime suite (test-fixture codec registration only) and fixes a postgres direct-driver transaction defect; neither requires downstream extension source translation. The SPI split itself is recorded as `driver-spi-splits-query-and-execute` in the 0.17-to-8.0.0-rc.1 transition.
604
+
605
+ PR #29902: `changes: []`. Generated contracts gain additive aggregate rows for new opt-in integer representation codecs, but existing extension schemas and source require no migration; extension authors re-emit only when adopting the new target-scoped types.
606
+ PR #29940: `changes: []`. Dependabot's weekly dev-dependency bumps, which move `@biomejs/biome` to 2.5.6 and realign every `biome.jsonc` `$schema` URL to match. The extension-package diff is that URL and nothing else, so it requires no downstream extension source translation.
607
+ PR #29965: `changes: []`. The same shape again: `@biomejs/biome` moves to 2.5.7 and every `biome.jsonc` `$schema` URL is realigned to match. The extension-package diff is that URL and nothing else, so it requires no downstream extension source translation.
608
+ -->
609
+
610
+ ## Why checks stopped being structured
611
+
612
+ A check is now one opaque SQL string that nothing parses. Postgres reprints predicates in its
613
+ own normalized form — a `varchar` membership test comes back as
614
+ `((col)::text = ANY ((ARRAY[…])::text[]))` — so any structured reading of a live predicate
615
+ drifts against the authored text. Equality for a wire-named check is name equality, because the
616
+ hash already commits to the predicate; only an exact-named check compares its body, and then
617
+ byte-for-byte.
618
+ ## If your target pack authors checks
619
+
620
+ Check emission is driven by a duck-typed `renderCheckExpressions` hook on the pack's
621
+ `authoring` contributions, resolved the same way `qualifyColumnType` is. It receives one
622
+ column's shape (`tableName`, `columnName`, `many`, and `memberValues` — the last present only
623
+ for a value set the toolchain owns) and returns `{ kind, columnName, expression }` candidates,
624
+ where `kind` is `'membership'` or `'elementNotNull'`. A pack without the hook emits no checks
625
+ at all, which is how SQLite keeps its no-CHECK stance. Nothing in the return value is a name:
626
+ the contract builder composes the prefix from the table, the column, and the kind, truncates it
627
+ to 54 UTF-8 bytes, and appends the content hash.
628
+ ## Hand-written checks are visible now
629
+
630
+ Postgres introspection reads `pg_get_expr(c.conbin, c.conrelid)` and stores the predicate
631
+ verbatim; it no longer recognises only the two shapes the old parser could parse. Every CHECK
632
+ constraint on a managed table therefore reaches the differ, and one the contract does not
633
+ declare is an ordinary undeclared extra: reported by `db verify --strict`, and dropped by a plan
634
+ whose control policy allows `destructive`.
635
+
636
+ For an extension this matters in one specific case — a check your extension installs through a
637
+ raw-SQL migration step rather than deriving in its contract space. That constraint used to be
638
+ invisible and is now drop-eligible against any database the extension manages. Declare it:
639
+ `@@check(expression: "…", map: "<its physical name>")` in the contract space adopts the
640
+ constraint under the name it already carries, after which it is owned rather than extra and no
641
+ plan drops it — see `authored-check-constraints` in this transition. Until you do, keep the
642
+ tables carrying it under an additive-only policy: the check survives, plain `db verify`
643
+ tolerates it, and only `--strict` reports it.
644
+
645
+ ## `runtime-query-execute-hard-cut`
646
+
647
+ The runtime SPI separates row streams from statement statistics. Inspect what each caller consumes rather than applying a global `execute` → `query` replacement: selects, returning writes, Mongo command-result plans, and other iterated or decoded results use `query`; non-returning DML uses eager `execute` and returns `{ affectedRows: number }`. Prepared row callers move from `target.queryPrepared(prepared, params, options?)` to `prepared.query(target, params, options?)`.
648
+
649
+ Connection, transaction, and role-bound scopes preserve their existing bound resource for both operations. Keep row results lazy and fully consume them inside a scope when required; statistics execution is eager. A count terminal must use `stats.affectedRows` from the write and never derive it from a row array.
650
+
651
+ Middleware hooks are operation-specific: query uses `beforeQuery`, `interceptQuery`, `onRow`, and `afterQuery`; statistics uses `beforeExecute`, `interceptExecute`, and `afterExecute`; `beforeCompile` remains shared. `interceptQuery` returns `{ rows }`, `interceptExecute` returns `{ stats }`, and hook selection carries the operation distinction, so contexts and results have no operation discriminator. Do not add compatibility aliases or generic fallback hooks. Split row and statistics fakes and spies so tests detect an incorrect route. Behavior intended for both operations assigns one private implementation to both corresponding hook names.
652
+
653
+ The Mongo facade keeps static `db.query`; it has no row-execution method named `query` and no compatibility `db.execute`. Build with `db.query`, obtain the connected runtime, and call `(await db.runtime()).query(plan)`. Leave genuine statistics calls, migration runners, and unrelated APIs named `execute` unchanged.
654
+
655
+ ## `config-file-is-prisma-config-with-an-orm-section`
656
+
657
+ Two mechanical steps, in either order:
658
+
659
+ 1. `git mv prisma-next.config.ts prisma.config.ts` (same for `.mts` / `.mjs` variants).
660
+ 2. Wrap the flat export in the engine shape:
661
+
662
+ ```ts
663
+ // before
664
+ import { defineConfig } from '@prisma/orm-postgres/config';
665
+ export default defineConfig({ ... });
666
+
667
+ // after
668
+ import { defineConfig } from '@prisma/cli-engine';
669
+ import { defineConfig as ormConfig } from '@prisma/orm-postgres/config';
670
+ export default defineConfig({ orm: ormConfig({ ... }) });
671
+ ```
672
+
673
+ Add `@prisma/cli-engine` to `devDependencies`. The inner config is unchanged — only the file
674
+ name and the outer wrapper move. The loader still discovers the deprecated filename and still
675
+ accepts the flat shape, each with a stderr deprecation warning, so nothing breaks mid-rename;
676
+ finish both steps to silence the warnings.
677
+
678
+ ## `published-prisma-next-bin-retired`
679
+
680
+ `prisma-next ...` in a package script resolved through a bin the database facades forwarded
681
+ from the toolchain. That chain is gone: the published toolchain is bin-less and exports the
682
+ `orm` command family at `@prisma/orm-toolchain/cli` for the unified `prisma` CLI (the
683
+ prisma-cli distribution) to mount. Point scripts and CI at the unified CLI, which serves the
684
+ same command paths.
685
+
686
+ ## `prepared-statements-split-by-declared-result`
687
+
688
+ The prepared surface answers what the plan declared. A rows plan still prepares into a
689
+ `PreparedStatement` you consume by querying it; a plan built from a statement that reports an
690
+ affected-row count prepares into a `PreparedExecution` you consume by executing it, which
691
+ resolves `SqlStatementStats` rather than streaming rows. The two handles share no consumption
692
+ method, so neither can be read the wrong way round.
693
+
694
+ Extensions meet this in two places.
695
+
696
+ **A facade that redeclares `prepare()`** — as the in-tree database facades do — swaps the
697
+ declared return type and nothing else:
698
+
699
+ ```ts
700
+ // Before
701
+ ): Promise<PreparedStatement<ParamsFromDeclaration<D, CT>, Row>> {
702
+ // After
703
+ ): Promise<PreparedFor<ParamsFromDeclaration<D, CT>, Row>> {
704
+ ```
705
+
706
+ `PreparedFor` comes from `@internal/sql-runtime` and resolves to whichever handle the plan
707
+ earns.
708
+
709
+ **A scope of your own** — a session, a role context, any object you hand to
710
+ `prepared.query(...)` — carries the prepared bridges. If it installs `preparedStatementQuery`,
711
+ it must now also install `preparedStatementExecute` beside it, routing to the same queryable:
712
+
713
+ ```ts
714
+ [preparedStatementExecute]<Params>(
715
+ prepared: PreparedExecution<Params>,
716
+ params: Params,
717
+ options?: RuntimeExecuteOptions,
718
+ ): Promise<SqlStatementStats> {
719
+ return runPreparedExecuteAgainstYourQueryable(prepared, params, yourQueryable, options);
720
+ }
721
+ ```
722
+
723
+ Both symbols come from `@internal/sql-runtime/internal/prepared-query`. A scope missing the
724
+ execute bridge typechecks but throws on the bridge invariant the first time someone executes a
725
+ prepared statement against it.
726
+
727
+ ## `prepare-defaults-to-the-contract-codec-map`
728
+
729
+ ```ts
730
+ // Before — the index signature on CodecTypesBase widens keyof CT to string
731
+ CT extends CodecTypesBase = ExtractCodecTypes<TContract> & CodecTypesBase,
732
+
733
+ // After
734
+ CT extends CodecTypesBase = ExtractCodecTypes<TContract>,
735
+ ```
736
+
737
+ Only the default changes. The constraint, `Declaration<CT>`, `ParamsFromDeclaration`, and the
738
+ returned prepared handle are all as they were, and what infers at existing call sites is
739
+ unchanged — this is about what the caller is offered and what the compiler refuses. With the
740
+ intersection in place, `prepare({ id: 'pg/int4' }, ...)` typechecks and then fails at
741
+ execution with `RUNTIME.PARAM_REF_MISSING_CODEC`; without it, the wrong id is a compile
742
+ error and the right ones complete.
743
+
744
+ The same narrowing reaches `.returns()` on a contract-bound raw tag through
745
+ `@internal/sql-builder`, so a facade that re-exposes that tag inherits it without doing
746
+ anything. Tags built for a contract-free lane are deliberately left open.