@prisma/orm-mongo 8.0.0-rc.13-dev.12 → 8.0.0-rc.14
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.
- package/dist/config.mjs +3 -2
- package/dist/config.mjs.map +1 -1
- package/package.json +8 -8
- package/skills/prisma-8/SKILL.md +1 -1
- package/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.13-to-8.0.0-rc.14/instructions.md +489 -0
- package/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.13-to-8.0.0-rc.14/instructions.md +143 -0
package/dist/config.mjs
CHANGED
|
@@ -6,10 +6,10 @@ import { mongoContract } from "@prisma/orm-family-mongo/contract-psl/provider";
|
|
|
6
6
|
import { typescriptContractFromPath } from "@prisma/orm-family-mongo/contract-ts/config-types";
|
|
7
7
|
import { MONGO_INT32_CODEC_ID, MONGO_STRING_CODEC_ID } from "@prisma/orm-target-mongo/target/codec-ids";
|
|
8
8
|
import { mongoTargetDescriptor } from "@prisma/orm-target-mongo/target/control";
|
|
9
|
+
import { prisma6MongoBinding } from "@prisma/orm-target-mongo/target/prisma6-binding";
|
|
9
10
|
import { ifDefined } from "@prisma/orm-framework/utils/defined";
|
|
10
11
|
import { extname, join } from "pathe";
|
|
11
12
|
import { prisma6Contract } from "@prisma/orm-family-mongo/contract-prisma6/provider";
|
|
12
|
-
import { prisma6MongoBinding } from "@prisma/orm-target-mongo/target/prisma6-binding";
|
|
13
13
|
//#region ../../../3-extensions/mongo/dist/config.mjs
|
|
14
14
|
function contractConfigFromPath(contractPath, output) {
|
|
15
15
|
return extname(contractPath) === ".ts" ? typescriptContractFromPath(contractPath, output) : mongoContract(contractPath, {
|
|
@@ -17,7 +17,8 @@ function contractConfigFromPath(contractPath, output) {
|
|
|
17
17
|
enumInferenceCodecs: {
|
|
18
18
|
text: MONGO_STRING_CODEC_ID,
|
|
19
19
|
int: MONGO_INT32_CODEC_ID
|
|
20
|
-
}
|
|
20
|
+
},
|
|
21
|
+
formerScalarCodecIds: prisma6MongoBinding.scalarCodecIds
|
|
21
22
|
});
|
|
22
23
|
}
|
|
23
24
|
function resolveContractConfig(options) {
|
package/dist/config.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.mjs","names":[],"sources":["../../../../3-extensions/mongo/dist/config.mjs"],"sourcesContent":["import mongoAdapter from \"@internal/adapter-mongo/control\";\nimport { defaultContractOutputPath, defineConfig as defineConfig$1 } from \"@internal/config/config-types\";\nimport mongoDriver from \"@internal/driver-mongo/control\";\nimport { mongoFamilyDescriptor } from \"@internal/family-mongo/control\";\nimport { mongoContract } from \"@internal/mongo-contract-psl/provider\";\nimport { typescriptContractFromPath } from \"@internal/mongo-contract-ts/config-types\";\nimport { MONGO_INT32_CODEC_ID, MONGO_STRING_CODEC_ID } from \"@internal/target-mongo/codec-ids\";\nimport { mongoTargetDescriptor } from \"@internal/target-mongo/control\";\nimport { ifDefined } from \"@internal/utils/defined\";\nimport { extname, join } from \"pathe\";\nimport { prisma6Contract } from \"@internal/mongo-contract-prisma6/provider\";\
|
|
1
|
+
{"version":3,"file":"config.mjs","names":[],"sources":["../../../../3-extensions/mongo/dist/config.mjs"],"sourcesContent":["import mongoAdapter from \"@internal/adapter-mongo/control\";\nimport { defaultContractOutputPath, defineConfig as defineConfig$1 } from \"@internal/config/config-types\";\nimport mongoDriver from \"@internal/driver-mongo/control\";\nimport { mongoFamilyDescriptor } from \"@internal/family-mongo/control\";\nimport { mongoContract } from \"@internal/mongo-contract-psl/provider\";\nimport { typescriptContractFromPath } from \"@internal/mongo-contract-ts/config-types\";\nimport { MONGO_INT32_CODEC_ID, MONGO_STRING_CODEC_ID } from \"@internal/target-mongo/codec-ids\";\nimport { mongoTargetDescriptor } from \"@internal/target-mongo/control\";\nimport { prisma6MongoBinding } from \"@internal/target-mongo/prisma6-binding\";\nimport { ifDefined } from \"@internal/utils/defined\";\nimport { extname, join } from \"pathe\";\nimport { prisma6Contract } from \"@internal/mongo-contract-prisma6/provider\";\n//#region src/config/define-config.ts\nfunction contractConfigFromPath(contractPath, output) {\n\treturn extname(contractPath) === \".ts\" ? typescriptContractFromPath(contractPath, output) : mongoContract(contractPath, {\n\t\toutput,\n\t\tenumInferenceCodecs: {\n\t\t\ttext: MONGO_STRING_CODEC_ID,\n\t\t\tint: MONGO_INT32_CODEC_ID\n\t\t},\n\t\tformerScalarCodecIds: prisma6MongoBinding.scalarCodecIds\n\t});\n}\nfunction resolveContractConfig(options) {\n\tconst explicitOutput = options.output !== void 0 ? join(options.output, \"contract.json\") : void 0;\n\tif (typeof options.contract === \"string\") return contractConfigFromPath(options.contract, explicitOutput ?? defaultContractOutputPath(options.contract));\n\tconst firstInput = options.contract.source.inputs?.[0];\n\tconst output = explicitOutput ?? options.contract.output ?? (firstInput !== void 0 ? defaultContractOutputPath(firstInput) : void 0);\n\treturn {\n\t\t...options.contract,\n\t\t...ifDefined(\"output\", output)\n\t};\n}\nfunction defineConfig(options) {\n\treturn defineConfig$1({\n\t\tfamily: mongoFamilyDescriptor,\n\t\ttarget: mongoTargetDescriptor,\n\t\tadapter: mongoAdapter,\n\t\tdriver: mongoDriver,\n\t\textensions: options.extensions ?? [],\n\t\tcontract: resolveContractConfig(options),\n\t\t...ifDefined(\"db\", options.db),\n\t\t...ifDefined(\"migrations\", options.migrations)\n\t});\n}\n//#endregion\n//#region src/config/prisma6-schema.ts\n/**\n* Reads a Prisma 6 MongoDB `schema.prisma` (or a directory of `.prisma` files) as the\n* contract source, so Prisma 8 can adopt a database a Prisma 6 application still uses.\n*/\nfunction prisma6Schema(schemaPath) {\n\treturn prisma6Contract(schemaPath, { binding: prisma6MongoBinding });\n}\n//#endregion\nexport { defineConfig, prisma6Schema };\n\n//# sourceMappingURL=config.mjs.map"],"mappings":";;;;;;;;;;;;;AAaA,SAAS,uBAAuB,cAAc,QAAQ;CACrD,OAAO,QAAQ,YAAY,MAAM,QAAQ,2BAA2B,cAAc,MAAM,IAAI,cAAc,cAAc;EACvH;EACA,qBAAqB;GACpB,MAAM;GACN,KAAK;EACN;EACA,sBAAsB,oBAAoB;CAC3C,CAAC;AACF;AACA,SAAS,sBAAsB,SAAS;CACvC,MAAM,iBAAiB,QAAQ,WAAW,KAAK,IAAI,KAAK,QAAQ,QAAQ,eAAe,IAAI,KAAK;CAChG,IAAI,OAAO,QAAQ,aAAa,UAAU,OAAO,uBAAuB,QAAQ,UAAU,kBAAkB,0BAA0B,QAAQ,QAAQ,CAAC;CACvJ,MAAM,aAAa,QAAQ,SAAS,OAAO,SAAS;CACpD,MAAM,SAAS,kBAAkB,QAAQ,SAAS,WAAW,eAAe,KAAK,IAAI,0BAA0B,UAAU,IAAI,KAAK;CAClI,OAAO;EACN,GAAG,QAAQ;EACX,GAAG,UAAU,UAAU,MAAM;CAC9B;AACD;AACA,SAAS,aAAa,SAAS;CAC9B,OAAO,eAAe;EACrB,QAAQ;EACR,QAAQ;EACR,SAAS;EACT,QAAQ;EACR,YAAY,QAAQ,cAAc,CAAC;EACnC,UAAU,sBAAsB,OAAO;EACvC,GAAG,UAAU,MAAM,QAAQ,EAAE;EAC7B,GAAG,UAAU,cAAc,QAAQ,UAAU;CAC9C,CAAC;AACF;;;;;AAOA,SAAS,cAAc,YAAY;CAClC,OAAO,gBAAgB,YAAY,EAAE,SAAS,oBAAoB,CAAC;AACpE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma/orm-mongo",
|
|
3
|
-
"version": "8.0.0-rc.
|
|
3
|
+
"version": "8.0.0-rc.14",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -10,18 +10,18 @@
|
|
|
10
10
|
"skills"
|
|
11
11
|
],
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@prisma/orm-family-mongo": "8.0.0-rc.
|
|
14
|
-
"@prisma/orm-framework": "8.0.0-rc.
|
|
15
|
-
"@prisma/orm-target-mongo": "8.0.0-rc.
|
|
16
|
-
"@prisma/orm-toolchain": "8.0.0-rc.
|
|
13
|
+
"@prisma/orm-family-mongo": "8.0.0-rc.14",
|
|
14
|
+
"@prisma/orm-framework": "8.0.0-rc.14",
|
|
15
|
+
"@prisma/orm-target-mongo": "8.0.0-rc.14",
|
|
16
|
+
"@prisma/orm-toolchain": "8.0.0-rc.14",
|
|
17
17
|
"mongodb-connection-string-url": "^7.0.1",
|
|
18
18
|
"pathe": "^2.0.3",
|
|
19
19
|
"tinyglobby": "^0.2.17"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
|
-
"@internal/mongo": "8.0.0-rc.
|
|
23
|
-
"@repo/tsconfig": "8.0.0-rc.
|
|
24
|
-
"@repo/tsdown": "8.0.0-rc.
|
|
22
|
+
"@internal/mongo": "8.0.0-rc.14",
|
|
23
|
+
"@repo/tsconfig": "8.0.0-rc.14",
|
|
24
|
+
"@repo/tsdown": "8.0.0-rc.14",
|
|
25
25
|
"tsdown": "0.22.14",
|
|
26
26
|
"typescript": "5.9.3"
|
|
27
27
|
},
|
package/skills/prisma-8/SKILL.md
CHANGED
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
---
|
|
2
|
+
from: "8.0.0-rc.13"
|
|
3
|
+
to: "8.0.0-rc.14"
|
|
4
|
+
changes:
|
|
5
|
+
- id: engine-pin-moves-to-0-6-2
|
|
6
|
+
summary: |
|
|
7
|
+
The toolchain now requires `@prisma/cli-engine@0.6.2` (up from 0.6.1). A project that pins `@prisma/cli-engine` itself must move the pin to `0.6.2`. With this engine the CLI prints its own name in hints and messages where it used to print a literal `{bin}`.
|
|
8
|
+
detection:
|
|
9
|
+
glob: "**/package.json"
|
|
10
|
+
contains:
|
|
11
|
+
- '"@prisma/cli-engine": "0.6.1"'
|
|
12
|
+
- id: temporal-polyfill-is-a-peer-dependency
|
|
13
|
+
summary: |
|
|
14
|
+
`temporal-polyfill` is now a required peer dependency of `@prisma/orm-postgres` and `@prisma/orm-target-postgres`, not a dependency. The Postgres control plane, such as the `prisma` commands and the Vite plugin, imports it. npm, pnpm and bun install it automatically. A project that installs with Yarn must add `temporal-polyfill` (`^1.0.4`) to its own dependencies.
|
|
15
|
+
detection:
|
|
16
|
+
glob: "**/yarn.lock"
|
|
17
|
+
contains:
|
|
18
|
+
- "@prisma/orm-postgres@"
|
|
19
|
+
- "@prisma/orm-target-postgres@"
|
|
20
|
+
anyMatch: true
|
|
21
|
+
- id: serverless-connect-returns-connection
|
|
22
|
+
summary: "connect({ url }) on the serverless client from @prisma/orm-postgres/serverless returns a connection, not a Runtime. Call db.runtime().query(plan) and db.runtime().execute(plan), and pass db.runtime() wherever the connect() result was used as a runtime. connect() now connects before it returns and rejects with DRIVER.CONNECTION_FAILED when the database cannot be reached."
|
|
23
|
+
detection:
|
|
24
|
+
glob: "**/*.{ts,mts,cts,tsx,js,mjs,cjs,jsx}"
|
|
25
|
+
matches:
|
|
26
|
+
- '[''"]@prisma/orm-postgres/serverless[''"]'
|
|
27
|
+
- id: serverless-connection-orm-and-transaction
|
|
28
|
+
summary: "Optional: the connection has orm and transaction(fn), so db.orm replaces a hand-built orm({ runtime, context }) in queries that call no custom collection method, and db.transaction(fn) replaces withTransaction(runtime, fn)."
|
|
29
|
+
detection:
|
|
30
|
+
glob: "**/*.{ts,mts,cts,tsx,js,mjs,cjs,jsx}"
|
|
31
|
+
matches:
|
|
32
|
+
- '[''"]@prisma/orm-postgres/serverless[''"]'
|
|
33
|
+
- id: serverless-cursor-default-off
|
|
34
|
+
summary: "Reads through connections from @prisma/orm-postgres/serverless no longer go through a server-side cursor by default. A path that must keep batched streaming opens its connection from a second serverless client with cursor: { batchSize: 100 }; that path hangs behind Cloudflare Hyperdrive. PostgresServerlessCursorOptions is now PostgresCursorOptions, which is { batchSize?: number | undefined } with no disabled flag, so cursor: { disabled: true } no longer compiles and must be deleted."
|
|
35
|
+
detection:
|
|
36
|
+
glob: "**/*.{ts,mts,cts,tsx,js,mjs,cjs,jsx}"
|
|
37
|
+
matches:
|
|
38
|
+
- '[''"]@prisma/orm-postgres/serverless[''"]'
|
|
39
|
+
- id: date-time-default-stored-in-canonical-form
|
|
40
|
+
summary: |
|
|
41
|
+
A PSL date or time default is stored in `contract.json` in its type's canonical form, however it was written: `@default("2024-01-01T01:00:00+01:00")` on a `DateTime` column is stored as `"2024-01-01T00:00:00Z"`. A default that was not already in that form gets a new storage hash when the contract is re-emitted. The database needs no change: re-emit, then `prisma db sign`, or record an empty migration with `prisma migration new`.
|
|
42
|
+
detection:
|
|
43
|
+
glob: "**/*.prisma"
|
|
44
|
+
matches:
|
|
45
|
+
- '\b(DateTime|Timestamptz|TimestamptzJsDate|TimestamptzString|Timestamp|TimestampString|Date|DateString|Time|TimeString|Timetz)(\([^)]*\))?(\[\])?\??([ \t]+@[\w.]+(\([^)\n]*\))?)*?[ \t]+@default\([\s\[]*"'
|
|
46
|
+
- id: date-time-default-refused-text
|
|
47
|
+
summary: |
|
|
48
|
+
`prisma contract emit` refuses a date or time default its column's type does not hold, with `PSL_INVALID_DEFAULT_LITERAL`: an offset on `Timestamp`, `Date` or `Time`, no offset on `DateTime`, `Timestamptz` or `Timetz`, a date on a time column, a time on a `Date` column, more than six digits after the decimal point (three on a SQLite `DateTime`), a date or time that does not exist, a year outside the range the type holds, or a ` BC` suffix on a SQLite `DateTime`. The message shows text the column takes.
|
|
49
|
+
detection:
|
|
50
|
+
glob: "**/*.prisma"
|
|
51
|
+
matches:
|
|
52
|
+
- '\b(DateTime|Timestamptz|TimestamptzJsDate|TimestamptzString|Timestamp|TimestampString|Date|DateString|Time|TimeString|Timetz)(\([^)]*\))?(\[\])?\??([ \t]+@[\w.]+(\([^)\n]*\))?)*?[ \t]+@default\([\s\[]*"'
|
|
53
|
+
- id: date-time-ts-default-stored-in-canonical-form
|
|
54
|
+
summary: |
|
|
55
|
+
In a TypeScript contract, a default on `field.temporal.timestamptzJsDate()`, `field.temporal.timestamptzString()`, `field.temporal.timestampString()`, the SQLite `field.temporal.datetime()`, and the `dateStringColumn`, `timeStringColumn`, `timetzColumn` and SQLite `sqliteDatetimeColumn` helpers is stored in the same canonical form as in PSL: `new Date('2024-01-01T00:00:00Z')` is stored as `"2024-01-01T00:00:00Z"`, not `"2024-01-01T00:00:00.000Z"`. A `Temporal` default with digits below one microsecond is refused. The storage hash changes; re-emit, then sign or migrate as for PSL.
|
|
56
|
+
detection:
|
|
57
|
+
glob: "**/*.{ts,mts,cts}"
|
|
58
|
+
matches:
|
|
59
|
+
- '\.(timestamptzJsDate|timestamptzString|timestampString|datetime)\([^)]*\)(\s*\.\w+\([^)]*\))*?\s*\.default\('
|
|
60
|
+
- '\b(timestamptzJsDateColumn|timestamptzStringColumn|timestampStringColumn|dateStringColumn|timeStringColumn|timetzColumn|sqliteDatetimeColumn|datetimeColumn)\b[^\n]*\.default\('
|
|
61
|
+
- id: mongo-create-returns-stored-document
|
|
62
|
+
summary: |
|
|
63
|
+
Mongo `create()` and `createAll()` now return each inserted document as stored, decoded like
|
|
64
|
+
a read, instead of the input with the new `_id`. The ORM computes it from the document it
|
|
65
|
+
sent, through the client's BSON options, without a second query. A `Bson` field comes back
|
|
66
|
+
as a read returns it (a `Long` in the safe-integer range as a number, bytes as `Binary`, a
|
|
67
|
+
`BSONRegExp` as a `RegExp`), and a nullable field left out comes back as `null`.
|
|
68
|
+
- id: mongo-reads-decode-includes-value-objects-and-absent-fields
|
|
69
|
+
summary: |
|
|
70
|
+
Mongo reads now decode included documents (`include(...)`) and composite-type (value
|
|
71
|
+
object) fields through their codecs, and a nullable field missing from the stored document
|
|
72
|
+
reads as `null` instead of `undefined`.
|
|
73
|
+
- id: mongo-where-filter-expressions-encoded
|
|
74
|
+
summary: |
|
|
75
|
+
A filter expression passed to the Mongo ORM's `where()` now encodes its comparison and
|
|
76
|
+
`$in`/`$nin` values through the field's codec, as the object form of `where()` does, and each
|
|
77
|
+
element of a whole-list comparison through the element codec. A value the codec refuses,
|
|
78
|
+
such as a driver `Long` for an `Int64` field (the codec takes a `bigint`) or a fraction for
|
|
79
|
+
an `Int32` field, fails with `RUNTIME.ENCODE_FAILED`. The object form of `where()` now also
|
|
80
|
+
refuses a fraction or an out-of-range number for an `Int32` field. This applies to the ORM
|
|
81
|
+
only: the query builder's `match()` sends values as given.
|
|
82
|
+
detection:
|
|
83
|
+
glob: "**/*.{ts,mts,cts}"
|
|
84
|
+
matches:
|
|
85
|
+
- '\.where\(\s*(?:MongoFieldFilter|MongoAndExpr|MongoOrExpr|MongoNotExpr)\.'
|
|
86
|
+
- id: mongo-writes-check-int32-enum-and-null-values
|
|
87
|
+
summary: |
|
|
88
|
+
The Mongo ORM refuses, with `RUNTIME.ENCODE_FAILED` naming the field, a write of a fraction
|
|
89
|
+
or an out-of-range number to an `Int32` field, of a value outside the field's enum, and of
|
|
90
|
+
`null` to a field that is not nullable. Before, depending on the field's type, the field's
|
|
91
|
+
codec refused it, a contract with a collection validator got a bare "Document failed
|
|
92
|
+
validation" from the server, or a contract without one (TypeScript builder, Prisma 6 schema)
|
|
93
|
+
stored the value. Filters still accept a value outside the enum and `null`, so they can find
|
|
94
|
+
documents that hold one.
|
|
95
|
+
- id: mongo-codecs-check-value-types
|
|
96
|
+
summary: |
|
|
97
|
+
The Mongo `String`, `Bool`, `Date`, `ObjectId` and vector codecs refuse a value of the wrong
|
|
98
|
+
type with `RUNTIME.ENCODE_FAILED` naming the field, in writes and filters, as `Int32` and
|
|
99
|
+
`Double` do. Before, a `null` element in a `String[]`, `Bool[]` or `Date[]` list or a
|
|
100
|
+
mistyped value was stored as given, an invalid `Date` was stored as the epoch, and `null`,
|
|
101
|
+
`undefined` or a number for an `ObjectId` field became a new id or a timestamp.
|
|
102
|
+
- id: mongo-list-elements-encoded
|
|
103
|
+
summary: |
|
|
104
|
+
The Mongo ORM encodes each element of a list field through the field's codec, on writes and
|
|
105
|
+
in filters. An `ObjectId[]`, `Int64[]`, `Decimal128[]` or `Binary[]` field now stores its
|
|
106
|
+
elements as `ObjectId`, `long`, `decimal` and `binData`, where before the whole list reached
|
|
107
|
+
the element codec and the write failed with `RUNTIME.ENCODE_FAILED`. A whole number in a
|
|
108
|
+
`Double[]` field is stored as a `double`.
|
|
109
|
+
- id: mongo-query-builder-bson-values-match
|
|
110
|
+
summary: |
|
|
111
|
+
A query-builder filter or raw command that compares with a driver class such as `ObjectId`,
|
|
112
|
+
`Long`, `Decimal128` or `Binary` now sends it as that BSON value. Before, the Mongo adapter
|
|
113
|
+
copied it into a plain object, so the filter matched nothing.
|
|
114
|
+
- id: mongo-upsert-keeps-create-values
|
|
115
|
+
summary: |
|
|
116
|
+
When `create` sets a field that has an update default (`@updatedAt`,
|
|
117
|
+
`temporal.updatedAt()`, `temporal.timestamp(onUpdate: now)`), `upsert()` now inserts the
|
|
118
|
+
`create` value, and still applies the update default on update, in one atomic command.
|
|
119
|
+
Before, the insert got the current time instead. Such an upsert refuses `pull()` by a match
|
|
120
|
+
document with `ORM.OPERATION_UNSUPPORTED`.
|
|
121
|
+
- id: contract-source-warnings-are-diagnostics
|
|
122
|
+
summary: |
|
|
123
|
+
`prisma contract emit` and `prisma contract print` report contract source warnings, such as
|
|
124
|
+
`PSL_DEPRECATED_SCALAR_NAME`, as `warn` diagnostics of the result instead of free-text
|
|
125
|
+
`warning …` messages. With `--json` they are in the result envelope's `diagnostics`. Their
|
|
126
|
+
file, and the file of a source error from either command, is shown relative to the working
|
|
127
|
+
directory.
|
|
128
|
+
- id: mongo-ts-preset-field-not-optional
|
|
129
|
+
summary: |
|
|
130
|
+
In a Mongo TypeScript contract, `.optional()` or `.many()` on a field a preset fills, such
|
|
131
|
+
as `field.temporal.createdAt()`, is now a type error that says "A preset fills this field on
|
|
132
|
+
write, so it cannot be optional" (or "a list"); it always failed when the contract was built.
|
|
133
|
+
detection:
|
|
134
|
+
glob: "**/*.{ts,mts,cts}"
|
|
135
|
+
matches:
|
|
136
|
+
- '\btemporal\.(?:createdAt|updatedAt|timestamp)\([^)]*\)\s*\.(?:optional|many)\('
|
|
137
|
+
- id: contract-artifacts-restamp
|
|
138
|
+
summary: |
|
|
139
|
+
The emitted `contract.json` / `contract.d.ts` embed the toolchain version, which moves
|
|
140
|
+
to 8.0.0-rc.14. Run `contract emit` once after upgrading so the emitted artifacts match
|
|
141
|
+
the installed toolchain.
|
|
142
|
+
detection:
|
|
143
|
+
glob: "**/contract.json"
|
|
144
|
+
contains:
|
|
145
|
+
- '"version": "8.0.0-rc.13"'
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
# 8.0.0-rc.13 → 8.0.0-rc.14 — User upgrade instructions
|
|
149
|
+
|
|
150
|
+
## `engine-pin-moves-to-0-6-2`
|
|
151
|
+
|
|
152
|
+
For every `package.json` matched by `detection`, change the `@prisma/cli-engine` version from `0.6.1` to `0.6.2` and reinstall. Projects assembled by the `prisma` CLI resolve the engine automatically.
|
|
153
|
+
|
|
154
|
+
Engine 0.6.2 replaces the placeholder `{bin}` with the name of the CLI that was run. A hint that used to print as `{bin} db migrate` now prints as `prisma db migrate`. The replacement applies to next actions, warnings, errors, and summary and list text, in human, `--json`, and `--format markdown` output. If a script or a test of yours matches the literal text `{bin}` in the CLI's output, change it to match the CLI name.
|
|
155
|
+
|
|
156
|
+
## `temporal-polyfill-is-a-peer-dependency`
|
|
157
|
+
|
|
158
|
+
For each Yarn project that `detection` matches, add `"temporal-polyfill": "^1.0.4"` to `dependencies` in the `package.json` that depends on `@prisma/orm-postgres` or `@prisma/orm-target-postgres`, then reinstall. Without it, `prisma contract emit` and the other commands fail because Node.js cannot find the package `temporal-polyfill`.
|
|
159
|
+
|
|
160
|
+
A project that already depends on `temporal-polyfill` with a range inside `^1.0.4` needs no change.
|
|
161
|
+
|
|
162
|
+
## `serverless-connect-returns-connection`
|
|
163
|
+
|
|
164
|
+
`postgresServerless(...).connect({ url })` used to return a `Runtime`. It now returns a **connection** with the members of a `postgres()` client except `connect`: `sql`, `raw`, `enums`, `nativeEnums`, `context`, `contract`, `stack`, `orm`, `runtime()`, `transaction(fn)`, `prepare(...)`, `close()` and `[Symbol.asyncDispose]`. It is not a `Runtime`: it has no `query` or `execute`. `db.runtime()` returns the runtime. A connection owns one database connection, and `await using` still closes it when the scope ends.
|
|
165
|
+
|
|
166
|
+
The **serverless client** returned by `postgresServerless(...)` still holds no database connection. It now also has `raw`, `enums` and `nativeEnums`.
|
|
167
|
+
|
|
168
|
+
In each file that imports `@prisma/orm-postgres/serverless`, or that uses the result of its `connect()`:
|
|
169
|
+
|
|
170
|
+
1. Name the serverless client `postgres` and the connection that `connect()` returns `db`. With these names, code written for a `postgres()` client (`db.orm...`, `db.transaction(...)`, `db.runtime().query(...)`) works unchanged inside a request, as long as every query is awaited before the `await using` scope ends: the connection closes when the scope ends, so a query returned from the scope without `await` fails when its rows are read, with `CONTRACT.MARKER_READ_FAILED` (whose cause is `DRIVER.NOT_CONNECTED`) under the default options, or `DRIVER.NOT_CONNECTED` after an earlier awaited query or with `verifyMarker: false`. Update every import of the serverless client to the new name.
|
|
171
|
+
2. Replace `runtime.query(plan)` with `db.runtime().query(plan)`, and `runtime.execute(plan)` with `db.runtime().execute(plan)`. The old `connect()` result was a full `Runtime`, so the same applies to its other methods: `runtime.connection()`, `runtime.telemetry()` and `runtime.prepare(...)` become `db.runtime().connection()`, `db.runtime().telemetry()` and `db.runtime().prepare(...)`. `db.prepare(...)` also exists and accepts ORM queries as well as SQL plans. `db.sql` is the same object as `postgres.sql`, so `postgres.sql...` inside a request may be written `db.sql...`.
|
|
172
|
+
3. Anything that took the `connect()` result as a runtime takes `db.runtime()` instead: `withTransaction(runtime, fn)`, `orm({ runtime, context })`, `preparedStatement.query(runtime, params)`, and your own functions whose parameter is typed `Runtime`. A function that needs both the runtime and the context can take the connection, typed with `PostgresServerlessConnection<Contract>` from `@prisma/orm-postgres/serverless`, and read `db.runtime()` and `db.context`. That removes any cast of the serverless client's `context` to `ExecutionContext<Contract>`, and the `Runtime`, `ExecutionContext` and serverless client imports that only served it.
|
|
173
|
+
4. Update comments that describe the old shape. A comment that names the old variable or says the runtime is acquired through `db.connect(...)` now names `postgres.connect({ url })` and the connection `db`. A comment that calls the serverless client a facade, or calls a connection a per-request client or a runtime, now says serverless client or connection. For example, the doc comment on the serverless client says that it is built once per isolate, holds no database connection, and that each request opens its own connection with `postgres.connect({ url })`; it sits directly above the `postgresServerless(...)` declaration, so move it there if it sits above another declaration. Update any README that describes the old shape in the same way.
|
|
174
|
+
5. `connect({ url })` now connects to the database before it returns. It rejects with `DRIVER.CONNECTION_FAILED` when the database refuses the connection, rejects the credentials, or does not answer within 20 seconds, and leaves nothing open; a malformed URL, or one with a scheme such as `http://`, now fails at `connect()` with `RUNTIME.BINDING_INVALID`; before, `connect()` resolved and the first query failed instead. Move any handling of an unreachable database (a `try`/`catch` that answers with an error response, for example) from the first query to the `connect()` call. Answer a request that needs no query, such as an unknown route or a missing query parameter, before `connect()`, because `connect()` now opens a database connection whether or not a query follows; move such checks above the `connect()` call.
|
|
175
|
+
|
|
176
|
+
Before:
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
// src/prisma/db.ts
|
|
180
|
+
export const db = postgresServerless<Contract>({ contractJson });
|
|
181
|
+
|
|
182
|
+
// src/orm-client/client.ts
|
|
183
|
+
import type { Runtime } from '@prisma/orm-postgres/family-runtime';
|
|
184
|
+
import type { ExecutionContext } from '@prisma/orm-postgres/relational-core/query-lane-context';
|
|
185
|
+
import { db } from '../prisma/db';
|
|
186
|
+
|
|
187
|
+
const context = db.context as ExecutionContext<Contract>;
|
|
188
|
+
|
|
189
|
+
export function createOrmClient(runtime: Runtime) {
|
|
190
|
+
return orm({ runtime, context, collections: { User: UserCollection } }).public;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// src/worker.ts
|
|
194
|
+
import { db } from './prisma/db';
|
|
195
|
+
|
|
196
|
+
await using runtime = await db.connect({ url: env.HYPERDRIVE.connectionString });
|
|
197
|
+
const rows = await runtime.query(db.sql.public.user.select('id').build());
|
|
198
|
+
const users = await createOrmClient(runtime).User.newestFirst().all();
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
After:
|
|
202
|
+
|
|
203
|
+
```ts
|
|
204
|
+
// src/prisma/db.ts
|
|
205
|
+
/**
|
|
206
|
+
* Serverless client, built once per isolate. It holds no database connection. Each request opens
|
|
207
|
+
* its own connection with `postgres.connect({ url })`.
|
|
208
|
+
*/
|
|
209
|
+
export const postgres = postgresServerless<Contract>({ contractJson });
|
|
210
|
+
|
|
211
|
+
// src/orm-client/client.ts
|
|
212
|
+
import { orm } from '@prisma/orm-postgres/orm-client';
|
|
213
|
+
import type { PostgresServerlessConnection } from '@prisma/orm-postgres/serverless';
|
|
214
|
+
import type { Contract } from '../prisma/contract.d';
|
|
215
|
+
import { UserCollection } from './collections';
|
|
216
|
+
|
|
217
|
+
export function createOrmClient(
|
|
218
|
+
db: Pick<PostgresServerlessConnection<Contract>, 'runtime' | 'context'>,
|
|
219
|
+
) {
|
|
220
|
+
return orm({
|
|
221
|
+
runtime: db.runtime(),
|
|
222
|
+
context: db.context,
|
|
223
|
+
collections: { User: UserCollection },
|
|
224
|
+
}).public;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// src/worker.ts
|
|
228
|
+
import { postgres } from './prisma/db';
|
|
229
|
+
|
|
230
|
+
await using db = await postgres.connect({ url: env.HYPERDRIVE.connectionString });
|
|
231
|
+
const rows = await db.runtime().query(db.sql.public.user.select('id').build());
|
|
232
|
+
const users = await createOrmClient(db).User.newestFirst().all();
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
The same applies to scripts that connect through the serverless client, for example a seed script: `await using db = await postgres.connect({ url })`, then `db.runtime().execute(...)`.
|
|
236
|
+
|
|
237
|
+
## `serverless-connection-orm-and-transaction`
|
|
238
|
+
|
|
239
|
+
This change is optional. The connection builds an ORM client and runs transactions itself.
|
|
240
|
+
|
|
241
|
+
- For each query on a hand-built `orm({ runtime, context })` client, check whether it calls a method defined on a custom collection class. If it calls none, run it on `db.orm` instead, even when the hand-built client registers custom collections, and drop the hand-built client from that code path when nothing else there uses it. For example, `const orm = createOrmClient(db); const rows = await orm.Post.where({ userId }).all();` becomes `const rows = await db.orm.public.Post.where({ userId }).all();`. Keep the hand-built client, built from `db.runtime()` and `db.context`, for queries that call custom collection methods, such as `orm.User.newestFirst()`.
|
|
242
|
+
- Replace `withTransaction(runtime, async (tx) => ...)` with `db.transaction(async (tx) => ...)`. `tx` has the same `execute` and `query` as before, plus `tx.sql`, `tx.orm`, `tx.enums` and `tx.nativeEnums`. Remove the `withTransaction` import when nothing else uses it.
|
|
243
|
+
- Update comments and README text that name `withTransaction(...)`, or a hand-built ORM client for a query that now runs on `db.orm`, to say `db.transaction(...)` and `db.orm`.
|
|
244
|
+
|
|
245
|
+
Before:
|
|
246
|
+
|
|
247
|
+
```ts
|
|
248
|
+
import { withTransaction } from '@prisma/orm-postgres/family-runtime';
|
|
249
|
+
|
|
250
|
+
await using runtime = await db.connect({ url });
|
|
251
|
+
const posts = await orm({ runtime, context }).public.Post.where({ userId }).all();
|
|
252
|
+
await withTransaction(runtime, async (tx) => {
|
|
253
|
+
await tx.execute(db.sql.public.user.update({ displayName }).where((f, fns) => fns.eq(f.id, userId)).build());
|
|
254
|
+
});
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
After:
|
|
258
|
+
|
|
259
|
+
```ts
|
|
260
|
+
await using db = await postgres.connect({ url });
|
|
261
|
+
const posts = await db.orm.public.Post.where({ userId }).all();
|
|
262
|
+
await db.transaction(async (tx) => {
|
|
263
|
+
await tx.execute(db.sql.public.user.update({ displayName }).where((f, fns) => fns.eq(f.id, userId)).build());
|
|
264
|
+
});
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## `serverless-cursor-default-off`
|
|
268
|
+
|
|
269
|
+
`postgresServerless()` used to read through `pg-cursor` in batches of 100 rows unless you passed `cursor: { disabled: true }`. Reads are now buffered by default, the same as on `postgres()`: the whole result arrives before the first row is yielded. The `cursor` option is now `PostgresCursorOptions`, which is `{ batchSize?: number | undefined }`: leaving it unset keeps cursors off, and setting it turns them on, in batches of 100 when `batchSize` is omitted or `undefined` (`{}`), or of `n` for `{ batchSize: n }` with a positive integer `n`; any other `batchSize` fails the factory call. There is no `disabled` flag: `cursor: { disabled: true }` and `cursor: { disabled: false }` no longer compile, and a `disabled` key that reaches the factory at run time, from JavaScript or from options loaded from JSON, fails the call with `RUNTIME.ARGUMENT_INVALID`.
|
|
270
|
+
|
|
271
|
+
1. Find the paths that rely on batched streaming, for example `for await` over a large result with an early `break`. Leave the serverless client every other path uses without a `cursor` option; those paths now buffer. To keep a streaming path streaming, give it its own serverless client, for example `streamingPostgres`, created with the same options as the serverless client the other paths use (such as `middleware` and `extensions`) plus `cursor: { batchSize: 100 }`, and open that path's connection from it, so each request still opens one connection. Document on that serverless client that it is used only by that path and that reads through its connections hang behind Cloudflare Hyperdrive. Behind real Cloudflare Hyperdrive that path hangs, because reads with cursors on hang there; the other paths do not. If the path must work behind real Hyperdrive, do not create the second serverless client and accept buffered reads on it instead. Never put the `cursor` option on the serverless client every path uses. On a connection from the streaming serverless client, a `for await` over a read must end before the next query through `db`; a query inside the loop waits forever, because the cursor holds the connection's only database connection until the loop ends.
|
|
272
|
+
2. Delete `cursor: { disabled: true }` from `postgresServerless(...)` options. Unset is now off, and the flag no longer compiles. Delete `disabled: false` and keep the rest: `cursor: { disabled: false }` becomes `cursor: {}` (batches of 100), and `cursor: { disabled: false, batchSize: 50 }` becomes `cursor: { batchSize: 50 }`. Leave `cursor: { batchSize: n }` as it is; it means the same as before.
|
|
273
|
+
3. Replace the type `PostgresServerlessCursorOptions` with `PostgresCursorOptions`, exported from `@prisma/orm-postgres/serverless` and `@prisma/orm-postgres/runtime`.
|
|
274
|
+
4. Update comments and README text that say the serverless client streams through a cursor by default. Say which paths use the streaming serverless client, and that those paths hang behind real Cloudflare Hyperdrive while the other paths do not.
|
|
275
|
+
|
|
276
|
+
A streaming path, before:
|
|
277
|
+
|
|
278
|
+
```ts
|
|
279
|
+
// src/prisma/db.ts
|
|
280
|
+
export const postgres = postgresServerless<Contract>({ contractJson });
|
|
281
|
+
|
|
282
|
+
// src/worker.ts, in fetch; the /cursor/large path streams with `for await` and breaks early
|
|
283
|
+
await using db = await postgres.connect({ url: env.HYPERDRIVE.connectionString });
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
After:
|
|
287
|
+
|
|
288
|
+
```ts
|
|
289
|
+
// src/prisma/db.ts
|
|
290
|
+
export const postgres = postgresServerless<Contract>({ contractJson });
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Serverless client with cursors on, used only by the `/cursor/large` route to stream a large
|
|
294
|
+
* result. Reads through its connections hang behind Cloudflare Hyperdrive.
|
|
295
|
+
*/
|
|
296
|
+
export const streamingPostgres = postgresServerless<Contract>({
|
|
297
|
+
contractJson,
|
|
298
|
+
cursor: { batchSize: 100 },
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
// src/worker.ts
|
|
302
|
+
import { postgres, streamingPostgres } from './prisma/db';
|
|
303
|
+
|
|
304
|
+
// in fetch
|
|
305
|
+
const routePostgres = url.pathname === '/cursor/large' ? streamingPostgres : postgres;
|
|
306
|
+
await using db = await routePostgres.connect({ url: env.HYPERDRIVE.connectionString });
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
`cursor: { disabled: true }` and the old type, before:
|
|
310
|
+
|
|
311
|
+
```ts
|
|
312
|
+
import postgresServerless, {
|
|
313
|
+
type PostgresServerlessCursorOptions,
|
|
314
|
+
} from '@prisma/orm-postgres/serverless';
|
|
315
|
+
|
|
316
|
+
const cursor: PostgresServerlessCursorOptions = { disabled: true };
|
|
317
|
+
|
|
318
|
+
export const postgres = postgresServerless<Contract>({ contractJson, cursor });
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
After:
|
|
322
|
+
|
|
323
|
+
```ts
|
|
324
|
+
import postgresServerless from '@prisma/orm-postgres/serverless';
|
|
325
|
+
|
|
326
|
+
export const postgres = postgresServerless<Contract>({ contractJson });
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
`disabled: false` next to a batch size, before:
|
|
330
|
+
|
|
331
|
+
```ts
|
|
332
|
+
export const streamingPostgres = postgresServerless<Contract>({
|
|
333
|
+
contractJson,
|
|
334
|
+
cursor: { disabled: false, batchSize: 50 },
|
|
335
|
+
});
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
After:
|
|
339
|
+
|
|
340
|
+
```ts
|
|
341
|
+
export const streamingPostgres = postgresServerless<Contract>({ contractJson, cursor: { batchSize: 50 } });
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
A batch size alone, before and after (unchanged):
|
|
345
|
+
|
|
346
|
+
```ts
|
|
347
|
+
export const streamingPostgres = postgresServerless<Contract>({ contractJson, cursor: { batchSize: 100 } });
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
## `date-time-default-stored-in-canonical-form`
|
|
351
|
+
|
|
352
|
+
Each date and time type now stores one text for each value, its canonical form. These three defaults are one instant, and each is stored as `"2024-01-01T00:00:00Z"`:
|
|
353
|
+
|
|
354
|
+
```prisma
|
|
355
|
+
model Event {
|
|
356
|
+
id Int @id
|
|
357
|
+
a DateTime @default("2024-01-01T00:00:00Z")
|
|
358
|
+
b DateTime @default("2024-01-01T00:00:00.000Z")
|
|
359
|
+
c DateTime @default("2024-01-01T01:00:00+01:00")
|
|
360
|
+
}
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
| Column type | Written | Stored before | Stored now |
|
|
364
|
+
| --- | --- | --- | --- |
|
|
365
|
+
| `DateTime`, `Timestamptz` | `2024-01-01 01:00:00+01` | as written | `2024-01-01T00:00:00Z` (UTC) |
|
|
366
|
+
| `Timestamp` | `2024-01-01 12:34:56.500` | as written | `2024-01-01T12:34:56.5` |
|
|
367
|
+
| `Date` | `0044-03-15 BC` | as written | `-000043-03-15` |
|
|
368
|
+
| `Time` | `12:34` | as written | `12:34:00` |
|
|
369
|
+
| `Timetz` | `12:34:56+02` | as written | `12:34:56+02:00` |
|
|
370
|
+
| SQLite `DateTime` | `2024-01-01 01:00:00+01:00` | as written | `2024-01-01T00:00:00Z` |
|
|
371
|
+
|
|
372
|
+
A year outside 0000 to 9999 is a sign and six digits, and year 0000 is 1 BC. `prisma contract infer` and `prisma contract print` print a date or time default in the same form.
|
|
373
|
+
|
|
374
|
+
1. Search your `.prisma` files for `@default("` on a date or time column.
|
|
375
|
+
2. Run `prisma contract emit` and review the diff of `contract.json`. A default whose text changed changes the storage hash. The database does not change: schema verification compares the old and the new text as the same value. Re-emit before you run `prisma db verify` or `prisma db update`. With the earlier `contract.json`, `prisma db verify` names the refusal of a default this version refuses and suggests re-emitting. `prisma db update`, `prisma db init` and `prisma migration plan` refuse to write that default with `CONTRACT.DEFAULT_INVALID`.
|
|
376
|
+
3. If you create the database with `prisma db init` or `prisma db update`, `prisma db verify` now reports that the database is signed with the earlier contract. Run `prisma db verify --schema-only` to confirm the schema matches, then `prisma db sign` to sign the database with the re-emitted contract. Sign the database before you deploy the re-emitted contract. Until then the application logs `CONTRACT.MARKER_MISMATCH`, because the database marker holds the earlier storage hash.
|
|
377
|
+
4. If you use migrations, `prisma migration plan` refuses with "Contract changed but planner produced no operations", because nothing in the database changes. Run `prisma migration new --name canonical-date-defaults` to write an empty migration from the earlier contract to the re-emitted one, then `prisma db migrate`. When `migration new` cannot tell where to start, pass `--from` with the storage hash of the earlier contract: the `to` hash of your latest migration, which `prisma migration list` shows. Apply the migration before you deploy the re-emitted contract. Until then the application logs `CONTRACT.MARKER_MISMATCH`, because the database marker holds the earlier storage hash.
|
|
378
|
+
|
|
379
|
+
### SQLite tables created before the upgrade
|
|
380
|
+
|
|
381
|
+
SQLite compares text byte by byte, so a `DateTime` default must be the same text the application writes for the same instant. Tables and columns created from now on get that text, such as `'2024-01-01T00:00:00.000Z'`. A table created before the upgrade keeps the default text it was created with, such as `'2024-01-01T01:00:00+01:00'` or `'2024-01-01T00:00:00Z'`. Every row that took that default compares and sorts wrongly against rows the application wrote. `prisma db verify` does not report it, because it compares the two texts as the same instant.
|
|
382
|
+
|
|
383
|
+
Find the rows that hold the old text, and rewrite them to the text the application writes:
|
|
384
|
+
|
|
385
|
+
```sql
|
|
386
|
+
SELECT count(*) FROM "event" WHERE "at" = '2024-01-01T01:00:00+01:00';
|
|
387
|
+
UPDATE "event" SET "at" = '2024-01-01T00:00:00.000Z' WHERE "at" = '2024-01-01T01:00:00+01:00';
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
SQLite cannot change a column's default in place, so new rows keep taking the old text until the table is rebuilt. A migration that rebuilds the table, which the planner writes for a change to one of its columns, writes the new default text. Until then, run the `UPDATE` again after inserts that take the default.
|
|
391
|
+
|
|
392
|
+
## `date-time-default-refused-text`
|
|
393
|
+
|
|
394
|
+
`prisma contract emit` now refuses a date or time default that its column's type does not hold, with `PSL_INVALID_DEFAULT_LITERAL`:
|
|
395
|
+
|
|
396
|
+
```text
|
|
397
|
+
Field "Event.localAt": pg/timestamp holds no UTC offset, but "2024-01-01T00:00:00Z" has one. Leave it out, as in "2024-01-01T12:34:56".
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
| Refused | Fix |
|
|
401
|
+
| --- | --- |
|
|
402
|
+
| an offset on `Timestamp`, `Date` or `Time` | remove the offset, or make the column `DateTime` if it holds an instant |
|
|
403
|
+
| no offset on `DateTime`, `Timestamptz`, `Timetz` or SQLite `DateTime` | add `Z` for UTC, or the offset, as in `2024-01-01T00:00:00Z` |
|
|
404
|
+
| a time on a `Date` column, or a date on a `Time` or `Timetz` column | remove the part the column does not hold |
|
|
405
|
+
| more than six digits after the decimal point, or more than three on a SQLite `DateTime`, which holds milliseconds | round to six digits or fewer, or three on SQLite |
|
|
406
|
+
| a date or time that does not exist, such as `2024-02-30`, `25:00:00` or `24:00:00` | write a real date or time; for `24:00:00`, write ``@default(sql`'24:00:00'::time`)`` |
|
|
407
|
+
| a date outside the range the column's type holds, such as a date before 24 November 4714 BC on Postgres | write a date inside the range the message names |
|
|
408
|
+
| a ` BC` suffix on a SQLite `DateTime` | write a signed year, as in `-000043-03-15T00:00:00Z` for 44 BC |
|
|
409
|
+
|
|
410
|
+
Run `prisma contract emit` after each fix until it succeeds.
|
|
411
|
+
|
|
412
|
+
A fix that gives the default a different value, such as a rounded time or another date, also changes the default the database holds. That needs a real migration: plan one with `prisma migration plan`, or run `prisma db update`. The empty migration and `prisma db sign` of `date-time-default-stored-in-canonical-form` cover only a new text for the same value.
|
|
413
|
+
|
|
414
|
+
## `date-time-ts-default-stored-in-canonical-form`
|
|
415
|
+
|
|
416
|
+
A TypeScript contract stores the same canonical form as PSL. The codecs whose value is a `Date` or database text now write it:
|
|
417
|
+
|
|
418
|
+
```typescript
|
|
419
|
+
createdAt: field.temporal.timestamptzJsDate().default(new Date('2024-01-01T00:00:00Z')),
|
|
420
|
+
// stored before: "2024-01-01T00:00:00.000Z"; stored now: "2024-01-01T00:00:00Z"
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
A `*String` preset or column helper default is read by the column's type the same way PSL text is, so `'2024-01-01 00:00:00+00'` on `field.temporal.timestamptzString()` is stored as `"2024-01-01T00:00:00Z"`. Text the type does not hold is refused with `CONTRACT.DEFAULT_INVALID`, and the message says what is wrong, as in the table above. Defaults on `field.dateTime()` and the other `Temporal` presets were already stored in this form and do not change. A `Temporal` default with digits below one microsecond, such as `Temporal.Instant.from('2024-01-01T00:00:00.123456789Z')`, is now refused the same way, because the database holds microseconds; round it to six digits.
|
|
424
|
+
|
|
425
|
+
1. Search your contract files for `.default(` on the presets and helpers named above.
|
|
426
|
+
2. Run `prisma contract emit` and review the diff of `contract.json`.
|
|
427
|
+
3. Sign the database or record an empty migration, as steps 3 and 4 of `date-time-default-stored-in-canonical-form` describe.
|
|
428
|
+
|
|
429
|
+
## `mongo-create-returns-stored-document`
|
|
430
|
+
|
|
431
|
+
This change has no detection pattern: it depends on what the code does with the value `create()` or `createAll()` returns.
|
|
432
|
+
|
|
433
|
+
Code that relied on getting its input objects back, such as a `Long`, `Int32`, `Double` or `Uint8Array` inside a `Bson` field, or a key missing for a nullable field it did not pass, now gets the values a read returns. Compare with what a read returns, or keep a reference to the input instead.
|
|
434
|
+
|
|
435
|
+
## `mongo-reads-decode-includes-value-objects-and-absent-fields`
|
|
436
|
+
|
|
437
|
+
This change has no detection pattern: it depends on the fields and the data.
|
|
438
|
+
|
|
439
|
+
Remove code that converted driver classes by hand in included documents or composite-type fields, such as `author._id.toHexString()`, `karma.toBigInt()` or `new Uint8Array(avatar.value())`: they now arrive as a hex string, `bigint`, decimal text and `Uint8Array`, as their types say. Replace checks like `user.name === undefined` for an optional field that Prisma 6 or the driver left out of the document with `user.name === null`.
|
|
440
|
+
|
|
441
|
+
## `mongo-where-filter-expressions-encoded`
|
|
442
|
+
|
|
443
|
+
The pattern finds filter expressions written inside `where(...)`; also check expressions built elsewhere and passed to the ORM's `where()`. Leave filters passed to the query builder's `match()` as they are.
|
|
444
|
+
|
|
445
|
+
For each field filter, pass the field's application value: a `bigint` for an `Int64` field, decimal text for a `Decimal128` field, a hex string or an `ObjectId` for an `ObjectId` field, and an integer in the signed 32-bit range for an `Int32` field. A value that is not a `MongoValue`, such as a `bigint`, an `ObjectId` or a driver `Long`, goes in a `MongoParamRef` from `@prisma/orm-mongo/value`: write `MongoFieldFilter.gt('views', new MongoParamRef(5n))` in place of `MongoFieldFilter.gt('views', Long.fromNumber(5))` or `5`. A value the field's codec refuses now fails with `RUNTIME.ENCODE_FAILED` before the query runs; before, it was sent as is. A filter on a `Bson` field with an `ObjectId`, `Long` or `Decimal128` now matches the stored value; before, it matched nothing.
|
|
446
|
+
|
|
447
|
+
## `mongo-codecs-check-value-types`
|
|
448
|
+
|
|
449
|
+
This change has no detection pattern: which writes carry such values depends on the data.
|
|
450
|
+
|
|
451
|
+
Pass each field's application type: a string for `String`, a boolean for `Bool`, a valid `Date` for `Date`, and a 24-digit hex string or an `ObjectId` for `ObjectId`. Remove `null` elements from lists before writing them; a list field's elements cannot be `null`.
|
|
452
|
+
|
|
453
|
+
## `mongo-list-elements-encoded`
|
|
454
|
+
|
|
455
|
+
This change has no detection pattern: it applies to every list field.
|
|
456
|
+
|
|
457
|
+
Remove workarounds for list fields that failed to write, such as declaring an `ObjectId[]` field as `String[]` or `Bson`.
|
|
458
|
+
|
|
459
|
+
## `mongo-query-builder-bson-values-match`
|
|
460
|
+
|
|
461
|
+
This change has no detection pattern: filters that matched nothing do not look different in code.
|
|
462
|
+
|
|
463
|
+
Remove workarounds for query-builder filters that compared with a driver class and matched nothing, such as running those queries through the driver's own collection.
|
|
464
|
+
|
|
465
|
+
## `mongo-upsert-keeps-create-values`
|
|
466
|
+
|
|
467
|
+
This change has no detection pattern: it depends on which fields `create` sets.
|
|
468
|
+
|
|
469
|
+
Remove code that corrected such a field after an upsert inserted a document. If an upsert whose `create` sets such a field pulls by a match document, pull a single value instead, or leave the field out of `create` so the update default applies.
|
|
470
|
+
|
|
471
|
+
## `mongo-writes-check-int32-enum-and-null-values`
|
|
472
|
+
|
|
473
|
+
This change has no detection pattern: which writes carry such values depends on the data.
|
|
474
|
+
|
|
475
|
+
Round a number before writing it to an `Int32` field, or declare the field `Double` or `Int64`. Map any value outside an enum, such as a Prisma 6 member name like `'ADMIN'` where the stored value is `'admin'`, to one of the enum's values. The error's `details.allowed` lists them. Instead of writing `null` to a required field, write a value, or declare the field optional (`String?`) when documents may hold `null`.
|
|
476
|
+
|
|
477
|
+
## `contract-source-warnings-are-diagnostics`
|
|
478
|
+
|
|
479
|
+
This change has no detection pattern: scripts that read the CLI's output are not TypeScript sources.
|
|
480
|
+
|
|
481
|
+
A script that read contract source warnings from `contract emit --json` or `contract print --json` message lines of the form `warning <path>:<line>:<column> <CODE> <message>` reads the result envelope's `diagnostics` instead. Each has `code: 'CONTRACT.SOURCE_DIAGNOSTIC'`, `severity: 'warn'`, `where: { path, line }` and `meta: { code, span }`, where `meta.code` is the source's code and `meta.span.start.column` the column.
|
|
482
|
+
|
|
483
|
+
## `mongo-ts-preset-field-not-optional`
|
|
484
|
+
|
|
485
|
+
Remove `.optional()` or `.many()` from each match. A field a preset fills cannot be optional or a list; to keep an optional timestamp that nothing fills, use `field.date().optional()`.
|
|
486
|
+
|
|
487
|
+
## `contract-artifacts-restamp`
|
|
488
|
+
|
|
489
|
+
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 embedded `version` moving to `8.0.0-rc.14`; any other difference in the emitted files comes from an earlier entry in this guide.
|
package/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.13-to-8.0.0-rc.14/instructions.md
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
---
|
|
2
|
+
from: "8.0.0-rc.13"
|
|
3
|
+
to: "8.0.0-rc.14"
|
|
4
|
+
changes:
|
|
5
|
+
- id: engine-pin-moves-to-0-6-2
|
|
6
|
+
summary: |
|
|
7
|
+
The toolchain now peers `@prisma/cli-engine@0.6.2` (up from 0.6.1). An extension package that pins `@prisma/cli-engine` for its tests or tooling must move the pin to `0.6.2`. With this engine the CLI prints its own name in hints and messages where it used to print a literal `{bin}`.
|
|
8
|
+
detection:
|
|
9
|
+
glob: "**/package.json"
|
|
10
|
+
contains:
|
|
11
|
+
- '"@prisma/cli-engine": "0.6.1"'
|
|
12
|
+
- id: temporal-polyfill-is-a-peer-dependency
|
|
13
|
+
summary: |
|
|
14
|
+
`temporal-polyfill` is now a required peer dependency of `@prisma/orm-target-postgres` and `@prisma/orm-postgres`, not a dependency. The target's control entry imports it. An extension pack that peers the target does not declare it; the application supplies it. npm, pnpm and bun install it automatically. An extension package that installs with Yarn and loads the target's control entry in its tests or tooling must add `temporal-polyfill` (`^1.0.4`) to its `devDependencies`.
|
|
15
|
+
detection:
|
|
16
|
+
glob: "**/yarn.lock"
|
|
17
|
+
contains:
|
|
18
|
+
- "@prisma/orm-postgres@"
|
|
19
|
+
- "@prisma/orm-target-postgres@"
|
|
20
|
+
anyMatch: true
|
|
21
|
+
- id: mongo-result-shape-includes-and-value-objects
|
|
22
|
+
summary: |
|
|
23
|
+
`contractModelToMongoResultShape(model, options)` (`@prisma/orm-mongo/query-builder`,
|
|
24
|
+
`@internal/mongo-query-builder`) takes `includes`, a map from relation name to the shape of
|
|
25
|
+
the included document, instead of `includeRelationNames`, and an optional `valueObjects`
|
|
26
|
+
map that makes value-object fields decode as documents. `contractFieldToMongoFieldShape`
|
|
27
|
+
takes the same `valueObjects` as an optional second argument.
|
|
28
|
+
detection:
|
|
29
|
+
glob: "**/*.{ts,mts,cts}"
|
|
30
|
+
matches:
|
|
31
|
+
- '\bincludeRelationNames\b'
|
|
32
|
+
- id: mongo-compile-query-value-objects
|
|
33
|
+
summary: |
|
|
34
|
+
`compileMongoQuery(collection, state, storageHash, model, valueObjects)`
|
|
35
|
+
(`@prisma/orm-mongo/orm`, `@internal/mongo-orm`) takes the contract's value objects as a
|
|
36
|
+
fifth argument, and each `MongoIncludeExpr` in the state carries the related model as
|
|
37
|
+
`targetModel`.
|
|
38
|
+
detection:
|
|
39
|
+
glob: "**/*.{ts,mts,cts}"
|
|
40
|
+
matches:
|
|
41
|
+
- '\bcompileMongoQuery\('
|
|
42
|
+
- id: psl-unknown-field-preset-lists-presets
|
|
43
|
+
summary: |
|
|
44
|
+
`reportUnknownFieldPreset(...)` (`@prisma/orm-framework/psl-parser/interpret`,
|
|
45
|
+
`@internal/psl-parser/interpret`) takes the `authoringContributions` it looks the namespace's
|
|
46
|
+
presets up in, and its message lists them.
|
|
47
|
+
detection:
|
|
48
|
+
glob: "**/*.{ts,mts,cts}"
|
|
49
|
+
matches:
|
|
50
|
+
- '\breportUnknownFieldPreset\('
|
|
51
|
+
- id: mongo-double-codec-encodes-double
|
|
52
|
+
summary: |
|
|
53
|
+
The `mongo/double@1` codec's `encode` (from `buildStandardCodecRegistry()` or
|
|
54
|
+
`mongoStandardCodecs` in `@prisma/orm-mongo/target/codecs`, `@internal/target-mongo/codecs`)
|
|
55
|
+
returns the driver's `Double` wrapping the number, so a whole number is stored as a BSON
|
|
56
|
+
double; it refuses a value that is not a number. The `mongo/int32@1` codec's `encode` refuses
|
|
57
|
+
a value that is not an integer in the signed 32-bit range. The `mongo/string@1`,
|
|
58
|
+
`mongo/bool@1`, `mongo/date@1`, `mongo/objectId@1` and `mongo/vector@1` codecs' `encode`
|
|
59
|
+
refuse a value of the wrong type, and `mongo/binary@1` decodes a `Buffer` or `Uint8Array` as
|
|
60
|
+
well as a `Binary`.
|
|
61
|
+
detection:
|
|
62
|
+
glob: "**/*.{ts,mts,cts}"
|
|
63
|
+
matches:
|
|
64
|
+
- "['\"]mongo/(?:double|int32)@1['\"]"
|
|
65
|
+
- id: mongo-insert-results-carry-documents
|
|
66
|
+
summary: |
|
|
67
|
+
`InsertOneResult` and `InsertManyResult` (`@prisma/orm-mongo/query-ast/execution`,
|
|
68
|
+
`@internal/mongo-query-ast/execution`) carry the inserted documents as stored: `document` on
|
|
69
|
+
one, `documents` on the other, in insert order. A Mongo driver's `insertOne` and `insertMany`
|
|
70
|
+
commands must yield them; the ORM returns them, decoded, from `create()` and `createAll()`.
|
|
71
|
+
detection:
|
|
72
|
+
glob: "**/*.{ts,mts,cts}"
|
|
73
|
+
matches:
|
|
74
|
+
- 'import[^;]*\bInsert(?:One|Many)Result\b[^;]*from\s*[''"]@(?:prisma/orm-mongo|internal/mongo-query-ast)/'
|
|
75
|
+
- id: mongo-adapter-passes-bson-values-through
|
|
76
|
+
summary: |
|
|
77
|
+
The Mongo adapter's `resolveValue` passes an instance of a `bson` class (an object with a
|
|
78
|
+
`_bsontype` tag and a class prototype), a `RegExp` and a `Uint8Array` through unchanged
|
|
79
|
+
instead of copying their fields into a plain object, at any depth of a command.
|
|
80
|
+
- id: mongo-codec-types-carry-traits
|
|
81
|
+
summary: |
|
|
82
|
+
Each entry of the Mongo `CodecTypes` map (`@prisma/orm-mongo/target/codec-types`,
|
|
83
|
+
`@internal/target-mongo/codec-types`) carries `traits`, the union of its codec descriptor's
|
|
84
|
+
traits. The ORM's field accessor gives `inc` and `mul` to a field whose codec has the
|
|
85
|
+
`numeric` trait, taking the codec's input type, and `FieldExpression` takes that operand
|
|
86
|
+
type as an optional second type parameter.
|
|
87
|
+
- id: mongo-field-builder-preset-not-optional
|
|
88
|
+
summary: |
|
|
89
|
+
The Mongo `FieldBuilder` (`@prisma/orm-mongo/contract-builder`,
|
|
90
|
+
`@internal/mongo-contract-ts/contract-builder`)'s `optional` and `many` are properties whose type refuses a call when
|
|
91
|
+
the builder carries execution defaults: a function whose `this` type is a string that says
|
|
92
|
+
why. The widest constraint,
|
|
93
|
+
`FieldBuilder<ContractFieldType, boolean, boolean, EnumTypeHandle | undefined, ExecutionMutationDefaultPhases | undefined>`,
|
|
94
|
+
still accepts every builder.
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
# 8.0.0-rc.13 → 8.0.0-rc.14 — Extension author upgrade instructions
|
|
98
|
+
|
|
99
|
+
## `engine-pin-moves-to-0-6-2`
|
|
100
|
+
|
|
101
|
+
For every `package.json` matched by `detection`, change the `@prisma/cli-engine` version from `0.6.1` to `0.6.2` and reinstall.
|
|
102
|
+
|
|
103
|
+
Engine 0.6.2 replaces the placeholder `{bin}` with the name of the CLI that was run. A hint that used to print as `{bin} db migrate` now prints as `prisma db migrate`. The replacement applies to next actions, warnings, errors, and summary and list text, in human, `--json`, and `--format markdown` output. Keep writing `{bin}` in the commands and messages your extension produces; the engine replaces it. If a test of yours runs the CLI and matches the literal text `{bin}` in its output, change it to match the CLI name.
|
|
104
|
+
|
|
105
|
+
## `temporal-polyfill-is-a-peer-dependency`
|
|
106
|
+
|
|
107
|
+
For each Yarn project that `detection` matches, add `"temporal-polyfill": "^1.0.4"` to `devDependencies` in the `package.json` that installs `@prisma/orm-target-postgres` or `@prisma/orm-postgres`, then reinstall. Without it, a test or script that loads the target's control entry fails because Node.js cannot find the package `temporal-polyfill`.
|
|
108
|
+
|
|
109
|
+
## `mongo-result-shape-includes-and-value-objects`
|
|
110
|
+
|
|
111
|
+
Replace `includeRelationNames: ['author']` with `includes: { author: shape }`, where `shape` describes the included document, for example `{ kind: 'document', nullable: true, fields }` for a to-one include with `fields` from `contractModelToMongoResultShape(targetModel, { valueObjects }).fields`, or an `array` of such documents for a to-many include. A shape of `{ kind: 'unknown' }` keeps the old behaviour of passing the included value through undecoded. Pass `valueObjects` (for example `domainValueObjectsAtDefaultNamespace(contract.domain)`) to decode value-object fields.
|
|
112
|
+
|
|
113
|
+
## `mongo-compile-query-value-objects`
|
|
114
|
+
|
|
115
|
+
Pass the contract's value objects, `domainValueObjectsAtDefaultNamespace(contract.domain) ?? {}`, as the fifth argument, and add `targetModel` (the related model's definition) to each include expression you build.
|
|
116
|
+
|
|
117
|
+
## `psl-unknown-field-preset-lists-presets`
|
|
118
|
+
|
|
119
|
+
Add the `authoringContributions` your interpreter already holds to each call.
|
|
120
|
+
|
|
121
|
+
## `mongo-double-codec-encodes-double`
|
|
122
|
+
|
|
123
|
+
Code that compares what the `mongo/double@1` codec's `encode` returns with a number unwraps it with `.valueOf()` or `Number(...)`. A test double or fixture that passed a fraction or an out-of-range number through the `mongo/int32@1` codec's `encode` now gets `RUNTIME.ENCODE_FAILED`; pass an integer. The same holds for a test double that passes a value of the wrong type through the string, boolean, date, ObjectId or vector codec, such as a number for an ObjectId; pass a 24-digit hex string or an `ObjectId`.
|
|
124
|
+
|
|
125
|
+
## `mongo-insert-results-carry-documents`
|
|
126
|
+
|
|
127
|
+
A driver of your own yields `{ insertedId, document }` for `insertOne`, where `document` is the command's document with the `_id` the database assigned, as the database stores it: serialise it with the database's BSON options and deserialise it again, as `BSON.deserialize(BSON.serialize(document, options), options)` does. It yields `{ insertedIds, insertedCount, documents }` for `insertMany`. Code that builds these results in a test double adds the same fields.
|
|
128
|
+
|
|
129
|
+
## `mongo-adapter-passes-bson-values-through`
|
|
130
|
+
|
|
131
|
+
This change has no detection pattern: it changes what reaches the driver, not how the adapter is called.
|
|
132
|
+
|
|
133
|
+
A codec or middleware that received a copied plain object in place of a `bson` class instance, and rebuilt the instance from it, now receives the instance. Remove the rebuilding.
|
|
134
|
+
|
|
135
|
+
## `mongo-codec-types-carry-traits`
|
|
136
|
+
|
|
137
|
+
This change has no detection pattern: few extensions build a Mongo codec types map themselves.
|
|
138
|
+
|
|
139
|
+
A type you declare to satisfy the Mongo `CodecTypes` shape, or a codec types map of your own that should get `inc` and `mul`, adds `traits` to each entry, such as `readonly traits: 'equality' | 'order' | 'numeric'` for a numeric codec.
|
|
140
|
+
|
|
141
|
+
## `mongo-field-builder-preset-not-optional`
|
|
142
|
+
|
|
143
|
+
This change has no detection pattern: few extensions implement `FieldBuilder` themselves. An object typed as a `FieldBuilder` that implements `optional()` and `many()` as methods assigns them through a cast to the conditional property types, as `createFieldBuilder` does, or builds the field with the contract builder's `field` helpers.
|