@prisma-next/cli 0.3.0-dev.5 → 0.3.0-dev.52

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.
Files changed (130) hide show
  1. package/README.md +145 -74
  2. package/dist/cli.d.mts +1 -0
  3. package/dist/cli.js +1 -2376
  4. package/dist/cli.mjs +169 -0
  5. package/dist/cli.mjs.map +1 -0
  6. package/dist/client-BSZKpZTF.mjs +711 -0
  7. package/dist/client-BSZKpZTF.mjs.map +1 -0
  8. package/dist/commands/contract-emit.d.mts +7 -0
  9. package/dist/commands/contract-emit.d.mts.map +1 -0
  10. package/dist/commands/contract-emit.mjs +147 -0
  11. package/dist/commands/contract-emit.mjs.map +1 -0
  12. package/dist/commands/db-init.d.mts +7 -0
  13. package/dist/commands/db-init.d.mts.map +1 -0
  14. package/dist/commands/db-init.mjs +179 -0
  15. package/dist/commands/db-init.mjs.map +1 -0
  16. package/dist/commands/db-introspect.d.mts +7 -0
  17. package/dist/commands/db-introspect.d.mts.map +1 -0
  18. package/dist/commands/db-introspect.mjs +120 -0
  19. package/dist/commands/db-introspect.mjs.map +1 -0
  20. package/dist/commands/db-schema-verify.d.mts +7 -0
  21. package/dist/commands/db-schema-verify.d.mts.map +1 -0
  22. package/dist/commands/db-schema-verify.mjs +116 -0
  23. package/dist/commands/db-schema-verify.mjs.map +1 -0
  24. package/dist/commands/db-sign.d.mts +7 -0
  25. package/dist/commands/db-sign.d.mts.map +1 -0
  26. package/dist/commands/db-sign.mjs +138 -0
  27. package/dist/commands/db-sign.mjs.map +1 -0
  28. package/dist/commands/db-verify.d.mts +7 -0
  29. package/dist/commands/db-verify.d.mts.map +1 -0
  30. package/dist/commands/db-verify.mjs +129 -0
  31. package/dist/commands/db-verify.mjs.map +1 -0
  32. package/dist/config-loader-BJ8HsEdA.mjs +42 -0
  33. package/dist/config-loader-BJ8HsEdA.mjs.map +1 -0
  34. package/dist/{config-loader.d.ts → config-loader.d.mts} +8 -3
  35. package/dist/config-loader.d.mts.map +1 -0
  36. package/dist/config-loader.mjs +3 -0
  37. package/dist/exports/config-types.d.mts +2 -0
  38. package/dist/exports/config-types.mjs +3 -0
  39. package/dist/exports/control-api.d.mts +433 -0
  40. package/dist/exports/control-api.d.mts.map +1 -0
  41. package/dist/exports/control-api.mjs +96 -0
  42. package/dist/exports/control-api.mjs.map +1 -0
  43. package/dist/{load-ts-contract.d.ts → exports/index.d.mts} +10 -5
  44. package/dist/exports/index.d.mts.map +1 -0
  45. package/dist/exports/index.mjs +132 -0
  46. package/dist/exports/index.mjs.map +1 -0
  47. package/dist/result-handler-BZPY7HX4.mjs +1029 -0
  48. package/dist/result-handler-BZPY7HX4.mjs.map +1 -0
  49. package/package.json +48 -37
  50. package/src/commands/contract-emit.ts +205 -111
  51. package/src/commands/db-init.ts +258 -359
  52. package/src/commands/db-introspect.ts +151 -184
  53. package/src/commands/db-schema-verify.ts +151 -149
  54. package/src/commands/db-sign.ts +202 -200
  55. package/src/commands/db-verify.ts +181 -155
  56. package/src/control-api/client.ts +610 -0
  57. package/src/control-api/operations/contract-emit.ts +161 -0
  58. package/src/control-api/operations/db-init.ts +281 -0
  59. package/src/control-api/types.ts +475 -0
  60. package/src/exports/control-api.ts +48 -0
  61. package/src/load-ts-contract.ts +16 -11
  62. package/src/utils/cli-errors.ts +1 -1
  63. package/src/utils/framework-components.ts +11 -30
  64. package/src/utils/output.ts +16 -10
  65. package/src/utils/progress-adapter.ts +86 -0
  66. package/dist/chunk-464LNZCE.js +0 -134
  67. package/dist/chunk-464LNZCE.js.map +0 -1
  68. package/dist/chunk-BZMBKEEQ.js +0 -997
  69. package/dist/chunk-BZMBKEEQ.js.map +0 -1
  70. package/dist/chunk-HWYQOCAJ.js +0 -47
  71. package/dist/chunk-HWYQOCAJ.js.map +0 -1
  72. package/dist/chunk-ZKYEJROM.js +0 -94
  73. package/dist/chunk-ZKYEJROM.js.map +0 -1
  74. package/dist/cli.d.ts +0 -2
  75. package/dist/cli.d.ts.map +0 -1
  76. package/dist/cli.js.map +0 -1
  77. package/dist/commands/contract-emit.d.ts +0 -3
  78. package/dist/commands/contract-emit.d.ts.map +0 -1
  79. package/dist/commands/contract-emit.js +0 -9
  80. package/dist/commands/contract-emit.js.map +0 -1
  81. package/dist/commands/db-init.d.ts +0 -3
  82. package/dist/commands/db-init.d.ts.map +0 -1
  83. package/dist/commands/db-init.js +0 -341
  84. package/dist/commands/db-init.js.map +0 -1
  85. package/dist/commands/db-introspect.d.ts +0 -3
  86. package/dist/commands/db-introspect.d.ts.map +0 -1
  87. package/dist/commands/db-introspect.js +0 -190
  88. package/dist/commands/db-introspect.js.map +0 -1
  89. package/dist/commands/db-schema-verify.d.ts +0 -3
  90. package/dist/commands/db-schema-verify.d.ts.map +0 -1
  91. package/dist/commands/db-schema-verify.js +0 -164
  92. package/dist/commands/db-schema-verify.js.map +0 -1
  93. package/dist/commands/db-sign.d.ts +0 -3
  94. package/dist/commands/db-sign.d.ts.map +0 -1
  95. package/dist/commands/db-sign.js +0 -199
  96. package/dist/commands/db-sign.js.map +0 -1
  97. package/dist/commands/db-verify.d.ts +0 -3
  98. package/dist/commands/db-verify.d.ts.map +0 -1
  99. package/dist/commands/db-verify.js +0 -173
  100. package/dist/commands/db-verify.js.map +0 -1
  101. package/dist/config-loader.d.ts.map +0 -1
  102. package/dist/config-loader.js +0 -7
  103. package/dist/config-loader.js.map +0 -1
  104. package/dist/exports/config-types.d.ts +0 -3
  105. package/dist/exports/config-types.d.ts.map +0 -1
  106. package/dist/exports/config-types.js +0 -6
  107. package/dist/exports/config-types.js.map +0 -1
  108. package/dist/exports/index.d.ts +0 -4
  109. package/dist/exports/index.d.ts.map +0 -1
  110. package/dist/exports/index.js +0 -175
  111. package/dist/exports/index.js.map +0 -1
  112. package/dist/load-ts-contract.d.ts.map +0 -1
  113. package/dist/utils/action.d.ts +0 -16
  114. package/dist/utils/action.d.ts.map +0 -1
  115. package/dist/utils/cli-errors.d.ts +0 -7
  116. package/dist/utils/cli-errors.d.ts.map +0 -1
  117. package/dist/utils/command-helpers.d.ts +0 -12
  118. package/dist/utils/command-helpers.d.ts.map +0 -1
  119. package/dist/utils/framework-components.d.ts +0 -81
  120. package/dist/utils/framework-components.d.ts.map +0 -1
  121. package/dist/utils/global-flags.d.ts +0 -25
  122. package/dist/utils/global-flags.d.ts.map +0 -1
  123. package/dist/utils/output.d.ts +0 -142
  124. package/dist/utils/output.d.ts.map +0 -1
  125. package/dist/utils/result-handler.d.ts +0 -15
  126. package/dist/utils/result-handler.d.ts.map +0 -1
  127. package/dist/utils/spinner.d.ts +0 -29
  128. package/dist/utils/spinner.d.ts.map +0 -1
  129. package/src/utils/action.ts +0 -43
  130. package/src/utils/spinner.ts +0 -67
