Skip to content

Commit 42bbbff

Browse files
committed
feat(paradedb): adopt emptyContract() and drop placeholder contract.prisma
Wire migrations-only contract emit via emptyContract with the same postgres target ref; document the pattern in ADR 212 and contract-space layout rule. Signed-off-by: Will Madden <madden@prisma.io>
1 parent 7a2b857 commit 42bbbff

5 files changed

Lines changed: 21 additions & 35 deletions

File tree

.agents/rules/contract-space-package-layout.mdc

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ applications) uses the **same** on-disk shape:
2424
│ ├── end-contract.d.ts
2525
│ └── migration.ts
2626
└── src/
27-
├── contract.prisma ← PSL source (preferred; TS fallback noted below)
27+
├── contract.prisma ← PSL source when the space has app-visible schema (preferred; exceptions below)
28+
├── contract.ts ← optional TS source (narrow exception)
2829
├── contract.json ← emitted (do not edit)
2930
├── contract.d.ts ← emitted (do not edit)
3031
└── exports/control.ts ← descriptor; JSON-imports the artefacts
@@ -35,17 +36,23 @@ applications) uses the **same** on-disk shape:
3536

3637
## Rules (these are the spots where mistakes recur)
3738

38-
- **Author contracts in PSL (`src/contract.prisma`)**, not TypeScript.
39+
- **Author contracts in PSL (`src/contract.prisma`)** when the space
40+
contributes app-visible schema (models, storage types, namespaces).
3941
PSL is the canonical authoring surface — it reads as a schema (not as
4042
builder calls), interoperates with brownfield Prisma schemas, and
4143
keeps the contract decoupled from the workspace's TS type system.
4244
Wire it with
4345
`prismaContract('./src/contract.prisma', { output: 'src/contract.json', target })`.
44-
The narrow exception is contracts that need to declare typed objects
45-
the PSL surface doesn't yet express (e.g. parameterised
46+
**Migrations-only spaces** that install invariants (e.g. a Postgres
47+
extension) but ship no tables or native types omit the source file
48+
and wire
49+
`emptyContract({ output: 'src/contract.json', target })` in
50+
`prisma-next.config.ts` (see `@prisma-next/extension-paradedb`).
51+
The narrow **TypeScript** exception is contracts that need to declare
52+
typed objects the PSL surface doesn't yet express (e.g. parameterised
4653
`storage.types` base-type registrations like pgvector's `vector` —
4754
`types {}` blocks instantiate, they don't register). Such packages
48-
may use `src/contract.ts` + `typescriptContract(contract, 'src/contract.json')`,
55+
use `src/contract.ts` + `typescriptContract(contract, 'src/contract.json')`,
4956
with a comment in the contract source naming the missing PSL surface.
5057
- **No `<space-id>` subdirectory inside `migrations/`.** A package owns
5158
exactly one contract space, so the `<space-id>` directory adds no

