@prisma/orm-mongo 8.0.0-rc.1-dev.46 → 8.0.0-rc.10
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/README.md +1 -5
- package/dist/contract-builder.d.mts +1 -0
- package/dist/contract-builder.d.mts.map +1 -1
- package/dist/utils__suppress-idle-connection-errors.d.mts +1 -0
- package/dist/utils__suppress-idle-connection-errors.mjs +2 -0
- package/package.json +14 -12
- package/skills/prisma-8/SKILL.md +96 -0
- package/skills/prisma-8/references/build.md +142 -0
- package/skills/prisma-8/references/contract.md +446 -0
- package/skills/prisma-8/references/debug.md +144 -0
- package/skills/prisma-8/references/feedback.md +251 -0
- package/skills/prisma-8/references/migration-model.md +175 -0
- package/skills/prisma-8/references/migration-review.md +241 -0
- package/skills/prisma-8/references/migrations.md +538 -0
- package/skills/prisma-8/references/queries-mongo.md +236 -0
- package/skills/prisma-8/references/queries-postgres.md +415 -0
- package/skills/prisma-8/references/queries.md +237 -0
- package/skills/prisma-8/references/quickstart.md +317 -0
- package/skills/prisma-8/references/runtime.md +366 -0
- package/skills/prisma-8/references/supabase.md +231 -0
- package/skills/prisma-8/references/upgrade-app.md +101 -0
- package/skills/prisma-8/references/upgrade-extension.md +105 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.10-to-0.11/instructions.md +56 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.11-to-0.12/instructions.md +381 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.11-to-0.12/re-emit-closed-mongo-contracts.ts +202 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.11-to-0.12/re-emit-domain-namespaced-contracts.ts +201 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.11-to-0.12/re-emit-postgres-public-default.ts +198 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.11-to-0.12/strip-migration-labels-hints.ts +340 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.12-to-0.13/instructions.md +339 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.12-to-0.13/re-emit-mti-variant-link-columns.ts +229 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.13-to-0.14/instructions.md +543 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.13-to-0.14/migration-op-factories-to-methods.ts +290 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.13-to-0.14/uuid-preset-rename.ts +43 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.14-to-0.15/instructions.md +359 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.15-to-0.16/instructions.md +173 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.16-to-0.17/instructions.md +805 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.16-to-0.17/strip-sha256-hash-prefixes.ts +382 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.17-to-8.0.0-rc.1/instructions.md +72 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.7-to-0.8/instructions.md +8 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.8-to-0.9/instructions.md +36 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.8-to-0.9/strip-inline-contracts.ts +226 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.9-to-0.10/instructions.md +86 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.9-to-0.10/stamp-storage-types-kind.ts +360 -0
- package/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.1-to-8.0.0-rc.2/instructions.md +588 -0
- package/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.2-to-8.0.0-rc.3/instructions.md +5 -0
- package/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.3-to-8.0.0-rc.4/instructions.md +158 -0
- package/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.4-to-8.0.0-rc.5/instructions.md +42 -0
- package/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.5-to-8.0.0-rc.6/instructions.md +157 -0
- package/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.6-to-8.0.0-rc.7/instructions.md +33 -0
- package/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.7-to-8.0.0-rc.8/instructions.md +36 -0
- package/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.8-to-8.0.0-rc.9/instructions.md +63 -0
- package/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.9-to-8.0.0-rc.10/instructions.md +84 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.10-to-0.11/instructions.md +276 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.11-to-0.12/instructions.md +738 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.11-to-0.12/migrate-contract-testing-imports.ts +97 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.11-to-0.12/regenerate-extension-public-baseline.ts +223 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.11-to-0.12/strip-migration-labels-hints.ts +340 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.12-to-0.13/instructions.md +266 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.13-to-0.14/instructions.md +522 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.13-to-0.14/migration-op-factories-to-methods.ts +290 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.13-to-0.14/uuid-preset-rename.ts +43 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.14-to-0.15/instructions.md +803 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.15-to-0.16/instructions.md +219 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.16-to-0.17/instructions.md +731 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.16-to-0.17/strip-sha256-hash-prefixes.ts +382 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.17-to-8.0.0-rc.1/instructions.md +194 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.7-to-0.8/instructions.md +8 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.8-to-0.9/instructions.md +57 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.8-to-0.9/strip-inline-contracts.ts +226 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.9-to-0.10/instructions.md +150 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.9-to-0.10/stamp-storage-types-kind.ts +360 -0
- package/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.1-to-8.0.0-rc.2/instructions.md +746 -0
- package/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.2-to-8.0.0-rc.3/instructions.md +5 -0
- package/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.3-to-8.0.0-rc.4/instructions.md +137 -0
- package/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.4-to-8.0.0-rc.5/instructions.md +129 -0
- package/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.5-to-8.0.0-rc.6/instructions.md +102 -0
- package/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.6-to-8.0.0-rc.7/instructions.md +33 -0
- package/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.7-to-8.0.0-rc.8/instructions.md +35 -0
- package/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.8-to-8.0.0-rc.9/instructions.md +86 -0
- package/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.9-to-8.0.0-rc.10/instructions.md +63 -0
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
|
|
2
|
+
# Prisma 8 — Contract Authoring
|
|
3
|
+
|
|
4
|
+
> **Edit your data contract. Prisma handles the rest.**
|
|
5
|
+
|
|
6
|
+
The data contract is the single source of truth for your data layer. You edit a contract source — `contract.prisma` (PSL, the canonical surface) or `contract.ts` (TypeScript builder) — and the framework derives types, migrations, and runtime configuration from it. The three-step user model:
|
|
7
|
+
|
|
8
|
+
1. **You edit your data contract.**
|
|
9
|
+
2. **The system plans the migrations for you.** (`references/migrations.md`)
|
|
10
|
+
3. **If you need data migrations, you edit `migration.ts` and execute it.** (`references/migrations.md`)
|
|
11
|
+
|
|
12
|
+
Behind step 1 the agent runs `prisma contract emit` after every contract edit (or installs the Vite plugin so the bundler runs it on save — see `references/build.md`). Emit reads the contract source through the provider the façade picks based on the file extension of `contract:` in `prisma.config.ts`, then writes two artefacts colocated with the source:
|
|
13
|
+
|
|
14
|
+
- `contract.json` — the canonical, content-hashed Contract IR. Read by the planner, the runtime, and `db verify`.
|
|
15
|
+
- `contract.d.ts` — the precise TypeScript types the runtime + lanes propagate when you import `Contract` from it.
|
|
16
|
+
|
|
17
|
+
Both files are **emitted artefacts**. Edit the source; never the JSON or `.d.ts`.
|
|
18
|
+
|
|
19
|
+
## When to Use
|
|
20
|
+
|
|
21
|
+
- User wants to add, change, or remove a model / field / relation.
|
|
22
|
+
- User wants to add an index, unique constraint, check constraint, enum, or value object (composite type).
|
|
23
|
+
- User wants to add a namespace block (Postgres schema) or a cross-contract foreign key.
|
|
24
|
+
- User wants to set `@@control` on a model or configure `defaultControlPolicy`.
|
|
25
|
+
- User wants to use a custom type from an extension (`pgvector.Vector(length: 1536)`, `postgis.Geometry(...)`).
|
|
26
|
+
- User wants to install or configure an extension via `extensions: [...]` in `prisma.config.ts`, including `@internal/extension-supabase`.
|
|
27
|
+
- User is migrating between authoring sources (PSL ↔ TypeScript builder).
|
|
28
|
+
- User received `CONFIG.CONTRACT_MISSING`, `CONTRACT.VALIDATION_FAILED`, or `CONFIG.MISSING_EXTENSION_PACKS` from `contract emit`.
|
|
29
|
+
- User mentions: *schema, fields, models, attributes, prisma schema, PSL, contract.prisma, contract.ts, contract.json, contract.d.ts, contract emit, façade imports, `@internal/postgres/config`, `@internal/postgres/contract-builder`, extensions, pgvector, postgis, paradedb, supabase, namespaces, cross-space FK, `@@control`, enums, check constraints, `@@check`, value objects, validations, callbacks, soft delete, paranoid, scopes*. (The last cluster routes to *What Prisma 8 doesn't do yet* below.)
|
|
30
|
+
|
|
31
|
+
## When Not to Use
|
|
32
|
+
|
|
33
|
+
- User wants to apply a contract change to the DB → `references/migrations.md`.
|
|
34
|
+
- User wants to write a query against the contract → `references/queries.md`.
|
|
35
|
+
- User wants to wire `db.ts` (runtime entry point, middleware, env config) → `references/runtime.md`.
|
|
36
|
+
- User wants the Vite / bundler integration → `references/build.md`.
|
|
37
|
+
- User wants to set up Prisma 8 for the first time → `references/quickstart.md`.
|
|
38
|
+
- User wants a deeper read of a single structured error envelope → `references/debug.md`.
|
|
39
|
+
- User wants to file a missing-feature request → `references/feedback.md`.
|
|
40
|
+
|
|
41
|
+
## Key Concepts
|
|
42
|
+
|
|
43
|
+
- **The `@internal/<target>` façade is the only surface user-authored code imports from.** For a Postgres app: `@internal/postgres/config`, `@internal/postgres/contract-builder`, `@internal/postgres/control`, `@internal/postgres/runtime`. Mongo has the same layout (`@internal/mongo/config`, `@internal/mongo/contract-builder`, `@internal/mongo/runtime`). Each extension publishes its own façade — `@internal/extension-pgvector/control`, `@internal/extension-postgis/control`, `@internal/extension-paradedb/control`. **Never reach into `@internal/cli/*`, `@internal/family-*`, `@internal/target-*`, `@internal/adapter-*`, `@internal/driver-*`, or `@internal/sql-contract-*` from user code.** The façade bakes the family / target / adapter / driver wiring in. See *Common Pitfalls* #4.
|
|
44
|
+
- **Contract source.** A file the framework reads and lowers to the canonical Contract IR. Two flavours, both first-class:
|
|
45
|
+
- **`contract.prisma` (PSL)** — schema-flavoured DSL. Canonical for typical apps and brownfield Prisma users. Wired by `contract: './<path>/contract.prisma'` — the target config (`ormConfig`, below) detects the `.prisma` extension and routes through the PSL provider. The first line of every `.prisma` file is `// use prisma-8`; `orm init` and `contract infer` write it, and the language server only serves diagnostics, completion, and formatting on files that carry it (it still recognises the header earlier releases wrote and rewrites it on Format).
|
|
46
|
+
- **`contract.ts` (TypeScript builder)** — programmatic authoring with `defineContract({...}, ({ field, model, rel, type }) => ({...}))` from `@internal/postgres/contract-builder` (or `@internal/mongo/contract-builder`). Wired by `contract: './<path>/contract.ts'` — the façade detects the `.ts` extension and routes through the TS provider. Use when you need programmatic composition (per-tenant variants, generated fields) or constructs PSL doesn't yet express (e.g. registering a parameterised extension type — see pgvector's contract).
|
|
47
|
+
- **`prisma.config.ts`.** Wires the contract source, the database connection, the migrations directory, and any installed extensions. The file is an *envelope*: the unified CLI's `definePrismaConfig({...})` from `@prisma/cli-engine` wraps an `orm:` section built by the target's `defineConfig` — conventionally imported as `ormConfig` from `@internal/postgres/config` (or `@internal/mongo/config`). There is no flat form; a bare `defineConfig({ contract, ... })` default export fails with `CONFIG.VERSION_MARKER_MISSING`.
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
// prisma.config.ts — what `prisma orm init` scaffolds
|
|
51
|
+
import 'dotenv/config';
|
|
52
|
+
import { definePrismaConfig } from '@prisma/cli-engine';
|
|
53
|
+
import { defineConfig as ormConfig } from '@internal/postgres/config';
|
|
54
|
+
|
|
55
|
+
export default definePrismaConfig({
|
|
56
|
+
orm: ormConfig({
|
|
57
|
+
contract: './src/prisma/contract.prisma',
|
|
58
|
+
db: { connection: process.env['DATABASE_URL']! },
|
|
59
|
+
}),
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The fields `ormConfig` accepts: `contract` (path string — `.prisma` or `.ts`), `output` (optional override for where `contract.json` lands), `db` (`{ connection?: string }`), `extensions` (array of control descriptors), `migrations` (`{ dir?: string }`). Without `output`, the path for `contract.json` is derived from `contract` (e.g. `./src/prisma/contract.prisma` → `./src/prisma/contract.json`). Keep `import 'dotenv/config'` when the config reads `process.env`.
|
|
64
|
+
- **Emit pipeline.** `prisma contract emit --config <path>?` reads `prisma.config.ts`, calls the provider the façade picked, validates the resulting Contract, then atomically writes `contract.json` + `contract.d.ts` colocated with the source.
|
|
65
|
+
- **Extension namespaces.** Extensions contribute namespaced constructors (`pgvector.Vector(length: 1536)`, `postgis.Geometry(...)`) and helper presets. Install them by adding the descriptor to **two** places — both fields are named `extensions`, but the two surfaces consume two different descriptor types and shapes:
|
|
66
|
+
- **In the config (façade and core):** `extensions: [pgvector]` — array of *control* descriptors imported from `@internal/extension-<name>/control`.
|
|
67
|
+
- **In the TS builder's `defineContract` (only when authoring `contract.ts`):** `extensions: { pgvector }` — record of *pack* descriptors imported from `@internal/extension-<name>/pack`.
|
|
68
|
+
- **Contract space.** Every package that emits a contract owns its own *contract space* — a `prisma.config.ts` at package root, a contract source, the colocated emitted artefacts, and a `migrations/` directory. **There are two intentional on-disk layouts**, picked by whether the contract space is the consuming application or a contract-space package (an extension, an internal aggregate-root package, etc.):
|
|
69
|
+
- **Application layout** (what you use when building an *app*). `prisma.config.ts` at repo root; `src/prisma/contract.{prisma,ts}`; `src/prisma/contract.{json,d.ts}` colocated; `src/prisma/db.ts` colocated; migrations under `migrations/app/<timestamp>_<slug>/`. The `app/` segment is the consuming application's space-id; extension space-ids land in sibling `migrations/<extension-space-id>/` directories that the extension packages manage. This is what `examples/prisma-8-demo` uses and what `prisma orm init` scaffolds by default (`src/prisma/contract.prisma` with `db.ts` beside it).
|
|
70
|
+
- **Contract-space-package layout** (what you use when *publishing* a contract-space package — extensions, internal monorepo packages). `prisma.config.ts` at package root; `src/contract.{prisma,ts}` directly (no `prisma/` subdir); `src/contract.{json,d.ts}` colocated; `migrations/<timestamp>_<slug>/` directly under `migrations/` (no `<space-id>` segment — the package *is* a single space). Documented in `.cursor/rules/contract-space-package-layout.mdc` and ADR 212.
|
|
71
|
+
|
|
72
|
+
Both layouts let `ormConfig`'s `contract:` path point at the source; the framework derives everything else (emit output, migration root) from there. Pick the layout that matches what you're building and stick with it — don't mix.
|
|
73
|
+
|
|
74
|
+
## Diagnostic codes you route on
|
|
75
|
+
|
|
76
|
+
`prisma contract emit` surfaces structured errors with stable codes; branch on `code` rather than message text.
|
|
77
|
+
|
|
78
|
+
| Code | Meaning | Next move |
|
|
79
|
+
|---|---|---|
|
|
80
|
+
| `CONFIG.CONTRACT_MISSING` | `contract` not set in `prisma.config.ts`. | Add `contract: './src/prisma/contract.prisma'` (app layout) or `'./src/contract.prisma'` (contract-space-package layout) — likewise for `.ts` sources — to the `ormConfig({...})` section. |
|
|
81
|
+
| `CONFIG.VERSION_MARKER_MISSING` | The default export was not produced by `definePrismaConfig` / `ormConfig` (a flat `defineConfig({...})`, a plain object, or a Prisma 7 config). | Rewrite to the envelope form in *Key Concepts* above. |
|
|
82
|
+
| `CONTRACT.VALIDATION_FAILED` | Source loaded but the Contract IR failed structural validation. | Read `meta.errors` for the offending model/field, fix the source, re-emit. |
|
|
83
|
+
| `CONFIG.MISSING_EXTENSION_PACKS` | The contract uses a namespaced constructor (e.g. `pgvector.Vector(...)`) but `extensions` in the config does not list a matching descriptor. `meta.missingExtensionPacks` names them. | Install the package, import its control descriptor (`import pgvector from '@internal/extension-pgvector/control'`), add it to `extensions: [...]` in `ormConfig({...})`. |
|
|
84
|
+
|
|
85
|
+
## Workflow — Read the contract source of truth
|
|
86
|
+
|
|
87
|
+
The concept: every contract change starts by locating the source file. The config is authoritative — read `prisma.config.ts`, find the `contract:` field (a path string under the façade), and open the file it points at. The same field tells you the installed `extensions: [...]`.
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
cat prisma.config.ts
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
If `contract:` ends in `.prisma`, the source is PSL; if it ends in `.ts`, the source is the TS builder. If `prisma.config.ts` is missing, route to `references/quickstart.md`.
|
|
94
|
+
|
|
95
|
+
## Workflow — Edit a model / field / relation (PSL)
|
|
96
|
+
|
|
97
|
+
The concept: PSL models lower to tables (or collections, on Mongo); fields lower to columns; `@relation(...)` declares the FK side. Add the relation only on the owning side — the framework derives the back-reference automatically.
|
|
98
|
+
|
|
99
|
+
```prisma
|
|
100
|
+
model User {
|
|
101
|
+
id Int @id @default(autoincrement())
|
|
102
|
+
email String @unique
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
model Post {
|
|
106
|
+
id Int @id @default(autoincrement())
|
|
107
|
+
title String
|
|
108
|
+
authorId Int
|
|
109
|
+
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
|
|
110
|
+
|
|
111
|
+
@@unique([title, authorId])
|
|
112
|
+
@@index([authorId])
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Then run `pnpm prisma contract emit` (or rely on the Vite plugin — see `references/build.md`). Specify cascade behaviour explicitly with `onDelete` / `onUpdate`; the default is `Restrict`.
|
|
117
|
+
|
|
118
|
+
**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`.
|
|
119
|
+
|
|
120
|
+
`@@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
|
+
|
|
122
|
+
```prisma
|
|
123
|
+
@@index(expression: "lower(email)", name: "users_email_lower")
|
|
124
|
+
@@index([authorId], where: "(archived_at IS NULL)", name: "posts_author_active")
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
`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`.
|
|
128
|
+
|
|
129
|
+
PSL alias surface for repeated types lives in a top-level `types {}` block:
|
|
130
|
+
|
|
131
|
+
```prisma
|
|
132
|
+
types {
|
|
133
|
+
Email = String
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
model User {
|
|
137
|
+
id Int @id @default(autoincrement())
|
|
138
|
+
email Email @unique
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Note: scalar lists (e.g. `String[]`) are gated on the target's `sql.scalarList` capability — PostgreSQL declares it, so `tags String[]` lowers to an array column (with a generated no-NULL-elements check, see `@noCheck` under *Enums*); SQLite does not, and rejects the field with `PSL_SCALAR_LIST_UNSUPPORTED_TARGET`. Implicit Prisma-ORM many-to-many (list navigation on both sides without a join model) is rejected by the SQL interpreter — declare the join model explicitly and the ORM traverses it for you (`references/queries.md`). Composite/embeddable types (`type Address { ... }` with `address Address` on a model) are supported: the interpreter lowers them to `valueObjects` in the domain and stores them as `jsonb` columns. See *Workflow — Value objects* below.
|
|
143
|
+
|
|
144
|
+
## Workflow — Edit a model / field / relation (TS builder)
|
|
145
|
+
|
|
146
|
+
The concept: same model, different authoring surface. The façade re-exports `defineContract`, `field`, `model`, `rel`, plus the `family`/`target` packs as default exports of `@internal/postgres/family` and `@internal/postgres/target`. Use the callback overload (`defineContract({...}, ({ field, model, rel, type }) => ({...}))`) to get the higher-level helpers (`field.text()`, `field.id.uuidv7String()`, `field.temporal.createdAt()`, `type.sql.String(35)`).
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
import sqlFamily from '@internal/postgres/family';
|
|
150
|
+
import { defineContract } from '@internal/postgres/contract-builder';
|
|
151
|
+
import postgresPack from '@internal/postgres/target';
|
|
152
|
+
|
|
153
|
+
export const contract = defineContract(
|
|
154
|
+
{
|
|
155
|
+
family: sqlFamily,
|
|
156
|
+
target: postgresPack,
|
|
157
|
+
},
|
|
158
|
+
({ field, model }) => ({
|
|
159
|
+
models: {
|
|
160
|
+
User: model('User', {
|
|
161
|
+
fields: {
|
|
162
|
+
id: field.id.uuidv7String(),
|
|
163
|
+
email: field.text().unique(),
|
|
164
|
+
createdAt: field.temporal.createdAt(),
|
|
165
|
+
},
|
|
166
|
+
}).sql({ table: 'app_user' }),
|
|
167
|
+
},
|
|
168
|
+
}),
|
|
169
|
+
);
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Then `pnpm prisma contract emit`. The `field.<scalar>()` helpers are only available inside the callback overload; outside the callback only `field.column(...)`, `field.generated(...)`, `field.namedType(...)` exist.
|
|
173
|
+
|
|
174
|
+
For Mongo, swap every `@internal/postgres/*` import for `@internal/mongo/*`. The Mongo builder also exposes `index` and `valueObject`.
|
|
175
|
+
|
|
176
|
+
## Workflow — Add an extension-typed scalar (pgvector)
|
|
177
|
+
|
|
178
|
+
The concept: an extension contributes a namespace (`pgvector.*`) plus two descriptor flavours — a *control* descriptor for the config and a *pack* descriptor for the TS builder. Register the control descriptor in `ormConfig`'s `extensions` (array form). If you're authoring with the TS builder, also register the pack descriptor in `defineContract.extensions` (record form). Then reference the namespaced constructor from the contract.
|
|
179
|
+
|
|
180
|
+
`prisma.config.ts`:
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
import 'dotenv/config';
|
|
184
|
+
import { definePrismaConfig } from '@prisma/cli-engine';
|
|
185
|
+
import pgvector from '@internal/extension-pgvector/control';
|
|
186
|
+
import { defineConfig as ormConfig } from '@internal/postgres/config';
|
|
187
|
+
|
|
188
|
+
export default definePrismaConfig({
|
|
189
|
+
orm: ormConfig({
|
|
190
|
+
contract: './src/prisma/contract.prisma',
|
|
191
|
+
extensions: [pgvector],
|
|
192
|
+
db: { connection: process.env['DATABASE_URL']! },
|
|
193
|
+
}),
|
|
194
|
+
});
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
`src/prisma/contract.prisma`:
|
|
198
|
+
|
|
199
|
+
```prisma
|
|
200
|
+
model Document {
|
|
201
|
+
id Int @id @default(autoincrement())
|
|
202
|
+
content String
|
|
203
|
+
embedding pgvector.Vector(length: 1536)
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Emit. The named-type lowering puts `vector(1536)` on the column and the type map in `contract.d.ts` carries the right TS type.
|
|
208
|
+
|
|
209
|
+
If you reference `pgvector.*` without registering the descriptor in the config, emit fails with `CONFIG.MISSING_EXTENSION_PACKS` and `meta.missingExtensionPacks: ['pgvector']`. The envelope's `fix` text says *"Add the missing extension descriptors to `extensions` in prisma.config.ts"* — that field name matches `ormConfig`.
|
|
210
|
+
|
|
211
|
+
For canonical worked examples covering single and multi-extension setups, read `examples/prisma-8-demo/prisma.config.ts`, `examples/multi-extension-monorepo/app/prisma.config.ts` and `examples/prisma-8-postgis-demo/prisma.config.ts`.
|
|
212
|
+
|
|
213
|
+
## Workflow — Polymorphism (`@@discriminator` / `@@base`)
|
|
214
|
+
|
|
215
|
+
The concept (SQL targets): one base model declares the discriminator field; each variant model declares its base + discriminator value. The variant chooses STI vs MTI by **whether it sets `@@map(...)`**: no `@@map` means the variant inherits the base's table (single-table inheritance); `@@map("variant_table")` means the variant gets its own table joined 1:1 by primary key (multi-table inheritance).
|
|
216
|
+
|
|
217
|
+
```prisma
|
|
218
|
+
model Task {
|
|
219
|
+
id Int @id @default(autoincrement())
|
|
220
|
+
title String
|
|
221
|
+
type String
|
|
222
|
+
|
|
223
|
+
@@discriminator(type)
|
|
224
|
+
@@map("tasks")
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// STI variant — shares the `tasks` table.
|
|
228
|
+
model Bug {
|
|
229
|
+
severity String
|
|
230
|
+
|
|
231
|
+
@@base(Task, "bug")
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// MTI variant — joins to `tasks` via PK; carries its own `features` table.
|
|
235
|
+
model Feature {
|
|
236
|
+
priority Int
|
|
237
|
+
|
|
238
|
+
@@base(Task, "feature")
|
|
239
|
+
@@map("features")
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Verify the polymorphism syntax against the interpreter tests if in doubt: `packages/2-sql/2-authoring/contract-psl/test/interpreter.polymorphism.test.ts`.
|
|
244
|
+
|
|
245
|
+
Mongo PSL takes the same `@@discriminator(field)` / `@@base(Base, "value")` attributes (the Mongo interpreter implements both; `examples/mongo-demo/src/contract.prisma` declares `Post` with `@@discriminator(kind)` and `Article` / `Tutorial` variants via `@@base(Post, "article")`). Variants share the base's collection — there is no `@@map` on a Mongo variant, since there is no second table to join. The TS builder's `discriminator` option on the model is the programmatic equivalent.
|
|
246
|
+
|
|
247
|
+
Querying the variants is a runtime concern — see `references/queries.md`.
|
|
248
|
+
|
|
249
|
+
## Workflow — Value objects (composite types)
|
|
250
|
+
|
|
251
|
+
The concept: `type Foo { ... }` blocks declare value-object shapes. The interpreter lowers them to `valueObjects` in the contract domain and stores them as `jsonb` columns. Nested value-object references are supported.
|
|
252
|
+
|
|
253
|
+
```prisma
|
|
254
|
+
type Address {
|
|
255
|
+
street String
|
|
256
|
+
city String
|
|
257
|
+
zip String?
|
|
258
|
+
country String
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
model User {
|
|
262
|
+
id String @id @default(uuid())
|
|
263
|
+
email String
|
|
264
|
+
address Address?
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Emitted `contract.json` carries `domain.namespaces.<ns>.valueObjects.Address` with its field descriptors, and the `address` column lands as `codecId: "pg/jsonb@1"` / `nativeType: "jsonb"` in `storage`.
|
|
269
|
+
|
|
270
|
+
Canonical worked example: `examples/prisma-8-demo/src/prisma/contract.prisma`.
|
|
271
|
+
|
|
272
|
+
## Workflow — Enums
|
|
273
|
+
|
|
274
|
+
The concept: PSL `enum` blocks declare a domain enum: a named value-set stored through a declared codec (`@@type("pg/text@1")` → a `text` column) and enforced by a planner-generated CHECK constraint. Each member maps to its database value with `Name = "value"`. Use the enum name as a field type on any model in the same contract.
|
|
275
|
+
|
|
276
|
+
```prisma
|
|
277
|
+
enum user_type {
|
|
278
|
+
@@type("pg/text@1")
|
|
279
|
+
admin = "admin"
|
|
280
|
+
user = "user"
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
model User {
|
|
284
|
+
id String @id @default(uuid())
|
|
285
|
+
kind user_type
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
**Waiving enforcement (`@noCheck`).** A field can decline the generated CHECK constraints for its column: bare `@noCheck` waives every kind the column's shape derives; `@noCheck(membership)` and `@noCheck(elementNotNull)` waive one kind (`membership` is the enum value-set check; `elementNotNull` is the no-NULL-elements check every list column gets). The TS authoring equivalent is `.noCheck(...)` on the field builder. Declared types do not change: the field still types as the enum union, and a list still types with non-null elements — once enforcement is waived, runtime values may diverge from what the types claim. That divergence is the author's accepted risk, and it is scoped to the kinds actually waived: waiving `membership` stops the database rejecting out-of-set values, waiving `elementNotNull` stops it rejecting NULL elements. A list that waives only `membership` still rejects NULL elements. `contract infer` emits `@noCheck(elementNotNull)` automatically for list columns whose source database does not carry the generated check.
|
|
290
|
+
|
|
291
|
+
Canonical worked example: `examples/prisma-8-demo/src/prisma/contract.prisma`.
|
|
292
|
+
|
|
293
|
+
## Workflow — CHECK constraints (`@@check`)
|
|
294
|
+
|
|
295
|
+
The concept: `@@check` declares a hand-written CHECK constraint on a model — a rule you want the database to enforce, on top of whatever generated checks the model's enums and list columns already get (see *Workflow — Enums* above). Without it, a constraint you added by hand had no way into the contract, so it looked like an undeclared extra a destructive migration could drop.
|
|
296
|
+
|
|
297
|
+
```prisma
|
|
298
|
+
model Order {
|
|
299
|
+
id Int @id
|
|
300
|
+
total Decimal
|
|
301
|
+
|
|
302
|
+
// name: is a prefix — the physical constraint becomes order_total_positive_<8-hex hash>.
|
|
303
|
+
@@check(expression: "total > 0", name: "order_total_positive")
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
`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:
|
|
308
|
+
|
|
309
|
+
- **`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.
|
|
310
|
+
- **`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.
|
|
311
|
+
|
|
312
|
+
A model can carry any number of `@@check` attributes. The TS builder mirrors this with `check({ expression, name })` / `check({ expression, map })` on a model's `.sql({ checks: [...] })`, next to `index()`.
|
|
313
|
+
|
|
314
|
+
Check constraints need the target's `checkConstraint` capability. Postgres has it; SQLite does not. `@@check` on a SQLite contract is rejected at authoring time; `check()` on a SQLite contract isn't caught until migration DDL is rendered, but either way you get a refusal, never a silently-dropped constraint.
|
|
315
|
+
|
|
316
|
+
## Workflow — Namespaces (Postgres schemas)
|
|
317
|
+
|
|
318
|
+
The concept: wrap models in a `namespace <name> { ... }` block to place them in a non-default Postgres schema. Models outside any block go into the implicit default namespace.
|
|
319
|
+
|
|
320
|
+
```prisma
|
|
321
|
+
namespace public {
|
|
322
|
+
model Profile {
|
|
323
|
+
id String @id @default(uuid())
|
|
324
|
+
username String
|
|
325
|
+
userId String @unique
|
|
326
|
+
@@map("profile")
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
Canonical worked example: `examples/supabase/src/contract.prisma`.
|
|
332
|
+
|
|
333
|
+
## Workflow — Cross-contract foreign keys
|
|
334
|
+
|
|
335
|
+
The concept: a relation field can reference a model in another contract space using the `<space>:<namespace>.<Model>` form. The contract also supports top-level named-type aliases in a `types {}` block, backed by the same bare type-position constructors used by fields. The `@db.X(args)` channel is removed: rewrite `@db.X` as `X` and `@db.X(args)` as `X(args)`; remaining uses fail with an actionable diagnostic naming the replacement.
|
|
336
|
+
|
|
337
|
+
```prisma
|
|
338
|
+
types {
|
|
339
|
+
AuthUserId = Uuid
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
namespace public {
|
|
343
|
+
model Profile {
|
|
344
|
+
id String @id @default(uuid())
|
|
345
|
+
username String
|
|
346
|
+
userId AuthUserId @unique
|
|
347
|
+
user supabase:auth.AuthUser @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
348
|
+
@@map("profile")
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
`supabase:auth.AuthUser` means: model `AuthUser` in namespace `auth` of contract space `supabase`. The target space is provided by a registered extension pack (here `@internal/extension-supabase/pack`).
|
|
354
|
+
|
|
355
|
+
Canonical worked example: `examples/supabase/src/contract.prisma`.
|
|
356
|
+
|
|
357
|
+
## Workflow — `@@control` (control policy)
|
|
358
|
+
|
|
359
|
+
The concept: `@@control(<policy>)` on a model sets whether Prisma manages that table's DDL in migrations. The argument is a positional lowercase literal — one of `managed`, `tolerated`, `external`, or `observed`.
|
|
360
|
+
|
|
361
|
+
```prisma
|
|
362
|
+
model AuditLog {
|
|
363
|
+
id Int @id
|
|
364
|
+
message String
|
|
365
|
+
|
|
366
|
+
@@control(observed)
|
|
367
|
+
}
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
A contract-level default can be set via `defaultControlPolicy` on `prismaContract(path, { defaultControlPolicy })`. See `references/migrations.md` for how control policies affect DDL planning.
|
|
371
|
+
|
|
372
|
+
## Workflow — `@internal/extension-supabase`
|
|
373
|
+
|
|
374
|
+
The concept: the Supabase extension provides the `supabase` contract space (the `auth` / `storage` schemas as `external` tables, plus the platform roles) and its own role-first runtime factory. Its descriptor is the `pack` export, and it goes into the same `extensions` array as any other extension. See `examples/supabase` for the full working pattern.
|
|
375
|
+
|
|
376
|
+
`prisma.config.ts` (mirrors `examples/supabase/prisma.config.ts`):
|
|
377
|
+
|
|
378
|
+
```typescript
|
|
379
|
+
import { definePrismaConfig } from '@prisma/cli-engine';
|
|
380
|
+
import supabasePack from '@internal/extension-supabase/pack';
|
|
381
|
+
import { defineConfig as ormConfig } from '@internal/postgres/config';
|
|
382
|
+
|
|
383
|
+
export default definePrismaConfig({
|
|
384
|
+
orm: ormConfig({
|
|
385
|
+
contract: './src/contract.prisma',
|
|
386
|
+
extensions: [supabasePack],
|
|
387
|
+
migrations: { dir: 'migrations' },
|
|
388
|
+
}),
|
|
389
|
+
});
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
`db.ts` does **not** use the stock `postgres()` factory — a Supabase app builds its client with the `supabase()` factory from `@internal/extension-supabase/runtime` (role-first: `asUser(jwt)` / `asAnon()` / `asServiceRole()`, JWT validation, RLS). That runtime — and RLS policy authoring (`policy_select` / `@@rls`) — is covered by **`references/supabase.md`**; load it for anything past the config wiring.
|
|
393
|
+
|
|
394
|
+
Export subpaths: `@internal/extension-supabase/pack`, `@internal/extension-supabase/runtime`, `@internal/extension-supabase/contract`. Canonical worked example: `examples/supabase`.
|
|
395
|
+
|
|
396
|
+
## Workflow — Brownfield introspection
|
|
397
|
+
|
|
398
|
+
The concept: pull a contract source out of an existing database and continue from there. `prisma contract infer --db <url>` reads the live schema and writes a `contract.prisma` file. It stops there — follow it with `contract emit` and (when the schema matches a pinned hash) `db sign` as separate steps.
|
|
399
|
+
|
|
400
|
+
```bash
|
|
401
|
+
pnpm prisma contract infer --db $DATABASE_URL --output ./src/prisma/contract.prisma
|
|
402
|
+
pnpm prisma contract emit
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
Infer captures indexes at full fidelity — expression, partial (`where:`), unique non-constraint, `type:`/`options:` — adopting each under `map:` with the live name, except that a name shaped like a wire name whose hash recomputes from the content re-detects as wire-named and emits `name:` with the prefix. RLS surfaces too: `@@rls` on RLS-enabled models, and every policy as a `policy_<operation>` block with `@@map("<live name>")`, verbatim predicate reprints, and `permissive = false` for RESTRICTIVE rows (a policy whose role name can't be spelled as a PSL identifier is skipped with a comment note). Replacing an adopted `map:` with the plain wire spelling later converges via a single rename migration.
|
|
406
|
+
|
|
407
|
+
## Common Pitfalls
|
|
408
|
+
|
|
409
|
+
1. **Forgetting to re-emit after an edit.** `contract.json` and `contract.d.ts` go stale; downstream typecheck and `migration plan` see the old shape. Re-emit, or install the Vite plugin (`references/build.md`).
|
|
410
|
+
2. **Editing the emitted artefacts.** `contract.json` and `contract.d.ts` are emitted; edits there round-trip away on the next emit. Edit the source.
|
|
411
|
+
3. **Wrong factory/import path for the TS builder.** `defineContract`, `field`, `model`, `rel` come from `@internal/postgres/contract-builder` (or `@internal/mongo/contract-builder`). Outside the callback overload, the available field constructors are `field.column(...)`, `field.generated(...)`, `field.namedType(...)`.
|
|
412
|
+
4. **Reaching into internal packages from user code.** User-authored files (`prisma.config.ts`, `contract.ts`, `db.ts`, control clients) import only from `@internal/<target>/<subpath>` and `@internal/extension-<name>/<subpath>`. Imports from `@internal/cli/*`, `@internal/family-*`, `@internal/target-*`, `@internal/adapter-*`, `@internal/driver-*`, or `@internal/sql-contract-*` are framework-internal — the façade composes them for you. If a façade subpath you need is missing for your target, see *What Prisma 8 doesn't do yet* and route to `references/feedback.md`. The canonical worked examples are `examples/multi-extension-monorepo/app/prisma.config.ts` and `examples/prisma-8-postgis-demo/prisma.config.ts`.
|
|
413
|
+
5. **Confusing the config `extensions` with the TS builder's `extensions`.** Same packs, two surfaces, one field name but two shapes: `ormConfig({ extensions: [pgvector] })` (array of *control* descriptors from `@internal/extension-<name>/control`) versus `defineContract({ extensions: { pgvector } })` (record of *pack* descriptors from `@internal/extension-<name>/pack`).
|
|
414
|
+
6. **Writing a flat `prisma.config.ts`.** `export default defineConfig({ contract, extensions })` from the target config alone is the pre-rc.4 shape and fails with `CONFIG.VERSION_MARKER_MISSING`. Wrap it: `definePrismaConfig({ orm: ormConfig({...}) })`.
|
|
415
|
+
7. **Renaming a field and expecting the planner to detect it.** Prisma 8 has no in-contract rename hint; the planner sees a destructive drop+add. Hand-edit `migration.ts` after `migration plan` (see `references/migrations.md`), or use the keep-then-drop two-migration pattern.
|
|
416
|
+
|
|
417
|
+
## What Prisma 8 doesn't do yet
|
|
418
|
+
|
|
419
|
+
- **In-contract rename hint.** No `@@rename(old: ..., new: ...)` or similar. Use the workarounds in *Common Pitfalls* #7. To request first-class rename, file via `references/feedback.md`.
|
|
420
|
+
- **Model validations.** No declarative `@validates(...)` surface. Validate in application code (arktype). To request declarative validations in the contract, file via `references/feedback.md`.
|
|
421
|
+
- **Lifecycle callbacks** (`beforeSave`, `afterCreate`, etc.). Not supported. Use middleware (`references/runtime.md`) or app code. To request lifecycle callbacks, file via `references/feedback.md`.
|
|
422
|
+
- **Soft delete / `paranoid: true`.** No built-in soft-delete column. Add a nullable `deletedAt DateTime?` and filter explicitly in queries (or in middleware). To request built-in soft delete, file via `references/feedback.md`.
|
|
423
|
+
- **Scopes / default filters.** No ActiveRecord-style scopes. Compose query helpers yourself. To request scopes, file via `references/feedback.md`.
|
|
424
|
+
- **Implicit Prisma-ORM many-to-many.** List navigation on both sides without an explicit join model is rejected. Author the join model explicitly. To request implicit M2M, file via `references/feedback.md`.
|
|
425
|
+
|
|
426
|
+
## Reference
|
|
427
|
+
|
|
428
|
+
- Run `pnpm prisma contract --help` for the live command surface.
|
|
429
|
+
- PSL feature surface and what the interpreter accepts: `packages/2-sql/2-authoring/contract-psl/README.md`.
|
|
430
|
+
- TS builder surface and the callback-helper vocabulary: `packages/2-sql/2-authoring/contract-ts/README.md`.
|
|
431
|
+
- Layouts (where `contract.prisma`, `contract.json`, `contract.d.ts`, and `migrations/` live):
|
|
432
|
+
- **App layout** (`src/prisma/...` + `migrations/app/...`) — what `examples/prisma-8-demo` demonstrates; the canonical shape consuming applications use.
|
|
433
|
+
- **Contract-space-package layout** (`src/contract.{prisma,ts}` directly, `migrations/<timestamp>_<slug>/` without a space-id segment) — for extensions and aggregate-root packages, documented in `.cursor/rules/contract-space-package-layout.mdc` and ADR 212.
|
|
434
|
+
|
|
435
|
+
## Checklist
|
|
436
|
+
|
|
437
|
+
- [ ] Read `prisma.config.ts` and identified the contract source (path string ending in `.prisma` or `.ts`) and the installed `extensions: [...]`.
|
|
438
|
+
- [ ] All user-authored imports resolve to `@internal/<target>/<subpath>` (e.g. `@internal/postgres/config`) or `@internal/extension-<name>/<subpath>`. No imports from `@internal/cli/*`, `@internal/family-*`, `@internal/target-*`, `@internal/adapter-*`, `@internal/driver-*`, or `@internal/sql-contract-*` in user files.
|
|
439
|
+
- [ ] Edited the contract source (`contract.prisma` or `contract.ts`), not an emitted artefact.
|
|
440
|
+
- [ ] For new extension namespaces: added the package, imported its control descriptor (`@internal/extension-<name>/control`), added it to `extensions: [...]` in `ormConfig({...})` (and the matching pack descriptor to `defineContract({extensions: {...}})` if using the TS builder).
|
|
441
|
+
- [ ] `prisma.config.ts` is the envelope form — `definePrismaConfig({ orm: ormConfig({...}) })` — not a flat `defineConfig({...})`.
|
|
442
|
+
- [ ] For renames: hand-edited `migration.ts` after `migration plan` (or used the keep-then-drop two-migration pattern) — Prisma 8 has no rename hint today.
|
|
443
|
+
- [ ] Ran `pnpm prisma contract emit` after the edit (or let the Vite plugin re-emit on save).
|
|
444
|
+
- [ ] Confirmed `contract.json` and `contract.d.ts` updated next to the source.
|
|
445
|
+
- [ ] Did **not** hand-edit `contract.json` / `contract.d.ts`.
|
|
446
|
+
- [ ] Did **not** confabulate a missing feature (validations, callbacks, soft delete, scopes, in-contract rename hint) — referred the user to *What Prisma 8 doesn't do yet* + `references/feedback.md`.
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
|
|
2
|
+
# Prisma 8 — Debug
|
|
3
|
+
|
|
4
|
+
> **Edit your data contract. Prisma handles the rest.**
|
|
5
|
+
|
|
6
|
+
When a Prisma 8 call fails, the framework returns a **structured envelope**. The agent's job is to read the envelope, route on the `code`, and chain to the right authoring skill for the actual fix. This skill teaches the envelope shapes and the routing — it does not duplicate sibling-skill workflows.
|
|
7
|
+
|
|
8
|
+
## When to Use
|
|
9
|
+
|
|
10
|
+
- User pastes an error envelope (CLI failure, runtime exception, `--json` output).
|
|
11
|
+
- User says *"my query won't typecheck"*, *"my migration won't apply"*, *"my emit failed"*, *"the runtime crashed"*.
|
|
12
|
+
- User mentions a stable code (`CONFIG.*`, `CLI.*`, `CONTRACT.*`, `MIGRATION.*`, `ORM.*`, `RUNTIME.*`, `DRIVER.*`, `LINT.*`, `BUDGET.*`, `PLAN.*`). A `PN-CLI-4001`-style numeric code is from a pre-0.17 release; the crosswalk to the dotted name is in ADR 239 and `docs/reference/error-reference.md`.
|
|
13
|
+
- User mentions: *Studio, EXPLAIN, query log, prepared statements, drift, hash mismatch, capability, planner*.
|
|
14
|
+
|
|
15
|
+
## When Not to Use
|
|
16
|
+
|
|
17
|
+
- User wants to author a query / model / migration → the matching authoring skill.
|
|
18
|
+
- User wants to *prevent* errors (lints, budgets, type-level guards) → `references/runtime.md`.
|
|
19
|
+
- User wants the framework changed because the surface itself is the problem (no envelope to route on, capability genuinely missing) → `references/feedback.md`.
|
|
20
|
+
|
|
21
|
+
## Key Concepts
|
|
22
|
+
|
|
23
|
+
### Two envelope shapes
|
|
24
|
+
|
|
25
|
+
Prisma 8 emits **two distinct envelopes** depending on which seam threw. Read which one you have *before* routing.
|
|
26
|
+
|
|
27
|
+
**1. CLI envelope** — produced by `prisma ...` commands (emit, db init/update/verify/sign/schema, migration plan/apply/show/status, init). Shape (see `CliErrorEnvelope` in `packages/1-framework/1-core/errors/src/control.ts`):
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"ok": false,
|
|
32
|
+
"code": "MIGRATION.UNFILLED_PLACEHOLDER",
|
|
33
|
+
"severity": "error",
|
|
34
|
+
"summary": "Unfilled migration placeholder",
|
|
35
|
+
"why": "...",
|
|
36
|
+
"fix": "...",
|
|
37
|
+
"nextActions": [],
|
|
38
|
+
"where": { "path": "...", "line": 42 },
|
|
39
|
+
"meta": { "slot": "..." },
|
|
40
|
+
"docsUrl": "https://docs.prisma.io/docs/orm/v8/reference/error-reference#MIGRATION.UNFILLED_PLACEHOLDER"
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Every code is a dotted `NAMESPACE.SUBCODE`; the namespace is the prefix (`CONFIG`, `CLI`, `CONTRACT`, `PSL`, `ORM`, `RUNTIME`, `DRIVER`, `MIGRATION`, `PLAN`, `BUDGET`, `LINT`, plus one per extension). The full catalogue, one entry per code with its payload, is `docs/reference/error-reference.md`. Severity is `error | warn | info`, and exit codes carry meaning: `2` is "could not run", `3` is a user abort, and `4` is "ran and found something" — `db verify` / `db sign` exit `4` with their findings as `error` diagnostics on a completed envelope, and `migration status` exits `0` with `warn` diagnostics. Route on **severity + code together**, not on exit code alone.
|
|
45
|
+
|
|
46
|
+
**2. Runtime envelope** — thrown by the in-process runtime when executing a query (see `RuntimeErrorEnvelope` in `packages/1-framework/1-core/framework-components/src/execution/runtime-error.ts`):
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
{ name: 'RuntimeError', code: 'BUDGET.TIME_EXCEEDED', category: 'BUDGET', severity: 'error', message: '...', details: { ... } }
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`category` is the prefix of `code` (`PLAN`, `CONTRACT`, `LINT`, `BUDGET`, `RUNTIME`, `ORM`, …). `details` holds the structured context (`details` is the runtime envelope's equivalent of the CLI envelope's `meta`). Recognise either shape programmatically with `isStructuredError` and match on `error.code` — never `instanceof`.
|
|
53
|
+
|
|
54
|
+
**3. SQL driver errors** — surface as `SqlQueryError` / `SqlConnectionError` (see `packages/2-sql/1-core/errors/`). Fields on `SqlQueryError`: `kind: 'sql_query'`, `sqlState` (Postgres SQLSTATE, e.g. `'23505'`), `constraint`, `table`, `column`, `detail`, `cause`. These carry no dotted code — route on `sqlState` and the constraint metadata. SQL driver errors are typically wrapped by middleware before reaching the user, but raw-SQL paths can surface them directly.
|
|
55
|
+
|
|
56
|
+
### Wrapped errors
|
|
57
|
+
|
|
58
|
+
`db migrate`, `db init`, and `db update` map an apply failure the runner did not classify into `MIGRATION.RUNNER_FAILED`, passing the failure's own `meta` through unchanged and its detail into `why`. Migration-tools failures that *are* classified (`MIGRATION.HASH_MISMATCH`, `MIGRATION.AMBIGUOUS_TARGET`, `MIGRATION.PATH_UNREACHABLE`, …) arrive under their own code. **When `code` is `MIGRATION.RUNNER_FAILED`, read `why` and `meta`** — that is where the routing-quality information lives (`meta.runnerErrorCode` at the legacy-marker-shape site).
|
|
59
|
+
|
|
60
|
+
### How to ask for the full envelope
|
|
61
|
+
|
|
62
|
+
If the user only pasted the human summary, ask for `--json` output (machine envelope) or re-run with `-v` (CLI prints the full structured fields). `--json` and `-v` are global flags on every CLI command.
|
|
63
|
+
|
|
64
|
+
## Routing — script teardown and closed client
|
|
65
|
+
|
|
66
|
+
These symptoms are not structured envelopes — route on the message text and chain to `references/runtime.md` § *Running as a script (teardown)*.
|
|
67
|
+
|
|
68
|
+
| Symptom | Next move |
|
|
69
|
+
|---|---|
|
|
70
|
+
| `TypeError: db.end is not a function` | The runtime client does not expose `db.end()` — that's the `node-postgres` pool API (`pool.end()`). The right call is `await db.close()`. See `references/runtime.md` § *Running as a script (teardown)*. |
|
|
71
|
+
| Script hangs after queries print / process won't exit | On Postgres the façade-owned `pg.Pool` keeps the event loop alive. Call `await db.close()` before the script returns, or `await using db = postgres<Contract>(...)` at the top of a script module (do NOT put `await using` inside a request handler — block-scoped, would close per-request). See `references/runtime.md` § *Running as a script (teardown)*. |
|
|
72
|
+
| `Error('Postgres client is closed')` / `Error('SQLite client is closed')` / `Error('Mongo client is closed')` | The client was closed via `db.close()` (terminal state). Remove the early `close()`, reorder so `close()` runs last after all queries, or construct a new `db` if reconnection is intended. See `references/runtime.md` § *Running as a script (teardown)*. |
|
|
73
|
+
|
|
74
|
+
## Routing — symptom and code → next move
|
|
75
|
+
|
|
76
|
+
The single source of truth: read the envelope, find the row by `code`, follow the next move. Every code below has an entry in `docs/reference/error-reference.md` (anchored `#<CODE>`); when a code is missing here, read it there.
|
|
77
|
+
|
|
78
|
+
| Code | Where it surfaces | Next move |
|
|
79
|
+
|---|---|---|
|
|
80
|
+
| `CONFIG.FILE_NOT_FOUND` | Most `prisma` commands | Run `prisma orm init`, or pass `--config <path>`. |
|
|
81
|
+
| `CONFIG.CONTRACT_MISSING` | `contract emit`, `db *` | Add `contract: './src/prisma/contract.prisma'` to the `ormConfig({...})` section of `prisma.config.ts`. See `references/contract.md`. |
|
|
82
|
+
| `CONFIG.VERSION_MARKER_MISSING` | Any command loading config | The default export was not built by the current `definePrismaConfig` / `ormConfig` pair (plain object, spread copy, or a Prisma 7 config). Rewrite to the envelope form in `references/contract.md`. |
|
|
83
|
+
| `CONFIG.VALIDATION_FAILED` | Any command reading the malformed section | `meta.section` / `meta.field` name the config section. Fix `prisma.config.ts`. |
|
|
84
|
+
| `CONTRACT.VALIDATION_FAILED` | `contract emit`, `db *` | Re-run `pnpm prisma contract emit` after fixing the contract source named in `where.path`; `meta.errors` lists the issues. See `references/contract.md`. |
|
|
85
|
+
| `CONFIG.DB_CONNECTION_REQUIRED` | `db *`, `db migrate`, `migration status` | Pass `--db <url>` or set `db.connection` in `prisma.config.ts`. |
|
|
86
|
+
| `CONFIG.MISSING_EXTENSION_PACKS` | `contract emit` (e.g. contract uses `pgvector.Vector(...)` but config does not list the pgvector descriptor) | Add the descriptors named in `meta.missingExtensionPacks` to `extensions` in `prisma.config.ts`. See `references/contract.md`. |
|
|
87
|
+
| `MIGRATION.PLANNING_FAILED` | `db init`, `db update` | Inspect `meta.conflicts`. Recovery is per-conflict — chain to `references/migrations.md`. |
|
|
88
|
+
| `CLI.INIT_MISSING_FLAGS` / `CLI.INIT_INVALID_FLAG_VALUE` / `CLI.INIT_REINIT_NEEDS_FORCE` / `CLI.INIT_*` | `prisma orm init` | Re-run with the flags listed in `meta.missingFlags` or `meta.allowed`; a re-init needs `--confirm <directory name>`. `CLI.INIT_INSTALL_FAILED` (exit 4) and `CLI.INIT_EMIT_FAILED` (exit 5) are findings on a completed scaffold — the files are on disk; fix and re-run the named step. |
|
|
89
|
+
| `MIGRATION.UNFILLED_PLACEHOLDER` | `node migrations/app/<dir>/migration.ts` (self-emit) or `db migrate` | Edit `migration.ts`, replace the `placeholder("<slot>")` named by `meta.slot` with a real query closure, self-emit. See `references/migrations.md`. |
|
|
90
|
+
| `MIGRATION.FILE_MISSING` | Reading a migration package | Restore from version control or scaffold a fresh package with `migration plan` / `migration new`. |
|
|
91
|
+
| `MIGRATION.INVALID_DEFAULT_EXPORT` | Loading `migration.ts` | Use `export default class extends Migration<Start, End> { ... }` (or a factory returning `{ operations, targetId, destination }`). See `references/migrations.md`. |
|
|
92
|
+
| `MIGRATION.DATA_TRANSFORM_CONTRACT_MISMATCH` | Building a data-transform query plan | Pass the same `endContract` reference to both `this.dataTransform(endContract, …)` and the query-builder context. |
|
|
93
|
+
| `MIGRATION.HASH_MISMATCH` | Any read of a migration package (`plan`, `list`, `db migrate`) | `ops.json` / `migration.json` were edited without self-emitting. Run `node migrations/app/<dir>/migration.ts` to re-emit. |
|
|
94
|
+
| `CONTRACT.MARKER_MISSING` | `db verify` (`error` diagnostic, exit 4), runtime startup (warning) | DB has no marker yet. Run `prisma db init --db <url>` (baseline empty DB), `db update --db <url>` (apply contract directly), or `db sign --db <url>` if the schema already matches the contract. |
|
|
95
|
+
| `CONTRACT.MARKER_MISMATCH` | `db verify` (exit 4), runtime startup (warning) | Marker disagrees with contract hash (`meta.expected` / `meta.actual`). Either migrate forward (`db migrate` / `db update`), or — if the DB is correct after a manual fix-up — `db sign`. See `references/migrations.md`. |
|
|
96
|
+
| `CONTRACT.TARGET_MISMATCH` | `db verify`, runtime startup | Contract target ≠ config target; align them (see `meta.expected` / `meta.actual`). |
|
|
97
|
+
| `CONTRACT.SCHEMA_VERIFICATION_FAILED` | `db verify`, `db sign` (both exit 4 with the finding) | Live schema does not satisfy the contract; `meta.issues` lists the drifted paths per `meta.space`. Run `db update` to reconcile, or adjust the contract. |
|
|
98
|
+
| `MIGRATION.RUNNER_FAILED` | `db migrate`, `db update`, `db init` | Wrapper for an unclassified apply failure; `why` and `meta` carry the detail. Reconcile the reported failure, then re-run. Previously applied migrations are preserved. |
|
|
99
|
+
| `MIGRATION.DESTRUCTIVE_CHANGES` | `db update` when run with nobody to ask (`--no-interactive`, CI) | Consent is the database name: interactively you type it; non-interactively pass `--confirm <database>`. `--yes` does **not** grant it. `--dry-run` previews. **Only `db update` has this flow** — `db migrate` does not gate destructive ops on a flag. |
|
|
100
|
+
| `MIGRATION.AMBIGUOUS_TARGET` / `MIGRATION.NO_INVARIANT_PATH` / `MIGRATION.UNKNOWN_INVARIANT` | `db migrate` | Concurrent-migration and invariant flows — `references/migration-review.md`. |
|
|
101
|
+
| `MIGRATION.PATH_UNREACHABLE` / `MIGRATION.MARKER_MISMATCH` | `db migrate` | Run `db migrate --show --db $URL` to inspect the path, then `migration plan --from <from> --to <target>` or `migration list` to audit the graph — see `references/migration-review.md`. |
|
|
102
|
+
| `MIGRATION.PLAN_ORIGIN_UNKNOWN` / `MIGRATION.HASH_NOT_IN_GRAPH` / `MIGRATION.SNAPSHOT_MISSING` | `migration plan` | Origin resolution — `references/migration-model.md` § *The trap* and `references/migrations.md` § *Dev → ship transition*. |
|
|
103
|
+
| `MIGRATION.MISSING_INVARIANTS` | `migration status` `warn` diagnostic (exit 0) | The live marker reached the destination hash structurally but doesn't carry all invariants the target ref requires. Run `db migrate --to <name> --db $URL` to take a path that covers the missing invariants. See `references/migration-review.md`. |
|
|
104
|
+
| `MIGRATION.MARKER_NOT_IN_HISTORY` / `CONTRACT.UNREADABLE` | `migration status` `warn` diagnostics (exit 0; CI gates parse `--json`) | Read `severity` *and* `code`. Up-to-date / pending / no-marker states are not codes — read `spaces[].currentContract` and `migrations[].status` in the `--json` document. `references/migration-review.md` covers the marker-out-of-history flow. |
|
|
105
|
+
| `BUDGET.ROWS_EXCEEDED` / `BUDGET.TIME_EXCEEDED` | Runtime, when the `budgets` middleware is active | Tune `budgets({ maxRows, maxLatencyMs, ... })` or rewrite the query. See `references/runtime.md`. |
|
|
106
|
+
| `LINT.SELECT_STAR` / `LINT.NO_LIMIT` / `LINT.DELETE_WITHOUT_WHERE` / `LINT.UPDATE_WITHOUT_WHERE` / `LINT.READ_ONLY_MUTATION` | Runtime, when the `lints` middleware is active | Fix the query (add a `WHERE` / `LIMIT` / explicit columns), or relax the lint config. See `references/runtime.md`. |
|
|
107
|
+
| `PLAN.HASH_MISMATCH` | Runtime, executing a precompiled plan | The contract the plan was built against does not match the runtime contract. Re-emit, rebuild, redeploy. |
|
|
108
|
+
| `RUNTIME.TEMPORAL_UNAVAILABLE` | Runtime, first read or write touching a Temporal-backed column (`Date`, `Timestamp(p)`, `Timestamptz(p)`, `Time(p)`) or a `temporal.updatedAt()` clock | No global `Temporal`. Node.js 26.8.2 and later ship `globalThis.Temporal`; 26.8.1 and earlier — including every 22 and 24 — do not. Either `import 'temporal-polyfill/full/global'` before the first query, or author the column as `DateString` / `TimestampString(p)` / `TimestamptzString(p)` / `TimeString(p)` to read PostgreSQL's own text. |
|
|
109
|
+
| `ORM.RELATION_MUTATION_UNSUPPORTED` | ORM nested `create` / `connect` on an N:M relation whose junction has required payload columns | Write the junction table directly or use the SQL builder (`meta.junction`). |
|
|
110
|
+
| `RUNTIME.ABORTED` (`details.phase` = `encode\|decode\|stream\|beforeExecute\|afterExecute\|onRow`) | Runtime, when an `AbortSignal` fires mid-execute | Cancellation, not a bug; surface to the caller. |
|
|
111
|
+
| `SqlQueryError` (no dotted code) | Raw-SQL paths surfacing a driver error | Inspect `sqlState` + `constraint` + `table` + `column`. Postgres `23505` = unique violation, `23503` = foreign-key violation, etc. Fix the data or the schema. |
|
|
112
|
+
| TypeScript error mentioning a capability (e.g. `returning()` not on the type, `include` of a many-relation off a many-load) | Authoring-time, before any envelope fires | Capability gates are declared in the **contract** (`capabilities` block, namespaced by target/family), not in `prisma.config.ts`. Route to `references/contract.md` for capability declaration and to `references/queries.md` for which method gates on which capability. Re-emit (`pnpm prisma contract emit`) after enabling. |
|
|
113
|
+
| TypeScript error mentioning a missing field/method on `db.orm.<ns>.<Model>` or a stale `Contract` shape | Authoring-time | Re-emit (`pnpm prisma contract emit`); confirm `db.ts` instantiates with `postgres<Contract>(...)` (the single type parameter propagates the contract types). See `references/runtime.md` and `references/contract.md`. |
|
|
114
|
+
|
|
115
|
+
If the envelope's `code` is not in this table, follow the envelope's `fix` field literally — it's the framework's first-party next move. If `fix` is empty or unhelpful, escalate via `references/feedback.md`.
|
|
116
|
+
|
|
117
|
+
## Common Pitfalls
|
|
118
|
+
|
|
119
|
+
1. **Reading only `summary`, not the rest of the envelope.** `code`, `severity`, `why`, `fix`, `meta`/`details`, and (for CLI errors) `where` all carry information the recovery depends on. The agent routes on `code`; the user sees `summary`.
|
|
120
|
+
2. **Ignoring `severity`.** `migration status` emits warn-level diagnostics and **exits 0**. An agent that only checks exit code misses every concurrent-migration warning.
|
|
121
|
+
3. **Stopping at `code` on `MIGRATION.RUNNER_FAILED`.** That envelope is a wrapper — the detail lives in `why` and `meta`.
|
|
122
|
+
4. **Treating drift as something to silence with `db sign`.** `db sign` writes the marker from the current contract hash and, by default, advances the `db` ref to it (`--no-advance-ref` skips the ref), but it requires schema verification to pass first. Run `db verify` before reaching for `db sign`.
|
|
123
|
+
5. **Re-running `db migrate` after a partial failure without inspecting state.** `db schema --db <url>` shows the live shape; `migration status --db <url> --json` shows where the marker actually is.
|
|
124
|
+
|
|
125
|
+
## What Prisma 8 doesn't do yet
|
|
126
|
+
|
|
127
|
+
- **Studio / GUI database browser.** No first-party Studio. Workaround: `prisma db schema` for a CLI tree of the live schema, or use a third-party tool (TablePlus, DataGrip, `psql`) against your `DATABASE_URL`. If you need a built-in GUI, file a feature request via `references/feedback.md`.
|
|
128
|
+
- **First-class query logger middleware.** No built-in "log every query" middleware ships with the framework. Workaround: write a small custom middleware that wraps each operation (see `references/runtime.md` for middleware composition). If you need a built-in query log, file a feature request via `references/feedback.md`.
|
|
129
|
+
- **`EXPLAIN` integration.** No first-class `.explain()` on plans. Workaround: write the EXPLAIN as a raw query (``db.raw.sql`EXPLAIN ANALYZE ...` ``; see `references/queries.md`). If you need first-class EXPLAIN, file a feature request via `references/feedback.md`.
|
|
130
|
+
- **Prepared statements are not a gap.** `db.prepare(declaration, (sql, params) => plan)` (or `runtime.prepare(...)`) builds a statement once; a row-returning statement runs with `ps.query(runtime, params)`, an affected-count one with `ps.execute(runtime, params)`. See *Prepared statements* in `references/queries.md`. TypedSQL (`.sql` files compiled to callables) is the thing that does not exist.
|
|
131
|
+
|
|
132
|
+
## Asking for help when the envelope doesn't route
|
|
133
|
+
|
|
134
|
+
1. Re-run with `-v` (or `--json` for machine output) to get the full envelope.
|
|
135
|
+
2. If the envelope is genuinely uninformative — empty `fix`, missing `meta`, generic `summary` — that's a framework affordance gap; route to `references/feedback.md` with the envelope, the contract source (sanitised), and the reproduction steps.
|
|
136
|
+
|
|
137
|
+
## Checklist
|
|
138
|
+
|
|
139
|
+
- [ ] Identified which envelope shape (`CliErrorEnvelope`, `RuntimeErrorEnvelope`, `SqlQueryError`).
|
|
140
|
+
- [ ] Read every field — `code`, `severity`, `why`, `fix`, `meta` (or `details`), `where` if present.
|
|
141
|
+
- [ ] If `code` is `MIGRATION.RUNNER_FAILED`, also read `why` and `meta`.
|
|
142
|
+
- [ ] Routed on `code` to the next move (and chained to the matching authoring skill where the table says so).
|
|
143
|
+
- [ ] Re-verified with the relevant CLI command (`db verify`, `migration status --json`, `contract emit`, `db migrate`).
|
|
144
|
+
- [ ] Did not confabulate a Studio / EXPLAIN / query-log API — used the documented workaround and routed unmet capability gaps to `references/feedback.md`.
|