@prisma-next/core-control-plane 0.1.0-dev.3 → 0.1.0-dev.30

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.
@@ -1,185 +1,263 @@
1
+ import { TargetBoundComponentDescriptor, FamilyInstance, DriverInstance, FamilyDescriptor, TargetInstance, TargetDescriptor, AdapterInstance, AdapterDescriptor, DriverDescriptor, ExtensionInstance, ExtensionDescriptor } from '@prisma-next/contract/framework-components';
1
2
  import { ContractIR } from '@prisma-next/contract/ir';
2
- import { ExtensionPackManifest } from '@prisma-next/contract/pack-manifest-types';
3
- import { TargetFamilyHook } from '@prisma-next/contract/types';
3
+ import { ContractMarkerRecord, TargetFamilyHook } from '@prisma-next/contract/types';
4
+ import { Result } from '@prisma-next/utils/result';
4
5
  import { CoreSchemaView } from './schema-view.js';
5
6
 
6
7
  /**
7
- * Base interface for control-plane family instances.
8
- * Families extend this with domain-specific methods.
8
+ * Core migration types for the framework control plane.
9
9
  *
10
- * @template TFamilyId - The family ID (e.g., 'sql', 'document')
10
+ * These are family-agnostic, display-oriented types that provide a stable
11
+ * vocabulary for CLI commands to work with migration planners and runners
12
+ * without importing family-specific types.
13
+ *
14
+ * Family-specific types (e.g., SqlMigrationPlan) extend these base types
15
+ * with additional fields for execution (precheck SQL, execute SQL, etc.).
16
+ */
17
+
18
+ /**
19
+ * Migration operation classes define the safety level of an operation.
20
+ * - 'additive': Adds new structures without modifying existing ones (safe)
21
+ * - 'widening': Relaxes constraints or expands types (generally safe)
22
+ * - 'destructive': Removes or alters existing structures (potentially unsafe)
11
23
  */
