@prisma/orm-mongo 8.0.0-rc.22-dev.15 → 8.0.0-rc.23

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.
@@ -0,0 +1,480 @@
1
+ ---
2
+ from: "8.0.0-rc.22"
3
+ to: "8.0.0-rc.23"
4
+ changes:
5
+ - id: temporal-polyfill-is-a-peer-dependency-of-mysql
6
+ summary: |
7
+ `temporal-polyfill` is now a required peer dependency of `@prisma/orm-mysql` and `@prisma/orm-target-mysql`. The MySQL control plane, such as the `prisma` commands, imports it, because MySQL now has the convenience date and time types, and `DateTime(as: temporal)`, `Date(as: temporal)` and `Time(as: temporal)` read `Temporal` values. npm, pnpm and bun install it automatically. A project that installs with Yarn must add `temporal-polyfill` (`^1.0.4`) to its own dependencies.
8
+ detection:
9
+ glob: "**/yarn.lock"
10
+ contains:
11
+ - "@prisma/orm-mysql@"
12
+ - "@prisma/orm-target-mysql@"
13
+ anyMatch: true
14
+ - id: mysql-datetime-writes-are-checked
15
+ summary: |
16
+ A MySQL `DateTime` field now refuses to write a year before 1000 or after 9999 with `RUNTIME.ENCODE_FAILED`, before the value reaches MySQL. Reads are unchanged, and no migration is needed.
17
+ detection:
18
+ glob: "**/package.json"
19
+ contains:
20
+ - "@prisma/orm-mysql"
21
+ - "@prisma/orm-target-mysql"
22
+ anyMatch: true
23
+ - id: temporal-polyfill-is-a-peer-dependency-of-sqlite
24
+ summary: |
25
+ `temporal-polyfill` is now a required peer dependency of `@prisma/orm-sqlite` and `@prisma/orm-target-sqlite`. The SQLite control plane, such as the `prisma` commands and the Vite plugin, imports it, because SQLite now has the convenience date and time types, and `DateTime(as: temporal)`, `Date(as: temporal)` and `Time(as: temporal)` read `Temporal` values. npm, pnpm and bun install it automatically. A project that installs with Yarn must add `temporal-polyfill` (`^1.0.4`) to its own dependencies.
26
+ detection:
27
+ glob: "**/yarn.lock"
28
+ contains:
29
+ - "@prisma/orm-sqlite@"
30
+ - "@prisma/orm-target-sqlite@"
31
+ anyMatch: true
32
+ - id: psl-renamed-postgres-scalar-types
33
+ summary: |
34
+ In a Prisma 8 schema on PostgreSQL, `SmallInt`, `BigIntNumber`, `Numeric`, `UnboundedInt`, `Real`, `VarChar` and `VarBit` are deprecated and report `PSL_DEPRECATED_SCALAR_NAME` as a warning. Write the convenience call that gives the same type instead: `Int(storage: int2)`, `BigInt(as: number)`, `Decimal(precision: p, scale: s)`, `BigInt(storage: numeric)`, `Float(storage: float4)` and `String(storage: varchar, length: n)`; `VarBit` has no convenience type and becomes `Varbit`. The contract does not change. A Prisma 7 schema, which has a `datasource` block, is not affected.
35
+ detection:
36
+ glob: "**/*.prisma"
37
+ matches:
38
+ - '(?<![\s\S])(?![\s\S]*(?<![\w$])datasource[ \t]+\w+[ \t]*\{)[\s\S]*\n[ \t]+[A-Za-z_]\w*[ \t]+(?:=[ \t]*)?(?:SmallInt|BigIntNumber|Numeric|UnboundedInt|Real|VarChar|VarBit)(?![\w$])'
39
+ - id: ts-renamed-postgres-scalar-helpers
40
+ summary: |
41
+ In a TypeScript contract on PostgreSQL, `type.SmallInt()`, `type.BigIntNumber()`, `type.Numeric(...)`, `type.UnboundedInt()`, `type.Real()`, `type.VarChar(...)` and `type.VarBit(...)` are deprecated. They still compile and give no warning, and they are removed together with the deprecated schema names. Call the convenience helper that gives the same type instead, such as `type.String({ storage: 'varchar', length: 64 })` for `type.VarChar(64)`, or `type.Varbit(n)` for `type.VarBit(n)`.
42
+ detection:
43
+ glob: "**/*.{ts,mts,cts,tsx}"
44
+ matches:
45
+ - '(?<![\w$])type\.(?:SmallInt|BigIntNumber|Numeric|UnboundedInt|Real|VarChar|VarBit)\('
46
+ - id: ts-renamed-postgres-column-helpers
47
+ summary: |
48
+ The PostgreSQL column helpers `int8Column` and `numericColumn` are renamed to `int8BigIntColumn` and `numericStringColumn`, after the explicit types they give (`Int8BigInt` and `NumericString`). The old names are removed. The contract does not change.
49
+ detection:
50
+ glob: "**/*.{ts,mts,cts,tsx}"
51
+ matches:
52
+ - 'import[ \t]+(?:type[ \t]+)?\{[^}]*(?<![\w$])(?:int8Column|numericColumn)(?![\w$])[^}]*\}[ \t]*from[ \t]*[''"](?:@prisma/orm-postgres/adapter/column-types|@internal/adapter-postgres/column-types)[''"]'
53
+ - id: pack-extension-found-on-search-path
54
+ summary: |
55
+ The pgvector, PostGIS, ParadeDB and citext packs declare their extension in the unbound namespace, so `db sign` and `db verify` accept the extension in any schema on the connection's search path, such as Supabase's `extensions`. Each pack has a new head and one new migration with no operations. A project that composes one of these packs runs `prisma contract emit`, which writes the pack's new migration under `migrations/<pack>/`, and then `prisma db migrate`, which applies it without changing the database.
56
+ detection:
57
+ glob: "**/prisma.config.{ts,mts,cts,js,mjs,cjs}"
58
+ matches:
59
+ - '@prisma/orm-extension-(pgvector|postgis|paradedb)/control|@prisma/orm-postgres-contrib/citext/control'
60
+ - id: remove-pack-extension-declaration
61
+ summary: |
62
+ An application contract may not declare an extension that a composed pack declares. `contract emit` refuses such a PSL declaration with `PSL_ENTITY_DECLARED_BY_PACK`, and `migration plan` and `db update` refuse it as a conflict. Remove the application's own `extension vector {}`, `extension postgis {}`, `extension pg_search {}` or `extension citext {}` block, or its `extension(...)` handle, when the project composes the pack that declares it.
63
+ detection:
64
+ glob: "**/*.{prisma,ts,mts,cts}"
65
+ matches:
66
+ - 'extension\s+(vector|postgis|pg_search|citext)\s*\{'
67
+ - 'extension\(\s*[''"](vector|postgis|pg_search|citext)[''"]'
68
+ - id: mongo-contracts-record-index-names
69
+ summary: |
70
+ Every index of a MongoDB contract records the name MongoDB stores it under, so every MongoDB contract with an index gets a new storage hash. An index written without a name is named after its keys and directions, such as `email_1`, which is the name earlier releases gave it in the database, so no database changes. A contract without index names is rejected with `CONTRACT.VALIDATION_FAILED`. Run `prisma contract emit`, run the colocated script, which names the indexes of the contract snapshots under `migrations/snapshots/`, then run `prisma db sign` or plan a migration, which has no operations.
71
+ detection:
72
+ glob: "**/*.json"
73
+ matches:
74
+ - '"kind"\s*:\s*"mongo-index"(?!\s*,\s*"name")'
75
+ script: ./scripts/mongo-index-names/mongo-index-names.ts
76
+ - id: mongo-unnamed-indexes-with-one-name-refused
77
+ summary: |
78
+ MongoDB PSL and TypeScript `defineContract` refuse two indexes of one collection that get one name. Two indexes written without a name on the same keys and directions, such as a unique and a plain index on `email`, a sparse and a plain one, or one `@@index([title])` on each of two variants of one base, are both named `email_1` or `title_1`. MongoDB could never hold both under that name, so no migration could create them. PSL reports `PSL_INVALID_INDEX` at the second index and TypeScript throws `CONTRACT.NAME_DUPLICATE`. Name one of them with `map:` in PSL or the index option `name` in TypeScript.
79
+ detection:
80
+ glob: "**/*.{prisma,ts,mts,cts}"
81
+ matches:
82
+ - '@@base\([\s\S]*@@(?:index|unique|textIndex)\('
83
+ - '\bindex\(\s*\{[^}]*\}\s*,\s*\{[^}]*\b(?:sparse|partialFilterExpression|collation|expireAfterSeconds)\b'
84
+ - id: mongo-prisma6-index-names
85
+ summary: |
86
+ A contract read from a Prisma 6 MongoDB schema with `prisma6Schema()` names each index as Prisma 6 named it: by `map:` (or `name:` on `@@index`), otherwise `<collection>_<fields>_key` for a unique index and `_idx` for others, such as `User_email_key`. `db verify` now checks these names against the database. The reader refuses what Prisma 6 refuses: two indexes of one model with one name (`PSL.PRISMA6_MONGO_INDEX_NAME_DUPLICATE`), and an empty `map` or one longer than 127 bytes (`PSL.PRISMA6_MONGO_INDEX_NAME_INVALID`).
87
+ detection:
88
+ glob: "**/*.prisma"
89
+ matches:
90
+ - '(?<![\s\S])(?=[\s\S]*@db\.ObjectId\b)[\s\S]*(?:@unique\b|@@(?:unique|index|fulltext)\()'
91
+ - id: mongo-index-operations-by-name
92
+ summary: |
93
+ A MongoDB migration creates and drops each index by its name. `prisma migration plan` writes the name into `migration.ts`, as in `createIndex("users", [{ field: "email", direction: 1 }], { name: "User_email_key", unique: true })` and `dropIndex("users", [{ field: "email", direction: 1 }], { name: "User_email_key", unique: true })`. The operation id is `index.<collection>.<create|drop>(<name>)`, such as `index.users.create(email_1)`, where it was `index.users.create(email:1)`. A `migration.ts` written by an earlier release keeps working: an index without a name is named after its keys, as before.
94
+ detection:
95
+ glob: "**/*.{ts,mts,cts,js,mjs,cjs,json,sh,yml,yaml}"
96
+ matches:
97
+ - 'index\.[\w$-]+\.(?:create|drop)\([^)]*:'
98
+ - id: mongo-prisma6-indexes-as-prisma6-reads-them
99
+ summary: |
100
+ A Prisma 6 MongoDB schema read by `prisma6Schema()` no longer has its indexes merged. Two indexes of one model with the same fields and the same written sort, of any kind (`@unique`, `@@unique`, `@@index`, `@@fulltext`), are refused with `PSL.PRISMA6_MONGO_INDEX_ALREADY_EXISTS`, as Prisma 6 refuses them. A unique and a plain index on the same keys that Prisma 6 accepts, such as `@unique` beside `@@index([email(sort: Asc)])`, are both kept, so the contract describes the database Prisma 6 built and gets a new storage hash. Prisma 8 PSL and TypeScript `defineContract` still merge identical and redundant indexes; their contracts do not change.
101
+ detection:
102
+ glob: "**/*.prisma"
103
+ matches:
104
+ - '(?<![\s\S])(?=[\s\S]*@db\.ObjectId\b)[\s\S]*(?:@unique\b|@@(?:unique|index|fulltext)\()'
105
+ - id: mongo-client-takes-extensions
106
+ summary: |
107
+ `mongo()` and `mongoStatic()` from `@prisma/orm-mongo` take an `extensions` option, as `postgres()` does, and refuse a contract that requires an extension pack they are not given, with `RUNTIME.MISSING_EXTENSION_PACK`. A MongoDB project whose `prisma.config.ts` composes extension packs passes each pack's runtime descriptor to `mongo({ extensions })` and `mongoStatic({ extensions })`.
108
+ detection:
109
+ glob: "**/*.{ts,mts,cts,tsx,js,mjs,cjs,jsx}"
110
+ matches:
111
+ - '(?<![\s\S])(?=[\s\S]*@prisma/orm-mongo/config)[\s\S]*[{,]\s{0,100}extensions\s{0,20}[:,}]'
112
+ - id: mongo-contract-requires-target
113
+ summary: |
114
+ A MongoDB `contract.json` must have `"target"`, as a SQL contract must. `contractFromJson`, `mongo()` and `mongoStatic()` refuse a contract without it with `CONTRACT.VALIDATION_FAILED`. Every contract `prisma contract emit` writes has it, so only a hand-written or hand-edited contract is affected.
115
+ detection:
116
+ glob: "**/*.json"
117
+ matches:
118
+ - '(?<![\s\S])(?![\s\S]*"target"\s*:)[\s\S]*"targetFamily"\s*:\s*"mongo"'
119
+ - id: supabase-one-binding-input
120
+ summary: |
121
+ `supabase()` now checks its database input the way `postgres()` does, when it is called (see the change `postgres-checks-database-input` for the checks both share). Passing both `url` and `pg` rejects with `RUNTIME.BINDING_INVALID`, where `pg` used to win silently; pass only one. An empty `url` used to make `pg` connect with the `PG*` environment variables or to localhost; it now rejects with `RUNTIME.BINDING_INVALID`, so check that the variable you pass as `url` is set. A `url` that cannot be parsed or is not `postgres://` / `postgresql://` also rejects with `RUNTIME.BINDING_INVALID`, instead of failing on the first query. `pg: null` and a `pg` value that is neither a `pg.Pool` nor a `pg.Client` now reject with `RUNTIME.BINDING_INVALID` instead of `SUPABASE.CONFIG_INVALID`. A `pg.Client` still rejects with `SUPABASE.CONFIG_INVALID`, and its message is now "Supabase takes a pg.Pool or a url, not a pg.Client". The `RUNTIME.BINDING_INVALID` errors of `supabase()` list only the inputs it takes (`url`, a `pg.Pool`, `binding: { kind: 'url', url }` or `{ kind: 'pgPool', pool }`) and carry `meta.extension: 'supabase'`.
122
+ detection:
123
+ glob: "**/*.{ts,mts,cts,tsx,js,mjs,cjs}"
124
+ matches:
125
+ - '(?<![\w$.])supabase(?:<[^>]*>)?\(\s*[{A-Za-z_$]'
126
+ - 'The pg option takes a pg\.Pool'
127
+ - 'import\s*\{[^}]*(?<![\w$])supabase\s+as\s+[A-Za-z_$][^}]*\}\s*from\s*[''"]@(?:prisma/orm-|internal/)extension-supabase(?:/[^''"]*)?[''"]'
128
+ - id: postgres-checks-database-input
129
+ summary: |
130
+ `postgres()` and `supabase()` check their database input when they are called and reject a wrong one with `RUNTIME.BINDING_INVALID`, with a message that names the option, a `why` and a `fix`; `meta.receivedType` gives the value's type, never the value. `null` or a value of the wrong type as `pg`, `url` or `binding` (a `pg` or `binding` that is not an object, a `url` that is not a string) rejects; `postgres()` used to throw a `TypeError` with no error code. The check runs before the inputs are counted, so `{ url: null, pg: pool }` fails on `url`, not with "Provide one binding input"; match on the new code, and pass the option only when it has a value. A `binding` object whose `kind` is not `'url'`, `'pgPool'` or `'pgClient'` (for example `{}`, an array or a misspelt kind) rejects; `postgres()` used to accept it and throw a `TypeError` on the first query. A `binding` of kind `'url'` whose `url` is not a string, is empty, cannot be parsed or is not `postgres://` / `postgresql://` rejects; `postgres()` used to pass it to `pg`, and an empty one connected with the `PG*` environment variables or to localhost. A `binding` whose `pool` or `client` is not an object rejects; `postgres()` used to fail on the first query.
131
+ detection:
132
+ glob: "**/*.{ts,mts,cts,tsx,js,mjs,cjs}"
133
+ matches:
134
+ - '(?<![\w$.])(?:pg|url|binding)\s*:\s*(?:null\b|[^,}\n]*(?:\?|\|\||:)\s*null\b)'
135
+ - '(?<![\w$.])(?:postgres|supabase)(?:<[^>]*>)?\(\s*\{(?:[^()]|\((?:[^()]|\([^()]*\))*\))*?(?<![\w$])binding\s*:\s*\{[^}]*?(?<![\w$])kind\s*:\s*([''"`])(?!(?:url|pgPool|pgClient)\1)[^''"`]*\1'
136
+ - '(?<![\w$.])postgres(?:<[^>]*>)?\(\s*\{(?:[^()]|\((?:[^()]|\([^()]*\))*\))*?(?<![\w$])binding\s*:\s*\{[^}]*?(?<![\w$])kind\s*:\s*([''"`])url\1'
137
+ - id: contract-artifacts-restamp
138
+ summary: |
139
+ An extension that writes its own package version into the contracts it emits, such as the Supabase extension, now writes 8.0.0-rc.23. Run `contract emit` once after upgrading so the emitted `contract.json` and `contract.d.ts` match the installed extension.
140
+ detection:
141
+ glob: "**/contract.json"
142
+ contains:
143
+ - '"version": "8.0.0-rc.22"'
144
+ ---
145
+
146
+ # 8.0.0-rc.22 → 8.0.0-rc.23 — User upgrade instructions
147
+
148
+ ## Order of the steps
149
+
150
+ Do the steps in this order, and skip a step that does not apply to the project:
151
+
152
+ 1. Upgrade the Prisma packages. A project that installs with Yarn and uses MySQL or SQLite adds `temporal-polyfill` to its dependencies (`temporal-polyfill-is-a-peer-dependency-of-mysql`, `temporal-polyfill-is-a-peer-dependency-of-sqlite`).
153
+ 2. Change the contract sources. On PostgreSQL, rewrite the deprecated scalar type names and helpers (`psl-renamed-postgres-scalar-types`, `ts-renamed-postgres-scalar-helpers`, `ts-renamed-postgres-column-helpers`). Remove the application's own declaration of an extension that a composed pack declares (`remove-pack-extension-declaration`). On MongoDB, name one of two indexes that get one name (`mongo-unnamed-indexes-with-one-name-refused`).
154
+ 3. Change MongoDB and PostgreSQL application code: pass the extension packs to `mongo()` and `mongoStatic()` (`mongo-client-takes-extensions`), and give `supabase()` and `postgres()` one valid database input (`supabase-one-binding-input`, `postgres-checks-database-input`).
155
+ 4. Run `prisma contract emit` and commit what it writes, including the files under `migrations/`. Fix what it refuses for a Prisma 6 MongoDB schema (`mongo-prisma6-indexes-as-prisma6-reads-them`, `mongo-prisma6-index-names`). This step also covers `mongo-contract-requires-target`, `pack-extension-found-on-search-path` and `contract-artifacts-restamp`.
156
+ 5. On MongoDB, run the colocated script to name the indexes of the contract snapshots under `migrations/snapshots/` (`mongo-contracts-record-index-names`).
157
+ 6. Bring each database up to date: run `prisma db migrate` for a pgvector, PostGIS, ParadeDB or citext pack (`pack-extension-found-on-search-path`). On MongoDB, run `prisma db sign`, or plan and apply the migration with no operations (`mongo-contracts-record-index-names`). For a Prisma 6 MongoDB schema, run `prisma db verify` before you sign (`mongo-prisma6-index-names`).
158
+ 7. Change the remaining code: scripts and tests that match a MongoDB index operation id (`mongo-index-operations-by-name`), and MySQL writes of a year before 1000 or after 9999 (`mysql-datetime-writes-are-checked`). Check a SQLite `date('now')` default that `db verify` reports (`temporal-polyfill-is-a-peer-dependency-of-sqlite`).
159
+
160
+
161
+ ## MySQL: `temporal-polyfill` is a peer dependency, and `DateTime` writes are range-checked
162
+
163
+ ### `temporal-polyfill-is-a-peer-dependency-of-mysql`
164
+
165
+ For each Yarn project that `detection` matches, add `"temporal-polyfill": "^1.0.4"` to `dependencies` in the `package.json` that depends on `@prisma/orm-mysql` or `@prisma/orm-target-mysql`, then reinstall. Without it, `prisma contract emit` and the other commands fail because Node.js cannot find the package `temporal-polyfill`.
166
+
167
+ A project that already depends on `temporal-polyfill` with a range inside `^1.0.4` needs no change.
168
+
169
+ An existing MySQL contract needs no other change: `DateTime` with no arguments is the type `DateTime` was before, renamed `DatetimeJsDate(3)`, and emits the same contract.
170
+
171
+ ### `mysql-datetime-writes-are-checked`
172
+
173
+ Nothing to change in code. A `Date` before the year 1000 or after 9999 written to a MySQL `DateTime` field is now refused with `RUNTIME.ENCODE_FAILED`. MySQL documents only those years for `datetime`. If the application writes such dates, keep them in range or store them in a `String` field.
174
+
175
+
176
+ ## SQLite: `temporal-polyfill` is a peer dependency
177
+
178
+ ### `temporal-polyfill-is-a-peer-dependency-of-sqlite`
179
+
180
+ For each Yarn project that `detection` matches, add `"temporal-polyfill": "^1.0.4"` to `dependencies` in the `package.json` that depends on `@prisma/orm-sqlite` or `@prisma/orm-target-sqlite`, then reinstall. Without it, `prisma contract emit` and the other commands fail because Node.js cannot find the package `temporal-polyfill`.
181
+
182
+ A project that already depends on `temporal-polyfill` with a range inside `^1.0.4` needs no change.
183
+
184
+ An existing SQLite contract needs no other change: `DateTime` with no arguments is the type `DateTime` was before, renamed `DatetimeJsDate`, and emits the same contract.
185
+
186
+ One case reads differently. `db verify` used to read a database default of `date('now')` or `CURRENT_DATE` as `now()`, so it matched a field written `@default(now())` of any type. It now reads it as the current date, which only a `Date @default(now())` field writes, and a `now()` on a `DateTime` or `String` field means the current instant. If `db verify` now reports such a column, write the field as `Date @default(now())` when it holds a calendar date, or as ``@default(sql`date('now')`)`` to keep its type. Do not run `db update` first: it would change the column's default to the current instant, and new rows would get the instant instead of the date.
187
+
188
+
189
+ ## PostgreSQL's integer, decimal, float and text types are convenience types
190
+
191
+ On PostgreSQL, `Int`, `BigInt`, `Float`, `Decimal`, `String`, `Boolean` and `Bytes` are now convenience types, as `DateTime` already is. Each is named for what a field means, and its optional `storage` argument (and `as`, for `BigInt`) chooses an explicit type named after the database type. Written bare, each gives the same type as before, so schemas that use these names need no change.
192
+
193
+ The explicit types took the database's names. Seven old names stay for one release line as deprecated aliases: they give the same codec and the same contract, and `prisma contract emit` and the editor report a warning where one is written.
194
+
195
+ ### `psl-renamed-postgres-scalar-types`
196
+
197
+ Rewrite each field or `types {}` alias written with an old name, keeping its other arguments and attributes:
198
+
199
+ | Before | After | Explicit type it gives |
200
+ | --- | --- | --- |
201
+ | `SmallInt` | `Int(storage: int2)` | `Int2` |
202
+ | `BigIntNumber` | `BigInt(as: number)` | `Int8Number` |
203
+ | `Numeric` | `Decimal` | `NumericString` |
204
+ | `Numeric(p)` | `Decimal(precision: p)` | `NumericString(p)` |
205
+ | `Numeric(p, s)` | `Decimal(precision: p, scale: s)` | `NumericString(p, s)` |
206
+ | `UnboundedInt` | `BigInt(storage: numeric)` | `NumericBigInt` |
207
+ | `Real` | `Float(storage: float4)` | `Float4` |
208
+ | `VarChar` | `String(storage: varchar)` | `Varchar` |
209
+ | `VarChar(n)` | `String(storage: varchar, length: n)` | `Varchar(n)` |
210
+ | `VarBit` | `Varbit` | `Varbit` |
211
+ | `VarBit(n)` | `Varbit(n)` | `Varbit(n)` |
212
+
213
+ `VarBit` has no convenience type, so its replacement is the explicit type. For the others, writing the explicit type in the last column instead is equally correct; it is the same type, and both emit the same contract. The warning names the explicit type because it is the exact replacement for the old name.
214
+
215
+ ```prisma
216
+ model Account {
217
+ id Int @id
218
+ visits Int(storage: int2) // was SmallInt
219
+ rank BigInt(as: number) // was BigIntNumber
220
+ price Decimal(precision: 10, scale: 2) // was Numeric(10, 2)
221
+ code String(storage: varchar, length: 12) // was VarChar(12)
222
+ }
223
+ ```
224
+
225
+ Rewrite only Prisma 8 schemas whose config uses the PostgreSQL target, including the `contract.prisma` copies in migration folders. The detection cannot tell which target a schema is for, so it also matches SQLite's `BigIntNumber` and MySQL's `VarChar`; a match in a project whose `prisma.config.ts` uses the SQLite or MySQL target needs no change. Update comments and documentation that name the old types the same way. Re-emit the contract with `prisma contract emit`: it is byte for byte the same as before.
226
+
227
+ ### `ts-renamed-postgres-scalar-helpers`
228
+
229
+ Rewrite only in contracts on PostgreSQL:
230
+
231
+ ```ts
232
+ field.column(type.Int({ storage: 'int2' })); // was type.SmallInt()
233
+ field.column(type.BigInt({ as: 'number' })); // was type.BigIntNumber()
234
+ field.column(type.Decimal({ precision: 10, scale: 2 })); // was type.Numeric(10, 2)
235
+ field.column(type.BigInt({ storage: 'numeric' })); // was type.UnboundedInt()
236
+ field.column(type.Float({ storage: 'float4' })); // was type.Real()
237
+ field.column(type.String({ storage: 'varchar', length: 64 })); // was type.VarChar(64)
238
+ field.column(type.Varbit(16)); // was type.VarBit(16)
239
+ ```
240
+
241
+ The explicit helpers `type.Int2()`, `type.Int8Number()`, `type.NumericString(10, 2)`, `type.NumericBigInt()`, `type.Float4()` and `type.Varchar(64)` give the same types.
242
+
243
+ The old helpers still compile, and nothing warns where they are called. They are removed in the same release as the deprecated schema names, so rewrite them now.
244
+
245
+ ### `ts-renamed-postgres-column-helpers`
246
+
247
+ The detection matches a file that imports `int8Column` or `numericColumn` from `@prisma/orm-postgres/adapter/column-types` or `@internal/adapter-postgres/column-types`. In that file, rename the import and every use of the imported name. Each column helper is named after the explicit type it gives:
248
+
249
+ ```ts
250
+ import { int8BigIntColumn, numericStringColumn } from '@prisma/orm-postgres/adapter/column-types';
251
+
252
+ field.column(int8BigIntColumn); // was int8Column
253
+ field.column(numericStringColumn(10, 2)); // was numericColumn(10, 2)
254
+ ```
255
+
256
+
257
+ ## Pack extensions are found through the search path
258
+
259
+ The pgvector pack's contract now declares `vector` in the unbound namespace, the same as this PSL:
260
+
261
+ ```prisma
262
+ namespace unbound {
263
+ extension vector {}
264
+ }
265
+ ```
266
+
267
+ ### `pack-extension-found-on-search-path`
268
+
269
+ 1. Run `prisma contract emit`. It writes the pack's new migration and head under `migrations/<pack>/`.
270
+ 2. Run `prisma db migrate` against each database. The new migration has no operations.
271
+
272
+ A database where the extension is in a schema that is not on the connection's search path now fails `db verify`, and the message names the schema and the search path. Add that schema to the search path of the role the application connects as, or reinstall the extension in a schema on the search path.
273
+
274
+ ### `remove-pack-extension-declaration`
275
+
276
+ If the project composes the pgvector, PostGIS, ParadeDB or citext pack, delete the application's own declaration of that pack's extension, then run `prisma contract emit`. The pack installs the extension. Keep the declaration of any other extension.
277
+
278
+
279
+ ## MongoDB indexes carry their names
280
+
281
+ Every index of a MongoDB contract now records its name, and migrations create and drop each index by that name. The Prisma 6 reader names indexes as Prisma 6 did, and it no longer merges them. Do the changes in this section with one `prisma contract emit` and one `prisma db sign` (or one migration) per project.
282
+
283
+ ### `mongo-contracts-record-index-names`
284
+
285
+ For this schema:
286
+
287
+ ```prisma
288
+ model Post {
289
+ id ObjectId @id @map("_id")
290
+ authorId ObjectId
291
+
292
+ @@index([authorId])
293
+ @@map("posts")
294
+ }
295
+ ```
296
+
297
+ each index in `contract.json` gains its name:
298
+
299
+ ```diff
300
+ {
301
+ "keys": [{ "direction": 1, "field": "authorId" }],
302
+ - "kind": "mongo-index"
303
+ + "kind": "mongo-index",
304
+ + "name": "authorId_1"
305
+ }
306
+ ```
307
+
308
+ `authorId_1` is the name the index already has in the database, so the database needs no change. Only the storage hash changes.
309
+
310
+ 1. Commit your work first, so the changes can be reviewed and undone with git.
311
+ 2. Run `prisma contract emit` and commit the regenerated `contract.json` and `contract.d.ts`.
312
+ 3. Run the colocated script from the project root:
313
+
314
+ ```sh
315
+ node <path-to-this-guide>/scripts/mongo-index-names/mongo-index-names.ts
316
+ ```
317
+
318
+ Node 24 or later runs the TypeScript script directly. It reads and writes files only. It rewrites every `*.json` file under the root that is a MongoDB contract with an index without a name, skipping `node_modules`, `.git`, `dist` and `build`, and the `contract.d.ts` next to it (`<name>.d.ts` next to `<name>.json`). These are the contract snapshots under `migrations/snapshots/<hash>/`, which no command regenerates. Each snapshot index takes the name that its own project's emitted contract gives the index with the same collection, keys and options, so a project whose contract comes from `prisma6Schema()` gets Prisma 6's names, such as `User_email_key`. A snapshot's project is the nearest folder, going up from the folder that holds its `migrations` folder, that holds an emitted contract, and the walk stops at the first folder with a `package.json`, the project root, so a name never comes from another project of a monorepo; contracts under a `snapshots` folder are never used as a source. When no emitted contract of the project has the index, or two of them name it differently, the index gets the name earlier releases gave it in the database, its keys and directions such as `email_1`, and the script prints one line saying so for each such index; check those names against the database with `prisma db verify` after the upgrade. The script writes each file in the layout `contract emit` writes it. The snapshot directory names stay as they are. A contract that step 2 emitted already has names and is left alone. The script prints each file it rewrote, or that nothing changed. It does not follow symbolic links: it names each one it finds and exits 1, and if a link holds migration snapshots, run the script again on the folder it points to. If it cannot match the index types of a `contract.d.ts` with its `contract.json`, it says so and exits 1; add `readonly name: '<name>'` beside `readonly kind: 'mongo-index'` in each index type by hand.
319
+
320
+ To rewrite a file by hand instead, add `"name"` to each object whose `kind` is `mongo-index`: the name the emitted contract gives the same index, or else its keys and directions, such as `"name": "createdAt_-1_authorId_1"` for the keys `createdAt` descending and `authorId` ascending. Add the matching type in `contract.d.ts`.
321
+ 4. Bring each database to the new storage hash:
322
+ - For a project kept with `db init` or `db update`, run `prisma db sign`.
323
+ - For a project with migrations, run `prisma migration plan`. The migration it plans has no operations; apply it with `prisma db migrate`.
324
+
325
+ For a disposable example or test database whose migration chain is regenerated from its schemas, regenerate the chain with its own tooling instead of steps 3 and 4, and recreate the database. In this repository, `pnpm fixtures:emit` re-emits every contract and regenerates the example migrations, writing their snapshots under the new storage hashes; delete the snapshot directories no migration names any more. This is not a procedure for a production database: keep applied migrations and their snapshots, and follow steps 3 and 4.
326
+
327
+ A database that a Prisma 6 schema read with `prisma6Schema()` describes holds the names Prisma 6 gave its indexes, not the names after their keys; see `mongo-prisma6-index-names`.
328
+
329
+ ### `mongo-unnamed-indexes-with-one-name-refused`
330
+
331
+ This schema is now refused, because both indexes would be named `email_1`:
332
+
333
+ ```prisma
334
+ model User {
335
+ id ObjectId @id @map("_id")
336
+ email String @unique
337
+
338
+ @@index([email], sparse: true)
339
+ @@map("users")
340
+ }
341
+ ```
342
+
343
+ Name one of them:
344
+
345
+ ```prisma
346
+ @@index([email], sparse: true, map: "email_sparse")
347
+ ```
348
+
349
+ In TypeScript:
350
+
351
+ ```ts
352
+ indexes: [index({ email: 1 }, { unique: true }), index({ email: 1 }, { sparse: true, name: 'email_sparse' })],
353
+ ```
354
+
355
+ No database holds such a pair yet: earlier releases planned both under `email_1`, and MongoDB refused the second. The index you leave unnamed keeps its name, so its database index stays; the migration creates the one you named.
356
+
357
+ Two indexes of variants of one base on the same field are refused the same way; name each, such as `@@index([title], map: "bug_title")` on `Bug` and `@@index([title], map: "feature_title")` on `Feature`.
358
+
359
+ Two identical indexes with different names are refused too, because MongoDB cannot hold an index twice: PSL reports `PSL_INVALID_INDEX` at the second, and TypeScript throws `CONTRACT.NAME_DUPLICATE`. Remove one, or give both the same name, which merges them.
360
+
361
+ ### `mongo-prisma6-indexes-as-prisma6-reads-them`
362
+
363
+ `prisma6Schema()` describes the database Prisma 6 built, so it no longer merges indexes. This schema is now refused, because Prisma 6 refuses it too:
364
+
365
+ ```prisma
366
+ model User {
367
+ id String @id @default(auto()) @map("_id") @db.ObjectId
368
+ email String @unique
369
+
370
+ @@index([email])
371
+ }
372
+ ```
373
+
374
+ 1. Run `prisma contract emit` for each project that reads a Prisma 6 schema with `prisma6Schema()`.
375
+ 2. If it reports `PSL.PRISMA6_MONGO_INDEX_ALREADY_EXISTS`, the schema has two indexes that Prisma 6 refuses, or two that Prisma 6 accepts but MongoDB cannot hold, because they have the same keys and options in one collection. The message names both. If one is unique and the other is not, remove the one that is not, so the unique constraint stays. Otherwise remove either.
376
+ 3. If the storage hash changed, the schema writes a unique and a plain index on the same keys with different written sorts, and the contract now holds both, as the database does. Run `prisma db sign` again.
377
+
378
+ ### `mongo-prisma6-index-names`
379
+
380
+ For this Prisma 6 schema:
381
+
382
+ ```prisma
383
+ model User {
384
+ id String @id @default(auto()) @map("_id") @db.ObjectId
385
+ email String @unique
386
+
387
+ @@index([email(sort: Asc)])
388
+ }
389
+ ```
390
+
391
+ the contract names the indexes `User_email_key` and `User_email_idx`, as Prisma 6 `db push` named them. Run `prisma contract emit` and `prisma db verify`; a database Prisma 6 built verifies. Then run `prisma db sign` for the new storage hash. Run the script of `mongo-contracts-record-index-names` after the emit: it gives each index of your migration snapshots the Prisma 6 name the emitted contract gives it, so the next `prisma migration plan` does not drop and recreate them.
392
+
393
+ If the emit reports `PSL.PRISMA6_MONGO_INDEX_NAME_DUPLICATE`, two indexes of one model get one name, which Prisma 6 refuses too, such as `@@index([name])` beside `@@index([name(sort: Asc)])`, both named `User_name_idx`. Give one a name with `map:`. If it reports `PSL.PRISMA6_MONGO_INDEX_NAME_INVALID`, a `map:` is empty or longer than 127 bytes; give the index a name Prisma 6 accepts.
394
+
395
+ If `db verify` reports an index whose name differs, the database holds that index under another name than Prisma 6 would give it. Write that name with `map:`, so the contract states what the database holds.
396
+
397
+ ### `mongo-index-operations-by-name`
398
+
399
+ A `migration.ts` that `prisma migration plan` writes now names each index:
400
+
401
+ ```ts
402
+ // before
403
+ createIndex('users', [{ field: 'email', direction: 1 }], { unique: true }),
404
+ dropIndex('users', [{ field: 'legacy', direction: 1 }]),
405
+ // after
406
+ createIndex('users', [{ field: 'email', direction: 1 }], { name: 'email_1', unique: true }),
407
+ dropIndex('users', [{ field: 'legacy', direction: 1 }], { name: 'legacy_1', unique: true }),
408
+ ```
409
+
410
+ `dropIndex`'s options must list every option the index has, as `migration plan` writes them: its precheck requires an index with that name, those keys and exactly those options, so `dropIndex('users', keys, { name: 'legacy_1' })` fails for a unique index. A `dropIndex` without options checks the keys only.
411
+
412
+ Existing migration files need no change: a call without `name` uses the name after the keys, as before, and a `dropIndex` without options checks the index's keys only, as before. An applied migration's `ops.json` is applied as written.
413
+
414
+ A script or test that matches an operation id, such as `index.users.create(email:1)`, matches the name instead: `index.users.create(email_1)`.
415
+
416
+
417
+ ## `mongo()` checks the extension packs a contract requires
418
+
419
+ A contract built with extension packs records them, and `mongo()` and `mongoStatic()` now refuse it unless they are given the same packs:
420
+
421
+ ```typescript
422
+ import mongo from '@prisma/orm-mongo/runtime';
423
+ import myPack from 'my-mongo-pack/runtime';
424
+
425
+ export const db = mongo({ contract, url, extensions: [myPack] });
426
+ ```
427
+
428
+ ### `mongo-client-takes-extensions`
429
+
430
+ 1. For each extension pack in `extensions` in `prisma.config.ts`, import the pack's runtime descriptor, usually from its `/runtime` entry.
431
+ 2. Pass the descriptors as `extensions` to every `mongo(...)` and `mongoStatic(...)` call that takes this project's contract.
432
+
433
+ ### `mongo-contract-requires-target`
434
+
435
+ Re-emit the contract with `prisma contract emit`, or add `"target": "mongo"` beside `"targetFamily": "mongo"` in a contract written by hand.
436
+
437
+
438
+ ## `supabase()` and `postgres()` check their database input when they are called
439
+
440
+ ### `supabase-one-binding-input`
441
+
442
+ ```ts
443
+ // Before: pg was used and url ignored
444
+ const db = await supabase({ contract, url, pg: pool, jwtSecret });
445
+
446
+ // After
447
+ const db = await supabase({ contract, pg: pool, jwtSecret });
448
+ ```
449
+
450
+ In each `supabase(...)` call, keep exactly one of `url`, `pg` and `binding`. If you import `supabase` under another name (`import { supabase as createDb } from ...`), check the calls of that name. If you passed both `url` and `pg`, keep `pg`: that is the one the client used.
451
+
452
+ If `url` comes from an environment variable, make sure it is set. An empty `url` used to connect to whatever the `PG*` environment variables or localhost pointed at; it now fails at startup.
453
+
454
+ Code or tests that match a `pg` refusal on `SUPABASE.CONFIG_INVALID` keep working for a `pg.Client`. Update a match on the old message "The pg option takes a pg.Pool" to "Supabase takes a pg.Pool or a url, not a pg.Client". A `pg` value that is neither a pool nor a client now fails with `RUNTIME.BINDING_INVALID`.
455
+
456
+ `supabase()`'s errors about its input list only what it takes, and their `meta.extension` is `'supabase'`.
457
+
458
+ ### `postgres-checks-database-input`
459
+
460
+ These checks apply to `postgres()` and to `supabase()`.
461
+
462
+ `null` as `pg`, `url` or `binding` now fails with `RUNTIME.BINDING_INVALID` on `postgres()` and on `supabase()`, even when another input is given beside it: `{ url: null, pg: pool }` fails on `url`. Code such as `pg: config.pool ?? null`, `url: value || null`, `url: ready ? value : null` or `url: ready ? null : value` should leave the option out instead:
463
+
464
+ ```ts
465
+ // Before
466
+ postgres({ contract, url: process.env['DATABASE_URL'] ?? null });
467
+
468
+ // After
469
+ const url = process.env['DATABASE_URL'];
470
+ postgres({ contract, ...(url === undefined ? {} : { url }) });
471
+ ```
472
+
473
+ A `binding` whose `kind` is not `'url'`, `'pgPool'` or `'pgClient'` now fails with `RUNTIME.BINDING_INVALID` when `postgres()` or `supabase()` is called, not on the first query. Correct the kind, for example `{ kind: 'pgpool', pool }` becomes `{ kind: 'pgPool', pool }`.
474
+
475
+ A `binding` of kind `'url'` now has its `url` checked like a top-level `url`: it must be a non-empty `postgres://` or `postgresql://` string. An empty one used to connect to whatever the `PG*` environment variables or localhost pointed at. A `binding` whose `pool` or `client` is missing or not an object now fails when `postgres()` is called, not on the first query.
476
+
477
+
478
+ ## `contract-artifacts-restamp`
479
+
480
+ For every `contract.json` matched by `detection`, run the project's emit command (`prisma contract emit`, or the project's `contract:emit` script) once after upgrading. This entry accounts for the extension's embedded `version` moving to `8.0.0-rc.23`; any other difference in the emitted files comes from an earlier entry in this guide.