@prisma-next/cli 0.0.1
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 +840 -0
- package/dist/chunk-W5YXBFPY.js +96 -0
- package/dist/chunk-W5YXBFPY.js.map +1 -0
- package/dist/cli.js +1872 -0
- package/dist/cli.js.map +1 -0
- package/dist/config-types.d.ts +1 -0
- package/dist/config-types.js +6 -0
- package/dist/config-types.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +803 -0
- package/dist/index.js.map +1 -0
- package/dist/pack-loading.d.ts +6 -0
- package/dist/pack-loading.js +9 -0
- package/dist/pack-loading.js.map +1 -0
- package/package.json +61 -0
package/README.md
ADDED
|
@@ -0,0 +1,840 @@
|
|
|
1
|
+
# @prisma-next/cli
|
|
2
|
+
|
|
3
|
+
Command-line interface for Prisma Next contract emission and management.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The CLI provides commands for emitting canonical `contract.json` and `contract.d.ts` files from TypeScript-authored contracts. It enforces import allowlists and validates contract purity to ensure deterministic, reproducible artifacts. Generated files include metadata and warning headers to indicate they're generated artifacts and should not be edited manually.
|
|
8
|
+
|
|
9
|
+
## Purpose
|
|
10
|
+
|
|
11
|
+
Provide a command-line interface that:
|
|
12
|
+
- Loads TypeScript-authored contracts using esbuild with import allowlisting
|
|
13
|
+
- Validates contract purity (JSON-serializable, no functions/getters)
|
|
14
|
+
- Invokes the emitter to produce canonical artifacts
|
|
15
|
+
- Handles all file I/O operations (CLI handles I/O; emitter returns strings)
|
|
16
|
+
|
|
17
|
+
## Responsibilities
|
|
18
|
+
|
|
19
|
+
- **TS Contract Loading**: Bundle and load TypeScript contract files with import allowlist enforcement
|
|
20
|
+
- **CLI Command Interface**: Parse arguments and route to command handlers using commander
|
|
21
|
+
- **File I/O**: Read TS contracts, write emitted artifacts (`contract.json`, `contract.d.ts`)
|
|
22
|
+
- **Extension Pack Loading**: Load adapter and extension pack manifests for emission
|
|
23
|
+
- **Help Output Formatting**: Custom styled help output with command trees and formatted descriptions
|
|
24
|
+
- **Config Management**: Load and validate `prisma-next.config.ts` files using Arktype validation
|
|
25
|
+
|
|
26
|
+
**Note**: Control plane domain actions (database verification, contract emission) are implemented in `@prisma-next/core-control-plane`. The CLI uses the control plane domain actions programmatically but does not define control plane types itself.
|
|
27
|
+
|
|
28
|
+
## Command Descriptions
|
|
29
|
+
|
|
30
|
+
Commands use separate short and long descriptions via `setCommandDescriptions()`:
|
|
31
|
+
|
|
32
|
+
- **Short description**: One-liner used in command trees and headers (e.g., "Emit signed contract artifacts")
|
|
33
|
+
- **Long description**: Multiline text shown at the bottom of help output with detailed context
|
|
34
|
+
|
|
35
|
+
See `.cursor/rules/cli-command-descriptions.mdc` for details.
|
|
36
|
+
|
|
37
|
+
## Commands
|
|
38
|
+
|
|
39
|
+
### `prisma-next contract emit` (canonical)
|
|
40
|
+
|
|
41
|
+
Emit `contract.json` and `contract.d.ts` from `config.contract`.
|
|
42
|
+
|
|
43
|
+
**Canonical command:**
|
|
44
|
+
```bash
|
|
45
|
+
prisma-next contract emit [--config <path>] [--json] [-v] [-q] [--timestamps] [--color/--no-color]
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Config File Requirements:**
|
|
49
|
+
|
|
50
|
+
The `contract emit` command requires a `driver` in the config (even though it doesn't use it) because `ControlFamilyDescriptor.create()` requires it for consistency:
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
import { defineConfig } from '@prisma-next/cli/config-types';
|
|
54
|
+
import postgresAdapter from '@prisma-next/adapter-postgres/control';
|
|
55
|
+
import postgresDriver from '@prisma-next/driver-postgres/control';
|
|
56
|
+
import postgres from '@prisma-next/targets-postgres/control';
|
|
57
|
+
import sql from '@prisma-next/family-sql/control';
|
|
58
|
+
import { contract } from './prisma/contract';
|
|
59
|
+
|
|
60
|
+
export default defineConfig({
|
|
61
|
+
family: sql,
|
|
62
|
+
target: postgres,
|
|
63
|
+
adapter: postgresAdapter,
|
|
64
|
+
driver: postgresDriver, // Required even though emit doesn't use it
|
|
65
|
+
extensions: [],
|
|
66
|
+
contract: {
|
|
67
|
+
source: contract,
|
|
68
|
+
output: 'src/prisma/contract.json',
|
|
69
|
+
types: 'src/prisma/contract.d.ts',
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Options:
|
|
75
|
+
- `--config <path>`: Optional. Path to `prisma-next.config.ts` (defaults to `./prisma-next.config.ts` if present)
|
|
76
|
+
- `--json`: Output as JSON object
|
|
77
|
+
- `-q, --quiet`: Quiet mode (errors only)
|
|
78
|
+
- `-v, --verbose`: Verbose output (debug info, timings)
|
|
79
|
+
- `-vv, --trace`: Trace output (deep internals, stack traces)
|
|
80
|
+
- `--timestamps`: Add timestamps to output
|
|
81
|
+
- `--color/--no-color`: Force/disable color output
|
|
82
|
+
|
|
83
|
+
Examples:
|
|
84
|
+
```bash
|
|
85
|
+
# Use config defaults
|
|
86
|
+
prisma-next contract emit
|
|
87
|
+
|
|
88
|
+
# JSON output
|
|
89
|
+
prisma-next contract emit --json
|
|
90
|
+
|
|
91
|
+
# Verbose output with timestamps
|
|
92
|
+
prisma-next contract emit -v --timestamps
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### `prisma-next db verify`
|
|
96
|
+
|
|
97
|
+
Verify that a database instance matches the emitted contract by checking marker presence, hash equality, and target compatibility.
|
|
98
|
+
|
|
99
|
+
**Command:**
|
|
100
|
+
```bash
|
|
101
|
+
prisma-next db verify [--db <url>] [--config <path>] [--json] [-v] [-q] [--timestamps] [--color/--no-color]
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Options:
|
|
105
|
+
- `--db <url>`: Database connection string (optional, falls back to `config.db.url` or `DATABASE_URL` environment variable)
|
|
106
|
+
- `--config <path>`: Optional. Path to `prisma-next.config.ts` (defaults to `./prisma-next.config.ts` if present)
|
|
107
|
+
- `--json`: Output as JSON object
|
|
108
|
+
- `-q, --quiet`: Quiet mode (errors only)
|
|
109
|
+
- `-v, --verbose`: Verbose output (debug info, timings)
|
|
110
|
+
- `-vv, --trace`: Trace output (deep internals, stack traces)
|
|
111
|
+
- `--timestamps`: Add timestamps to output
|
|
112
|
+
- `--color/--no-color`: Force/disable color output
|
|
113
|
+
|
|
114
|
+
Examples:
|
|
115
|
+
```bash
|
|
116
|
+
# Use config defaults
|
|
117
|
+
prisma-next db verify
|
|
118
|
+
|
|
119
|
+
# Specify database URL
|
|
120
|
+
prisma-next db verify --db postgresql://user:pass@localhost/db
|
|
121
|
+
|
|
122
|
+
# JSON output
|
|
123
|
+
prisma-next db verify --json
|
|
124
|
+
|
|
125
|
+
# Verbose output with timestamps
|
|
126
|
+
prisma-next db verify -v --timestamps
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Config File Requirements:**
|
|
130
|
+
|
|
131
|
+
The `db verify` command requires a `driver` in the config to connect to the database:
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
import { defineConfig } from '@prisma-next/cli/config-types';
|
|
135
|
+
import postgresAdapter from '@prisma-next/adapter-postgres/control';
|
|
136
|
+
import postgresDriver from '@prisma-next/driver-postgres/control';
|
|
137
|
+
import postgres from '@prisma-next/targets-postgres/control';
|
|
138
|
+
import sql from '@prisma-next/family-sql/control';
|
|
139
|
+
import { contract } from './prisma/contract';
|
|
140
|
+
|
|
141
|
+
export default defineConfig({
|
|
142
|
+
family: sql,
|
|
143
|
+
target: postgres,
|
|
144
|
+
adapter: postgresAdapter,
|
|
145
|
+
driver: postgresDriver,
|
|
146
|
+
extensions: [],
|
|
147
|
+
contract: {
|
|
148
|
+
source: contract,
|
|
149
|
+
output: 'src/prisma/contract.json',
|
|
150
|
+
types: 'src/prisma/contract.d.ts',
|
|
151
|
+
},
|
|
152
|
+
db: {
|
|
153
|
+
url: process.env.DATABASE_URL, // Optional: can also use --db flag
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Verification Process:**
|
|
159
|
+
|
|
160
|
+
1. **Load Contract**: Reads the emitted `contract.json` from `config.contract.output`
|
|
161
|
+
2. **Connect to Database**: Uses `config.driver.create(url)` to create a driver
|
|
162
|
+
3. **Create Family Instance**: Calls `config.family.create()` with target, adapter, driver, and extensions to create a family instance
|
|
163
|
+
4. **Verify**: Calls `familyInstance.verify()` which:
|
|
164
|
+
- Reads the contract marker from the database
|
|
165
|
+
- Compares marker presence: Returns `PN-RTM-3001` if marker is missing
|
|
166
|
+
- Compares target compatibility: Returns `PN-RTM-3003` if contract target doesn't match config target
|
|
167
|
+
- Compares core hash: Returns `PN-RTM-3002` if `coreHash` doesn't match
|
|
168
|
+
- Compares profile hash: Returns `PN-RTM-3002` if `profileHash` doesn't match (when present)
|
|
169
|
+
- Checks codec coverage (optional): Compares contract column types against supported codec types and reports missing codecs
|
|
170
|
+
|
|
171
|
+
**Output Format (TTY):**
|
|
172
|
+
|
|
173
|
+
Success:
|
|
174
|
+
```
|
|
175
|
+
✔ Database matches contract
|
|
176
|
+
coreHash: sha256:abc123...
|
|
177
|
+
profileHash: sha256:def456...
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Failure:
|
|
181
|
+
```
|
|
182
|
+
✖ Marker missing (PN-RTM-3001)
|
|
183
|
+
Why: Contract marker not found in database
|
|
184
|
+
Fix: Run `prisma-next db sign --db <url>` to create marker
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
**Output Format (JSON):**
|
|
188
|
+
|
|
189
|
+
```json
|
|
190
|
+
{
|
|
191
|
+
"ok": true,
|
|
192
|
+
"summary": "Database matches contract",
|
|
193
|
+
"contract": {
|
|
194
|
+
"coreHash": "sha256:abc123...",
|
|
195
|
+
"profileHash": "sha256:def456..."
|
|
196
|
+
},
|
|
197
|
+
"marker": {
|
|
198
|
+
"coreHash": "sha256:abc123...",
|
|
199
|
+
"profileHash": "sha256:def456..."
|
|
200
|
+
},
|
|
201
|
+
"target": {
|
|
202
|
+
"expected": "postgres"
|
|
203
|
+
},
|
|
204
|
+
"missingCodecs": [],
|
|
205
|
+
"meta": {
|
|
206
|
+
"configPath": "/path/to/prisma-next.config.ts",
|
|
207
|
+
"contractPath": "/path/to/src/prisma/contract.json"
|
|
208
|
+
},
|
|
209
|
+
"timings": {
|
|
210
|
+
"total": 42
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
**Error Codes:**
|
|
216
|
+
|
|
217
|
+
- `PN-CLI-4010`: Missing driver in config — provide a driver descriptor
|
|
218
|
+
- `PN-RTM-3001`: Marker missing - Contract marker not found in database
|
|
219
|
+
- `PN-RTM-3002`: Hash mismatch - Contract hash does not match database marker
|
|
220
|
+
- `PN-RTM-3003`: Target mismatch - Contract target does not match config target
|
|
221
|
+
|
|
222
|
+
**Family Requirements:**
|
|
223
|
+
|
|
224
|
+
The family must provide a `create()` method in the family descriptor that returns a `FamilyInstance` with a `verify()` method:
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
interface FamilyDescriptor {
|
|
228
|
+
create(options: {
|
|
229
|
+
target: TargetDescriptor;
|
|
230
|
+
adapter: AdapterDescriptor;
|
|
231
|
+
extensions: ExtensionDescriptor[];
|
|
232
|
+
}): FamilyInstance;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
interface FamilyInstance {
|
|
236
|
+
verify(options: {
|
|
237
|
+
driver: ControlPlaneDriver;
|
|
238
|
+
contractIR: ContractIR;
|
|
239
|
+
expectedTargetId: string;
|
|
240
|
+
contractPath: string;
|
|
241
|
+
configPath?: string;
|
|
242
|
+
}): Promise<VerifyDatabaseResult>;
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
The SQL family provides this via `@prisma-next/family-sql/control`. The `verify()` method handles reading the marker, comparing hashes, and checking codec coverage internally.
|
|
247
|
+
|
|
248
|
+
### `prisma-next db introspect`
|
|
249
|
+
|
|
250
|
+
Inspect the live database schema and display it as a human-readable tree or machine-consumable JSON.
|
|
251
|
+
|
|
252
|
+
**Command:**
|
|
253
|
+
```bash
|
|
254
|
+
prisma-next db introspect [--db <url>] [--config <path>] [--json] [-v] [-q] [--timestamps] [--color/--no-color]
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Options:
|
|
258
|
+
- `--db <url>`: Database connection string (optional, falls back to `config.db.url` or `DATABASE_URL` environment variable)
|
|
259
|
+
- `--config <path>`: Optional. Path to `prisma-next.config.ts` (defaults to `./prisma-next.config.ts` if present)
|
|
260
|
+
- `--json`: Output as JSON object
|
|
261
|
+
- `-q, --quiet`: Quiet mode (errors only)
|
|
262
|
+
- `-v, --verbose`: Verbose output (debug info, timings)
|
|
263
|
+
- `-vv, --trace`: Trace output (deep internals, stack traces)
|
|
264
|
+
- `--timestamps`: Add timestamps to output
|
|
265
|
+
- `--color/--no-color`: Force/disable color output
|
|
266
|
+
|
|
267
|
+
Examples:
|
|
268
|
+
```bash
|
|
269
|
+
# Use config defaults
|
|
270
|
+
prisma-next db introspect
|
|
271
|
+
|
|
272
|
+
# Specify database URL
|
|
273
|
+
prisma-next db introspect --db postgresql://user:pass@localhost/db
|
|
274
|
+
|
|
275
|
+
# JSON output
|
|
276
|
+
prisma-next db introspect --json
|
|
277
|
+
|
|
278
|
+
# Verbose output with timestamps
|
|
279
|
+
prisma-next db introspect -v --timestamps
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
**Config File Requirements:**
|
|
283
|
+
|
|
284
|
+
The `db introspect` command requires a `driver` in the config to connect to the database:
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
import { defineConfig } from '@prisma-next/cli/config-types';
|
|
288
|
+
import postgresAdapter from '@prisma-next/adapter-postgres/control';
|
|
289
|
+
import postgresDriver from '@prisma-next/driver-postgres/control';
|
|
290
|
+
import postgres from '@prisma-next/targets-postgres/control';
|
|
291
|
+
import sql from '@prisma-next/family-sql/control';
|
|
292
|
+
|
|
293
|
+
export default defineConfig({
|
|
294
|
+
family: sql,
|
|
295
|
+
target: postgres,
|
|
296
|
+
adapter: postgresAdapter,
|
|
297
|
+
driver: postgresDriver,
|
|
298
|
+
extensions: [],
|
|
299
|
+
db: {
|
|
300
|
+
url: process.env.DATABASE_URL, // Optional: can also use --db flag
|
|
301
|
+
},
|
|
302
|
+
});
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
**Introspection Process:**
|
|
306
|
+
|
|
307
|
+
1. **Connect to Database**: Uses `config.driver.create(url)` to create a driver
|
|
308
|
+
2. **Create Family Instance**: Calls `config.family.create()` with target, adapter, driver, and extensions to create a family instance
|
|
309
|
+
3. **Introspect**: Calls `familyInstance.introspect()` which:
|
|
310
|
+
- Queries the database catalog to discover schema structure
|
|
311
|
+
- Returns a family-specific schema IR (e.g., `SqlSchemaIR` for SQL family)
|
|
312
|
+
4. **Transform to Schema View**: Calls `familyInstance.toSchemaView()` to project the schema IR into a `CoreSchemaView` for display
|
|
313
|
+
5. **Format Output**: Formats the schema view as a human-readable tree or JSON envelope
|
|
314
|
+
|
|
315
|
+
**Output Format (TTY):**
|
|
316
|
+
|
|
317
|
+
Human-readable schema tree:
|
|
318
|
+
```
|
|
319
|
+
sql schema (tables: 2)
|
|
320
|
+
├─ table user
|
|
321
|
+
│ ├─ id: int4 (not null)
|
|
322
|
+
│ ├─ email: text (not null)
|
|
323
|
+
│ └─ unique user_email_key
|
|
324
|
+
├─ table post
|
|
325
|
+
│ ├─ id: int4 (not null)
|
|
326
|
+
│ ├─ title: text (not null)
|
|
327
|
+
│ └─ userId: int4 (not null)
|
|
328
|
+
├─ extension plpgsql
|
|
329
|
+
└─ extension vector
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
**Output Format (JSON):**
|
|
333
|
+
|
|
334
|
+
```json
|
|
335
|
+
{
|
|
336
|
+
"ok": true,
|
|
337
|
+
"summary": "Schema introspected successfully",
|
|
338
|
+
"schema": {
|
|
339
|
+
"root": {
|
|
340
|
+
"kind": "root",
|
|
341
|
+
"id": "sql-schema",
|
|
342
|
+
"label": "sql schema (tables: 2)",
|
|
343
|
+
"children": [
|
|
344
|
+
{
|
|
345
|
+
"kind": "entity",
|
|
346
|
+
"id": "table-user",
|
|
347
|
+
"label": "table user",
|
|
348
|
+
"children": [
|
|
349
|
+
{
|
|
350
|
+
"kind": "field",
|
|
351
|
+
"id": "column-user-id",
|
|
352
|
+
"label": "id: int4 (not null)",
|
|
353
|
+
"meta": {
|
|
354
|
+
"typeId": "pg/int4@1",
|
|
355
|
+
"nullable": false,
|
|
356
|
+
"nativeType": "int4"
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
]
|
|
360
|
+
}
|
|
361
|
+
]
|
|
362
|
+
}
|
|
363
|
+
},
|
|
364
|
+
"meta": {
|
|
365
|
+
"configPath": "/path/to/prisma-next.config.ts",
|
|
366
|
+
"dbUrl": "postgresql://user:pass@localhost/db"
|
|
367
|
+
},
|
|
368
|
+
"timings": {
|
|
369
|
+
"total": 42
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
**Error Codes:**
|
|
375
|
+
- `PN-CLI-4010`: Missing driver in config — provide a driver descriptor
|
|
376
|
+
- `PN-CLI-4011`: Missing database URL — provide `--db` flag or `config.db.url` or `DATABASE_URL` environment variable
|
|
377
|
+
|
|
378
|
+
**Family Requirements:**
|
|
379
|
+
|
|
380
|
+
The family must provide:
|
|
381
|
+
1. A `create()` method in the family descriptor that returns a `FamilyInstance` with an `introspect()` method
|
|
382
|
+
2. An optional `toSchemaView()` method on the `FamilyInstance` to project family-specific schema IR into `CoreSchemaView`
|
|
383
|
+
|
|
384
|
+
```typescript
|
|
385
|
+
interface FamilyInstance {
|
|
386
|
+
introspect(options: {
|
|
387
|
+
driver: ControlDriverInstance;
|
|
388
|
+
contractIR?: ContractIR;
|
|
389
|
+
schema?: string;
|
|
390
|
+
}): Promise<FamilySchemaIR>;
|
|
391
|
+
|
|
392
|
+
toSchemaView?(schema: FamilySchemaIR): CoreSchemaView;
|
|
393
|
+
}
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
The SQL family provides this via `@prisma-next/family-sql/control`. The `introspect()` method queries the database catalog and returns `SqlSchemaIR`, and `toSchemaView()` projects it into a `CoreSchemaView` for display.
|
|
397
|
+
|
|
398
|
+
**Note:** The introspection output displays native database types (e.g., `int4`, `text`, `timestamptz`) rather than mapped codec IDs (e.g., `pg/int4@1`). This reflects the actual database state, which may be enriched with type mappings later.
|
|
399
|
+
|
|
400
|
+
### `prisma-next db sign`
|
|
401
|
+
|
|
402
|
+
Mark the database as matching the emitted contract by writing or updating the contract marker. This command verifies that the database schema satisfies the contract before signing, ensuring the marker is only written when the database is fully aligned.
|
|
403
|
+
|
|
404
|
+
**Command:**
|
|
405
|
+
```bash
|
|
406
|
+
prisma-next db sign [--db <url>] [--config <path>] [--json] [-v] [-q] [--timestamps] [--color/--no-color]
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
Options:
|
|
410
|
+
- `--db <url>`: Database connection string (optional, falls back to `config.db.url` or `DATABASE_URL` environment variable)
|
|
411
|
+
- `--config <path>`: Optional. Path to `prisma-next.config.ts` (defaults to `./prisma-next.config.ts` if present)
|
|
412
|
+
- `--json`: Output as JSON object
|
|
413
|
+
- `-q, --quiet`: Quiet mode (errors only)
|
|
414
|
+
- `-v, --verbose`: Verbose output (debug info, timings)
|
|
415
|
+
- `-vv, --trace`: Trace output (deep internals, stack traces)
|
|
416
|
+
- `--timestamps`: Add timestamps to output
|
|
417
|
+
- `--color/--no-color`: Force/disable color output
|
|
418
|
+
|
|
419
|
+
Examples:
|
|
420
|
+
```bash
|
|
421
|
+
# Use config defaults
|
|
422
|
+
prisma-next db sign
|
|
423
|
+
|
|
424
|
+
# Specify database URL
|
|
425
|
+
prisma-next db sign --db postgresql://user:pass@localhost/db
|
|
426
|
+
|
|
427
|
+
# JSON output
|
|
428
|
+
prisma-next db sign --json
|
|
429
|
+
|
|
430
|
+
# Verbose output with timestamps
|
|
431
|
+
prisma-next db sign -v --timestamps
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
**Config File Requirements:**
|
|
435
|
+
|
|
436
|
+
The `db sign` command requires a `driver` in the config to connect to the database and a `contract.output` path to locate the emitted contract:
|
|
437
|
+
|
|
438
|
+
```typescript
|
|
439
|
+
import { defineConfig } from '@prisma-next/cli/config-types';
|
|
440
|
+
import postgresAdapter from '@prisma-next/adapter-postgres/control';
|
|
441
|
+
import postgresDriver from '@prisma-next/driver-postgres/control';
|
|
442
|
+
import postgres from '@prisma-next/targets-postgres/control';
|
|
443
|
+
import sql from '@prisma-next/family-sql/control';
|
|
444
|
+
import { contract } from './prisma/contract';
|
|
445
|
+
|
|
446
|
+
export default defineConfig({
|
|
447
|
+
family: sql,
|
|
448
|
+
target: postgres,
|
|
449
|
+
adapter: postgresAdapter,
|
|
450
|
+
driver: postgresDriver,
|
|
451
|
+
extensions: [],
|
|
452
|
+
contract: {
|
|
453
|
+
source: contract,
|
|
454
|
+
output: 'src/prisma/contract.json',
|
|
455
|
+
types: 'src/prisma/contract.d.ts',
|
|
456
|
+
},
|
|
457
|
+
db: {
|
|
458
|
+
url: process.env.DATABASE_URL, // Optional: can also use --db flag
|
|
459
|
+
},
|
|
460
|
+
});
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
**Signing Process:**
|
|
464
|
+
|
|
465
|
+
1. **Load Contract**: Reads the emitted `contract.json` from `config.contract.output`
|
|
466
|
+
2. **Connect to Database**: Uses `config.driver.create(url)` to create a driver
|
|
467
|
+
3. **Create Family Instance**: Calls `config.family.create()` with target, adapter, driver, and extensions to create a family instance
|
|
468
|
+
4. **Schema Verification (Precondition)**: Calls `familyInstance.schemaVerify()` to verify the database schema matches the contract:
|
|
469
|
+
- If verification fails: Prints schema verification output and exits with code 1 (marker is not written)
|
|
470
|
+
- If verification passes: Proceeds to marker signing
|
|
471
|
+
5. **Sign**: Calls `familyInstance.sign()` which:
|
|
472
|
+
- Ensures the marker schema and table exist
|
|
473
|
+
- Reads any existing marker from the database
|
|
474
|
+
- Compares contract hashes with existing marker:
|
|
475
|
+
- If marker is missing: Inserts a new marker row
|
|
476
|
+
- If hashes differ: Updates the existing marker row
|
|
477
|
+
- If hashes match: No-op (idempotent)
|
|
478
|
+
|
|
479
|
+
**Output Format (TTY):**
|
|
480
|
+
|
|
481
|
+
Success (new marker):
|
|
482
|
+
```
|
|
483
|
+
✔ Database signed (marker created)
|
|
484
|
+
coreHash: sha256:abc123...
|
|
485
|
+
profileHash: sha256:def456...
|
|
486
|
+
Total time: 42ms
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
Success (updated marker):
|
|
490
|
+
```
|
|
491
|
+
✔ Database signed (marker updated from sha256:old-hash)
|
|
492
|
+
coreHash: sha256:abc123...
|
|
493
|
+
profileHash: sha256:def456...
|
|
494
|
+
previous coreHash: sha256:old-hash
|
|
495
|
+
Total time: 42ms
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
Success (already up-to-date):
|
|
499
|
+
```
|
|
500
|
+
✔ Database already signed with this contract
|
|
501
|
+
coreHash: sha256:abc123...
|
|
502
|
+
profileHash: sha256:def456...
|
|
503
|
+
Total time: 42ms
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
Failure (schema mismatch):
|
|
507
|
+
```
|
|
508
|
+
✖ Schema verification failed
|
|
509
|
+
[Schema verification tree output]
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
**Output Format (JSON):**
|
|
513
|
+
|
|
514
|
+
```json
|
|
515
|
+
{
|
|
516
|
+
"ok": true,
|
|
517
|
+
"summary": "Database signed (marker created)",
|
|
518
|
+
"contract": {
|
|
519
|
+
"coreHash": "sha256:abc123...",
|
|
520
|
+
"profileHash": "sha256:def456..."
|
|
521
|
+
},
|
|
522
|
+
"target": {
|
|
523
|
+
"expected": "postgres",
|
|
524
|
+
"actual": "postgres"
|
|
525
|
+
},
|
|
526
|
+
"marker": {
|
|
527
|
+
"created": true,
|
|
528
|
+
"updated": false
|
|
529
|
+
},
|
|
530
|
+
"meta": {
|
|
531
|
+
"configPath": "/path/to/prisma-next.config.ts",
|
|
532
|
+
"contractPath": "/path/to/src/prisma/contract.json"
|
|
533
|
+
},
|
|
534
|
+
"timings": {
|
|
535
|
+
"total": 42
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
For updated markers:
|
|
541
|
+
```json
|
|
542
|
+
{
|
|
543
|
+
"ok": true,
|
|
544
|
+
"summary": "Database signed (marker updated from sha256:old-hash)",
|
|
545
|
+
"contract": {
|
|
546
|
+
"coreHash": "sha256:abc123...",
|
|
547
|
+
"profileHash": "sha256:def456..."
|
|
548
|
+
},
|
|
549
|
+
"target": {
|
|
550
|
+
"expected": "postgres",
|
|
551
|
+
"actual": "postgres"
|
|
552
|
+
},
|
|
553
|
+
"marker": {
|
|
554
|
+
"created": false,
|
|
555
|
+
"updated": true,
|
|
556
|
+
"previous": {
|
|
557
|
+
"coreHash": "sha256:old-hash",
|
|
558
|
+
"profileHash": "sha256:old-profile-hash"
|
|
559
|
+
}
|
|
560
|
+
},
|
|
561
|
+
"meta": {
|
|
562
|
+
"configPath": "/path/to/prisma-next.config.ts",
|
|
563
|
+
"contractPath": "/path/to/src/prisma/contract.json"
|
|
564
|
+
},
|
|
565
|
+
"timings": {
|
|
566
|
+
"total": 42
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
**Error Codes:**
|
|
572
|
+
- `PN-CLI-4010`: Missing driver in config — provide a driver descriptor
|
|
573
|
+
- `PN-CLI-4011`: Missing database URL — provide `--db` flag or `config.db.url` or `DATABASE_URL` environment variable
|
|
574
|
+
- Exit code 1: Schema verification failed — database schema does not match contract (marker is not written)
|
|
575
|
+
|
|
576
|
+
**Relationship to Other Commands:**
|
|
577
|
+
- **`db schema-verify`**: `db sign` calls `schemaVerify` as a precondition before writing the marker. If schema verification fails, `db sign` exits without writing the marker.
|
|
578
|
+
- **`db verify`**: `db verify` checks that the marker exists and matches the contract. `db sign` writes the marker that `db verify` checks.
|
|
579
|
+
|
|
580
|
+
**Idempotency:**
|
|
581
|
+
The `db sign` command is idempotent and safe to run multiple times:
|
|
582
|
+
- If the marker already matches the contract (same hashes), no database changes are made
|
|
583
|
+
- The command reports success in all cases (new marker, updated marker, or already up-to-date)
|
|
584
|
+
- Safe to run in CI/deployment pipelines
|
|
585
|
+
|
|
586
|
+
**Family Requirements:**
|
|
587
|
+
The family must provide a `create()` method in the family descriptor that returns a `FamilyInstance` with `schemaVerify()` and `sign()` methods:
|
|
588
|
+
|
|
589
|
+
```typescript
|
|
590
|
+
interface FamilyInstance {
|
|
591
|
+
schemaVerify(options: {
|
|
592
|
+
driver: ControlDriverInstance;
|
|
593
|
+
contractIR: ContractIR;
|
|
594
|
+
strict: boolean;
|
|
595
|
+
contractPath: string;
|
|
596
|
+
configPath?: string;
|
|
597
|
+
}): Promise<VerifyDatabaseSchemaResult>;
|
|
598
|
+
|
|
599
|
+
sign(options: {
|
|
600
|
+
driver: ControlDriverInstance;
|
|
601
|
+
contractIR: ContractIR;
|
|
602
|
+
contractPath: string;
|
|
603
|
+
configPath?: string;
|
|
604
|
+
}): Promise<SignDatabaseResult>;
|
|
605
|
+
}
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
The SQL family provides this via `@prisma-next/family-sql/control`. The `sign()` method handles ensuring the marker schema/table exist, reading existing markers, comparing hashes, and writing/updating markers internally.
|
|
609
|
+
|
|
610
|
+
**Config File (`prisma-next.config.ts`):**
|
|
611
|
+
|
|
612
|
+
The CLI uses a config file to specify the target family, target, adapter, extensions, and contract.
|
|
613
|
+
|
|
614
|
+
**Config Discovery:**
|
|
615
|
+
- `--config <path>`: Explicit path (relative or absolute)
|
|
616
|
+
- Default: `./prisma-next.config.ts` in current working directory
|
|
617
|
+
- No upward search (stays in CWD)
|
|
618
|
+
|
|
619
|
+
**Note:** The CLI uses `c12` for config loading, but constrains it to the current working directory (no upward search) to match the style guide's discovery precedence.
|
|
620
|
+
|
|
621
|
+
```typescript
|
|
622
|
+
import { defineConfig } from '@prisma-next/cli/config-types';
|
|
623
|
+
import postgresAdapter from '@prisma-next/adapter-postgres/control';
|
|
624
|
+
import postgres from '@prisma-next/targets-postgres/control';
|
|
625
|
+
import sql from '@prisma-next/family-sql/control';
|
|
626
|
+
import { contract } from './prisma/contract';
|
|
627
|
+
|
|
628
|
+
export default defineConfig({
|
|
629
|
+
family: sql,
|
|
630
|
+
target: postgres,
|
|
631
|
+
adapter: postgresAdapter,
|
|
632
|
+
extensions: [],
|
|
633
|
+
contract: {
|
|
634
|
+
source: contract, // Can be a value or a function: () => import('./contract').then(m => m.contract)
|
|
635
|
+
output: 'src/prisma/contract.json', // Optional: defaults to 'src/prisma/contract.json'
|
|
636
|
+
types: 'src/prisma/contract.d.ts', // Optional: defaults to output with .d.ts extension
|
|
637
|
+
},
|
|
638
|
+
});
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
The `contract.source` field can be:
|
|
642
|
+
- A direct value: `source: contract`
|
|
643
|
+
- A synchronous function: `source: () => contract`
|
|
644
|
+
- An asynchronous function: `source: () => import('./contract').then(m => m.contract)`
|
|
645
|
+
|
|
646
|
+
The `contract.output` field specifies the path to `contract.json`. This is the canonical location where other CLI commands can find the contract JSON artifact. Defaults to `'src/prisma/contract.json'` if not specified.
|
|
647
|
+
|
|
648
|
+
The `contract.types` field specifies the path to `contract.d.ts`. Defaults to `output` with `.d.ts` extension (replaces `.json` with `.d.ts` if output ends with `.json`, otherwise appends `contract.d.ts` to the directory containing output).
|
|
649
|
+
|
|
650
|
+
**Output:**
|
|
651
|
+
- `contract.json`: Includes `_generated` metadata field indicating it's a generated artifact (excluded from canonicalization/hashing)
|
|
652
|
+
- `contract.d.ts`: Includes warning header comments indicating it's a generated file
|
|
653
|
+
|
|
654
|
+
## Architecture
|
|
655
|
+
|
|
656
|
+
```mermaid
|
|
657
|
+
flowchart TD
|
|
658
|
+
CLI[CLI Entry Point]
|
|
659
|
+
CMD[Emit Command]
|
|
660
|
+
LOAD[TS Contract Loader]
|
|
661
|
+
EMIT[Emitter]
|
|
662
|
+
FS[File System]
|
|
663
|
+
|
|
664
|
+
CLI --> CMD
|
|
665
|
+
CMD --> LOAD
|
|
666
|
+
LOAD --> EMIT
|
|
667
|
+
EMIT --> CMD
|
|
668
|
+
CMD --> FS
|
|
669
|
+
```
|
|
670
|
+
|
|
671
|
+
## Config Validation and Normalization
|
|
672
|
+
|
|
673
|
+
The `defineConfig()` function validates and normalizes configs using Arktype:
|
|
674
|
+
|
|
675
|
+
- **Validation**: Validates config structure using Arktype schemas
|
|
676
|
+
- **Normalization**: Applies default values (e.g., `contract.output` defaults to `'src/prisma/contract.json'`)
|
|
677
|
+
- **Error Messages**: Provides clear, actionable error messages on validation failure
|
|
678
|
+
|
|
679
|
+
See `.cursor/rules/config-validation-and-normalization.mdc` for detailed patterns.
|
|
680
|
+
|
|
681
|
+
## Components
|
|
682
|
+
|
|
683
|
+
### CLI Entry Point (`cli.ts`)
|
|
684
|
+
- Main entry point using commander
|
|
685
|
+
- Parses arguments and routes to command handlers
|
|
686
|
+
- Handles global flags (`--help`, `--version`)
|
|
687
|
+
- Exit codes: 0 (success), 1 (runtime error), 2 (usage/config error)
|
|
688
|
+
- **Error Handling**: Uses `exitOverride()` to catch unhandled errors (non-structured errors that fail fast) and print stack traces. Commands handle structured errors themselves via `process.exit()`.
|
|
689
|
+
- **Command Taxonomy**: Groups commands by domain/plane (e.g., `contract emit`)
|
|
690
|
+
- **Help Formatting**: Uses `configureHelp()` to customize help output with styled format matching normal command output. Root help shows "prisma-next" title with command tree; command help shows "prisma-next <command> ➜ <description>" with options and docs URLs. See `utils/output.ts` for help formatters.
|
|
691
|
+
- **Command Descriptions**: Commands use `setCommandDescriptions()` to set separate short and long descriptions. See `utils/command-helpers.ts` and `.cursor/rules/cli-command-descriptions.mdc`.
|
|
692
|
+
|
|
693
|
+
### Contract Emit Command (`commands/contract-emit.ts`)
|
|
694
|
+
- Canonical command implementation using commander
|
|
695
|
+
- Supports global flags (JSON, verbosity, color, timestamps)
|
|
696
|
+
- **Error Handling**: Uses structured errors (`CliStructuredError`), Result pattern (`performAction`), and `process.exit()`. Commands wrap logic in `performAction()`, process results with `handleResult()`, and call `process.exit(exitCode)` directly. See `.cursor/rules/cli-error-handling.mdc` for details.
|
|
697
|
+
- Loads the user's config module (`prisma-next.config.ts`)
|
|
698
|
+
- Resolves contract from config:
|
|
699
|
+
- Uses `config.contract.source` (supports sync and async functions)
|
|
700
|
+
- User's config is responsible for loading the contract (can use `loadContractFromTs` or any other method)
|
|
701
|
+
- Throws error if `config.contract` is missing
|
|
702
|
+
- Uses artifact paths from `config.contract.output/types` (already normalized by `defineConfig()` with defaults applied)
|
|
703
|
+
- Creates family instance via `config.family.create()` (assembles operation registry, type imports, extension IDs)
|
|
704
|
+
- Calls `familyInstance.emitContract()` with raw contract (instance handles stripping mappings and validation internally)
|
|
705
|
+
- Outputs human-readable or JSON format based on flags
|
|
706
|
+
|
|
707
|
+
### Programmatic API (`api/emit-contract.ts`)
|
|
708
|
+
- **`emitContract(options)`**: Programmatic API for emitting contracts
|
|
709
|
+
- Accepts resolved contract IR, output paths, and assembly data
|
|
710
|
+
- Caller is responsible for loading the contract and resolving paths
|
|
711
|
+
- Returns result with hashes, file paths, and timings
|
|
712
|
+
- Used by CLI command internally
|
|
713
|
+
|
|
714
|
+
### Error Handling (`utils/errors.ts`, `utils/cli-errors.ts`, `utils/result.ts`, `utils/result-handler.ts`)
|
|
715
|
+
- **Structured Errors**: Call sites throw `CliStructuredError` instances with full context (why, fix, docsUrl, etc.)
|
|
716
|
+
- **Result Pattern**: Commands wrap logic in `performAction()` which only catches `CliStructuredError` instances
|
|
717
|
+
- **Error Conversion**: `CliStructuredError.toEnvelope()` converts errors to envelopes for output formatting
|
|
718
|
+
- **Result Processing**: `handleResult()` processes Results, formats output, and returns exit codes
|
|
719
|
+
- **Exit Codes**:
|
|
720
|
+
- Usage/config errors (PN-CLI-4001-4007) → exit code 2
|
|
721
|
+
- Runtime errors (PN-RTM-3xxx) → exit code 1
|
|
722
|
+
- Success → exit code 0
|
|
723
|
+
- **Fail Fast**: Non-structured errors propagate and are caught by Commander.js's `exitOverride()` with stack traces
|
|
724
|
+
- See `.cursor/rules/cli-error-handling.mdc` for detailed patterns
|
|
725
|
+
|
|
726
|
+
### Pack Assembly
|
|
727
|
+
- **Family instances** now handle pack assembly internally. The CLI creates a family instance via `config.family.create()` and reads assembly data (operation registry, type imports, extension IDs) from the instance.
|
|
728
|
+
- **Removed**: `pack-assembly.ts` has been removed. Pack assembly is now handled by family instances. For SQL family, tests can import pack-based helpers directly from `packages/sql/family/src/core/assembly.ts` using relative paths.
|
|
729
|
+
- Assembly logic is family-specific and owned by each family's instance implementation (e.g., `createSqlFamilyInstance` in `@prisma-next/family-sql`).
|
|
730
|
+
|
|
731
|
+
### Output Formatting (`utils/output.ts`)
|
|
732
|
+
- **Command Output Formatters**: Format human-readable output for commands (emit, verify, etc.)
|
|
733
|
+
- Paths are shown as relative paths from current working directory (using `relative(process.cwd(), path)`)
|
|
734
|
+
- Success indicators use consistent checkmark (✔) throughout
|
|
735
|
+
- **Error Output Formatters**: Format error output for human-readable and JSON display
|
|
736
|
+
- **Styled Headers**: `formatStyledHeader()` creates styled headers for command output with "prisma-next <command> ➜ <description>" format
|
|
737
|
+
- Parameter labels include colons (e.g., `config:`, `contract:`)
|
|
738
|
+
- Uses fixed 20-character left column width for consistent alignment
|
|
739
|
+
- **Help Formatters**:
|
|
740
|
+
- `formatRootHelp()` - Formats root help with "prisma-next" title, command tree, and multiline description
|
|
741
|
+
- `formatCommandHelp()` - Formats command help with "prisma-next <command> ➜ <description>", options, subcommands, docs URLs, and multiline description
|
|
742
|
+
- `renderCommandTree()` - Shared function to render hierarchical command trees with tree characters (├─, └─, │)
|
|
743
|
+
- **Fixed-Width Formatting**: All two-column output (help, styled headers) uses fixed 20-character left column width
|
|
744
|
+
- **Text Wrapping**: Right column wraps at 90 characters using `wrap-ansi` for ANSI-aware wrapping
|
|
745
|
+
- **Default Values**: Options with default values display `default: <value>` on the following line (dimmed)
|
|
746
|
+
- **ANSI-Aware Padding**: Uses `string-width` and `strip-ansi` to measure and pad text correctly with ANSI codes
|
|
747
|
+
- Help formatters use the same styling system as normal command output (colors, dim text, badges)
|
|
748
|
+
- Short descriptions appear in command trees and headers; long descriptions appear at the bottom of help output
|
|
749
|
+
- Help formatting is configured via `configureHelp()` in `cli.ts` to apply to all commands
|
|
750
|
+
|
|
751
|
+
### Family Descriptor (provided by family /cli entrypoint)
|
|
752
|
+
- The SQL family (and other families) provide:
|
|
753
|
+
- `create(options)` - Creates a family instance that implements domain actions
|
|
754
|
+
- `hook` - Target family hook for contract emission
|
|
755
|
+
- Family instances provide:
|
|
756
|
+
- `validateContractIR(contractJson)` - Validates and normalizes contract, returns ContractIR without mappings
|
|
757
|
+
- `emitContract(options)` - Emits contract (handles stripping mappings and validation internally)
|
|
758
|
+
- `verify(options)` - Verifies database marker against contract
|
|
759
|
+
- `schemaVerify(options)` - Verifies database schema against contract
|
|
760
|
+
- `introspect(options)` - Introspects database schema
|
|
761
|
+
|
|
762
|
+
### Pack Manifest Types (IR)
|
|
763
|
+
- Families define their manifest IR and related types under their own tooling packages. CLI treats manifests as opaque data.
|
|
764
|
+
|
|
765
|
+
## Dependencies
|
|
766
|
+
|
|
767
|
+
- **`commander`**: CLI argument parsing and command routing
|
|
768
|
+
- **`esbuild`**: Bundling TypeScript contract files with import allowlisting
|
|
769
|
+
- **`@prisma-next/emitter`**: Contract emission engine (returns strings)
|
|
770
|
+
|
|
771
|
+
## Design Decisions
|
|
772
|
+
|
|
773
|
+
1. **Import Allowlist**: Only `@prisma-next/*` packages allowed (MVP). Expand later if needed.
|
|
774
|
+
2. **Utility Separation**: TS contract loading is a utility function, not a command. Commands use utilities.
|
|
775
|
+
3. **CLI Framework**: Use `commander` library for robust CLI argument parsing.
|
|
776
|
+
4. **File I/O**: CLI handles all I/O; emitter returns strings (no file operations in emitter).
|
|
777
|
+
5. **Generated File Metadata**: Adds `_generated` metadata field to `contract.json` to indicate it's a generated artifact. This field is excluded from canonicalization/hashing to ensure determinism. The `contract.d.ts` file includes warning header comments generated by the emitter hook.
|
|
778
|
+
|
|
779
|
+
## Testing
|
|
780
|
+
|
|
781
|
+
The CLI package includes unit tests, integration tests, and e2e tests:
|
|
782
|
+
|
|
783
|
+
- **Unit tests**: Test individual functions and utilities in isolation
|
|
784
|
+
- **Integration tests**: Test component interactions (e.g., config loading, pack assembly)
|
|
785
|
+
- **E2E tests**: Test complete command execution with real config files
|
|
786
|
+
|
|
787
|
+
### E2E Test Patterns
|
|
788
|
+
|
|
789
|
+
E2E tests use a shared fixture app pattern to ensure proper module resolution:
|
|
790
|
+
|
|
791
|
+
- **Shared fixture app**: `test/cli-e2e-test-app/` contains a static `package.json` with dependencies
|
|
792
|
+
- **Fixture organization**: Fixtures are organized by command in subdirectories (e.g., `fixtures/emit/`, `fixtures/db-verify/`)
|
|
793
|
+
- **Ephemeral test directories**: Each test creates an isolated directory with files copied from fixtures
|
|
794
|
+
- **No package.json in test directories**: Test directories inherit workspace dependencies from the parent `package.json` at the root
|
|
795
|
+
- **Helper function**: `setupTestDirectoryFromFixtures()` handles directory setup and returns a cleanup function
|
|
796
|
+
- **Cleanup responsibility**: Each test must clean up its own directory (use `afterEach` hooks or `finally` blocks)
|
|
797
|
+
|
|
798
|
+
**Example:**
|
|
799
|
+
```typescript
|
|
800
|
+
import { setupTestDirectoryFromFixtures } from './utils/test-helpers';
|
|
801
|
+
|
|
802
|
+
const fixtureSubdir = 'emit';
|
|
803
|
+
|
|
804
|
+
it('test description', async () => {
|
|
805
|
+
const testSetup = setupTestDirectoryFromFixtures(
|
|
806
|
+
fixtureSubdir,
|
|
807
|
+
'prisma-next.config.emit.ts',
|
|
808
|
+
);
|
|
809
|
+
const cleanupDir = testSetup.cleanup;
|
|
810
|
+
|
|
811
|
+
try {
|
|
812
|
+
// ... test code ...
|
|
813
|
+
} finally {
|
|
814
|
+
cleanupDir(); // Each test cleans up its own directory
|
|
815
|
+
}
|
|
816
|
+
});
|
|
817
|
+
```
|
|
818
|
+
|
|
819
|
+
See `.cursor/rules/cli-e2e-test-patterns.mdc` for detailed patterns and examples.
|
|
820
|
+
|
|
821
|
+
Run tests:
|
|
822
|
+
```bash
|
|
823
|
+
pnpm test # Run all tests
|
|
824
|
+
pnpm test:unit # Run unit tests only
|
|
825
|
+
pnpm test:integration # Run integration tests only
|
|
826
|
+
pnpm test:e2e # Run e2e tests only
|
|
827
|
+
```
|
|
828
|
+
|
|
829
|
+
## Package Location
|
|
830
|
+
|
|
831
|
+
This package is part of the **framework domain**, **tooling layer**, **migration plane**:
|
|
832
|
+
- **Domain**: framework (target-agnostic)
|
|
833
|
+
- **Layer**: tooling
|
|
834
|
+
- **Plane**: migration
|
|
835
|
+
- **Path**: `packages/framework/tooling/cli`
|
|
836
|
+
|
|
837
|
+
## See Also
|
|
838
|
+
|
|
839
|
+
- [`@prisma-next/emitter`](../emitter/README.md) - Contract emission engine
|
|
840
|
+
- Project Brief — CLI Support for Extension Packs: `docs/briefs/complete/20-CLI-Support-for-Extension-Packs.md`
|