12
- interface ControlFamilyInstance<TFamilyId extends string = string> {
13
- readonly familyId: TFamilyId;
24
+ type MigrationOperationClass = 'additive' | 'widening' | 'destructive';
25
+ /**
26
+ * Policy defining which operation classes are allowed during a migration.
27
+ */
28
+ interface MigrationOperationPolicy {
29
+ readonly allowedOperationClasses: readonly MigrationOperationClass[];
14
30
  }
15
31
  /**
16
- * Base interface for control-plane target instances.
17
- *
18
- * @template TFamilyId - The family ID (e.g., 'sql', 'document')
19
- * @template TTargetId - The target ID (e.g., 'postgres', 'mysql')
32
+ * A single migration operation for display purposes.
33
+ * Contains only the fields needed for CLI output (tree view, JSON envelope).
20
34
  */
21
- interface ControlTargetInstance<TFamilyId extends string = string, TTargetId extends string = string> {
22
- readonly familyId: TFamilyId;
23
- readonly targetId: TTargetId;
35
+ interface MigrationPlanOperation {
36
+ /** Unique identifier for this operation (e.g., "table.users.create"). */
37
+ readonly id: string;
38
+ /** Human-readable label for display in UI/CLI (e.g., "Create table users"). */
39
+ readonly label: string;
40
+ /** The class of operation (additive, widening, destructive). */
41
+ readonly operationClass: MigrationOperationClass;
24
42
  }
25
43
  /**
26
- * Base interface for control-plane adapter instances.
27
- * Families extend this with family-specific adapter interfaces.
28
- *
29
- * @template TFamilyId - The family ID (e.g., 'sql', 'document')
30
- * @template TTargetId - The target ID (e.g., 'postgres', 'mysql')
44
+ * A migration plan for display purposes.
45
+ * Contains only the fields needed for CLI output (summary, JSON envelope).
31
46
  */
32
- interface ControlAdapterInstance<TFamilyId extends string = string, TTargetId extends string = string> {
33
- readonly familyId: TFamilyId;
34
- readonly targetId: TTargetId;
47
+ interface MigrationPlan {
48
+ /** The target ID this plan is for (e.g., 'postgres'). */
49
+ readonly targetId: string;
50
+ /** Destination contract identity that the plan intends to reach. */
51
+ readonly destination: {
52
+ readonly coreHash: string;
53
+ readonly profileHash?: string;
54
+ };
55
+ /** Ordered list of operations to execute. */
56
+ readonly operations: readonly MigrationPlanOperation[];
35
57
  }
36
58
  /**
37
- * Base interface for control-plane driver instances.
38
- * Replaces ControlPlaneDriver with plane-first naming.
39
- *
40
- * @template TTargetId - The target ID (e.g., 'postgres', 'mysql')
59
+ * A conflict detected during migration planning.
41
60
  */
42
- interface ControlDriverInstance<TTargetId extends string = string> {
43
- readonly targetId?: TTargetId;
44
- query<Row = Record<string, unknown>>(sql: string, params?: readonly unknown[]): Promise<{
45
- readonly rows: Row[];
46
- }>;
47
- close(): Promise<void>;
61
+ interface MigrationPlannerConflict {
62
+ /** Kind of conflict (e.g., 'typeMismatch', 'nullabilityConflict'). */
63
+ readonly kind: string;
64
+ /** Human-readable summary of the conflict. */
65
+ readonly summary: string;
66
+ /** Optional explanation of why this conflict occurred. */
67
+ readonly why?: string;
48
68
  }
49
69
  /**
50
- * Base interface for control-plane extension instances.
51
- *
52
- * @template TFamilyId - The family ID (e.g., 'sql', 'document')
53
- * @template TTargetId - The target ID (e.g., 'postgres', 'mysql')
70
+ * Successful planner result with the migration plan.
54
71
  */
55
- interface ControlExtensionInstance<TFamilyId extends string = string, TTargetId extends string = string> {
56
- readonly familyId: TFamilyId;
57
- readonly targetId: TTargetId;
72
+ interface MigrationPlannerSuccessResult {
73
+ readonly kind: 'success';
74
+ readonly plan: MigrationPlan;
58
75
  }
59
76
  /**
60
- * Descriptor for a control-plane family (e.g., SQL).
61
- * Provides the family hook and factory method.
62
- *
63
- * @template TFamilyId - The family ID (e.g., 'sql', 'document')
64
- * @template TFamilyInstance - The family instance type
77
+ * Failed planner result with the list of conflicts.
65
78
  */
66
- interface ControlFamilyDescriptor<TFamilyId extends string, TFamilyInstance extends ControlFamilyInstance<TFamilyId> = ControlFamilyInstance<TFamilyId>> {
67
- readonly kind: 'family';
68
- readonly id: string;
69
- readonly familyId: TFamilyId;
70
- readonly manifest: ExtensionPackManifest;
71
- readonly hook: TargetFamilyHook;
72
- create<TTargetId extends string>(options: {
73
- readonly target: ControlTargetDescriptor<TFamilyId, TTargetId>;
74
- readonly adapter: ControlAdapterDescriptor<TFamilyId, TTargetId>;
75
- readonly driver: ControlDriverDescriptor<TFamilyId, TTargetId>;
76
- readonly extensions: readonly ControlExtensionDescriptor<TFamilyId, TTargetId>[];
77
- }): TFamilyInstance;
79
+ interface MigrationPlannerFailureResult {
80
+ readonly kind: 'failure';
81
+ readonly conflicts: readonly MigrationPlannerConflict[];
78
82
  }
79
83
  /**
80
- * Descriptor for a control-plane target pack (e.g., Postgres target).
81
- *
82
- * @template TFamilyId - The family ID (e.g., 'sql', 'document')
83
- * @template TTargetId - The target ID (e.g., 'postgres', 'mysql')
84
- * @template TTargetInstance - The target instance type
84
+ * Union type for planner results.
85
85
  */
86
- interface ControlTargetDescriptor<TFamilyId extends string, TTargetId extends string, TTargetInstance extends ControlTargetInstance<TFamilyId, TTargetId> = ControlTargetInstance<TFamilyId, TTargetId>> {
87
- readonly kind: 'target';
88
- readonly id: string;
89
- readonly familyId: TFamilyId;
90
- readonly targetId: TTargetId;
91
- readonly manifest: ExtensionPackManifest;
92
- create(): TTargetInstance;
86
+ type MigrationPlannerResult = MigrationPlannerSuccessResult | MigrationPlannerFailureResult;
87
+ /**
88
+ * Success value for migration runner execution.
89
+ */
90
+ interface MigrationRunnerSuccessValue {
91
+ readonly operationsPlanned: number;
92
+ readonly operationsExecuted: number;
93
+ }
94
+ /**
95
+ * Failure details for migration runner execution.
96
+ */
97
+ interface MigrationRunnerFailure {
98
+ /** Error code for the failure. */
99
+ readonly code: string;
100
+ /** Human-readable summary of the failure. */
101
+ readonly summary: string;
102
+ /** Optional explanation of why the failure occurred. */
103
+ readonly why?: string;
104
+ /** Optional metadata for debugging and UX (e.g., schema issues, SQL state). */
105
+ readonly meta?: Record<string, unknown>;
106
+ }
107
+ /**
108
+ * Result type for migration runner execution.
109
+ */
110
+ type MigrationRunnerResult = Result<MigrationRunnerSuccessValue, MigrationRunnerFailure>;
111
+ /**
112
+ * Execution-time checks configuration for migration runners.
113
+ * All checks default to `true` (enabled) when omitted.
114
+ */
115
+ interface MigrationRunnerExecutionChecks {
116
+ /**
117
+ * Whether to run prechecks before executing operations.
118
+ * Defaults to `true` (prechecks are run).
119
+ */
120
+ readonly prechecks?: boolean;
121
+ /**
122
+ * Whether to run postchecks after executing operations.
123
+ * Defaults to `true` (postchecks are run).
124
+ */
125
+ readonly postchecks?: boolean;
126
+ /**
127
+ * Whether to run idempotency probe (check if postcheck is already satisfied before execution).
128
+ * Defaults to `true` (idempotency probe is run).
129
+ */
130
+ readonly idempotencyChecks?: boolean;
93
131
  }
94
132
  /**
95
- * Descriptor for a control-plane adapter pack (e.g., Postgres adapter).
133
+ * Migration planner interface for planning schema changes.
134
+ * This is the minimal interface that CLI commands use.
96
135
  *
97
136
  * @template TFamilyId - The family ID (e.g., 'sql', 'document')
98
137
  * @template TTargetId - The target ID (e.g., 'postgres', 'mysql')
99
- * @template TAdapterInstance - The adapter instance type
100
138
  */
101
- interface ControlAdapterDescriptor<TFamilyId extends string, TTargetId extends string, TAdapterInstance extends ControlAdapterInstance<TFamilyId, TTargetId> = ControlAdapterInstance<TFamilyId, TTargetId>> {
102
- readonly kind: 'adapter';
103
- readonly id: string;
104
- readonly familyId: TFamilyId;
105
- readonly targetId: TTargetId;
106
- readonly manifest: ExtensionPackManifest;
107
- create(): TAdapterInstance;
139
+ interface MigrationPlanner<TFamilyId extends string = string, TTargetId extends string = string> {
140
+ plan(options: {
141
+ readonly contract: unknown;
142
+ readonly schema: unknown;
143
+ readonly policy: MigrationOperationPolicy;
144
+ /**
145
+ * Active framework components participating in this composition.
146
+ * Families/targets can interpret this list to derive family-specific metadata.
147
+ * All components must have matching familyId and targetId.
148
+ */
149
+ readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<TFamilyId, TTargetId>>;
150
+ }): MigrationPlannerResult;
108
151
  }
109
152
  /**
110
- * Descriptor for a control-plane driver pack (e.g., Postgres driver).
153
+ * Migration runner interface for executing migration plans.
154
+ * This is the minimal interface that CLI commands use.
111
155
  *
112
156
  * @template TFamilyId - The family ID (e.g., 'sql', 'document')
113
157
  * @template TTargetId - The target ID (e.g., 'postgres', 'mysql')
114
- * @template TDriverInstance - The driver instance type
115
158
  */
116
- interface ControlDriverDescriptor<TFamilyId extends string, TTargetId extends string, TDriverInstance extends ControlDriverInstance<TTargetId> = ControlDriverInstance<TTargetId>> {
117
- readonly kind: 'driver';
118
- readonly id: string;
119
- readonly familyId: TFamilyId;
120
- readonly targetId: TTargetId;
121
- readonly manifest: ExtensionPackManifest;
122
- create(url: string): Promise<TDriverInstance>;
159
+ interface MigrationRunner<TFamilyId extends string = string, TTargetId extends string = string> {
160
+ execute(options: {
161
+ readonly plan: MigrationPlan;
162
+ readonly driver: ControlDriverInstance<TFamilyId, TTargetId>;
163
+ readonly destinationContract: unknown;
164
+ readonly policy: MigrationOperationPolicy;
165
+ readonly callbacks?: {
166
+ onOperationStart?(op: MigrationPlanOperation): void;
167
+ onOperationComplete?(op: MigrationPlanOperation): void;
168
+ };
169
+ /**
170
+ * Execution-time checks configuration.
171
+ * All checks default to `true` (enabled) when omitted.
172
+ */
173
+ readonly executionChecks?: MigrationRunnerExecutionChecks;
174
+ /**
175
+ * Active framework components participating in this composition.
176
+ * Families/targets can interpret this list to derive family-specific metadata.
177
+ * All components must have matching familyId and targetId.
178
+ */
179
+ readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<TFamilyId, TTargetId>>;
180
+ }): Promise<MigrationRunnerResult>;
123
181
  }
124
182
  /**
125
- * Descriptor for a control-plane extension pack (e.g., pgvector).
183
+ * Optional capability interface for targets that support migrations.
184
+ * Targets that implement migrations expose this via their descriptor.
126
185
  *
127
186
  * @template TFamilyId - The family ID (e.g., 'sql', 'document')
128
187
  * @template TTargetId - The target ID (e.g., 'postgres', 'mysql')
129
- * @template TExtensionInstance - The extension instance type
188
+ * @template TFamilyInstance - The family instance type (e.g., SqlControlFamilyInstance)
130
189
  */
131
- interface ControlExtensionDescriptor<TFamilyId extends string, TTargetId extends string, TExtensionInstance extends ControlExtensionInstance<TFamilyId, TTargetId> = ControlExtensionInstance<TFamilyId, TTargetId>> {
132
- readonly kind: 'extension';
133
- readonly id: string;
134
- readonly familyId: TFamilyId;
135
- readonly targetId: TTargetId;
136
- readonly manifest: ExtensionPackManifest;
137
- create(): TExtensionInstance;
190
+ interface TargetMigrationsCapability<TFamilyId extends string = string, TTargetId extends string = string, TFamilyInstance extends ControlFamilyInstance<TFamilyId> = ControlFamilyInstance<TFamilyId>> {
191
+ createPlanner(family: TFamilyInstance): MigrationPlanner<TFamilyId, TTargetId>;
192
+ createRunner(family: TFamilyInstance): MigrationRunner<TFamilyId, TTargetId>;
138
193
  }
194
+
139
195
  /**
140
- * Family instance interface for control-plane domain actions.
141
- * Each family implements this interface with family-specific types.
196
+ * Control-plane family instance interface.
197
+ * Extends the base FamilyInstance with control-plane domain actions.
198
+ *
199
+ * @template TFamilyId - The family ID (e.g., 'sql', 'document')
200
+ * @template TSchemaIR - The schema IR type returned by introspect() (family-specific)
142
201
  */
143
- interface FamilyInstance<TFamilyId extends string, TSchemaIR = unknown, TVerifyResult = unknown, TSchemaVerifyResult = unknown, TSignResult = unknown> {
144
- readonly familyId: TFamilyId;
202
+ interface ControlFamilyInstance<TFamilyId extends string, TSchemaIR = unknown> extends FamilyInstance<TFamilyId> {
145
203
  /**
146
204
  * Validates a contract JSON and returns a validated ContractIR (without mappings).
147
205
  * Mappings are runtime-only and should not be part of ContractIR.
206
+ *
207
+ * Note: The returned ContractIR may include additional fields from the emitted contract
208
+ * (like coreHash, profileHash) that are not part of the ContractIR type but are preserved
209
+ * for use by verify/sign operations.
148
210
  */
149
- validateContractIR(contractJson: unknown): unknown;
211
+ validateContractIR(contractJson: unknown): ContractIR;
150
212
  /**
151
213
  * Verifies the database marker against the contract.
152
214
  * Compares target, coreHash, and profileHash.
215
+ *
216
+ * @param options.contractIR - The validated contract (from validateContractIR). Must have
217
+ * coreHash and target fields for verification. These fields are present in emitted
218
+ * contracts but not in the ContractIR type definition.
153
219
  */
154
220
  verify(options: {
155
- readonly driver: ControlDriverInstance;
221
+ readonly driver: ControlDriverInstance<TFamilyId, string>;
156
222
  readonly contractIR: unknown;
157
223
  readonly expectedTargetId: string;
158
224
  readonly contractPath: string;
159
225
  readonly configPath?: string;
160
- }): Promise<TVerifyResult>;
226
+ }): Promise<VerifyDatabaseResult>;
161
227
  /**
162
228
  * Verifies the database schema against the contract.
163
229
  * Compares contract requirements against live database schema.
164
230
  */
165
231
  schemaVerify(options: {
166
- readonly driver: ControlDriverInstance;
232
+ readonly driver: ControlDriverInstance<TFamilyId, string>;
167
233
  readonly contractIR: unknown;
168
234
  readonly strict: boolean;
169
235
  readonly contractPath: string;
170
236
  readonly configPath?: string;
171
- }): Promise<TSchemaVerifyResult>;
237
+ /**
238
+ * Active framework components participating in this composition.
239
+ * All components must have matching familyId and targetId.
240
+ */
241
+ readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<TFamilyId, string>>;
242
+ }): Promise<VerifyDatabaseSchemaResult>;
172
243
  /**
173
244
  * Signs the database with the contract marker.
174
245
  * Writes or updates the contract marker if schema verification passes.
175
246
  * This operation is idempotent - if the marker already matches, no changes are made.
176
247
  */
177
248
  sign(options: {
178
- readonly driver: ControlDriverInstance;
249
+ readonly driver: ControlDriverInstance<TFamilyId, string>;
179
250
  readonly contractIR: unknown;
180
251
  readonly contractPath: string;
181
252
  readonly configPath?: string;
182
- }): Promise<TSignResult>;
253
+ }): Promise<SignDatabaseResult>;
254
+ /**
255
+ * Reads the contract marker from the database.
256
+ * Returns null if no marker exists.
257
+ */
258
+ readMarker(options: {
259
+ readonly driver: ControlDriverInstance<TFamilyId, string>;
260
+ }): Promise<ContractMarkerRecord | null>;
183
261
  /**
184
262
  * Introspects the database schema and returns a family-specific schema IR.
185
263
  *
@@ -189,15 +267,15 @@ interface FamilyInstance<TFamilyId extends string, TSchemaIR = unknown, TVerifyR
189
267
  *
190
268
  * @param options - Introspection options
191
269
  * @param options.driver - Control plane driver for database connection
192
- * @param options.contractIR - Optional contract IR for contract-guided introspection.
270
+ * @param options.contractIR - Optional contract for contract-guided introspection.
193
271
  * When provided, families may use it for filtering, optimization, or validation
194
- * during introspection. The contract IR does not change the meaning of "what exists"
272
+ * during introspection. The contract does not change the meaning of "what exists"
195
273
  * in the database - it only guides how introspection is performed.
196
274
  * @returns Promise resolving to the family-specific Schema IR (e.g., `SqlSchemaIR` for SQL).
197
275
  * The IR represents the complete schema snapshot at the time of introspection.
198
276
  */
199
277
  introspect(options: {
200
- readonly driver: ControlDriverInstance;
278
+ readonly driver: ControlDriverInstance<TFamilyId, string>;
201
279
  readonly contractIR?: unknown;
202
280
  }): Promise<TSchemaIR>;
203
281
  /**
@@ -215,6 +293,146 @@ interface FamilyInstance<TFamilyId extends string, TSchemaIR = unknown, TVerifyR
215
293
  readonly contractIR: ContractIR | unknown;
216
294
  }): Promise<EmitContractResult>;
217
295
  }
296
+ /**
297
+ * Control-plane target instance interface.
298
+ * Extends the base TargetInstance with control-plane specific behavior.
299
+ *
300
+ * @template TFamilyId - The family ID (e.g., 'sql', 'document')
301
+ * @template TTargetId - The target ID (e.g., 'postgres', 'mysql')
302
+ */
303
+ interface ControlTargetInstance<TFamilyId extends string, TTargetId extends string> extends TargetInstance<TFamilyId, TTargetId> {
304
+ }
305
+ /**
306
+ * Control-plane adapter instance interface.
307
+ * Extends the base AdapterInstance with control-plane specific behavior.
308
+ * Families extend this with family-specific adapter interfaces.
309
+ *
310
+ * @template TFamilyId - The family ID (e.g., 'sql', 'document')
311
+ * @template TTargetId - The target ID (e.g., 'postgres', 'mysql')
312
+ */
313
+ interface ControlAdapterInstance<TFamilyId extends string, TTargetId extends string> extends AdapterInstance<TFamilyId, TTargetId> {
314
+ }
315
+ /**
316
+ * Control-plane driver instance interface.
317
+ * Extends the base DriverInstance with control-plane specific behavior.
318
+ *
319
+ * @template TFamilyId - The family ID (e.g., 'sql', 'document')
320
+ * @template TTargetId - The target ID (e.g., 'postgres', 'mysql')
321
+ */
322
+ interface ControlDriverInstance<TFamilyId extends string, TTargetId extends string> extends DriverInstance<TFamilyId, TTargetId> {
323
+ query<Row = Record<string, unknown>>(sql: string, params?: readonly unknown[]): Promise<{
324
+ readonly rows: Row[];
325
+ }>;
326
+ close(): Promise<void>;
327
+ }
328
+ /**
329
+ * Control-plane extension instance interface.
330
+ * Extends the base ExtensionInstance with control-plane specific behavior.
331
+ *
332
+ * @template TFamilyId - The family ID (e.g., 'sql', 'document')
333
+ * @template TTargetId - The target ID (e.g., 'postgres', 'mysql')
334
+ */
335
+ interface ControlExtensionInstance<TFamilyId extends string, TTargetId extends string> extends ExtensionInstance<TFamilyId, TTargetId> {
336
+ }
337
+ /**
338
+ * Operation context for propagating metadata through control-plane operation call chains.
339
+ * Inspired by OpenTelemetry's Context and Sentry's Scope patterns.
340
+ *
341
+ * This context carries informational metadata (like file paths) that can be used for
342
+ * error reporting, logging, and debugging, but is not required for structural correctness.
343
+ * It allows subsystems to propagate context without coupling to file I/O concerns.
344
+ *
345
+ * @example
346
+ * ```typescript
347
+ * const context: OperationContext = {
348
+ * contractPath: './contract.json',
349
+ * configPath: './prisma-next.config.ts',
350
+ * };
351
+ *
352
+ * await runner.execute({ plan, driver, destinationContract: contract, context });
353
+ * ```
354
+ */
355
+ interface OperationContext {
356
+ /**
357
+ * Path to the contract file (if applicable).
358
+ * Used for error reporting and metadata, not for file I/O.
359
+ */
360
+ readonly contractPath?: string;
361
+ /**
362
+ * Path to the configuration file (if applicable).
363
+ * Used for error reporting and metadata, not for file I/O.
364
+ */
365
+ readonly configPath?: string;
366
+ /**
367
+ * Additional metadata that can be propagated through the call chain.
368
+ * Extensible for future needs without breaking changes.
369
+ */
370
+ readonly meta?: Readonly<Record<string, unknown>>;
371
+ }
372
+ /**
373
+ * Descriptor for a control-plane family (e.g., SQL).
374
+ * Provides the family hook and factory method.
375
+ *
376
+ * @template TFamilyId - The family ID (e.g., 'sql', 'document')
377
+ * @template TFamilyInstance - The family instance type
378
+ */
379
+ interface ControlFamilyDescriptor<TFamilyId extends string, TFamilyInstance extends ControlFamilyInstance<TFamilyId> = ControlFamilyInstance<TFamilyId>> extends FamilyDescriptor<TFamilyId> {
380
+ readonly hook: TargetFamilyHook;
381
+ create<TTargetId extends string>(options: {
382
+ readonly target: ControlTargetDescriptor<TFamilyId, TTargetId>;
383
+ readonly adapter: ControlAdapterDescriptor<TFamilyId, TTargetId>;
384
+ readonly driver: ControlDriverDescriptor<TFamilyId, TTargetId>;
385
+ readonly extensionPacks: readonly ControlExtensionDescriptor<TFamilyId, TTargetId>[];
386
+ }): TFamilyInstance;
387
+ }
388
+ /**
389
+ * Descriptor for a control-plane target component (e.g., Postgres target).
390
+ *
391
+ * @template TFamilyId - The family ID (e.g., 'sql', 'document')
392
+ * @template TTargetId - The target ID (e.g., 'postgres', 'mysql')
393
+ * @template TTargetInstance - The target instance type
394
+ * @template TFamilyInstance - The family instance type for migrations (optional)
395
+ */
396
+ interface ControlTargetDescriptor<TFamilyId extends string, TTargetId extends string, TTargetInstance extends ControlTargetInstance<TFamilyId, TTargetId> = ControlTargetInstance<TFamilyId, TTargetId>, TFamilyInstance extends ControlFamilyInstance<TFamilyId> = ControlFamilyInstance<TFamilyId>> extends TargetDescriptor<TFamilyId, TTargetId> {
397
+ /**
398
+ * Optional migrations capability.
399
+ * Targets that support migrations expose this property.
400
+ * The capability is parameterized by family and target IDs to ensure type-level
401
+ * compatibility of framework components.
402
+ */
403
+ readonly migrations?: TargetMigrationsCapability<TFamilyId, TTargetId, TFamilyInstance>;
404
+ create(): TTargetInstance;
405
+ }
406
+ /**
407
+ * Descriptor for a control-plane adapter component (e.g., Postgres adapter).
408
+ *
409
+ * @template TFamilyId - The family ID (e.g., 'sql', 'document')
410
+ * @template TTargetId - The target ID (e.g., 'postgres', 'mysql')
411
+ * @template TAdapterInstance - The adapter instance type
412
+ */
413
+ interface ControlAdapterDescriptor<TFamilyId extends string, TTargetId extends string, TAdapterInstance extends ControlAdapterInstance<TFamilyId, TTargetId> = ControlAdapterInstance<TFamilyId, TTargetId>> extends AdapterDescriptor<TFamilyId, TTargetId> {
414
+ create(): TAdapterInstance;
415
+ }
416
+ /**
417
+ * Descriptor for a control-plane driver component (e.g., Postgres driver).
418
+ *
419
+ * @template TFamilyId - The family ID (e.g., 'sql', 'document')
420
+ * @template TTargetId - The target ID (e.g., 'postgres', 'mysql')
421
+ * @template TDriverInstance - The driver instance type
422
+ */
423
+ interface ControlDriverDescriptor<TFamilyId extends string, TTargetId extends string, TDriverInstance extends ControlDriverInstance<TFamilyId, TTargetId> = ControlDriverInstance<TFamilyId, TTargetId>> extends DriverDescriptor<TFamilyId, TTargetId> {
424
+ create(url: string): Promise<TDriverInstance>;
425
+ }
426
+ /**
427
+ * Descriptor for a control-plane extension component (e.g., pgvector).
428
+ *
429
+ * @template TFamilyId - The family ID (e.g., 'sql', 'document')
430
+ * @template TTargetId - The target ID (e.g., 'postgres', 'mysql')
431
+ * @template TExtensionInstance - The extension instance type
432
+ */
433
+ interface ControlExtensionDescriptor<TFamilyId extends string, TTargetId extends string, TExtensionInstance extends ControlExtensionInstance<TFamilyId, TTargetId> = ControlExtensionInstance<TFamilyId, TTargetId>> extends ExtensionDescriptor<TFamilyId, TTargetId> {
434
+ create(): TExtensionInstance;
435
+ }
218
436
  /**
219
437
  * Result type for database marker verification operations.
220
438
  */
@@ -248,7 +466,7 @@ interface VerifyDatabaseResult {
248
466
  * Schema issue type for schema verification results.
249
467
  */
250
468
  interface SchemaIssue {
251
- readonly kind: 'missing_table' | 'missing_column' | 'type_mismatch' | 'nullability_mismatch' | 'primary_key_mismatch' | 'foreign_key_mismatch' | 'unique_constraint_mismatch' | 'index_mismatch' | 'extension_missing';
469
+ readonly kind: 'missing_table' | 'missing_column' | 'extra_table' | 'extra_column' | 'extra_primary_key' | 'extra_foreign_key' | 'extra_unique_constraint' | 'extra_index' | 'type_mismatch' | 'nullability_mismatch' | 'primary_key_mismatch' | 'foreign_key_mismatch' | 'unique_constraint_mismatch' | 'index_mismatch' | 'extension_missing';
252
470
  readonly table: string;
253
471
  readonly column?: string;
254
472
  readonly indexOrConstraint?: string;
@@ -298,7 +516,7 @@ interface VerifyDatabaseSchemaResult {
298
516
  };
299
517
  readonly meta?: {
300
518
  readonly configPath?: string;
301
- readonly contractPath: string;
519
+ readonly contractPath?: string;
302
520
  readonly strict: boolean;
303
521
  };
304
522
  readonly timings: {
@@ -320,7 +538,7 @@ interface EmitContractResult {
320
538
  *
321
539
  * @template TSchemaIR - The family-specific Schema IR type (e.g., `SqlSchemaIR` for SQL)
322
540
  */
323
- interface IntrospectSchemaResult<TSchemaIR = unknown> {
541
+ interface IntrospectSchemaResult<TSchemaIR> {
324
542
  readonly ok: true;
325
543
  readonly summary: string;
326
544
  readonly target: {
@@ -368,4 +586,4 @@ interface SignDatabaseResult {
368
586
  };
369
587
  }
370
588
 
371
- export type { ControlAdapterDescriptor, ControlAdapterInstance, ControlDriverDescriptor, ControlDriverInstance, ControlExtensionDescriptor, ControlExtensionInstance, ControlFamilyDescriptor, ControlFamilyInstance, ControlTargetDescriptor, ControlTargetInstance, EmitContractResult, FamilyInstance, IntrospectSchemaResult, SchemaIssue, SchemaVerificationNode, SignDatabaseResult, VerifyDatabaseResult, VerifyDatabaseSchemaResult };
589
+ export type { ControlAdapterDescriptor, ControlAdapterInstance, ControlDriverDescriptor, ControlDriverInstance, ControlExtensionDescriptor, ControlExtensionInstance, ControlFamilyDescriptor, ControlFamilyInstance, ControlTargetDescriptor, ControlTargetInstance, EmitContractResult, IntrospectSchemaResult, MigrationOperationClass, MigrationOperationPolicy, MigrationPlan, MigrationPlanOperation, MigrationPlanner, MigrationPlannerConflict, MigrationPlannerFailureResult, MigrationPlannerResult, MigrationPlannerSuccessResult, MigrationRunner, MigrationRunnerExecutionChecks, MigrationRunnerFailure, MigrationRunnerResult, MigrationRunnerSuccessValue, OperationContext, SchemaIssue, SchemaVerificationNode, SignDatabaseResult, TargetMigrationsCapability, VerifyDatabaseResult, VerifyDatabaseSchemaResult };
package/package.json CHANGED
@@ -1,19 +1,21 @@
1
1
  {
2
2
  "name": "@prisma-next/core-control-plane",
3
- "version": "0.1.0-dev.3",
3
+ "version": "0.1.0-dev.30",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "description": "Control plane domain actions, config types, validation, and error factories for Prisma Next",
7
7
  "dependencies": {
8
8
  "arktype": "^2.1.26",
9
9
  "prettier": "^3.3.3",
10
- "@prisma-next/operations": "0.1.0-dev.3",
11
- "@prisma-next/contract": "0.1.0-dev.3"
10
+ "@prisma-next/contract": "0.1.0-dev.30",
11
+ "@prisma-next/operations": "0.1.0-dev.30",
12
+ "@prisma-next/utils": "0.1.0-dev.30"
12
13
  },
13
14
  "devDependencies": {
15
+ "@vitest/coverage-v8": "^4.0.0",
14
16
  "tsup": "^8.3.0",
15
17
  "typescript": "^5.9.3",
16
- "vitest": "^2.1.1",
18
+ "vitest": "^4.0.16",
17
19
  "@prisma-next/test-utils": "0.0.1"
18
20
  },
19
21
  "files": [
@@ -50,7 +52,9 @@
50
52
  "test": "vitest run --passWithNoTests",
51
53
  "test:coverage": "vitest run --coverage --passWithNoTests",
52
54
  "typecheck": "tsc --project tsconfig.json --noEmit",
53
- "lint": "biome check . --config-path ../../../biome.json --error-on-warnings",
54
- "clean": "node ../../../scripts/clean.mjs"
55
+ "lint": "biome check . --config-path ../../../../../biome.json --error-on-warnings",
56
+ "lint:fix": "biome check --write . --config-path ../../../biome.json",
57
+ "lint:fix:unsafe": "biome check --write --unsafe . --config-path ../../../biome.json",
58
+ "clean": "node ../../../../../scripts/clean.mjs"
55
59
  }
56
60
  }
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/errors.ts"],"sourcesContent":["/**\n * CLI error envelope for output formatting.\n * This is the serialized form of a CliStructuredError.\n */\nexport interface CliErrorEnvelope {\n readonly code: string;\n readonly domain: string;\n readonly severity: 'error' | 'warn' | 'info';\n readonly summary: string;\n readonly why: string | undefined;\n readonly fix: string | undefined;\n readonly where:\n | {\n readonly path: string | undefined;\n readonly line: number | undefined;\n }\n | undefined;\n readonly meta: Record<string, unknown> | undefined;\n readonly docsUrl: string | undefined;\n}\n\n/**\n * Structured CLI error that contains all information needed for error envelopes.\n * Call sites throw these errors with full context.\n */\nexport class CliStructuredError extends Error {\n readonly code: string;\n readonly domain: 'CLI' | 'RTM';\n readonly severity: 'error' | 'warn' | 'info';\n readonly why: string | undefined;\n readonly fix: string | undefined;\n readonly where:\n | {\n readonly path: string | undefined;\n readonly line: number | undefined;\n }\n | undefined;\n readonly meta: Record<string, unknown> | undefined;\n readonly docsUrl: string | undefined;\n\n constructor(\n code: string,\n summary: string,\n options?: {\n readonly domain?: 'CLI' | 'RTM';\n readonly severity?: 'error' | 'warn' | 'info';\n readonly why?: string;\n readonly fix?: string;\n readonly where?: { readonly path?: string; readonly line?: number };\n readonly meta?: Record<string, unknown>;\n readonly docsUrl?: string;\n },\n ) {\n super(summary);\n this.name = 'CliStructuredError';\n this.code = code;\n this.domain = options?.domain ?? 'CLI';\n this.severity = options?.severity ?? 'error';\n this.why = options?.why;\n this.fix = options?.fix;\n this.where = options?.where\n ? {\n path: options.where.path,\n line: options.where.line,\n }\n : undefined;\n this.meta = options?.meta;\n this.docsUrl = options?.docsUrl;\n }\n\n /**\n * Converts this error to a CLI error envelope for output formatting.\n */\n toEnvelope(): CliErrorEnvelope {\n const codePrefix = this.domain === 'CLI' ? 'PN-CLI-' : 'PN-RTM-';\n return {\n code: `${codePrefix}${this.code}`,\n domain: this.domain,\n severity: this.severity,\n summary: this.message,\n why: this.why,\n fix: this.fix,\n where: this.where,\n meta: this.meta,\n docsUrl: this.docsUrl,\n };\n }\n}\n\n// ============================================================================\n// Config Errors (PN-CLI-4001-4007)\n// ============================================================================\n\n/**\n * Config file not found or missing.\n */\nexport function errorConfigFileNotFound(\n configPath?: string,\n options?: {\n readonly why?: string;\n },\n): CliStructuredError {\n return new CliStructuredError('4001', 'Config file not found', {\n domain: 'CLI',\n ...(options?.why ? { why: options.why } : { why: 'Config file not found' }),\n fix: \"Run 'prisma-next init' to create a config file\",\n docsUrl: 'https://prisma-next.dev/docs/cli/config',\n ...(configPath ? { where: { path: configPath } } : {}),\n });\n}\n\n/**\n * Contract configuration missing from config.\n */\nexport function errorContractConfigMissing(options?: {\n readonly why?: string;\n}): CliStructuredError {\n return new CliStructuredError('4002', 'Contract configuration missing', {\n domain: 'CLI',\n why: options?.why ?? 'The contract configuration is required for emit',\n fix: 'Add contract configuration to your prisma-next.config.ts',\n docsUrl: 'https://prisma-next.dev/docs/cli/contract-emit',\n });\n}\n\n/**\n * Contract validation failed.\n */\nexport function errorContractValidationFailed(\n reason: string,\n options?: {\n readonly where?: { readonly path?: string; readonly line?: number };\n },\n): CliStructuredError {\n return new CliStructuredError('4003', 'Contract validation failed', {\n domain: 'CLI',\n why: reason,\n fix: 'Check your contract file for errors',\n docsUrl: 'https://prisma-next.dev/docs/contracts',\n ...(options?.where ? { where: options.where } : {}),\n });\n}\n\n/**\n * File not found.\n */\nexport function errorFileNotFound(\n filePath: string,\n options?: {\n readonly why?: string;\n },\n): CliStructuredError {\n return new CliStructuredError('4004', 'File not found', {\n domain: 'CLI',\n why: options?.why ?? `File not found: ${filePath}`,\n fix: 'Check that the file path is correct',\n where: { path: filePath },\n });\n}\n\n/**\n * Database URL is required but not provided.\n */\nexport function errorDatabaseUrlRequired(options?: { readonly why?: string }): CliStructuredError {\n return new CliStructuredError('4005', 'Database URL is required', {\n domain: 'CLI',\n why: options?.why ?? 'Database URL is required for db verify',\n fix: 'Provide --db flag or config.db.url in prisma-next.config.ts',\n });\n}\n\n/**\n * Query runner factory is required but not provided in config.\n */\nexport function errorQueryRunnerFactoryRequired(options?: {\n readonly why?: string;\n}): CliStructuredError {\n return new CliStructuredError('4006', 'Query runner factory is required', {\n domain: 'CLI',\n why: options?.why ?? 'Config.db.queryRunnerFactory is required for db verify',\n fix: 'Add db.queryRunnerFactory to prisma-next.config.ts',\n docsUrl: 'https://prisma-next.dev/docs/cli/db-verify',\n });\n}\n\n/**\n * Family verify.readMarker is required but not provided.\n */\nexport function errorFamilyReadMarkerSqlRequired(options?: {\n readonly why?: string;\n}): CliStructuredError {\n return new CliStructuredError('4007', 'Family readMarker() is required', {\n domain: 'CLI',\n why: options?.why ?? 'Family verify.readMarker is required for db verify',\n fix: 'Ensure family.verify.readMarker() is exported by your family package',\n docsUrl: 'https://prisma-next.dev/docs/cli/db-verify',\n });\n}\n\n/**\n * Driver is required for DB-connected commands but not provided.\n */\nexport function errorDriverRequired(options?: { readonly why?: string }): CliStructuredError {\n return new CliStructuredError('4010', 'Driver is required for DB-connected commands', {\n domain: 'CLI',\n why: options?.why ?? 'Config.driver is required for db verify',\n fix: 'Add driver to prisma-next.config.ts',\n docsUrl: 'https://prisma-next.dev/docs/cli/db-verify',\n });\n}\n\n/**\n * Config validation error (missing required fields).\n */\nexport function errorConfigValidation(\n field: string,\n options?: {\n readonly why?: string;\n },\n): CliStructuredError {\n return new CliStructuredError('4001', 'Config file not found', {\n domain: 'CLI',\n why: options?.why ?? `Config must have a \"${field}\" field`,\n fix: \"Run 'prisma-next init' to create a config file\",\n docsUrl: 'https://prisma-next.dev/docs/cli/config',\n });\n}\n\n// ============================================================================\n// Runtime Errors (PN-RTM-3000-3003)\n// ============================================================================\n\n/**\n * Contract marker not found in database.\n */\nexport function errorMarkerMissing(options?: {\n readonly why?: string;\n readonly dbUrl?: string;\n}): CliStructuredError {\n return new CliStructuredError('3001', 'Marker missing', {\n domain: 'RTM',\n why: options?.why ?? 'Contract marker not found in database',\n fix: 'Run `prisma-next db sign --db <url>` to create marker',\n });\n}\n\n/**\n * Contract hash does not match database marker.\n */\nexport function errorHashMismatch(options?: {\n readonly why?: string;\n readonly expected?: string;\n readonly actual?: string;\n}): CliStructuredError {\n return new CliStructuredError('3002', 'Hash mismatch', {\n domain: 'RTM',\n why: options?.why ?? 'Contract hash does not match database marker',\n fix: 'Migrate database or re-sign if intentional',\n ...(options?.expected || options?.actual\n ? {\n meta: {\n ...(options.expected ? { expected: options.expected } : {}),\n ...(options.actual ? { actual: options.actual } : {}),\n },\n }\n : {}),\n });\n}\n\n/**\n * Contract target does not match config target.\n */\nexport function errorTargetMismatch(\n expected: string,\n actual: string,\n options?: {\n readonly why?: string;\n },\n): CliStructuredError {\n return new CliStructuredError('3003', 'Target mismatch', {\n domain: 'RTM',\n why:\n options?.why ??\n `Contract target does not match config target (expected: ${expected}, actual: ${actual})`,\n fix: 'Align contract target and config target',\n meta: { expected, actual },\n });\n}\n\n/**\n * Generic runtime error.\n */\nexport function errorRuntime(\n summary: string,\n options?: {\n readonly why?: string;\n readonly fix?: string;\n readonly meta?: Record<string, unknown>;\n },\n): CliStructuredError {\n return new CliStructuredError('3000', summary, {\n domain: 'RTM',\n ...(options?.why ? { why: options.why } : { why: 'Verification failed' }),\n ...(options?.fix ? { fix: options.fix } : { fix: 'Check contract and database state' }),\n ...(options?.meta ? { meta: options.meta } : {}),\n });\n}\n\n// ============================================================================\n// Generic Error\n// ============================================================================\n\n/**\n * Generic unexpected error.\n */\nexport function errorUnexpected(\n message: string,\n options?: {\n readonly why?: string;\n readonly fix?: string;\n },\n): CliStructuredError {\n return new CliStructuredError('4999', 'Unexpected error', {\n domain: 'CLI',\n why: options?.why ?? message,\n fix: options?.fix ?? 'Check the error message and try again',\n });\n}\n"],"mappings":"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","names":[]}