@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,731 @@
1
+ ---
2
+ from: "0.16"
3
+ to: "0.17"
4
+ changes:
5
+ - id: strip-sha256-hash-prefixes
6
+ summary: |
7
+ Content hashes are bare lowercase hex from 0.17 — the `sha256:` prefix is gone from every
8
+ surface (emitted `contract.json` / `contract.d.ts`, migration manifests, refs, CLI output,
9
+ and the database marker/ledger), and framework validators (`coreHash()` / `profileHash()`
10
+ constructors, manifest and contract loaders) reject the legacy prefixed form. Contract hash
11
+ VALUES are unchanged (only the prefix drops; re-emit your pack's committed contract
12
+ artefacts), but `migrationHash` VALUES change because the hashed manifest bytes embed the
13
+ now-bare `from`/`to` strings. Run the colocated codemod over your extension's checked-in
14
+ `migrations/` trees FIRST, before the snapshot-layout migrator in the entries below — the
15
+ 0.17 layout migrator accepts only bare-hex trees. The codemod handles both layouts: it
16
+ strips the prefix from every hash literal (manifests, `ops.json`, pre-store sibling
17
+ contract snapshots, store entries under `migrations/snapshots/`, `.d.ts` branded
18
+ literals), maps the empty-tree sentinel `sha256:empty` to `empty`, recomputes each
19
+ `migrationHash`, and repoints `refs/*.json`. Then drop the prefix from any hash literal
20
+ your pack's source, fixtures, or tests hard-code — a prefixed literal now fails validation
21
+ instead of round-tripping. Signed databases your extension maintains (acceptance
22
+ harnesses, reference instances) whose marker/ledger still hold prefixed values report a
23
+ hash mismatch on verify — there is no compatibility shim; re-sign against the regenerated
24
+ contract (`prisma-next db sign`).
25
+ detection:
26
+ glob: "**/*.{json,ts,mts,cts,tsx}"
27
+ contains:
28
+ - 'sha256:'
29
+ anyMatch: true
30
+ script: ./strip-sha256-hash-prefixes.ts
31
+ - id: migration-contract-snapshots-moved-to-content-addressed-store
32
+ summary: |
33
+ Committed migration contract snapshots move from per-package sibling files
34
+ (`start-contract.json` / `start-contract.d.ts` / `end-contract.json` /
35
+ `end-contract.d.ts`) into a single content-addressed store per migrations
36
+ root, at `migrations/snapshots/<hex>/contract.json` + `contract.d.ts`,
37
+ where `<hex>` is the contract's 64-hex storage hash (bare hex after the
38
+ `strip-sha256-hash-prefixes` entry above, which must run first).
39
+ Every distinct contract is stored once, however many migrations
40
+ reference it. An extension source repo keeps the shallow layout — migration
41
+ packages sit directly under `migrations/` (no `app/` segment), so its store
42
+ is `migrations/snapshots/` at the same depth, and every emitted
43
+ `migration.ts` imports its bookend contracts one level up:
44
+ `../snapshots/<hex>/contract.json` / `../snapshots/<hex>/contract.d.ts`
45
+ (a consuming project's `app/`-nested migrations import two levels up,
46
+ `../../snapshots/...` — do not copy that depth into an extension repo).
47
+ This is a clean break: there is no fallback reader for the old sibling-file
48
+ layout, so a committed migrations tree that has not been converted fails to
49
+ load once you upgrade your extension's tooling — `migration plan` /
50
+ `migration new` / `migration check` all read contract snapshots through the
51
+ store only, and a missing store entry fails with
52
+ `MIGRATION.CONTRACT_SNAPSHOT_MISSING` naming the expected hash and path.
53
+ `migration.json` / `ops.json` / `migrationHash` are unaffected — the
54
+ contract snapshot was never part of migration identity, so converting the
55
+ layout changes no migration's hash, and no extension-authoring SPI changes.
56
+ To convert an existing extension repo, run the migrator once from a
57
+ checkout of the `prisma/prisma` repository at (or above) the version
58
+ you're upgrading to, pointed at your extension's migrations root:
59
+ `node scripts/migrate-migrations-layout.mjs <path-to-your-migrations-dir>`.
60
+ Per migration package, it reads `migration.json`, write-if-absents the
61
+ destination contract (and the source contract, when present) into the
62
+ store under the matching hash, rewrites the committed `migration.ts`
63
+ import specifiers, and deletes the four sibling files. It asserts every
64
+ contract's inner `storage.storageHash` against the hash it's filed under
65
+ before writing anything (mismatch aborts the whole run, nothing is
66
+ deleted), and re-verifies every `migrationHash` is unchanged after
67
+ conversion. Run it, review the diff, then typecheck your extension package
68
+ to confirm every rewritten `migration.ts` import resolves.
69
+ detection:
70
+ glob: "**/migration.ts"
71
+ contains:
72
+ - "./start-contract.json"
73
+ - "./end-contract.json"
74
+ - "./start-contract'"
75
+ - "./end-contract'"
76
+ anyMatch: true
77
+ - id: ref-paired-snapshots-moved-to-content-addressed-store
78
+ summary: |
79
+ Ref-paired contract snapshot files (`refs/<name>.contract.json` /
80
+ `refs/<name>.contract.d.ts`) are no longer written or read. A ref is now
81
+ only its pointer file, `refs/<name>.json` (`{ hash, invariants }`); the
82
+ contract it names resolves through the same content-addressed store as
83
+ every migration graph node, `migrations/snapshots/<hex>/contract.json` +
84
+ `contract.d.ts`, by that hash. Your extension repo's `migrations/refs/`
85
+ normally carries only the system `head.json` pointer, which was never
86
+ ref-paired — this only matters if your repo also carries named refs
87
+ (e.g. from testing `ref set` against the extension's own migrations
88
+ root). A pointer whose store entry is missing now fails with
89
+ `MIGRATION.CONTRACT_SNAPSHOT_MISSING` naming the expected hash and path,
90
+ rather than silently falling back to the migration graph. The same
91
+ one-shot migrator that folds per-package sibling snapshots (see the
92
+ entry above) also folds any existing `refs/<name>.contract.json` /
93
+ `refs/<name>.contract.d.ts` pairs: it write-if-absents the pair into the
94
+ store under the sibling pointer's `hash`, then deletes the pair — the
95
+ pointer file itself is read but never written, so it stays
96
+ byte-identical. A `.contract.json` with no sibling pointer, or whose
97
+ inner `storage.storageHash` disagrees with the pointer's `hash`, aborts
98
+ the whole run before anything is written or deleted. Run `node
99
+ scripts/migrate-migrations-layout.mjs <path-to-your-migrations-dir>`
100
+ (same invocation as above; one run folds both migration-package and
101
+ ref-paired snapshots), then review the diff.
102
+ detection:
103
+ glob: "**/refs/*.contract.json"
104
+ anyMatch: true
105
+ - id: extension-packs-key-renamed-to-extensions
106
+ summary: |
107
+ The `extensionPacks` key is renamed to `extensions` across the config
108
+ surface, the SPI, and the contract document. In your extension repo:
109
+ (1) any `prisma.config.ts` (the extension's own contract space, a
110
+ sibling example app, tests) renames `extensionPacks:` to `extensions:` —
111
+ the old key fails loudly with "Config.extensionPacks is no longer
112
+ supported; rename it to Config.extensions"; (2) the provider-API field
113
+ `ContractSourceContext.composedExtensionPacks` is now
114
+ `composedExtensions`; (3) the emitted `contract.json` / `contract.d.ts`
115
+ top-level key is `extensions`, and because the key sits in the
116
+ canonicalized bytes, every contract's `storageHash` / `executionHash` /
117
+ `profileHash` changes. Re-run your contract-space build
118
+ (`build:contract-space` or `prisma-next contract emit`), re-anchor
119
+ `migrations/refs/head.json` and the `migrations/snapshots/<hex>/` store
120
+ to the new hashes, and re-emit `ops.json` / `migration.json` for the
121
+ head migration (its `to` hash changes). Concept-level SPI type names
122
+ (`ExtensionPackRef`, `ControlExtensionDescriptor`,
123
+ `validateExtensionPackRefs`) are unchanged. Also renamed in the same
124
+ release: `contract.source.sourceFormat` → `format`, and the target
125
+ façades' `defineConfig` option `outputPath` → `output`.
126
+ detection:
127
+ glob: "**/*.{ts,json}"
128
+ contains:
129
+ - "extensionPacks"
130
+ - "composedExtensionPacks"
131
+ anyMatch: true
132
+ - id: orm-count-only-mutation-terminals-renamed
133
+ summary: Replace `createCount(...)`, `updateCount(...)`, and `deleteCount()` with `createAndCount(...)`, `updateAndCount(...)`, and `deleteAndCount()` in ORM call sites; arguments, guards, behavior, and `Promise<number>` results are unchanged, and no compatibility aliases remain.
134
+ detection:
135
+ glob: "**/*.{ts,tsx,mts,cts}"
136
+ contains:
137
+ - ".createCount("
138
+ - ".updateCount("
139
+ - ".deleteCount("
140
+ anyMatch: true
141
+ - id: adopt-sql-json-projection-ast-foundations
142
+ summary: Migrate relational AST construction and traversal to explicit JSON projection wrappers, expanded scalar-expression variants, grouped function-source aliases, and codec-preserving forwarded projections.
143
+ detection:
144
+ glob: "**/*.{ts,tsx}"
145
+ contains:
146
+ - "JsonObjectExpr"
147
+ - "JsonArrayAggExpr"
148
+ - "ExprVisitor"
149
+ - "AnyExpression"
150
+ - "FunctionSource.of"
151
+ - "ProjectionItem.of"
152
+ anyMatch: true
153
+ - id: scalar-type-descriptors-channel-removed
154
+ summary: |
155
+ `ComponentMetadata.scalarTypeDescriptors` is retired — the unified authoring type namespace
156
+ is now the single channel for scalar types. If your extension/adapter descriptor declared
157
+ `scalarTypeDescriptors: new Map([['String', 'pg/text@1'], ...])`, move each entry to a
158
+ zero-arg type-constructor contribution in the descriptor's `authoring.type` namespace:
159
+ `String: { kind: 'typeConstructor', output: { codecId: 'pg/text@1', nativeType: 'text' } }`.
160
+ The `nativeType` is now explicit — it was previously derived from the codec's first target
161
+ type, so check the codec manifest for the value to inline. Code that read
162
+ `ControlStack.scalarTypeDescriptors` / `ContractSourceContext.scalarTypeDescriptors` should
163
+ read `stack.scalarTypes` (the scalar type names) or derive the name ->
164
+ `{ codecId, nativeType }` map via `collectScalarTypeConstructors(stack.authoringContributions.type)`
165
+ from `@internal/framework-components/authoring`. `assembleScalarTypeDescriptors` is
166
+ deleted, and `validateScalarTypeCodecIds` now takes the authoring type namespace instead of
167
+ a descriptor map.
168
+ detection:
169
+ glob: "**/*.{ts,mts,cts}"
170
+ contains:
171
+ - "scalarTypeDescriptors"
172
+ - "assembleScalarTypeDescriptors"
173
+ anyMatch: true
174
+ - id: postgres-native-types-move-to-type-position
175
+ summary: |
176
+ PostgreSQL native storage types are authored directly in PSL type position, and the legacy `@db.*` attribute channel is removed. Rewrite `BaseType @db.Type` as `Type` and `BaseType @db.Type(args)` as `Type(args)` in extension schemas and test fixtures, then re-run contract emission. Any remaining `@db.X(args)` fails with `@db.X(args) is no longer supported; use X(args) in type position`, preserving the constructor name and arguments in the suggested replacement. The supported translations are `@db.Char` → `Char`, `@db.VarChar` → `VarChar`, `@db.Numeric` → `Numeric`, `@db.Uuid` → `Uuid`, `@db.Inet` → `Inet`, `@db.SmallInt` → `SmallInt`, `@db.Real` → `Real`, `@db.Timestamp` → `Timestamp`, `@db.Timestamptz` → `Timestamptz`, `@db.Date` → `Date`, `@db.Time` → `Time`, and `@db.Timetz` → `Timetz`; preserve constructor arguments. Rewrite the old native-json spelling `Json @db.Json` as bare `Json`. This source migration preserves native types and supplied type parameters. It also preserves codec ids except for `@db.Date` → `Date`, which rebinds `pg/timestamptz@1` to `pg/date@1`, changes the contract storage hash, and requires re-emission plus re-signing; see the `postgres-date-rebound-to-pg-date` entry below. Separately, apply the `postgres-json-rebound-to-native-json` entry below to old bare `Json` fields that meant jsonb storage.
177
+ detection:
178
+ glob: "**/*.prisma"
179
+ contains:
180
+ - "@db."
181
+ anyMatch: true
182
+ - id: postgres-json-rebound-to-native-json
183
+ summary: |
184
+ On the postgres target the PSL `Json` scalar re-binds from `pg/jsonb@1` / `jsonb` to
185
+ `pg/json@1` / `json`; a new bare `Jsonb` scalar carries `pg/jsonb@1` / `jsonb`
186
+ (`postgresScalarAuthoringTypes` in `@internal/adapter-postgres`). Extension test
187
+ schemas and fixtures that author postgres `Json` fields and mean jsonb storage must
188
+ switch those fields to `Jsonb`; assertions that pin the `Json` name's derived binding
189
+ (e.g. over `collectScalarTypeConstructors(stack.authoringContributions.type)` or
190
+ `stack.scalarTypes`) now expect `Json -> { codecId: 'pg/json@1', nativeType: 'json' }`
191
+ plus the new `Jsonb -> { codecId: 'pg/jsonb@1', nativeType: 'jsonb' }` entry. PSL
192
+ value-object storage columns still emit jsonb (the interpreter now prefers the target's `Jsonb` scalar and falls back to `Json`). The removed `@db.Json` spelling must be rewritten from `Json @db.Json` to bare `Json`; any remaining use fails with migration guidance to use `Json` in type position. SQLite and Mongo `Json` bindings and the TS builder surface (`field.json()`, `jsonbColumn`) are unchanged.
193
+ detection:
194
+ glob: "**/*.{prisma,ts,mts,cts}"
195
+ contains:
196
+ - "Json"
197
+ anyMatch: true
198
+ - id: default-generators-no-longer-set-storage
199
+ summary: |
200
+ `@default(<generator>)` never mutates a column's storage any more — the type position is
201
+ the only storage decider — and the whole generator-storage-override SPI is retired with
202
+ it. Removed surfaces: `MutationDefaultGeneratorDescriptor.resolveGeneratedColumnDescriptor`
203
+ (`@internal/framework-components/control`) — generator descriptors are now
204
+ `{ id, applicableCodecIds?, buildPhases? }` only, and `applicableCodecIds` remains the
205
+ validation channel (`PSL_INVALID_DEFAULT_APPLICABILITY` on mismatch); the transitional
206
+ `baseScalar` marker on `AuthoringTypeConstructorDescriptor` and
207
+ `ScalarTypeConstructorOutput` (`@internal/framework-components/authoring`) — scalar
208
+ type-constructor contributions and the derived scalar view are plain
209
+ `{ codecId, nativeType, typeParams? }` again; and the `@internal/ids` exports
210
+ `resolveBuiltinGeneratedColumnDescriptor` / `GeneratedColumnDescriptor` (the TS spec
211
+ helpers `uuidv4()`, `nanoid()`, … still return `GeneratedColumnSpec` bundling their
212
+ explicit `sql/char@1` column). Packs that registered a generator descriptor with a
213
+ storage-resolution hook must drop the hook; PSL schemas in extension fixtures relying on
214
+ `String @default(uuid()/cuid()/nanoid()/ulid())` producing `character(N)` columns must
215
+ either accept the target String storage (postgres: `pg/text@1` / `text`) or author the
216
+ char storage explicitly in the type position (`Char(36) @default(uuid())`, …), then
217
+ re-emit.
218
+ detection:
219
+ glob: "**/*.{ts,mts,cts,prisma}"
220
+ contains:
221
+ - "resolveGeneratedColumnDescriptor"
222
+ - "resolveBuiltinGeneratedColumnDescriptor"
223
+ - "baseScalar"
224
+ - "@default(uuid("
225
+ - "@default(cuid("
226
+ - "@default(nanoid("
227
+ - "@default(ulid("
228
+ anyMatch: true
229
+ - id: postgres-date-rebound-to-pg-date
230
+ summary: |
231
+ On the postgres target, the bare `Date` type constructor (`postgresNativeAuthoringTypes` in `@internal/adapter-postgres`) re-binds from `pg/timestamptz@1` to the dedicated `pg/date@1` codec. Rewrite the removed `DateTime @db.Date` spelling as `Date`; leaving it unchanged now fails with migration guidance to use `Date` in type position. The stored native type is unchanged (`date`). Extension assertions over `collectScalarTypeConstructors(stack.authoringContributions.type)` now expect `Date -> { codecId: 'pg/date@1', nativeType: 'date' }`. Extension test schemas and fixtures with date columns produce a different codec ref and contract storage hash on re-emit; regenerate committed contract artefacts and update pinned hash or codec-ref literals. Runtime fixtures change shape too:
232
+ `pg/date@1` canonicalizes the JS value as a `Date` at UTC midnight
233
+ (`new Date(Date.UTC(y, m, d))`) instead of passing through the driver's local-midnight
234
+ `Date`, and its JSON form is the bare `YYYY-MM-DD` string, so relation `.include()`
235
+ decode over date columns now succeeds instead of failing with `RUNTIME.DECODE_FAILED`.
236
+ Contracts emitted before the upgrade keep working (`pg/timestamptz@1` still exists).
237
+ detection:
238
+ glob: "**/*.{prisma,ts,mts,cts}"
239
+ regex:
240
+ - '@db\.Date'
241
+ - '\sDate(\s|\?|\[|$)'
242
+ anyMatch: true
243
+ - id: sql-escape-error-class-removed
244
+ summary: |
245
+ The `SqlEscapeError` class is deleted from `@internal/target-postgres` and
246
+ `@internal/target-sqlite` (including its re-export from the postgres/sqlite
247
+ adapter `control` entrypoints). Identifier/literal escaping failures now throw a
248
+ structured envelope with code `CONTRACT.IDENTIFIER_INVALID`. Replace
249
+ `error instanceof SqlEscapeError` with
250
+ `isStructuredError(error) && error.code === 'CONTRACT.IDENTIFIER_INVALID'`
251
+ (`isStructuredError` from `@internal/utils/structured-error`). Message text is
252
+ unchanged.
253
+ detection:
254
+ glob: "**/*.{ts,mts,cts}"
255
+ contains:
256
+ - "SqlEscapeError"
257
+ - id: supabase-error-classes-removed
258
+ summary: |
259
+ The `SupabaseConfigError` and `InvalidJwtError` classes are deleted from
260
+ `@internal/extension-supabase/runtime`. The same failures now throw
261
+ structured envelopes with codes `SUPABASE.CONFIG_INVALID` and
262
+ `SUPABASE.JWT_INVALID`. Replace `error instanceof SupabaseConfigError` with
263
+ `isStructuredError(error) && error.code === 'SUPABASE.CONFIG_INVALID'` and
264
+ `error instanceof InvalidJwtError` with
265
+ `isStructuredError(error) && error.code === 'SUPABASE.JWT_INVALID'`
266
+ (`isStructuredError` from `@internal/utils/structured-error`). Message
267
+ text is unchanged.
268
+ detection:
269
+ glob: "**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}"
270
+ contains:
271
+ - "SupabaseConfigError"
272
+ - "InvalidJwtError"
273
+ anyMatch: true
274
+
275
+ - id: rls-wire-name-helpers-moved-to-sql-schema-ir-naming
276
+ summary: |
277
+ The RLS wire-name helpers moved out of the target-postgres RLS surface into the
278
+ family-shared naming module, with generalized names — the wire-name scheme now serves
279
+ indexes as well as policies. Deleted from
280
+ `@internal/target-postgres/rls-canonicalize`: `formatRlsPolicyWireName`,
281
+ `parseRlsPolicyWireName`, `normalizePredicate`, and the `RlsPolicyWireName` type. Import
282
+ the replacements from `@internal/sql-schema-ir/naming` instead: `formatWireName`,
283
+ `parseWireName`, `normalizeSqlBody`, and `WireName`. Behavior is byte-identical (same
284
+ `<prefix>_<8hex>` format, same all-prefix-on-no-parse contract, same trim +
285
+ whitespace-collapse normalizer). `@internal/target-postgres/rls-canonicalize` still
286
+ exports the RLS-specific surface: `computeContentHash`, `ContentHashParts`,
287
+ `POLICY_OPERATION_PREDICATES`, `RlsPolicyOperation`. The naming module also gains
288
+ `computeIndexContentHash`, `WIRE_NAME_PREFIX_MAX_LENGTH`, and
289
+ `assertWireNamePrefixLength` for packs that construct index wire names.
290
+ detection:
291
+ glob: "**/*.{ts,mts,cts}"
292
+ contains:
293
+ - "formatRlsPolicyWireName"
294
+ - "parseRlsPolicyWireName"
295
+ - "normalizePredicate"
296
+ - "RlsPolicyWireName"
297
+ anyMatch: true
298
+ - id: rls-policy-migration-literal-carries-the-naming-union
299
+ summary: |
300
+ The Postgres RLS policy migration literal spells its name as one union field from 0.17.
301
+ `PostgresRlsPolicyMigrationInput` is deleted from `@internal/target-postgres/types`;
302
+ the parameter `Migration#createRlsPolicy` accepts and the renderer writes is
303
+ `RenderedRlsPolicyLiteral`, which is `PostgresRlsPolicyInput` with absent-valued keys
304
+ omittable — so the flat `name` / `prefix` pair becomes `naming: { kind: "exact", name }`
305
+ or `naming: { kind: "wire", prefix, hash }`. The contract-JSON shape is unchanged and
306
+ keeps its own type, `SerializedRlsPolicy`, hydrated by `policyInputFromSerialized`
307
+ (0.16's `rlsPolicyInputFromFlat`). Pack code constructing `PostgresRlsPolicy` directly
308
+ already passed the `naming` union and is unaffected; pack code that built the flat
309
+ migration literal, or that imported `PostgresRlsPolicyMigrationInput`, must switch to
310
+ `RenderedRlsPolicyLiteral` and the union field. Regenerate any committed migration your
311
+ pack ships that calls `createRlsPolicy` — a 0.16 file fails to compile with
312
+ `Property 'naming' is missing`.
313
+ detection:
314
+ glob: "**/*.{ts,mts,cts}"
315
+ contains:
316
+ - "PostgresRlsPolicyMigrationInput"
317
+ - "rlsPolicyInputFromFlat"
318
+ - "createRlsPolicy"
319
+ anyMatch: true
320
+ - id: sql-index-entities-are-name-identified
321
+ summary: |
322
+ SQL index entities are name-identified from 0.17, at both layers an extension touches.
323
+ Contract IR (`Index` from `@internal/sql-contract/types`): `name` (full physical
324
+ name) and `unique` are required, `prefix` marks a wire name (`name` must parse
325
+ back to `prefix` + 8-hex suffix), and `columns` became optional — exactly one of
326
+ `columns` / `expression` must be set; the constructor and `IndexSchema` validation
327
+ throw on the old shape, so any pack code or test fixture building `indexes: [{ columns:
328
+ [...] }]` must add a real `name` and `unique`. The `index(...)` factory from
329
+ `@internal/sql-contract/factories` is now `index(name, columns, opts?)`. Schema IR
330
+ (`SqlIndexIR` from `@internal/sql-schema-ir/types`): the input requires `name` plus
331
+ explicit `prefix` / `expression` / `where` keys, the diff-tree id is `index:<name>`
332
+ (tuple-derived ids are gone — assertions on `index:<col,col>` ids must switch to the
333
+ name), and `isEqualTo` is mode-selected: both modes compare `unique`/`type` strict,
334
+ `options` loose, `columns` ordered-strict when both sides carry them; an exact-named
335
+ node (no `prefix`) additionally byte-compares `expression`/`where`; a wire-named node
336
+ never compares bodies. Construction sites must supply the real physical name — never a
337
+ placeholder. Re-emit your pack's committed contract space (`build:contract-space` /
338
+ `contract:generate`); storage hashes move for every contract that declares indexes, and
339
+ wire-named index physical names gain the `_<8hex>` content-hash suffix (see the user-skill
340
+ `indexes-are-name-identified` entry for the database-convergence flow — the first
341
+ widening plan is renames-only).
342
+ detection:
343
+ glob: "**/*.{ts,mts,cts}"
344
+ contains:
345
+ - "SqlIndexIR"
346
+ - "indexes: ["
347
+ - "indexes:["
348
+ anyMatch: true
349
+ - id: framework-error-classes-removed
350
+ summary: |
351
+ Three exported framework error classes are deleted: `ConfigFileNotFoundError`
352
+ (from `@internal/config-loader`), `ConfigValidationError` (from
353
+ `@internal/config/config-validation`), and `DomainNamespaceResolutionError`
354
+ (from `@internal/contract/types`). The same failures now throw structured
355
+ envelopes with codes `CONFIG.FILE_NOT_FOUND`, `CONFIG.VALIDATION_FAILED`, and
356
+ `CONTRACT.NAMESPACE_INVALID` respectively. Replace each
357
+ `error instanceof <Class>` with
358
+ `isStructuredError(error) && error.code === '<CODE>'`
359
+ (`isStructuredError` from `@internal/utils/structured-error`). Message
360
+ text is unchanged.
361
+ detection:
362
+ glob: "**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}"
363
+ contains:
364
+ - "ConfigFileNotFoundError"
365
+ - "ConfigValidationError"
366
+ - "DomainNamespaceResolutionError"
367
+ anyMatch: true
368
+ - id: postgres-extension-codecs-require-target-descriptors
369
+ summary: Migrate PostgreSQL-bound extension codecs to the target-owned descriptor protocol and contribute one target-typed descriptor set through runtime and control stacks.
370
+ detection:
371
+ glob: "**/*.{ts,tsx}"
372
+ contains:
373
+ - "extends CodecDescriptorImpl"
374
+ - "CodecDescriptorImpl<"
375
+ - "readonly AnyCodecDescriptor[]"
376
+ anyMatch: true
377
+ - id: rls-policies-gain-exact-names
378
+ summary: |
379
+ RLS policies adopt the same wire/exact identity split as indexes. On
380
+ `PostgresRlsPolicyInput` / `PostgresPolicySchemaNodeInput`, `prefix`, `using`,
381
+ `withCheck` (and the node input's `dependsOn`) change from optional keys to REQUIRED
382
+ keys typed `| undefined` — a hard compile break for every existing construction site
383
+ that omitted them: `new PostgresRlsPolicy({ …, using })` without `withCheck` no longer
384
+ compiles. Fix: state the absent keys explicitly (`withCheck: undefined`,
385
+ `prefix: undefined`, `dependsOn: undefined`). In the serialized policy contract schema
386
+ `prefix` stays optional — its presence means wire-named, absence means
387
+ exact-named (a verbatim adopted physical name). Both constructors now ENFORCE that a
388
+ declared
389
+ `prefix` matches the wire name's parsed prefix: pack code or test fixtures building a
390
+ `PostgresRlsPolicy` / `PostgresPolicySchemaNode` whose `name` is not
391
+ `<prefix>_<8hex>`-shaped while still passing a `prefix` (e.g. `prefix: name` for a
392
+ hand-written legacy name) now throw — omit `prefix` for such names; that is the
393
+ exact-named spelling. Exact-named policy nodes compare by content (`operation` /
394
+ `permissive` strict, `roles` as a deduplicated sorted set, `using` / `withCheck`
395
+ byte-for-byte), so a
396
+ body-drifted same-named exact policy now surfaces as a `not-equal` verify issue and a
397
+ drop + create plan instead of being invisible. PSL policy blocks newly accept
398
+ `@@map("physical name")` to author the exact mode (additive — wire lowering is
399
+ byte-unchanged).
400
+ detection:
401
+ glob: "**/*.{ts,mts,cts,prisma}"
402
+ contains:
403
+ - "PostgresRlsPolicy"
404
+ - "PostgresPolicySchemaNode"
405
+ - "policy_select"
406
+ - "policy_insert"
407
+ - "policy_update"
408
+ - "policy_delete"
409
+ - "policy_all"
410
+ - id: codec-json-projections-must-agree-with-encode-json
411
+ summary: |
412
+ A target codec descriptor's `jsonProjection` hook is now live rather than a placeholder:
413
+ it states, as SQL, the canonical JSON of the codec it belongs to, and the two sides are
414
+ required to agree — the parsed projection must equal `encodeJson(value)`, and
415
+ `decodeJson` of that must return the application value. If your extension contributes a
416
+ PostgreSQL or SQLite codec descriptor whose `jsonProjection` still returns its argument
417
+ unchanged, check that the target's own JSON conversion really is your canonical form; if
418
+ it is not, the projection has to replace that conversion rather than post-process it.
419
+ Where a cast is needed it belongs *inside* the projected expression, so it applies before
420
+ the JSON constructor sees the value — a cast applied to the constructor's result is too
421
+ late, because the value has already been converted. `encodeJson` / `decodeJson` move with
422
+ the projection, in the same change.
423
+ detection:
424
+ glob: "**/*.{ts,tsx}"
425
+ contains:
426
+ - "jsonProjection"
427
+ - "PostgresCodecDescriptor"
428
+ - "SqliteCodecDescriptor"
429
+ anyMatch: true
430
+ - id: pgvector-json-form-is-a-numeric-array
431
+ summary: |
432
+ `pg/vector@1` encodes to and decodes from a JSON numeric array — `[1,2,3]` — where it used
433
+ the string `"[1,2,3]"`. `decodeJson` rejects the string form, because reading a vector's
434
+ text back at double width lands on a different number: a vector's elements are `real`, and
435
+ its text form prints the shortest decimal that round-trips *as a real*. If your extension
436
+ consumes `pg/vector@1` values out of a contract, or models a codec on it, switch to the
437
+ array form. Note the element type: an application value must be exactly representable as a
438
+ 32-bit float to round-trip at all.
439
+ detection:
440
+ glob: "**/*.{ts,tsx,json}"
441
+ contains:
442
+ - "pg/vector@1"
443
+ anyMatch: true
444
+ - id: default-literal-value-resolves-through-the-codec-json-channel
445
+ summary: |
446
+ `ExtractCodecTypes` gains a `json` channel beside `input` and `output`, read off a codec's
447
+ declared `encodeJson` return type, and the emitted `DefaultLiteralValue` helper resolves a
448
+ contract's literal default through that channel rather than through the codec's application
449
+ type. A literal default lives in `contract.json`, so it is typed by what the file holds and
450
+ not by what the application receives — the two diverge for any codec whose canonical JSON
451
+ differs from its application value. Re-run your contract-space build
452
+ (`pnpm build:contract-space`, or `prisma-next contract emit`) to regenerate every
453
+ `contract.d.ts`; the alias definition changes in each. A codec that narrows its
454
+ `encodeJson` return type publishes its JSON type through the new channel; one that does not
455
+ keeps the `JsonValue` the base signature promises.
456
+ detection:
457
+ glob: "**/*.{ts,d.ts}"
458
+ contains:
459
+ - "DefaultLiteralValue"
460
+ - "ExtractCodecTypes"
461
+ anyMatch: true
462
+ - id: pg-int8-application-values-are-bigint
463
+ summary: |
464
+ `pg/int8@1` carries `bigint` application values where it carried `number`, and its JSON
465
+ form is decimal text. Extension code that reads an `int8` column, resolves an aggregate to
466
+ that codec, or hand-writes an `int8` literal default must move to `bigint` and to the
467
+ decimal-string JSON spelling. `parsePostgresDefault` changes with it: an introspected
468
+ `int8` default now reads as decimal text across the whole signed 64-bit range, where it
469
+ previously returned a `number` for a safe integer and a string only past 2^53 — if your
470
+ extension compares introspected defaults against contract defaults, that split is gone.
471
+ detection:
472
+ glob: "**/*.{ts,tsx}"
473
+ contains:
474
+ - "pg/int8@1"
475
+ - "parsePostgresDefault"
476
+ anyMatch: true
477
+ - id: extension-column-codecs-must-be-declared-as-target-descriptors
478
+ summary: |
479
+ An extension that contributes a codec used on a *column* must publish its target descriptors
480
+ through `types.codecTypes.codecDescriptors` on the runtime (and control) extension
481
+ descriptor. The renderers resolve a column's JSON projection out of the assembled descriptor
482
+ registry, which is built from that field alone — `codecs()` feeds a different path. A codec
483
+ reachable through `codecs()` but absent from `types.codecTypes.codecDescriptors` now fails at
484
+ lowering with `RUNTIME.PARAM_REF_MISSING_CODEC` naming the codec id, where the renderer
485
+ previously ignored codec identity and emitted the bare column. The failure is deliberate:
486
+ emitting the bare column would produce exactly the uncanonical JSON the projection exists to
487
+ replace. Publish the same target-typed set in both places.
488
+ detection:
489
+ glob: "**/*.{ts,tsx}"
490
+ contains:
491
+ - "codecTypes"
492
+ - "codecs: () =>"
493
+ - "SqlRuntimeExtensionDescriptor"
494
+ anyMatch: true
495
+ - id: contract-infer-emits-full-fidelity
496
+ summary: |
497
+ `contract infer` emits every non-constraint index (expression, partial `where:`,
498
+ unique, `type:`/`options:`) and the RLS surface (`@@rls` natively, every policy as a
499
+ `policy_<operation>` block with `@@map` and verbatim reprinted bodies, `permissive =
500
+ false` for RESTRICTIVE rows). A pack whose contract-space generator runs infer (the
501
+ supabase pattern) sees ADDITIVE movement on its next regeneration: previously omitted
502
+ partial/expression/unique indexes adopt as exact `map:` entries, and `@@rls` no longer
503
+ needs an out-of-band appender — delete any `applyRlsEnablement`-style post-processing
504
+ and regenerate through the checked-in generator; re-emit moves the storage hash. The
505
+ duplicate-index validation keys exact-mode entries by name, so a reference database's
506
+ content-identical twin indexes now validate. An index whose live name is wire-shaped
507
+ and whose hash recomputes against the introspected content re-infers as wire-named
508
+ `name:` — a heuristic: a name that is not wire-shaped, or whose hash does not
509
+ recompute, adopts as exact `map:`.
510
+ detection:
511
+ glob: "**/*.{ts,mts,cts,prisma}"
512
+ contains:
513
+ - "inferPslContract"
514
+ - "contract infer"
515
+ - "@@rls"
516
+ anyMatch: true
517
+ - id: postgres-packages-now-ship-types-pg
518
+ summary: |
519
+ `@internal/postgres`, `@internal/extension-supabase`, and
520
+ `@internal/driver-postgres` re-export `pg` types from their published
521
+ declarations, so each now carries `@types/pg` in `dependencies` instead of
522
+ `devDependencies`. Extension authors previously had to add `@types/pg` to their own
523
+ devDependencies to compile against those declarations — that workaround is now the
524
+ hazard. `pg` ships no types of its own, so a second `@types/pg` copy at a different
525
+ version gives `pg.Client` / `pg.Pool` two identities, and handing your own client or
526
+ pool to a Prisma Next API stops compiling with `Argument of type 'Client' is not
527
+ assignable to parameter of type 'Client'` (`Type 'Client' is missing the following
528
+ properties from type 'Client': connection, setTypeParser, getTypeParser`). Drop
529
+ `@types/pg` from your extension and take it transitively, or pin it to the version
530
+ `@internal/postgres` depends on.
531
+ detection:
532
+ glob: "**/package.json"
533
+ contains:
534
+ - "@types/pg"
535
+ anyMatch: true
536
+ - id: build-against-published-packages-not-workspace-names
537
+ summary: |
538
+ The `@internal/*` packages are gone from the registry. Until 0.17 every
539
+ workspace package published, so a pack could depend on `@internal/contract`,
540
+ `@internal/sql-contract`, `@internal/framework-components` and the rest
541
+ directly — and many do. From 0.17 the published surface is 17 `@prisma/*`
542
+ packages and everything else carries `"private": true` in its manifest, so
543
+ those dependencies no longer resolve anywhere: a stale name fails at install
544
+ time rather than resolving to an outdated artifact left on the registry.
545
+ Build against the platform packages instead: `@prisma/orm-framework` for contract,
546
+ components, errors and the PSL tooling; `@prisma/orm-family-sql` or
547
+ `@prisma/orm-family-mongo` for the family surfaces; `@prisma/orm-target-<db>` for the
548
+ target, adapter and driver your pack extends; `@prisma/orm-toolchain` for the emitter and
549
+ migration tooling. Each internal package became a subpath entrypoint of exactly one of
550
+ those, so the module you imported still exists under a new name — the mapping is one hop
551
+ and the symbols are unchanged. Declare the target shell your pack extends as a peer
552
+ dependency rather than a dependency, so an application cannot end up with two copies of
553
+ the target it is registered against.
554
+ detection:
555
+ glob: "**/package.json"
556
+ contains:
557
+ - '"@internal/'
558
+ anyMatch: true
559
+ ---
560
+
561
+ # 0.16 → 0.17 — Extension-author upgrade instructions
562
+
563
+ ## `strip-sha256-hash-prefixes`
564
+
565
+ Starting at the 0.17 release, every content hash the framework mints or accepts is bare lowercase hex — the `sha256:` prefix is removed across the board: emitted `contract.json` / `contract.d.ts` (including `StorageHashBase<'…'>` / `ProfileHashBase<'…'>` branded type literals), migration manifests, refs, CLI output, and the marker/ledger tables. The prefix carried no information (the algorithm never varied per hash), and the hash **value** — not an in-band tag — signals a format change. The hash constructors and every loader now reject the legacy prefixed form.
566
+
567
+ Two distinct effects on your pack's checked-in artefacts:
568
+
569
+ - **Contract hashes keep their value.** `storageHash` / `profileHash` are computed over contract content, which never embedded its own hash — only the textual prefix drops.
570
+ - **Migration hash values change.** `migrationHash` is computed over the manifest bytes, which embed the `from` / `to` contract-hash strings; with those now bare, every recomputed `migrationHash` differs from the stored one.
571
+
572
+ ### Migrate checked-in `migrations/` trees — before the layout migrator
573
+
574
+ Run the colocated codemod from your extension's repository root, **before** `scripts/migrate-migrations-layout.mjs` (the snapshot-layout entries above) — the 0.17 layout migrator accepts only bare-hex trees:
575
+
576
+ ```bash
577
+ pnpm exec tsx ./strip-sha256-hash-prefixes.ts
578
+ ```
579
+
580
+ For every on-disk migration package (a `migration.json` with a sibling `ops.json`) it strips the prefix from the manifest's `from` / `to`, from hash literals inside `ops.json`, in pre-store sibling contract snapshots (`*-contract.json`, `*.d.ts`, `migration.ts`), and in content-addressed store entries (`migrations/snapshots/<hex>/contract.json` + `contract.d.ts` — the directory name is the hash's hex and does not change), recomputes `migrationHash` over the bare-hex content, and rewrites `refs/*.json` — repointing refs that held old migration hashes at the recomputed ones, and mapping the empty-tree sentinel `sha256:empty` to `empty`. The edit is format-preserving (only hash literals and the recomputed hash value change) and idempotent: re-running over an already-bare tree makes no further changes.
581
+
582
+ Use `--check` for a dry run that lists files still needing the fix and exits non-zero if any remain:
583
+
584
+ ```bash
585
+ pnpm exec tsx ./strip-sha256-hash-prefixes.ts --check
586
+ ```
587
+
588
+ ### Re-emit committed contract artefacts
589
+
590
+ If your pack commits emitted contract artefacts (a pack contract under `src/contract/`, test fixtures, example spaces), re-emit them the way your pack generates them (`prisma-next contract emit` or your regeneration script). The regenerated files differ only in hash representation — the hash values themselves are unchanged.
591
+
592
+ ### Update hash literals your pack hard-codes
593
+
594
+ Sweep your pack's source, fixtures, and tests for `sha256:`-prefixed literals — hand-built contract fixtures, expected `migrationHash` assertions, stub hashes in unit tests. Drop the prefix everywhere; for migration hashes, take the new value from the regenerated manifest, since the value itself changed. Constructing a hash via the framework's `coreHash()` / `profileHash()` constructors with a prefixed string now throws instead of round-tripping.
595
+
596
+ ### Database marker/ledger
597
+
598
+ There is no compatibility shim: a database whose marker/ledger rows still hold prefixed values reports a hash mismatch on `prisma-next db verify`. This applies to any signed database your extension maintains — acceptance harnesses, reference instances. Re-sign each against its regenerated contract:
599
+
600
+ ```bash
601
+ prisma-next db sign
602
+ ```
603
+
604
+ ### Validation
605
+
606
+ After the codemod and re-emit, run `pnpm typecheck && pnpm test` in your extension repo, and exercise any flow that loads your migrations — the loader recomputes and verifies each manifest's `migrationHash` on read, so a stale or still-prefixed manifest fails immediately. `git grep -n "sha256:"` over your repository should return no hits in committed artefacts.
607
+
608
+ ## `adopt-sql-json-projection-ast-foundations`
609
+
610
+ Relational JSON container AST construction now requires an explicit value-projection variant. Import `NativeJsonValueProjection` from `@internal/sql-relational-core/ast` and wrap every expression that 0.16 code passed directly to `JsonObjectExpr.entry(key, expression)` or `JsonArrayAggExpr.of(expression, ...)`: use `JsonObjectExpr.entry(key, new NativeJsonValueProjection(expression))` and `JsonArrayAggExpr.of(new NativeJsonValueProjection(expression), ...)`. `NativeJsonValueProjection` preserves the pre-0.17 target-native JSON conversion. Use `CodecJsonValueProjection` only when the extension deliberately supplies a `CodecRef` for codec-owned JSON conversion, and use `JsonDocumentProjection` only when the wrapped expression already produces a JSON document.
611
+
612
+ `ExprVisitor<R>` and the `AnyExpression` union now include `FunctionCallExpr` (`kind: 'function-call'`), `CastExpr` (`kind: 'cast'`), and `CaseExpr` (`kind: 'case'`). Add `functionCall`, `cast`, and `case` methods to every visitor object, and add all three discriminants to exhaustive `expr.kind` switches. Binding or rewriting visitors should route these nodes through their normal recursive expression path; restricted visitors such as grouped `HAVING` validators should reject them explicitly when the context does not support them.
613
+
614
+ `FunctionSource.of(fn, args, alias)` now groups alias state so returned-column aliases cannot exist without a table alias. Replace a string third argument such as `FunctionSource.of(fn, args, 'rows')` with `FunctionSource.of(fn, args, { alias: 'rows' })`; when returned-column names are required, pass `{ alias: 'rows', columnAliases: ['value', 'ordinality'] }`. Calls that omit the alias remain unchanged.
615
+
616
+ When an extension forwards an existing `ProjectionItem` through a derived-table or row-number wrapper, preserve its known codec in the reconstructed projection: use `ProjectionItem.of(item.alias, ColumnRef.of(wrapperAlias, item.alias), item.codec)`. Leave the codec undefined only for computed or otherwise unknown projected results. After applying the applicable edits, run the extension's typecheck and tests; update AST-shape fixtures to assert the explicit wrapper nodes and preserved codec metadata.
617
+ ## `rls-wire-name-helpers-moved-to-sql-schema-ir-naming`
618
+
619
+ The wire-name mechanics are family-shared from 0.17 (they now serve secondary indexes as well as RLS policies), so the helpers moved from the target-postgres RLS module to `@internal/sql-schema-ir/naming` under generalized names. Apply the mechanical rename:
620
+
621
+ | 0.16 (`@internal/target-postgres/rls-canonicalize`) | 0.17 (`@internal/sql-schema-ir/naming`) |
622
+ | --- | --- |
623
+ | `formatRlsPolicyWireName(prefix, hash)` | `formatWireName(prefix, hash)` |
624
+ | `parseRlsPolicyWireName(name)` | `parseWireName(name)` |
625
+ | `normalizePredicate(sql)` | `normalizeSqlBody(sql)` |
626
+ | `RlsPolicyWireName` (type) | `WireName` (type) |
627
+
628
+ Behavior is byte-identical — same `<prefix>_<8hex>` format and parse pattern, same treat-as-all-prefix contract for names that do not parse, same minimal trim-and-collapse normalizer (still a stability commitment: any change would re-suffix every wire name). `@internal/target-postgres/rls-canonicalize` keeps the RLS-specific exports (`computeContentHash`, `ContentHashParts`, `POLICY_OPERATION_PREDICATES`, `RlsPolicyOperation`); there are no re-export shims for the moved names. The naming module additionally exposes `computeIndexContentHash`, `WIRE_NAME_PREFIX_MAX_LENGTH` (54), and `assertWireNamePrefixLength` — the index-side siblings of the RLS hash assembly.
629
+
630
+ ## `sql-index-entities-are-name-identified`
631
+
632
+ Indexes are name-identified end to end. Two SPI layers change shape; take them in order.
633
+
634
+ ### Contract IR: `Index` / `IndexSchema`
635
+
636
+ An index entity (`Index` from `@internal/sql-contract/types`; the `indexes: []` array in a `StorageTable`) now requires:
637
+
638
+ - `name: string` — the full physical name, always present.
639
+ - `unique: boolean` — always present.
640
+ - `prefix?: string` — present iff the name is wire-named; the constructor enforces that `name` parses back to `prefix` + an 8-hex suffix.
641
+ - `columns?` xor `expression?` — exactly one must be set; `where?` carries a partial-index predicate. All body strings are opaque SQL, never parsed or escaped.
642
+
643
+ Both the class constructor and the arktype `IndexSchema` reject the 0.16 shape. Contract-space fixtures or pack code that load `indexes: [{ columns: ['email'] }]` through validation fail with a `Contract structural validation failed: storage.namespaces.<ns> …` error whose message contains `indexes[0].name must be a string (was missing)` and `indexes[0].unique must be boolean (was missing)`; constructing the entity directly throws `Index: every index carries a full physical name…`. Add the real physical name and `unique: false`. The `index(...)` convenience factory from `@internal/sql-contract/factories` changed signature accordingly: `index(name, columns, opts?)` (opts: `prefix`, `unique`, `type`, `options`). Packs that lower authored index inputs themselves can reuse `lowerAuthoredIndex` from `@internal/sql-contract/index-naming` — it implements the wire/exact naming rules (default prefix, `name:`-as-prefix, `map:`-as-exact) including the 54-character prefix cap.
644
+
645
+ ### Schema IR: `SqlIndexIR`
646
+
647
+ `SqlIndexIRInput` requires `name` and adds explicit `prefix` / `expression` / `where` keys (the package's every-field-required convention: state absence with `undefined`, never by omission), and `columns` became `readonly string[] | undefined` (xor `expression`). The node's diff-tree `id` is now `index:<name>` — the old tuple-derived `index:<col,col>` ids are gone, so any test asserting child ids or diff paths must use the physical name. `isEqualTo` is mode-selected by the receiver: both modes compare `unique` strict, `type` strict, `options` loosely (`String()`-coerced), and `columns` ordered-strict when both sides carry them; an exact-named receiver (`prefix === undefined`) additionally byte-compares `expression ?? ''` and `where ?? ''`; a wire-named receiver never compares bodies. Introspection now captures expression and partial indexes at full fidelity and preserves same-tuple twin indexes — packs must not assume one index per column tuple.
648
+
649
+ ### Committed contract spaces
650
+
651
+ Re-emit your pack's contract space with the upgraded toolchain (`build:contract-space`, or your generator script à la `contract:generate`): every contract that declares indexes gets the new entry shape and a new storage hash, and wire-named index physical names gain the `_<8hex>` content-hash suffix. Databases your pack maintains (acceptance harnesses, reference instances) converge via a renames-only widening plan — see the user-skill `indexes-are-name-identified` entry for that flow. Expression and partial indexes are authorable from 0.17 (PSL `@@index(expression:/where:/unique:/type:/name: xor map:)`, TS `constraints.index` with the same matrix); declare them with `name:` for wire names, or `map:` for infer-captured exact names — hand-authoring a body under `map:` warns (`PN_EXACT_NAME_BODY_COMPARISON`) because drift detection byte-compares the authored text against Postgres's reprint. Live indexes you choose not to declare stay tolerated under an `external` control policy. The rules behind all of this — why an index is identified by its name, when the two naming modes apply, and what re-inference does to each — are decided in ADR 243, "Name-identified indexes and exact-name adoption".
652
+
653
+ ## `rls-policy-migration-literal-carries-the-naming-union`
654
+
655
+ `PostgresRlsPolicyMigrationInput` is gone. The parameter `Migration#createRlsPolicy` accepts, and the shape `CreatePostgresRlsPolicyCall.renderTypeScript` writes into a generated migration, is now `RenderedRlsPolicyLiteral` — `PostgresRlsPolicyInput` with absent-valued keys omittable, which is how a machine-rendered literal spells absence. The practical difference is the name:
656
+
657
+ | 0.16 | 0.17 |
658
+ | --- | --- |
659
+ | `name: "post_owner_a1b2c3d4", prefix: "post_owner"` | `naming: { kind: "wire", prefix: "post_owner", hash: "a1b2c3d4" }` |
660
+ | `name: "Tenant members can read"` | `naming: { kind: "exact", name: "Tenant members can read" }` |
661
+
662
+ The two flat fields could disagree; the union cannot be written wrong, which is why the migration authoring surface carries it.
663
+
664
+ The stored `contract.json` shape does not change — it stays flat and now has its own type: `SerializedRlsPolicy`, hydrated by `policyInputFromSerialized` (0.16's `rlsPolicyInputFromFlat`, renamed for the boundary it serves). Both are exported from `@internal/target-postgres/types`.
665
+
666
+ Pack code that constructs `PostgresRlsPolicy` directly already passes `naming` and needs no edit. Pack code that builds the migration literal, or that names `PostgresRlsPolicyMigrationInput` in a signature, switches to `RenderedRlsPolicyLiteral`. Regenerate any migration your pack ships that calls `createRlsPolicy`.
667
+
668
+ ## `postgres-extension-codecs-require-target-descriptors`
669
+
670
+ For every codec descriptor contributed by a PostgreSQL extension, add `@internal/target-postgres` at the same 0.17 version as the extension's other `@internal/*` packages under `dependencies`. Do not leave it only in `devDependencies`: production descriptor modules and runtime/control stack metadata import and expose this protocol. Import the target API from the lean `@internal/target-postgres/codec-descriptor` subpath, and import `ProjectionExpr` from `@internal/sql-relational-core/ast`.
671
+
672
+ Change a PostgreSQL-bound descriptor that extends `CodecDescriptorImpl<P>` to extend `PostgresCodecDescriptor<P>`. Keep its codec id, traits, target types, `paramsSchema`, factory, output renderer and column helpers unchanged; there is no longer a `meta` / `metaFor` channel to carry alongside, and the descriptor's `nativeTypeFor()` is the only place a native type is declared. Add `protected override nativeType(params: P): string` returning the same trusted PostgreSQL native type spelling the extension already uses, and add `protected override jsonProjection(expression: ProjectionExpr, params: P): ProjectionExpr`.
673
+
674
+ `return expression` is a claim, not a placeholder: the production JSON renderers call `projectJson()` for every column-valued entry they build, so an identity projection asserts that your codec's stored form already *is* its canonical JSON. Write one only where that holds, and where it does not, see `codec-json-projections-must-agree-with-encode-json` below.
675
+
676
+ When the extension contributes a reusable target-neutral SQL descriptor instead of owning its descriptor class, keep the generic descriptor unchanged and wrap it with `postgresCodec(genericDescriptor, { nativeType, jsonProjection })`. Supply the native type and the canonical JSON projection as above. The wrapper delegates the generic descriptor's codec id, literals, parameter schema, factory, renderers and target types, and adds the PostgreSQL discriminant and target methods.
677
+
678
+ Replace broad exported descriptor arrays such as `readonly AnyCodecDescriptor[]` with `definePostgresCodecs([...])`. Use the resulting canonical target-typed set in both runtime and control `types.codecTypes.codecDescriptors`, and return that same set from runtime `codecs()` when the runtime extension SPI requires it. Do not construct independently maintained generic and PostgreSQL descriptor collections. Update exact-type tests that expected `readonly AnyCodecDescriptor[]` to accept `readonly AnyPostgresCodecDescriptor[]`; ordinary descriptors still satisfy `AnyCodecDescriptor` individually.
679
+
680
+ Review every `CodecRef` construction for a parameterized descriptor, including parameter refs, contract/storage fixtures, and test-created refs. Supply `typeParams` with every field required by the target descriptor `paramsSchema`; for example, a `pg/vector@1` ref for a three-element vector must include `typeParams: { length: 3 }`. Do not make a required descriptor parameter optional merely to preserve an invalid unparameterized ref.
681
+
682
+ When a parameterized descriptor intentionally supports an unparameterized column or contract reference, make its parameter type and Standard Schema accept the empty validated parameter object used during representative materialization. Express only genuinely absent fields as optional and preserve the existing unparameterized factory behavior; do not add a hidden default or change the codec encoded representation.
683
+
684
+ After the migration, run the extension package's typecheck, lint, and tests. Verify its public codec ids, factories, column helpers, rendered types, SQL/wire behavior, `encodeJson` / `decodeJson`, runtime/control descriptor membership, and emitted contract behavior are unchanged apart from the descriptor types becoming PostgreSQL-specific.
685
+
686
+ ## `postgres-packages-now-ship-types-pg`
687
+
688
+ `@internal/postgres`, `@internal/extension-supabase`, and `@internal/driver-postgres` re-export `pg` types from their published `.d.mts` files, so each declares `@types/pg` under `dependencies` from 0.17. Compiling against those declarations no longer requires your extension to supply `@types/pg` itself.
689
+
690
+ If your extension's `package.json` declares `@types/pg`, act on it. `pg` carries no types of its own, so two `@types/pg` copies in the tree give `pg.Client` and `pg.Pool` two distinct identities. Any call that hands your own client or pool to a Prisma Next API — `new PostgresControlDriver(client)`, a driver `connect: { pool }` — then fails:
691
+
692
+ ```text
693
+ Argument of type 'Client' is not assignable to parameter of type 'Client'.
694
+ Type 'Client' is missing the following properties from type 'Client': connection, setTypeParser, getTypeParser
695
+ ```
696
+
697
+ The error names the same type on both sides; the two paths under `node_modules/.pnpm/@types+pg@<version>/` in the full message are what identify it.
698
+
699
+ Prefer dropping `@types/pg` from your extension's `devDependencies` and taking it transitively, so its version tracks Prisma Next's. If you keep the entry — because your own code imports `pg` directly and you want the dependency explicit — pin it to the version `@internal/postgres` depends on rather than a range that can resolve elsewhere.
700
+
701
+ ## Incidental lint-config bumps
702
+
703
+ Biome `$schema` version alignment in `packages/3-extensions/` (dependabot `dev-deps` group, PR #1058) requires no Prisma Next-specific upgrade action by extension authors.
704
+
705
+ ## `codec-json-projections-must-agree-with-encode-json`
706
+
707
+ Slice-2 shipped `jsonProjection` as a typed hook that every descriptor implemented as `return expression`. Those hooks now carry real SQL, and a descriptor's two sides are held to agreeing with each other.
708
+
709
+ The ordering is the part worth internalising, because it is the same in all three shapes it has taken:
710
+
711
+ - a numeric cast to `text` sits inside the projected expression, so it applies before the JSON constructor can render the value as a JSON number;
712
+ - a base64 encoding *replaces* the target's own conversion rather than post-processing it, because the target would otherwise have already emitted its hex form;
713
+ - a widening of a narrow float to a wider one happens before any text rendering, because the printing is what discards precision.
714
+
715
+ In each case a transformation applied to the constructor's output would be too late. If you are authoring a projection, ask what the target does to the value if you do nothing, and whether that is recoverable.
716
+
717
+ Two properties are worth testing against a real database rather than reasoned about, because both turned out to be contingent: whether your form survives a value at the edge of its representation, and whether it depends on a session setting your consumers may not share.
718
+
719
+ ## `pgvector-json-form-is-a-numeric-array`
720
+
721
+ The route matters as much as the destination here. Casting a vector's text form to `json` produces an array of numbers and looks correct — but the text prints the shortest decimal that round-trips as a `real`, so reading it at double width yields a different number than the application holds. Widening each element to `float8` before building the array keeps the exact value the `real` denotes.
722
+
723
+ ## `default-literal-value-resolves-through-the-codec-json-channel`
724
+
725
+ Before this change the emitted type said a literal default had the codec's *application* type. That was true only while the two coincided. For a codec whose application value is a `bigint` and whose canonical JSON is a decimal string, it described a `bigint` sitting in a file that holds `"0"` — which surfaced as an assignability failure rather than as a wrong-but-quiet type.
726
+
727
+ Regenerating is sufficient; no hand edits to a `contract.d.ts` are needed.
728
+
729
+ ## Incidental release version bump to 0.17.0
730
+
731
+ The `chore(release): bump to 0.17.0` commit rewrites every workspace manifest's `version` field and `workspace:` pins, which touches `packages/3-extensions/` manifests. No extension-author action beyond the entries above.