docs/architecture docs/adrs/ADR 212 - Contract spaces.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,8 @@ Every package that exposes a contract space — published extensions (`packages/
103103
│ ├── end-contract.d.ts
104104
│ └── migration.ts ← `Migration` subclass
105105
└── src/
106-
├── contract.prisma ← PSL schema source (preferred — see below)
106+
├── contract.prisma ← PSL schema source when the space has app-visible schema (preferred — see below)
107+
├── contract.ts ← optional TS source (narrow exception — see below)
107108
├── contract.json ← emitted (do not edit)
108109
├── contract.d.ts ← emitted (do not edit)
109110
└── exports/control.ts ← descriptor; JSON-imports the artefacts
@@ -113,7 +114,7 @@ Every package that exposes a contract space — published extensions (`packages/
113114

114115
Key rules (these are the spots where mistakes recur, so the convention spells them out explicitly):
115116

116-
- **Author contracts in PSL (`src/contract.prisma`).** PSL is the canonical authoring surface — it reads as a schema (not as builder calls), interoperates with brownfield Prisma schemas, and keeps the contract decoupled from the workspace's TS type system. Wire it with `prismaContract('./src/contract.prisma', { output: 'src/contract.json', target })`. The narrow exception is contracts that need to declare typed objects the PSL surface doesn't yet express (e.g. parameterised `storage.types` base-type registrations like pgvector's `vector``types {}` blocks instantiate, they don't register a parameterised base type). Such packages may keep a `src/contract.ts` + `typescriptContract(contract, 'src/contract.json')` config, with a comment in the contract source naming the missing PSL surface.
117+
- **Author contracts in PSL (`src/contract.prisma`)** when the space contributes app-visible schema (models, storage types, namespaces). PSL is the canonical authoring surface — it reads as a schema (not as builder calls), interoperates with brownfield Prisma schemas, and keeps the contract decoupled from the workspace's TS type system. Wire it with `prismaContract('./src/contract.prisma', { output: 'src/contract.json', target })`. **Migrations-only spaces** that install invariants (e.g. a Postgres extension) but ship no tables or native types of their own omit the source file entirely and wire `emptyContract({ output: 'src/contract.json', target })` in `prisma-next.config.ts` (see `@prisma-next/extension-paradedb`). The narrow **TypeScript** exception is contracts that need to declare typed objects the PSL surface doesn't yet express (e.g. parameterised `storage.types` base-type registrations like pgvector's `vector` — `types {}` blocks instantiate, they don't register a parameterised base type). Such packages keep `src/contract.ts` + `typescriptContract(contract, 'src/contract.json')`, with a comment in the contract source naming the missing PSL surface.
117118
- **No `<space-id>` subdirectory inside `migrations/`.** A package owns exactly one contract space, so the space-id directory adds no information. Migration directories sit *directly* under `migrations/`, and `refs/` sits at `migrations/refs/`. Configure this with `migrations.dir: 'migrations'` (not `migrations/<space-id>`).
118119
- **No `src/contract/` subdirectory.** The contract source, emitted `contract.json`, and emitted `contract.d.ts` sit *directly* in `src/`.
119120
- **`prisma-next.config.ts` is at the package root.** The CLI treats each contract-space package as a self-contained "project"; the in-package config is the source of truth for that project's emit and migration paths.

packages/3-extensions/paradedb/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ ParadeDB BM25 indexes require a `key_field` — a unique column that identifies
7575

7676
The extension's contract + baseline migration are emitted on-disk inside this package using the same pipeline application authors use:
7777

78-
- `pnpm build:contract-space` — runs `prisma-next contract emit` to produce `src/contract.{json,d.ts}` from the PSL source at `src/contract.prisma`.
79-
- `pnpm exec prisma-next migration plan --name <slug>` (run from this package directory) — scaffolds a new migration directory under `migrations/<dirName>/` for schema changes. **Not chained into `pnpm build`**: `migration plan` is non-idempotent (each invocation generates a new timestamped directory), so it runs manually when the contract source changes. Note: paradedb's contract declares no tables or models, so the planner currently refuses to scaffold the baseline migration (this is **Path B** authoring per [ADR 212](../../../docs/architecture%20docs/adrs/ADR%20212%20-%20Contract%20spaces.md#contract-space-package-layout)). That directory was hand-authored once (Migration subclass + seed `migration.json` preserving the full `toContract`) and `pnpm tsx migrations/<dirName>/migration.ts` re-emits `ops.json` + `migration.json` deterministically. Future migrations that add tables or models can use `migration plan` directly (Path A).
78+
- `pnpm build:contract-space` — runs `prisma-next contract emit` to produce `src/contract.{json,d.ts}` from `emptyContract({ output: 'src/contract.json', target })` in `prisma-next.config.ts` (migrations-only space: no `contract.prisma` source file).
79+
- `pnpm exec prisma-next migration plan --name <slug>` (run from this package directory) — scaffolds a new migration directory under `migrations/<dirName>/` for schema changes. **Not chained into `pnpm build`**: `migration plan` is non-idempotent (each invocation generates a new timestamped directory), so it runs manually when the contract changes. Note: paradedb's contract declares no tables or models, so the planner currently refuses to scaffold the baseline migration (this is **Path B** authoring per [ADR 212](../../../docs/architecture%20docs/adrs/ADR%20212%20-%20Contract%20spaces.md#contract-space-package-layout)). That directory was hand-authored once (Migration subclass + seed `migration.json` preserving the full `toContract`) and `pnpm tsx migrations/<dirName>/migration.ts` re-emits `ops.json` + `migration.json` deterministically. Future migrations that add tables or models can use `migration plan` directly (Path A).
8080
- `pnpm tsx migrations/<dirName>/migration.ts` (run from this package directory) — re-emits `ops.json` + `migration.json` from the hand-edited subclass. Use `tsx`, not bare `node`, because the Migration subclass imports relative TypeScript siblings which Node's native loader can't resolve without a TS-aware loader.
8181
- `migrations/refs/head.json` is hand-pinned with the latest migration's `to` hash + `providedInvariants`.
8282

packages/3-extensions/paradedb/prisma-next.config.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
*
44
* The extension package is treated as a self-contained "project" for
55
* the CLI: `prisma-next contract emit` writes
6-
* `<package>/src/contract.{json,d.ts}` (colocated with the
7-
* `src/contract.prisma` source); `prisma-next migration plan` writes
6+
* `<package>/src/contract.{json,d.ts}`; `prisma-next migration plan` writes
87
* `<package>/migrations/<dirName>/...`. The descriptor at
98
* `src/exports/control.ts` then JSON-imports those artefacts.
109
*
@@ -16,14 +15,15 @@
1615
import postgresAdapter from '@prisma-next/adapter-postgres/control';
1716
import { defineConfig } from '@prisma-next/cli/config-types';
1817
import sql from '@prisma-next/family-sql/control';
19-
import { prismaContract } from '@prisma-next/sql-contract-psl/provider';
18+
import { emptyContract } from '@prisma-next/sql-contract-ts/config-types';
2019
import postgres from '@prisma-next/target-postgres/control';
2120

2221
export default defineConfig({
2322
family: sql,
2423
target: postgres,
2524
adapter: postgresAdapter,
26-
contract: prismaContract('./src/contract.prisma', {
25+
// migrations-only contract space: installs pg_search via migrations, contributes no app-visible schema
26+
contract: emptyContract({
2727
output: 'src/contract.json',
2828
target: postgres,
2929
}),

packages/3-extensions/paradedb/src/contract.prisma

Lines changed: 0 additions & 22 deletions
This file was deleted.

0 commit comments

Comments
 (0)