@prisma/orm-postgres 8.0.0-rc.17-dev.5 → 8.0.0-rc.17-dev.7
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma/orm-postgres",
|
|
3
|
-
"version": "8.0.0-rc.17-dev.
|
|
3
|
+
"version": "8.0.0-rc.17-dev.7",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
"skills"
|
|
11
11
|
],
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@prisma/orm-family-sql": "8.0.0-rc.17-dev.
|
|
14
|
-
"@prisma/orm-framework": "8.0.0-rc.17-dev.
|
|
15
|
-
"@prisma/orm-target-postgres": "8.0.0-rc.17-dev.
|
|
16
|
-
"@prisma/orm-toolchain": "8.0.0-rc.17-dev.
|
|
13
|
+
"@prisma/orm-family-sql": "8.0.0-rc.17-dev.7",
|
|
14
|
+
"@prisma/orm-framework": "8.0.0-rc.17-dev.7",
|
|
15
|
+
"@prisma/orm-target-postgres": "8.0.0-rc.17-dev.7",
|
|
16
|
+
"@prisma/orm-toolchain": "8.0.0-rc.17-dev.7",
|
|
17
17
|
"@types/node": "26.1.2",
|
|
18
18
|
"@types/pg": "8.20.4",
|
|
19
19
|
"arktype": "^2.2.2",
|
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
"tinyglobby": "^0.2.17"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@internal/postgres": "8.0.0-rc.17-dev.
|
|
26
|
-
"@repo/tsconfig": "8.0.0-rc.17-dev.
|
|
27
|
-
"@repo/tsdown": "8.0.0-rc.17-dev.
|
|
25
|
+
"@internal/postgres": "8.0.0-rc.17-dev.7",
|
|
26
|
+
"@repo/tsconfig": "8.0.0-rc.17-dev.7",
|
|
27
|
+
"@repo/tsdown": "8.0.0-rc.17-dev.7",
|
|
28
28
|
"tsdown": "0.22.14",
|
|
29
29
|
"typescript": "5.9.3"
|
|
30
30
|
},
|
package/skills/prisma-8/SKILL.md
CHANGED
|
@@ -15,7 +15,7 @@ description: >-
|
|
|
15
15
|
ORM 7 or earlier (schema.prisma + @prisma/client).
|
|
16
16
|
metadata:
|
|
17
17
|
library: '@prisma/orm-postgres'
|
|
18
|
-
library_version: '8.0.0-rc.17-dev.
|
|
18
|
+
library_version: '8.0.0-rc.17-dev.7'
|
|
19
19
|
version: '2026-09-12'
|
|
20
20
|
---
|
|
21
21
|
|
|
@@ -62,7 +62,7 @@ Open the reference whose triggers match the task. If more than one matches, open
|
|
|
62
62
|
| Author migrations | [`references/migrations.md`](references/migrations.md) | `db update` vs `migration plan`, `db migrate`, `migration new`, `migration show`, `db update --dry-run`, `db verify`, `db sign`, data migration, `dataTransform`, placeholder sentinels in framework-rendered `migration.ts`, `MIGRATION.HASH_MISMATCH`, `MIGRATION.UNFILLED_PLACEHOLDER`, data-loss questions (`--delete`, `--rename`, `--allow`, `CLI.CONSENT_REQUIRED`), schema drift |
|
|
63
63
|
| Migration graph, refs, plan origin | [`references/migration-model.md`](references/migration-model.md) | migration graph, refs, `migration ref set` / `list` / `delete`, the `db` ref, `--advance-ref`, `--no-advance-ref`, `migration plan --from`, `from: (baseline)` in plan output, greenfield / from-scratch plan, baseline, first migration before deploy (Composer / CD-managed databases), chaining migrations, retrofitting migrations onto an existing database, `MIGRATION.HASH_NOT_IN_GRAPH`, `MIGRATION.PATH_UNREACHABLE` at plan/chain time |
|
|
64
64
|
| Review migrations on deploy | [`references/migration-review.md`](references/migration-review.md) | "what migrations are going to run", "what runs on deploy / merge", merge conflict, diamond convergence, concurrent migrations, migration status, ref management for CI, staging / production environment refs, `MIGRATION.MARKER_NOT_IN_HISTORY`, `MIGRATION.MISSING_INVARIANTS`, `db migrate --show` |
|
|
65
|
-
| Write queries | [`references/queries.md`](references/queries.md) | query, where, select, project, orderBy, limit, offset, take, skip, include, lookup, first, all, count, aggregate, groupBy, create, update, delete, upsert, returning, transaction, `db.orm`, `db.sql`, `runtime.query(plan)` vs `runtime.execute(plan)`, `db.prepare` / prepared statements, streaming / `for await`, many-to-many `include` and nested `connect`, `and` / `or` / `not` from `@prisma/orm-postgres/orm-client`, `db.query.from(...)` (Mongo pipeline), namespace-aware accessors, `.all()` Thenable, single-use iterators (`RUNTIME.ITERATOR_CONSUMED`), target-declared aggregate types (`count`, integer `sum`, and integer `avg` are `number`; `count` and integer `sum` throw outside ±(2^53 − 1) rather than round, while `avg` is a fraction already and carries no guard; `countBigInt` / `sumBigInt` / `avgDecimal` are the lossless forms, `avgDecimal` on PostgreSQL only), custom collection class (`extends Collection`), `with` and query fragments (`
|
|
65
|
+
| Write queries | [`references/queries.md`](references/queries.md) | query, where, select, project, orderBy, limit, offset, take, skip, include, lookup, first, all, count, aggregate, groupBy, create, update, delete, upsert, returning, transaction, `db.orm`, `db.sql`, `runtime.query(plan)` vs `runtime.execute(plan)`, `db.prepare` / prepared statements, streaming / `for await`, many-to-many `include` and nested `connect`, `and` / `or` / `not` from `@prisma/orm-postgres/orm-client`, `db.query.from(...)` (Mongo pipeline), namespace-aware accessors, `.all()` Thenable, single-use iterators (`RUNTIME.ITERATOR_CONSUMED`), target-declared aggregate types (`count`, integer `sum`, and integer `avg` are `number`; `count` and integer `sum` throw outside ±(2^53 − 1) rather than round, while `avg` is a fraction already and carries no guard; `countBigInt` / `sumBigInt` / `avgDecimal` are the lossless forms, `avgDecimal` on PostgreSQL only), custom collection class (`extends Collection`), `with` and query fragments (`QueryFragment`, `Filtered`, `Ordered`), shared query fragments and scopes (`db.orm.fragment(fields, body)` for a filter on every model with given fields, `CodecField`, `db.orm.public.Post.fragment(...)` for a shared `select` / `include`, `orderByField` for an order or sort parameter from a request), drizzle-style, kysely-style. Naming types (§ *Naming model and result types*): "type of my model", "return type of a query", `ResultType`, `Scalars`, `Shape`, `models` / `Models` namespace in `contract.d.ts`, `Models.public_User`, Prisma 7 `Prisma.User` / `GetPayload` / `UserGetPayload` equivalents. Postgres/SQLite specifics: [`references/queries-postgres.md`](references/queries-postgres.md); Mongo specifics: [`references/queries-mongo.md`](references/queries-mongo.md) |
|
|
66
66
|
| Wire the runtime | [`references/runtime.md`](references/runtime.md) | `db.ts`, `postgres<Contract>(...)` / `sqlite<Contract>(...)` / `mongo<Contract>(...)` façades, middleware composition (lints, budgets, cache via `@prisma/orm-extension-middleware-cache`, custom `afterQuery` middleware, `@prisma/orm-postgres/family-runtime`), `DATABASE_URL`, `.env`, connection pool / `poolOptions`, dev vs prod config, transactions, read replicas, multi-database, script won't exit / hangs, `db.close` / `pool.end`, `await using` / `[Symbol.asyncDispose]` |
|
|
67
67
|
| Build-tool integration | [`references/build.md`](references/build.md) | Vite plugin (`@internal/vite-plugin-contract-emit`, Vite 7/8), `vite.config.ts`, contract emit on save, HMR / dev server, Next.js / Webpack / esbuild / Rollup / Turbopack (named gaps, not fabricated) |
|
|
68
68
|
| Supabase | [`references/supabase.md`](references/supabase.md) | `@internal/extension-supabase`, RLS, row level security, policies (`policy_select` / `policy_update` / `@@rls`, `auth.uid()`), role binding (`asUser(jwt)` / `asAnon()` / `asServiceRole()`), `auth.users`, cross-space FKs to `supabase:auth.AuthUser`, JWT / JWKS (`SUPABASE_JWKS_URL`, `SUPABASE_JWT_SECRET`), `SUPABASE.JWT_INVALID`, `SUPABASE.CONFIG_INVALID`, `RoleBoundDb`, session pooler |
|
|
@@ -118,11 +118,11 @@ Then run `pnpm prisma contract emit` (or rely on the Vite plugin — see `refere
|
|
|
118
118
|
|
|
119
119
|
**Temporal columns.** On PostgreSQL, `Date`, `Timestamp(p)`, `Timestamptz(p)` and `Time(p)` read and write `Temporal` values (`Temporal.PlainDate`, `PlainDateTime`, `Instant`, `PlainTime`), never JavaScript `Date`. They need a global `Temporal` at query time: Node.js 26.8.2 and later ship `globalThis.Temporal`; 26.8.1 and earlier — including every 22 and 24 release — do not, and the first read or write of such a column throws `RUNTIME.TEMPORAL_UNAVAILABLE`. On those runtimes either `import 'temporal-polyfill/full/global'` before the first query (add `temporal-polyfill` as a dependency) or author the column as `DateString` / `TimestampString(p)` / `TimestamptzString(p)` / `TimeString(p)`, which carry PostgreSQL's own text and need no `Temporal`. `prisma contract emit`, `prisma db init` and the other commands need no global `Temporal`: the Postgres target loads `temporal-polyfill` as a fallback `Temporal` for the control plane, and sets no global. `temporal-polyfill` is a required peer dependency of `@prisma/orm-postgres`. npm, pnpm and bun install it automatically. With Yarn, add `temporal-polyfill` (`^1.0.4`) to the project's dependencies, or the commands fail because Node.js cannot find the package. That fallback is held once per process. An application that loads control-plane code in its own process, such as server code under `vite dev` with the Prisma Vite plugin or a script that calls the control client, decodes dates without its own `Temporal`, and then fails in production. An application that uses the Temporal codecs must load its own `Temporal`. A TypeScript contract file that constructs a `Temporal` value must load an implementation too.
|
|
120
120
|
|
|
121
|
-
`@@index` also accepts `expression:` (instead of a fields list), `where:` (partial-index predicate), `unique:`, `type:`/`options:` (target-registered access method), and `name:` xor `map
|
|
121
|
+
`@@index` also accepts `expression:` (instead of a fields list), `where:` (partial-index predicate), `unique:`, `type:`/`options:` (target-registered access method), and `name:` xor `map:`. Raw SQL in `expression:` and `where:` is a `sql` literal; a quoted string is refused with `PSL_VALUE_TYPE_INCOMPATIBLE`, whose message gives the rewrite:
|
|
122
122
|
|
|
123
123
|
```prisma
|
|
124
|
-
@@index(expression:
|
|
125
|
-
@@index([authorId], where:
|
|
124
|
+
@@index(expression: sql`lower(email)`, name: "users_email_lower")
|
|
125
|
+
@@index([authorId], where: sql`(archived_at IS NULL)`, name: "posts_author_active")
|
|
126
126
|
```
|
|
127
127
|
|
|
128
128
|
`name:` declares a wire-named index (physical name `<name>_<8-hex hash>`, renames plan as `ALTER INDEX … RENAME`); `map:` adopts an exact physical name verbatim (for infer-captured objects — combining it with a SQL body warns, because drift detection byte-compares the authored text against Postgres's reprint). An `expression:` requires `name:` or `map:`. The TS builder mirrors this via `constraints.index([cols.x], {...})` / `constraints.index({ expression, ... })` — see `packages/2-sql/2-authoring/contract-ts/README.md`.
|
|
@@ -132,7 +132,7 @@ Then run `pnpm prisma contract emit` (or rely on the Vite plugin — see `refere
|
|
|
132
132
|
```prisma
|
|
133
133
|
@@fullTextIndex([text], name: "message_text_search")
|
|
134
134
|
@@fullTextIndex([summary], language: "german", name: "message_summary_search_de")
|
|
135
|
-
@@fullTextIndex([text], where:
|
|
135
|
+
@@fullTextIndex([text], where: sql`archived_at IS NULL`, name: "message_text_search_live")
|
|
136
136
|
@@fullTextIndex([[title, subtitle], body], name: "post_search")
|
|
137
137
|
```
|
|
138
138
|
|
|
@@ -326,11 +326,11 @@ model Order {
|
|
|
326
326
|
total Decimal
|
|
327
327
|
|
|
328
328
|
// name: is a prefix — the physical constraint becomes order_total_positive_<8-hex hash>.
|
|
329
|
-
@@check(expression:
|
|
329
|
+
@@check(expression: sql`total > 0`, name: "order_total_positive")
|
|
330
330
|
}
|
|
331
331
|
```
|
|
332
332
|
|
|
333
|
-
`expression` is the raw predicate — the text that goes inside `CHECK (...)` — and it is never parsed, so get it right; Prisma 8 does not validate SQL syntax. Exactly one of `name:` or `map:` is required, and they're mutually exclusive:
|
|
333
|
+
`expression` is the raw predicate — the text that goes inside `CHECK (...)`, written as a `sql` literal (a quoted string is refused) — and it is never parsed, so get it right; Prisma 8 does not validate SQL syntax. Exactly one of `name:` or `map:` is required, and they're mutually exclusive:
|
|
334
334
|
|
|
335
335
|
- **`name:`** — declaring a new rule. Prisma 8 picks the physical constraint name and future plans compare by that name, so Postgres's own reprint of your predicate (which rarely matches what you typed byte-for-byte) never causes false drift.
|
|
336
336
|
- **`map:`** — adopting a rule that already exists. Give the constraint's exact physical name and Prisma 8 compares the predicate byte-for-byte against what's live. This is the form `contract infer` writes for you (see *Workflow — Brownfield introspection* below) when it finds a hand-written check in the database. Every `map:` body warns at emit time (`PN_EXACT_NAME_BODY_COMPARISON`) — the warning fires on the text, not on who wrote it, so the check `contract infer` just wrote warns again on your next `contract emit` too. That is expected, not a defect: the comparison is still sound because both sides are Postgres's own reprint. Prefer `name:` for anything you're authoring fresh: your text and Postgres's reprint of it rarely match character-for-character, and a byte comparison reports that as drift even when both mean exactly the same thing. Reserve `map:` for adopting what's already there, where both sides are the database's own reprint and so do match.
|
|
@@ -104,7 +104,7 @@ Without an index Postgres recomputes `to_tsvector` for every row, and it only us
|
|
|
104
104
|
|
|
105
105
|
```prisma
|
|
106
106
|
@@fullTextIndex([text], name: "message_text_search")
|
|
107
|
-
@@fullTextIndex([text], where:
|
|
107
|
+
@@fullTextIndex([text], where: sql`archived_at IS NULL`, name: "message_text_search_live")
|
|
108
108
|
```
|
|
109
109
|
|
|
110
110
|
Give the index and the operation the same `language`: a mismatch raises no error, the query silently falls back to a sequential scan.
|
|
@@ -331,7 +331,7 @@ The same holds for an `if` with an early return, a `switch`, a loop and a reassi
|
|
|
331
331
|
A custom collection class gives a model its own named queries. Extend `Collection`, register the class with `orm({ collections })`, and build that client inside the request from `db.runtime()` and `db.context`:
|
|
332
332
|
|
|
333
333
|
```typescript
|
|
334
|
-
import { Collection, type Filtered, type
|
|
334
|
+
import { Collection, type Filtered, type Ordered, orm, type QueryFragment } from '@prisma/orm-postgres/orm-client';
|
|
335
335
|
import type { Contract } from './prisma/contract.d';
|
|
336
336
|
|
|
337
337
|
class PostCollection extends Collection<Contract, 'Post'> {
|
|
@@ -363,10 +363,10 @@ After `.select(...)` or `.variant(...)` the class methods are gone: those return
|
|
|
363
363
|
|
|
364
364
|
Inside a class body, a class method called on the result of another call loses what that call established. So a class method whose body chains two class methods loses the first call's facts for every caller: with `latest() { return this.byAuthor(id).newestFirst(); }`, `Post.latest()` is known to be ordered but not filtered. The same holds for `.prepared` after `.include(...)` inside the class: it describes the class's row without the included relation. Inside the class, follow a class method with built-in methods (`this.byAuthor(id).orderBy(...)`), or chain the class methods from outside the class, where they keep every fact.
|
|
365
365
|
|
|
366
|
-
`with(fn)` calls a function with the collection and returns its result. The function is a query fragment, described in Workflow — Query fragments below, of type `
|
|
366
|
+
`with(fn)` calls a function with the collection and returns its result. The function is a query fragment, described in Workflow — Query fragments below, of type `QueryFragment<In, Out>`, so a query can be written once and applied to any collection of that class:
|
|
367
367
|
|
|
368
368
|
```typescript
|
|
369
|
-
const newest:
|
|
369
|
+
const newest: QueryFragment<PostCollection, Ordered<PostCollection>> = (posts) => posts.newestFirst();
|
|
370
370
|
await Post.with(newest).limit(20).all();
|
|
371
371
|
```
|
|
372
372
|
|
|
@@ -69,14 +69,14 @@ namespace public {
|
|
|
69
69
|
policy_select profile_owner_read {
|
|
70
70
|
target = Profile
|
|
71
71
|
roles = [authenticated]
|
|
72
|
-
using = "
|
|
72
|
+
using = sql`"userId"::uuid = auth.uid()`
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
// anon may read every profile (a public directory listing).
|
|
76
76
|
policy_select profile_public_read {
|
|
77
77
|
target = Profile
|
|
78
78
|
roles = [anon]
|
|
79
|
-
using =
|
|
79
|
+
using = sql`true`
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
// authenticated may update only their own profile, and may not
|
|
@@ -84,8 +84,8 @@ namespace public {
|
|
|
84
84
|
policy_update profile_owner_write {
|
|
85
85
|
target = Profile
|
|
86
86
|
roles = [authenticated]
|
|
87
|
-
using = "
|
|
88
|
-
withCheck = "
|
|
87
|
+
using = sql`"userId"::uuid = auth.uid()`
|
|
88
|
+
withCheck = sql`"userId"::uuid = auth.uid()`
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
```
|
|
@@ -94,9 +94,9 @@ The `Uuid` constructor selects native UUID storage in type position. The legacy
|
|
|
94
94
|
|
|
95
95
|
The pieces:
|
|
96
96
|
|
|
97
|
-
- **Per-operation policy blocks**: `policy_select`, `policy_insert`, `policy_update`, `policy_delete`, `policy_all`. Body is `key = value`: `target` (a model in this namespace), `roles` (resolve against the composed contract — the pack supplies `anon` / `authenticated` / `service_role`), `using`, and (for write operations) `withCheck`. Multiple permissive policies per `(target, operation)` are valid — Postgres ORs them. A block may also carry `@@map("physical name")` to adopt an existing live policy under its exact name (no wire-name hash; drift detection then byte-compares the body against Postgres's reprint, so keep the text as captured — hand-authoring it warns).
|
|
97
|
+
- **Per-operation policy blocks**: `policy_select`, `policy_insert`, `policy_update`, `policy_delete`, `policy_all`. Body is `key = value`: `target` (a model in this namespace), `roles` (resolve against the composed contract — the pack supplies `anon` / `authenticated` / `service_role`), `using`, and (for write operations) `withCheck`. `using` and `withCheck` are `sql` literals, as in `` using = sql`"userId"::uuid = auth.uid()` ``; a quoted string is refused, and the error message gives the rewrite. Multiple permissive policies per `(target, operation)` are valid — Postgres ORs them. A block may also carry `@@map("physical name")` to adopt an existing live policy under its exact name (no wire-name hash; drift detection then byte-compares the body against Postgres's reprint, so keep the text as captured — hand-authoring it warns).
|
|
98
98
|
- **`@@rls` is required on policy targets.** A `policy_*` block whose target model lacks `@@rls` fails emit with `PSL_EXTENSION_TARGET_MODEL_MISSING_ATTRIBUTE`. A model with `@@rls` and *no* policies is also meaningful: RLS enabled, deny-all.
|
|
99
|
-
- **Predicates are verbatim SQL
|
|
99
|
+
- **Predicates are `sql` literals holding verbatim SQL.** Quote camelCase column names inside them with ordinary double quotes (`"userId"`; inside a backtick `sql` literal the only escapes are `` \` `` and `\\`), and cast where needed — `auth.uid()` returns `uuid`. Renames in your contract do not rewrite predicate bodies.
|
|
100
100
|
- **TS-builder parity exists.** `@internal/postgres/contract-builder` exports `policySelect` / `policyInsert` / `policyUpdate` / `policyDelete` / `policyAll`, `rlsEnabled(Model)`, and `role('anon')` — mirroring the PSL lowering key-for-key (identical emitted wire names). PSL is the canonical path shown here.
|
|
101
101
|
|
|
102
102
|
Emit + migrate as usual (`prisma contract emit`, then `references/migrations.md`). The plan creates your table, its FK, `ENABLE ROW LEVEL SECURITY`, and the `CREATE POLICY` statements — and **no DDL for `auth.*`**.
|