@@ -0,0 +1,475 @@
1
+ import type {
2
+ ContractSourceDiagnostics,
3
+ ContractSourceProvider,
4
+ } from '@prisma-next/core-control-plane/config-types';
5
+ import type { CoreSchemaView } from '@prisma-next/core-control-plane/schema-view';
6
+ import type {
7
+ ControlAdapterDescriptor,
8
+ ControlDriverDescriptor,
9
+ ControlExtensionDescriptor,
10
+ ControlFamilyDescriptor,
11
+ ControlTargetDescriptor,
12
+ MigrationPlannerConflict,
13
+ SignDatabaseResult,
14
+ VerifyDatabaseResult,
15
+ VerifyDatabaseSchemaResult,
16
+ } from '@prisma-next/core-control-plane/types';
17
+ import type { Result } from '@prisma-next/utils/result';
18
+
19
+ // ============================================================================
20
+ // Client Options
21
+ // ============================================================================
22
+
23
+ /**
24
+ * Options for creating a control client.
25
+ *
26
+ * Note: This is NOT the same as CLI config. There's no `contract` field,
27
+ * no file paths. The client is config-agnostic.
28
+ *
29
+ * The descriptor types use permissive `any` because family-specific descriptors
30
+ * (e.g., SqlFamilyDescriptor) have more specific `create` method signatures that
31
+ * are not compatible with the base ControlFamilyDescriptor type due to TypeScript
32
+ * variance rules. The client implementation casts these internally.
33
+ */
34
+ export interface ControlClientOptions {
35
+ // biome-ignore lint/suspicious/noExplicitAny: required for contravariance - SqlFamilyDescriptor.create has specific parameter types
36
+ readonly family: ControlFamilyDescriptor<any, any>;
37
+ // biome-ignore lint/suspicious/noExplicitAny: required for contravariance - SqlControlTargetDescriptor extends with additional methods
38
+ readonly target: ControlTargetDescriptor<any, any, any, any>;
39
+ // biome-ignore lint/suspicious/noExplicitAny: required for contravariance in adapter.create()
40
+ readonly adapter: ControlAdapterDescriptor<any, any, any>;
41
+ /** Optional - control client can be created without driver for offline operations */
42
+ // biome-ignore lint/suspicious/noExplicitAny: required for contravariance in driver.create()
43
+ readonly driver?: ControlDriverDescriptor<any, any, any, any>;
44
+ // biome-ignore lint/suspicious/noExplicitAny: required for contravariance in extension.create()
45
+ readonly extensionPacks?: ReadonlyArray<ControlExtensionDescriptor<any, any, any>>;
46
+ /**
47
+ * Optional default connection for auto-connect.
48
+ * When provided, operations will auto-connect if not already connected.
49
+ * The type is driver-specific (e.g., string URL for Postgres).
50
+ */
51
+ readonly connection?: unknown;
52
+ }
53
+
54
+ // ============================================================================
55
+ // Progress Events
56
+ // ============================================================================
57
+
58
+ /**
59
+ * Action names for control-api operations that can emit progress events.
60
+ */
61
+ export type ControlActionName =
62
+ | 'dbInit'
63
+ | 'verify'
64
+ | 'schemaVerify'
65
+ | 'sign'
66
+ | 'introspect'
67
+ | 'emit';
68
+
69
+ /**
70
+ * Progress event emitted during control-api operation execution.
71
+ *
72
+ * Events model operation progress using a span-based model:
73
+ * - `spanStart`: Begin a timed segment (supports nesting via parentSpanId)
74
+ * - `spanEnd`: Complete a timed segment
75
+ *
76
+ * All operation-specific progress (e.g., per-migration-operation) is modeled
77
+ * as nested spans rather than special event types.
78
+ *
79
+ * Events are delivered via an optional `onProgress` callback to avoid polluting
80
+ * return types. If the callback is absent, operations emit no events (zero overhead).
81
+ */
82
+ export type ControlProgressEvent =
83
+ | {
84
+ readonly action: ControlActionName;
85
+ readonly kind: 'spanStart';
86
+ readonly spanId: string;
87
+ readonly parentSpanId?: string;
88
+ readonly label: string;
89
+ }
90
+ | {
91
+ readonly action: ControlActionName;
92
+ readonly kind: 'spanEnd';
93
+ readonly spanId: string;
94
+ readonly outcome: 'ok' | 'skipped' | 'error';
95
+ };
96
+
97
+ /**
98
+ * Callback function for receiving progress events during control-api operations.
99
+ *
100
+ * @param event - The progress event emitted by the operation
101
+ */
102
+ export type OnControlProgress = (event: ControlProgressEvent) => void;
103
+
104
+ // ============================================================================
105
+ // Operation Options
106
+ // ============================================================================
107
+
108
+ /**
109
+ * Options for the verify operation.
110
+ */
111
+ export interface VerifyOptions {
112
+ /** Contract IR or unvalidated JSON - validated at runtime via familyInstance.validateContractIR() */
113
+ readonly contractIR: unknown;
114
+ /**
115
+ * Database connection. If provided, verify will connect before executing.
116
+ * If omitted, the client must already be connected.
117
+ * The type is driver-specific (e.g., string URL for Postgres).
118
+ */
119
+ readonly connection?: unknown;
120
+ /** Optional progress callback for observing operation progress */
121
+ readonly onProgress?: OnControlProgress;
122
+ }
123
+
124
+ /**
125
+ * Options for the schemaVerify operation.
126
+ */
127
+ export interface SchemaVerifyOptions {
128
+ /** Contract IR or unvalidated JSON - validated at runtime via familyInstance.validateContractIR() */
129
+ readonly contractIR: unknown;
130
+ /**
131
+ * Whether to use strict mode for schema verification.
132
+ * In strict mode, extra tables/columns are reported as issues.
133
+ * Default: false (tolerant mode - allows superset)
134
+ */
135
+ readonly strict?: boolean;
136
+ /**
137
+ * Database connection. If provided, schemaVerify will connect before executing.
138
+ * If omitted, the client must already be connected.
139
+ * The type is driver-specific (e.g., string URL for Postgres).
140
+ */
141
+ readonly connection?: unknown;
142
+ /** Optional progress callback for observing operation progress */
143
+ readonly onProgress?: OnControlProgress;
144
+ }
145
+
146
+ /**
147
+ * Options for the sign operation.
148
+ */
149
+ export interface SignOptions {
150
+ /** Contract IR or unvalidated JSON - validated at runtime via familyInstance.validateContractIR() */
151
+ readonly contractIR: unknown;
152
+ /**
153
+ * Path to the contract file (for metadata in the result).
154
+ */
155
+ readonly contractPath?: string;
156
+ /**
157
+ * Path to the config file (for metadata in the result).
158
+ */
159
+ readonly configPath?: string;
160
+ /**
161
+ * Database connection. If provided, sign will connect before executing.
162
+ * If omitted, the client must already be connected.
163
+ * The type is driver-specific (e.g., string URL for Postgres).
164
+ */
165
+ readonly connection?: unknown;
166
+ /** Optional progress callback for observing operation progress */
167
+ readonly onProgress?: OnControlProgress;
168
+ }
169
+
170
+ /**
171
+ * Options for the dbInit operation.
172
+ */
173
+ export interface DbInitOptions {
174
+ /** Contract IR or unvalidated JSON - validated at runtime via familyInstance.validateContractIR() */
175
+ readonly contractIR: unknown;
176
+ /**
177
+ * Mode for the dbInit operation.
178
+ * - 'plan': Returns planned operations without applying
179
+ * - 'apply': Applies operations and writes marker
180
+ */
181
+ readonly mode: 'plan' | 'apply';
182
+ /**
183
+ * Database connection. If provided, dbInit will connect before executing.
184
+ * If omitted, the client must already be connected.
185
+ * The type is driver-specific (e.g., string URL for Postgres).
186
+ */
187
+ readonly connection?: unknown;
188
+ /** Optional progress callback for observing operation progress */
189
+ readonly onProgress?: OnControlProgress;
190
+ }
191
+
192
+ /**
193
+ * Options for the introspect operation.
194
+ */
195
+ export interface IntrospectOptions {
196
+ /**
197
+ * Optional schema name to introspect.
198
+ */
199
+ readonly schema?: string;
200
+ /**
201
+ * Database connection. If provided, introspect will connect before executing.
202
+ * If omitted, the client must already be connected.
203
+ * The type is driver-specific (e.g., string URL for Postgres).
204
+ */
205
+ readonly connection?: unknown;
206
+ /** Optional progress callback for observing operation progress */
207
+ readonly onProgress?: OnControlProgress;
208
+ }
209
+
210
+ /**
211
+ * Contract configuration for emit operation.
212
+ */
213
+ export interface EmitContractConfig {
214
+ /**
215
+ * Contract source provider.
216
+ */
217
+ readonly sourceProvider: ContractSourceProvider;
218
+ /**
219
+ * Output path for contract.json.
220
+ * The .d.ts types file will be colocated (e.g., contract.json → contract.d.ts).
221
+ */
222
+ readonly output: string;
223
+ }
224
+
225
+ /**
226
+ * Options for the emit operation.
227
+ */
228
+ export interface EmitOptions {
229
+ /**
230
+ * Contract configuration containing source, output, and types paths.
231
+ */
232
+ readonly contractConfig: EmitContractConfig;
233
+ /** Optional progress callback for observing operation progress */
234
+ readonly onProgress?: OnControlProgress;
235
+ }
236
+
237
+ // ============================================================================
238
+ // Result Types
239
+ // ============================================================================
240
+
241
+ /**
242
+ * Successful dbInit result.
243
+ */
244
+ export interface DbInitSuccess {
245
+ readonly mode: 'plan' | 'apply';
246
+ readonly plan: {
247
+ readonly operations: ReadonlyArray<{
248
+ readonly id: string;
249
+ readonly label: string;
250
+ readonly operationClass: string;
251
+ }>;
252
+ };
253
+ readonly execution?: {
254
+ readonly operationsPlanned: number;
255
+ readonly operationsExecuted: number;
256
+ };
257
+ readonly marker?: {
258
+ readonly storageHash: string;
259
+ readonly profileHash?: string;
260
+ };
261
+ readonly summary: string;
262
+ }
263
+
264
+ /**
265
+ * Failure codes for dbInit operation.
266
+ */
267
+ export type DbInitFailureCode = 'PLANNING_FAILED' | 'MARKER_ORIGIN_MISMATCH' | 'RUNNER_FAILED';
268
+
269
+ /**
270
+ * Failure details for dbInit operation.
271
+ */
272
+ export interface DbInitFailure {
273
+ readonly code: DbInitFailureCode;
274
+ readonly summary: string;
275
+ readonly why: string | undefined;
276
+ readonly conflicts: ReadonlyArray<MigrationPlannerConflict> | undefined;
277
+ readonly meta: Record<string, unknown> | undefined;
278
+ readonly marker?: {
279
+ readonly storageHash?: string;
280
+ readonly profileHash?: string;
281
+ };
282
+ readonly destination?: {
283
+ readonly storageHash: string;
284
+ readonly profileHash?: string | undefined;
285
+ };
286
+ }
287
+
288
+ /**
289
+ * Result type for dbInit operation.
290
+ * Uses Result pattern: success returns DbInitSuccess, failure returns DbInitFailure.
291
+ */
292
+ export type DbInitResult = Result<DbInitSuccess, DbInitFailure>;
293
+
294
+ /**
295
+ * Successful emit result.
296
+ * Contains the hashes and paths of emitted files.
297
+ */
298
+ export interface EmitSuccess {
299
+ /** Storage hash of the emitted contract */
300
+ readonly storageHash: string;
301
+ /** Execution hash of the emitted contract (if execution section exists) */
302
+ readonly executionHash?: string;
303
+ /** Profile hash of the emitted contract (target-specific) */
304
+ readonly profileHash: string;
305
+ /** The emitted contract as JSON string */
306
+ readonly contractJson: string;
307
+ /** The emitted contract TypeScript declarations */
308
+ readonly contractDts: string;
309
+ }
310
+
311
+ /**
312
+ * Failure codes for emit operation.
313
+ */
314
+ export type EmitFailureCode = 'CONTRACT_SOURCE_INVALID' | 'EMIT_FAILED';
315
+
316
+ /**
317
+ * Failure details for emit operation.
318
+ */
319
+ export interface EmitFailure {
320
+ readonly code: EmitFailureCode;
321
+ readonly summary: string;
322
+ readonly why: string | undefined;
323
+ readonly meta: Record<string, unknown> | undefined;
324
+ readonly diagnostics?: ContractSourceDiagnostics;
325
+ }
326
+
327
+ /**
328
+ * Result type for emit operation.
329
+ * Uses Result pattern: success returns EmitSuccess, failure returns EmitFailure.
330
+ */
331
+ export type EmitResult = Result<EmitSuccess, EmitFailure>;
332
+
333
+ // ============================================================================
334
+ // Standalone Contract Emit Types
335
+ // ============================================================================
336
+
337
+ /**
338
+ * Options for the standalone executeContractEmit function.
339
+ * Used by tooling (e.g., Vite plugin) that needs to emit contracts
340
+ * without the full ControlClient infrastructure.
341
+ */
342
+ export interface ContractEmitOptions {
343
+ /** Path to the prisma-next.config.ts file */
344
+ readonly configPath: string;
345
+ /** Optional AbortSignal for cancellation support */
346
+ readonly signal?: AbortSignal;
347
+ }
348
+
349
+ /**
350
+ * Result from the standalone executeContractEmit function.
351
+ */
352
+ export interface ContractEmitResult {
353
+ /** Hash of the storage contract (schema-level) */
354
+ readonly storageHash: string;
355
+ /** Hash of the execution contract (if execution section exists) */
356
+ readonly executionHash?: string;
357
+ /** Hash of the profile (target+extensions) */
358
+ readonly profileHash: string;
359
+ /** Paths to the emitted files */
360
+ readonly files: {
361
+ /** Path to the emitted contract.json file */
362
+ readonly json: string;
363
+ /** Path to the emitted contract.d.ts file */
364
+ readonly dts: string;
365
+ };
366
+ }
367
+
368
+ // ============================================================================
369
+ // Client Interface
370
+ // ============================================================================
371
+
372
+ /**
373
+ * Programmatic control client for Prisma Next operations.
374
+ *
375
+ * Lifecycle: `connect(connection)` before operations, `close()` when done.
376
+ * Both `init()` and `connect()` are auto-called by operations if needed,
377
+ * but `connect()` requires a connection so must be called explicitly first
378
+ * unless a default connection was provided in options.
379
+ *
380
+ * @see README.md "Programmatic Control API" section for usage examples
381
+ */
382
+ export interface ControlClient {
383
+ /**
384
+ * Initializes the client by creating the control plane stack,
385
+ * family instance, and validating framework components.
386
+ *
387
+ * Idempotent (safe to call multiple times).
388
+ * Called automatically by `connect()` if not already initialized.
389
+ */
390
+ init(): void;
391
+
392
+ /**
393
+ * Establishes a database connection.
394
+ * Auto-calls `init()` if not already initialized.
395
+ * Must be called before any database operations unless a default connection
396
+ * was provided in options.
397
+ *
398
+ * @param connection - Driver-specific connection input (e.g., URL string for Postgres).
399
+ * If omitted, uses the default connection from options (if provided).
400
+ * @throws If connection fails, already connected, driver is not configured,
401
+ * or no connection provided and no default connection in options.
402
+ */
403
+ connect(connection?: unknown): Promise<void>;
404
+
405
+ /**
406
+ * Closes the database connection.
407
+ * Idempotent (safe to call multiple times).
408
+ * After close(), can call `connect()` again with same or different URL.
409
+ */
410
+ close(): Promise<void>;
411
+
412
+ /**
413
+ * Verifies database marker matches the contract.
414
+ * Compares storageHash and profileHash.
415
+ *
416
+ * @returns Structured result (ok: false for mismatch, not throwing)
417
+ * @throws If not connected or infrastructure failure
418
+ */
419
+ verify(options: VerifyOptions): Promise<VerifyDatabaseResult>;
420
+
421
+ /**
422
+ * Verifies database schema satisfies the contract requirements.
423
+ *
424
+ * @param options.strict - If true, extra tables/columns are issues. Default: false
425
+ * @returns Structured result with schema issues
426
+ * @throws If not connected or infrastructure failure
427
+ */
428
+ schemaVerify(options: SchemaVerifyOptions): Promise<VerifyDatabaseSchemaResult>;
429
+
430
+ /**
431
+ * Signs the database with a contract marker.
432
+ * Writes or updates the contract marker if schema verification passes.
433
+ * Idempotent (no-op if marker already matches).
434
+ *
435
+ * @returns Structured result
436
+ * @throws If not connected or infrastructure failure
437
+ */
438
+ sign(options: SignOptions): Promise<SignDatabaseResult>;
439
+
440
+ /**
441
+ * Initializes database schema from contract.
442
+ * Uses additive-only policy (no destructive changes).
443
+ *
444
+ * @param options.mode - 'plan' to preview, 'apply' to execute
445
+ * @returns Result pattern: Ok with planned/executed operations, NotOk with failure details
446
+ * @throws If not connected, target doesn't support migrations, or infrastructure failure
447
+ */
448
+ dbInit(options: DbInitOptions): Promise<DbInitResult>;
449
+
450
+ /**
451
+ * Introspects the database schema.
452
+ *
453
+ * @returns Raw schema IR
454
+ * @throws If not connected or infrastructure failure
455
+ */
456
+ introspect(options?: IntrospectOptions): Promise<unknown>;
457
+
458
+ /**
459
+ * Converts a schema IR to a schema view for CLI tree rendering.
460
+ * Delegates to the family instance's toSchemaView method.
461
+ *
462
+ * @param schemaIR - The schema IR from introspect()
463
+ * @returns CoreSchemaView if the family supports it, undefined otherwise
464
+ */
465
+ toSchemaView(schemaIR: unknown): CoreSchemaView | undefined;
466
+
467
+ /**
468
+ * Emits the contract to JSON and TypeScript declarations.
469
+ * This is an offline operation that does NOT require a database connection.
470
+ * Uses `init()` to create the stack but does NOT call `connect()`.
471
+ *
472
+ * @returns Result pattern: Ok with emit details, NotOk with failure details
473
+ */
474
+ emit(options: EmitOptions): Promise<EmitResult>;
475
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Programmatic Control API for Prisma Next.
3
+ *
4
+ * This module exports the control client factory and types for programmatic
5
+ * access to control-plane operations without using the CLI.
6
+ *
7
+ * @see README.md "Programmatic Control API" section for usage examples
8
+ * @module
9
+ */
10
+
11
+ // Re-export core control plane types for consumer convenience
12
+ export type {
13
+ ControlPlaneStack,
14
+ SignDatabaseResult,
15
+ VerifyDatabaseResult,
16
+ VerifyDatabaseSchemaResult,
17
+ } from '@prisma-next/core-control-plane/types';
18
+ // Client factory
19
+ export { createControlClient } from '../control-api/client';
20
+
21
+ // Standalone operations (for tooling that doesn't need full client)
22
+ export { executeContractEmit } from '../control-api/operations/contract-emit';
23
+
24
+ // CLI-specific types
25
+ export type {
26
+ ContractEmitOptions,
27
+ ContractEmitResult,
28
+ ControlActionName,
29
+ ControlClient,
30
+ ControlClientOptions,
31
+ ControlProgressEvent,
32
+ DbInitFailure,
33
+ DbInitFailureCode,
34
+ DbInitOptions,
35
+ DbInitResult,
36
+ DbInitSuccess,
37
+ EmitContractConfig,
38
+ EmitFailure,
39
+ EmitFailureCode,
40
+ EmitOptions,
41
+ EmitResult,
42
+ EmitSuccess,
43
+ IntrospectOptions,
44
+ OnControlProgress,
45
+ SchemaVerifyOptions,
46
+ SignOptions,
47
+ VerifyOptions,
48
+ } from '../control-api/types';
@@ -30,26 +30,31 @@ function validatePurity(value: unknown): void {
30
30
  return;
31
31
  }
32
32
 
33
- const seen = new WeakSet();
33
+ const path = new WeakSet();
34
+
34
35
  function check(value: unknown): void {
35
36
  if (value === null || typeof value !== 'object') {
36
37
  return;
37
38
  }
38
39
 
39
- if (seen.has(value)) {
40
+ if (path.has(value)) {
40
41
  throw new Error('Contract export contains circular references');
41
42
  }
42
- seen.add(value);
43
+ path.add(value);
43
44
 
44
- for (const key in value) {
45
- const descriptor = Object.getOwnPropertyDescriptor(value, key);
46
- if (descriptor && (descriptor.get || descriptor.set)) {
47
- throw new Error(`Contract export contains getter/setter at key "${key}"`);
48
- }
49
- if (descriptor && typeof descriptor.value === 'function') {
50
- throw new Error(`Contract export contains function at key "${key}"`);
45
+ try {
46
+ for (const key in value) {
47
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
48
+ if (descriptor && (descriptor.get || descriptor.set)) {
49
+ throw new Error(`Contract export contains getter/setter at key "${key}"`);
50
+ }
51
+ if (descriptor && typeof descriptor.value === 'function') {
52
+ throw new Error(`Contract export contains function at key "${key}"`);
53
+ }
54
+ check((value as Record<string, unknown>)[key]);
51
55
  }
52
- check((value as Record<string, unknown>)[key]);
56
+ } finally {
57
+ path.delete(value);
53
58
  }
54
59
  }
55
60
 
@@ -10,7 +10,7 @@ export {
10
10
  errorContractConfigMissing,
11
11
  errorContractMissingExtensionPacks,
12
12
  errorContractValidationFailed,
13
- errorDatabaseUrlRequired,
13
+ errorDatabaseConnectionRequired,
14
14
  errorDriverRequired,
15
15
  errorFamilyReadMarkerSqlRequired,
16
16
  errorFileNotFound,
@@ -3,12 +3,7 @@ import {
3
3
  type TargetBoundComponentDescriptor,
4
4
  } from '@prisma-next/contract/framework-components';
5
5
  import type { ContractIR } from '@prisma-next/contract/ir';
6
- import type {
7
- ControlAdapterDescriptor,
8
- ControlExtensionDescriptor,
9
- ControlFamilyDescriptor,
10
- ControlTargetDescriptor,
11
- } from '@prisma-next/core-control-plane/types';
6
+ import type { ControlPlaneStack } from '@prisma-next/core-control-plane/types';
12
7
  import { errorConfigValidation, errorContractMissingExtensionPacks } from './cli-errors';
13
8
 
14
9
  /**
@@ -113,17 +108,14 @@ export function assertFrameworkComponentsCompatible<
113
108
  }
114
109
 
115
110
  /**
116
- * Validates that a contract is compatible with the configured family, target, adapter,
111
+ * Validates that a contract is compatible with the configured target, adapter,
117
112
  * and extension packs. Throws on family/target mismatches or missing extension packs.
118
113
  *
119
114
  * This check ensures the emitted contract matches the CLI config before running
120
115
  * commands that depend on the contract (e.g., db verify, db sign).
121
116
  *
122
117
  * @param contract - The contract IR to validate (must include targetFamily, target, extensionPacks).
123
- * @param family - The configured family descriptor.
124
- * @param target - The configured target descriptor.
125
- * @param adapter - The configured adapter descriptor.
126
- * @param extensionPacks - Optional array of extension descriptors provided by the config.
118
+ * @param stack - The control plane stack (target, adapter, driver, extensionPacks).
127
119
  *
128
120
  * @throws {CliStructuredError} errorConfigValidation when contract.targetFamily or contract.target
129
121
  * doesn't match the configured family/target.
@@ -136,15 +128,10 @@ export function assertFrameworkComponentsCompatible<
136
128
  *
137
129
  * const config = await loadConfig();
138
130
  * const contractIR = await loadContractJson(config.contract.output);
131
+ * const stack = createControlPlaneStack({ target: config.target, adapter: config.adapter, ... });
139
132
  *
140
133
  * // Throws if contract is incompatible with config
141
- * assertContractRequirementsSatisfied({
142
- * contract: contractIR,
143
- * family: config.family,
144
- * target: config.target,
145
- * adapter: config.adapter,
146
- * extensionPacks: config.extensionPacks,
147
- * });
134
+ * assertContractRequirementsSatisfied({ contract: contractIR, stack });
148
135
  * ```
149
136
  */
150
137
  export function assertContractRequirementsSatisfied<
@@ -152,26 +139,20 @@ export function assertContractRequirementsSatisfied<
152
139
  TTargetId extends string,
153
140
  >({
154
141
  contract,
155
- family,
156
- target,
157
- adapter,
158
- extensionPacks,
142
+ stack,
159
143
  }: {
160
144
  readonly contract: Pick<ContractIR, 'targetFamily' | 'target' | 'extensionPacks'>;
161
- readonly family: ControlFamilyDescriptor<TFamilyId>;
162
- readonly target: ControlTargetDescriptor<TFamilyId, TTargetId>;
163
- readonly adapter: ControlAdapterDescriptor<TFamilyId, TTargetId>;
164
- readonly extensionPacks?: readonly ControlExtensionDescriptor<TFamilyId, TTargetId>[] | undefined;
145
+ readonly stack: ControlPlaneStack<TFamilyId, TTargetId>;
165
146
  }): void {
166
- const providedComponentIds = new Set<string>([target.id, adapter.id]);
167
- for (const extension of extensionPacks ?? []) {
147
+ const providedComponentIds = new Set<string>([stack.target.id, stack.adapter.id]);
148
+ for (const extension of stack.extensionPacks) {
168
149
  providedComponentIds.add(extension.id);
169
150
  }
170
151
 
171
152
  const result = checkContractComponentRequirements({
172
153
  contract,
173
- expectedTargetFamily: family.familyId,
174
- expectedTargetId: target.targetId,
154
+ expectedTargetFamily: stack.target.familyId,
155
+ expectedTargetId: stack.target.targetId,
175
156
  providedComponentIds,
176
157
  });
177
158