@prisma-next/cli 0.3.0-dev.11 → 0.3.0-dev.114

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 (215) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +470 -134
  3. package/dist/cli-errors-ByGuoqNj.mjs +3 -0
  4. package/dist/cli-errors-D6HxRn3A.d.mts +2 -0
  5. package/dist/cli.d.mts +1 -0
  6. package/dist/cli.js +1 -2350
  7. package/dist/cli.mjs +235 -0
  8. package/dist/cli.mjs.map +1 -0
  9. package/dist/client-612RJJD_.mjs +1069 -0
  10. package/dist/client-612RJJD_.mjs.map +1 -0
  11. package/dist/commands/contract-emit.d.mts +7 -0
  12. package/dist/commands/contract-emit.d.mts.map +1 -0
  13. package/dist/commands/contract-emit.mjs +4 -0
  14. package/dist/commands/contract-infer.d.mts +7 -0
  15. package/dist/commands/contract-infer.d.mts.map +1 -0
  16. package/dist/commands/contract-infer.mjs +4 -0
  17. package/dist/commands/db-init.d.mts +7 -0
  18. package/dist/commands/db-init.d.mts.map +1 -0
  19. package/dist/commands/db-init.mjs +124 -0
  20. package/dist/commands/db-init.mjs.map +1 -0
  21. package/dist/commands/db-schema.d.mts +7 -0
  22. package/dist/commands/db-schema.d.mts.map +1 -0
  23. package/dist/commands/db-schema.mjs +52 -0
  24. package/dist/commands/db-schema.mjs.map +1 -0
  25. package/dist/commands/db-sign.d.mts +7 -0
  26. package/dist/commands/db-sign.d.mts.map +1 -0
  27. package/dist/commands/db-sign.mjs +135 -0
  28. package/dist/commands/db-sign.mjs.map +1 -0
  29. package/dist/commands/db-update.d.mts +7 -0
  30. package/dist/commands/db-update.d.mts.map +1 -0
  31. package/dist/commands/db-update.mjs +121 -0
  32. package/dist/commands/db-update.mjs.map +1 -0
  33. package/dist/commands/db-verify.d.mts +7 -0
  34. package/dist/commands/db-verify.d.mts.map +1 -0
  35. package/dist/commands/db-verify.mjs +310 -0
  36. package/dist/commands/db-verify.mjs.map +1 -0
  37. package/dist/commands/migration-apply.d.mts +36 -0
  38. package/dist/commands/migration-apply.d.mts.map +1 -0
  39. package/dist/commands/migration-apply.mjs +240 -0
  40. package/dist/commands/migration-apply.mjs.map +1 -0
  41. package/dist/commands/migration-plan.d.mts +47 -0
  42. package/dist/commands/migration-plan.d.mts.map +1 -0
  43. package/dist/commands/migration-plan.mjs +288 -0
  44. package/dist/commands/migration-plan.mjs.map +1 -0
  45. package/dist/commands/migration-ref.d.mts +43 -0
  46. package/dist/commands/migration-ref.d.mts.map +1 -0
  47. package/dist/commands/migration-ref.mjs +194 -0
  48. package/dist/commands/migration-ref.mjs.map +1 -0
  49. package/dist/commands/migration-show.d.mts +28 -0
  50. package/dist/commands/migration-show.d.mts.map +1 -0
  51. package/dist/commands/migration-show.mjs +139 -0
  52. package/dist/commands/migration-show.mjs.map +1 -0
  53. package/dist/commands/migration-status.d.mts +85 -0
  54. package/dist/commands/migration-status.d.mts.map +1 -0
  55. package/dist/commands/migration-status.mjs +4 -0
  56. package/dist/commands/migration-verify.d.mts +16 -0
  57. package/dist/commands/migration-verify.d.mts.map +1 -0
  58. package/dist/commands/migration-verify.mjs +87 -0
  59. package/dist/commands/migration-verify.mjs.map +1 -0
  60. package/dist/config-loader-d_KF19Tw.mjs +43 -0
  61. package/dist/config-loader-d_KF19Tw.mjs.map +1 -0
  62. package/dist/{config-loader.d.ts → config-loader.d.mts} +8 -3
  63. package/dist/config-loader.d.mts.map +1 -0
  64. package/dist/config-loader.mjs +3 -0
  65. package/dist/contract-emit-CVv7dbQ9.mjs +187 -0
  66. package/dist/contract-emit-CVv7dbQ9.mjs.map +1 -0
  67. package/dist/contract-infer-Bvw8u8Eu.mjs +83 -0
  68. package/dist/contract-infer-Bvw8u8Eu.mjs.map +1 -0
  69. package/dist/exports/config-types.d.mts +2 -0
  70. package/dist/exports/config-types.mjs +3 -0
  71. package/dist/exports/control-api.d.mts +626 -0
  72. package/dist/exports/control-api.d.mts.map +1 -0
  73. package/dist/exports/control-api.mjs +107 -0
  74. package/dist/exports/control-api.mjs.map +1 -0
  75. package/dist/{load-ts-contract.d.ts → exports/index.d.mts} +10 -5
  76. package/dist/exports/index.d.mts.map +1 -0
  77. package/dist/exports/index.mjs +130 -0
  78. package/dist/exports/index.mjs.map +1 -0
  79. package/dist/extract-sql-ddl-Jf5blEO0.mjs +26 -0
  80. package/dist/extract-sql-ddl-Jf5blEO0.mjs.map +1 -0
  81. package/dist/framework-components-M2j-qPfr.mjs +59 -0
  82. package/dist/framework-components-M2j-qPfr.mjs.map +1 -0
  83. package/dist/inspect-live-schema-BQe5i4YE.mjs +90 -0
  84. package/dist/inspect-live-schema-BQe5i4YE.mjs.map +1 -0
  85. package/dist/migration-command-scaffold-SLrjcKXS.mjs +104 -0
  86. package/dist/migration-command-scaffold-SLrjcKXS.mjs.map +1 -0
  87. package/dist/migration-status-B7OVZ-Ka.mjs +1576 -0
  88. package/dist/migration-status-B7OVZ-Ka.mjs.map +1 -0
  89. package/dist/migrations-Db_ea9eE.mjs +173 -0
  90. package/dist/migrations-Db_ea9eE.mjs.map +1 -0
  91. package/dist/progress-adapter-DRNe2idZ.mjs +43 -0
  92. package/dist/progress-adapter-DRNe2idZ.mjs.map +1 -0
  93. package/dist/terminal-ui-DAcMBRKf.mjs +980 -0
  94. package/dist/terminal-ui-DAcMBRKf.mjs.map +1 -0
  95. package/dist/verify-DXKxBFvU.mjs +385 -0
  96. package/dist/verify-DXKxBFvU.mjs.map +1 -0
  97. package/package.json +88 -43
  98. package/src/cli.ts +109 -58
  99. package/src/commands/contract-emit.ts +236 -143
  100. package/src/commands/contract-infer-paths.ts +32 -0
  101. package/src/commands/contract-infer.ts +131 -0
  102. package/src/commands/db-init.ts +211 -425
  103. package/src/commands/db-schema.ts +77 -0
  104. package/src/commands/db-sign.ts +207 -228
  105. package/src/commands/db-update.ts +236 -0
  106. package/src/commands/db-verify.ts +484 -186
  107. package/src/commands/inspect-live-schema.ts +171 -0
  108. package/src/commands/migration-apply.ts +416 -0
  109. package/src/commands/migration-plan.ts +451 -0
  110. package/src/commands/migration-ref.ts +305 -0
  111. package/src/commands/migration-show.ts +246 -0
  112. package/src/commands/migration-status.ts +838 -0
  113. package/src/commands/migration-verify.ts +134 -0
  114. package/src/config-loader.ts +13 -3
  115. package/src/control-api/client.ts +614 -0
  116. package/src/control-api/contract-enrichment.ts +135 -0
  117. package/src/control-api/errors.ts +9 -0
  118. package/src/control-api/operations/contract-emit.ts +173 -0
  119. package/src/control-api/operations/db-init.ts +286 -0
  120. package/src/control-api/operations/db-update.ts +221 -0
  121. package/src/control-api/operations/extract-sql-ddl.ts +47 -0
  122. package/src/control-api/operations/migration-apply.ts +194 -0
  123. package/src/control-api/operations/migration-helpers.ts +49 -0
  124. package/src/control-api/types.ts +683 -0
  125. package/src/exports/config-types.ts +4 -3
  126. package/src/exports/control-api.ts +56 -0
  127. package/src/load-ts-contract.ts +16 -11
  128. package/src/utils/cli-errors.ts +5 -2
  129. package/src/utils/command-helpers.ts +293 -3
  130. package/src/utils/formatters/emit.ts +67 -0
  131. package/src/utils/formatters/errors.ts +82 -0
  132. package/src/utils/formatters/graph-migration-mapper.ts +220 -0
  133. package/src/utils/formatters/graph-render.ts +1317 -0
  134. package/src/utils/formatters/graph-types.ts +114 -0
  135. package/src/utils/formatters/help.ts +380 -0
  136. package/src/utils/formatters/helpers.ts +28 -0
  137. package/src/utils/formatters/migrations.ts +346 -0
  138. package/src/utils/formatters/styled.ts +212 -0
  139. package/src/utils/formatters/verify.ts +620 -0
  140. package/src/utils/global-flags.ts +41 -23
  141. package/src/utils/migration-command-scaffold.ts +187 -0
  142. package/src/utils/migration-types.ts +12 -0
  143. package/src/utils/progress-adapter.ts +75 -0
  144. package/src/utils/result-handler.ts +12 -13
  145. package/src/utils/shutdown.ts +92 -0
  146. package/src/utils/suggest-command.ts +31 -0
  147. package/src/utils/terminal-ui.ts +276 -0
  148. package/dist/chunk-BZMBKEEQ.js +0 -997
  149. package/dist/chunk-BZMBKEEQ.js.map +0 -1
  150. package/dist/chunk-CVNWLFXO.js +0 -91
  151. package/dist/chunk-CVNWLFXO.js.map +0 -1
  152. package/dist/chunk-HWYQOCAJ.js +0 -47
  153. package/dist/chunk-HWYQOCAJ.js.map +0 -1
  154. package/dist/chunk-QUPBU4KV.js +0 -131
  155. package/dist/chunk-QUPBU4KV.js.map +0 -1
  156. package/dist/cli.d.ts +0 -2
  157. package/dist/cli.d.ts.map +0 -1
  158. package/dist/cli.js.map +0 -1
  159. package/dist/commands/contract-emit.d.ts +0 -3
  160. package/dist/commands/contract-emit.d.ts.map +0 -1
  161. package/dist/commands/contract-emit.js +0 -9
  162. package/dist/commands/contract-emit.js.map +0 -1
  163. package/dist/commands/db-init.d.ts +0 -3
  164. package/dist/commands/db-init.d.ts.map +0 -1
  165. package/dist/commands/db-init.js +0 -337
  166. package/dist/commands/db-init.js.map +0 -1
  167. package/dist/commands/db-introspect.d.ts +0 -3
  168. package/dist/commands/db-introspect.d.ts.map +0 -1
  169. package/dist/commands/db-introspect.js +0 -186
  170. package/dist/commands/db-introspect.js.map +0 -1
  171. package/dist/commands/db-schema-verify.d.ts +0 -3
  172. package/dist/commands/db-schema-verify.d.ts.map +0 -1
  173. package/dist/commands/db-schema-verify.js +0 -160
  174. package/dist/commands/db-schema-verify.js.map +0 -1
  175. package/dist/commands/db-sign.d.ts +0 -3
  176. package/dist/commands/db-sign.d.ts.map +0 -1
  177. package/dist/commands/db-sign.js +0 -195
  178. package/dist/commands/db-sign.js.map +0 -1
  179. package/dist/commands/db-verify.d.ts +0 -3
  180. package/dist/commands/db-verify.d.ts.map +0 -1
  181. package/dist/commands/db-verify.js +0 -169
  182. package/dist/commands/db-verify.js.map +0 -1
  183. package/dist/config-loader.d.ts.map +0 -1
  184. package/dist/config-loader.js +0 -7
  185. package/dist/config-loader.js.map +0 -1
  186. package/dist/exports/config-types.d.ts +0 -3
  187. package/dist/exports/config-types.d.ts.map +0 -1
  188. package/dist/exports/config-types.js +0 -6
  189. package/dist/exports/config-types.js.map +0 -1
  190. package/dist/exports/index.d.ts +0 -4
  191. package/dist/exports/index.d.ts.map +0 -1
  192. package/dist/exports/index.js +0 -175
  193. package/dist/exports/index.js.map +0 -1
  194. package/dist/load-ts-contract.d.ts.map +0 -1
  195. package/dist/utils/action.d.ts +0 -16
  196. package/dist/utils/action.d.ts.map +0 -1
  197. package/dist/utils/cli-errors.d.ts +0 -7
  198. package/dist/utils/cli-errors.d.ts.map +0 -1
  199. package/dist/utils/command-helpers.d.ts +0 -12
  200. package/dist/utils/command-helpers.d.ts.map +0 -1
  201. package/dist/utils/framework-components.d.ts +0 -70
  202. package/dist/utils/framework-components.d.ts.map +0 -1
  203. package/dist/utils/global-flags.d.ts +0 -25
  204. package/dist/utils/global-flags.d.ts.map +0 -1
  205. package/dist/utils/output.d.ts +0 -142
  206. package/dist/utils/output.d.ts.map +0 -1
  207. package/dist/utils/result-handler.d.ts +0 -15
  208. package/dist/utils/result-handler.d.ts.map +0 -1
  209. package/dist/utils/spinner.d.ts +0 -29
  210. package/dist/utils/spinner.d.ts.map +0 -1
  211. package/src/commands/db-introspect.ts +0 -256
  212. package/src/commands/db-schema-verify.ts +0 -232
  213. package/src/utils/action.ts +0 -43
  214. package/src/utils/output.ts +0 -1471
  215. package/src/utils/spinner.ts +0 -67
@@ -0,0 +1,614 @@
1
+ import type { TargetBoundComponentDescriptor } from '@prisma-next/contract/framework-components';
2
+ import type { ContractIR } from '@prisma-next/contract/ir';
3
+ import type { ContractMarkerRecord } from '@prisma-next/contract/types';
4
+ import type { CoreSchemaView } from '@prisma-next/core-control-plane/schema-view';
5
+ import { createControlPlaneStack } from '@prisma-next/core-control-plane/stack';
6
+ import type {
7
+ ControlDriverInstance,
8
+ ControlFamilyInstance,
9
+ ControlPlaneStack,
10
+ SignDatabaseResult,
11
+ VerifyDatabaseResult,
12
+ VerifyDatabaseSchemaResult,
13
+ } from '@prisma-next/core-control-plane/types';
14
+ import { ifDefined } from '@prisma-next/utils/defined';
15
+ import { notOk, ok } from '@prisma-next/utils/result';
16
+ import { assertFrameworkComponentsCompatible } from '../utils/framework-components';
17
+ import { enrichContractIR } from './contract-enrichment';
18
+ import { ContractValidationError } from './errors';
19
+ import { executeDbInit } from './operations/db-init';
20
+ import { executeDbUpdate } from './operations/db-update';
21
+ import { executeMigrationApply } from './operations/migration-apply';
22
+ import type {
23
+ ControlActionName,
24
+ ControlClient,
25
+ ControlClientOptions,
26
+ DbInitOptions,
27
+ DbInitResult,
28
+ DbUpdateOptions,
29
+ DbUpdateResult,
30
+ EmitOptions,
31
+ EmitResult,
32
+ IntrospectOptions,
33
+ MigrationApplyOptions,
34
+ MigrationApplyResult,
35
+ OnControlProgress,
36
+ SchemaVerifyOptions,
37
+ SignOptions,
38
+ VerifyOptions,
39
+ } from './types';
40
+
41
+ /**
42
+ * Creates a programmatic control client for Prisma Next operations.
43
+ *
44
+ * The client accepts framework component descriptors at creation time,
45
+ * manages driver lifecycle via connect()/close(), and exposes domain
46
+ * operations that delegate to the existing family instance methods.
47
+ *
48
+ * @see {@link ControlClient} for the client interface
49
+ * @see README.md "Programmatic Control API" section for usage examples
50
+ */
51
+ export function createControlClient(options: ControlClientOptions): ControlClient {
52
+ return new ControlClientImpl(options);
53
+ }
54
+
55
+ /**
56
+ * Implementation of ControlClient.
57
+ * Manages initialization and connection state, delegates operations to family instance.
58
+ */
59
+ class ControlClientImpl implements ControlClient {
60
+ private readonly options: ControlClientOptions;
61
+ private stack: ControlPlaneStack<string, string> | null = null;
62
+ private driver: ControlDriverInstance<string, string> | null = null;
63
+ private familyInstance: ControlFamilyInstance<string> | null = null;
64
+ private frameworkComponents: ReadonlyArray<
65
+ TargetBoundComponentDescriptor<string, string>
66
+ > | null = null;
67
+ private initialized = false;
68
+ private readonly defaultConnection: unknown;
69
+
70
+ constructor(options: ControlClientOptions) {
71
+ this.options = options;
72
+ this.defaultConnection = options.connection;
73
+ }
74
+
75
+ init(): void {
76
+ if (this.initialized) {
77
+ return; // Idempotent
78
+ }
79
+
80
+ // Create the control plane stack
81
+ this.stack = createControlPlaneStack({
82
+ target: this.options.target,
83
+ adapter: this.options.adapter,
84
+ driver: this.options.driver,
85
+ extensionPacks: this.options.extensionPacks,
86
+ });
87
+
88
+ // Create family instance using the stack
89
+ this.familyInstance = this.options.family.create(this.stack);
90
+
91
+ // Validate and type-narrow framework components
92
+ const rawComponents = [
93
+ this.options.target,
94
+ this.options.adapter,
95
+ ...(this.options.extensionPacks ?? []),
96
+ ];
97
+ this.frameworkComponents = assertFrameworkComponentsCompatible(
98
+ this.options.family.familyId,
99
+ this.options.target.targetId,
100
+ rawComponents,
101
+ );
102
+
103
+ this.initialized = true;
104
+ }
105
+
106
+ async connect(connection?: unknown): Promise<void> {
107
+ // Auto-init if needed
108
+ this.init();
109
+
110
+ if (this.driver) {
111
+ throw new Error('Already connected. Call close() before reconnecting.');
112
+ }
113
+
114
+ // Resolve connection: argument > default from options
115
+ const resolvedConnection = connection ?? this.defaultConnection;
116
+ if (resolvedConnection === undefined) {
117
+ throw new Error(
118
+ 'No connection provided. Pass a connection to connect() or provide a default connection when creating the client.',
119
+ );
120
+ }
121
+
122
+ // Check for driver descriptor
123
+ if (!this.stack?.driver) {
124
+ throw new Error(
125
+ 'Driver is not configured. Pass a driver descriptor when creating the control client to enable database operations.',
126
+ );
127
+ }
128
+
129
+ // Create driver instance
130
+ // Cast through any since connection type is driver-specific at runtime.
131
+ // The driver descriptor is typed with any for TConnection in ControlClientOptions,
132
+ // but createControlPlaneStack defaults it to string. We bridge this at runtime.
133
+ // biome-ignore lint/suspicious/noExplicitAny: required for runtime connection type flexibility
134
+ this.driver = await this.stack?.driver.create(resolvedConnection as any);
135
+ }
136
+
137
+ async close(): Promise<void> {
138
+ if (this.driver) {
139
+ await this.driver.close();
140
+ this.driver = null;
141
+ }
142
+ }
143
+
144
+ private async ensureConnected(): Promise<{
145
+ driver: ControlDriverInstance<string, string>;
146
+ familyInstance: ControlFamilyInstance<string>;
147
+ frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<string, string>>;
148
+ }> {
149
+ // Auto-init if needed
150
+ this.init();
151
+
152
+ // Auto-connect if not connected and default connection is available
153
+ if (!this.driver && this.defaultConnection !== undefined) {
154
+ await this.connect(this.defaultConnection);
155
+ }
156
+
157
+ if (!this.driver || !this.familyInstance || !this.frameworkComponents) {
158
+ throw new Error('Not connected. Call connect(connection) first.');
159
+ }
160
+ return {
161
+ driver: this.driver,
162
+ familyInstance: this.familyInstance,
163
+ frameworkComponents: this.frameworkComponents,
164
+ };
165
+ }
166
+
167
+ private async connectWithProgress(
168
+ connection: unknown | undefined,
169
+ action: ControlActionName,
170
+ onProgress?: OnControlProgress,
171
+ ): Promise<void> {
172
+ if (connection === undefined) return;
173
+ onProgress?.({
174
+ action,
175
+ kind: 'spanStart',
176
+ spanId: 'connect',
177
+ label: 'Connecting to database...',
178
+ });
179
+ try {
180
+ await this.connect(connection);
181
+ onProgress?.({ action, kind: 'spanEnd', spanId: 'connect', outcome: 'ok' });
182
+ } catch (error) {
183
+ onProgress?.({ action, kind: 'spanEnd', spanId: 'connect', outcome: 'error' });
184
+ throw error;
185
+ }
186
+ }
187
+
188
+ async verify(options: VerifyOptions): Promise<VerifyDatabaseResult> {
189
+ const { onProgress } = options;
190
+ await this.connectWithProgress(options.connection, 'verify', onProgress);
191
+ const { driver, familyInstance } = await this.ensureConnected();
192
+
193
+ // Validate contract using family instance
194
+ let contractIR: ContractIR;
195
+ try {
196
+ contractIR = familyInstance.validateContractIR(options.contractIR);
197
+ } catch (error) {
198
+ const message = error instanceof Error ? error.message : String(error);
199
+ throw new ContractValidationError(message, error);
200
+ }
201
+
202
+ // Emit verify span
203
+ onProgress?.({
204
+ action: 'verify',
205
+ kind: 'spanStart',
206
+ spanId: 'verify',
207
+ label: 'Verifying database marker...',
208
+ });
209
+
210
+ try {
211
+ // Delegate to family instance verify method
212
+ // Note: We pass empty strings for contractPath/configPath since the programmatic
213
+ // API doesn't deal with file paths. The family instance accepts these as optional
214
+ // metadata for error reporting.
215
+ const result = await familyInstance.verify({
216
+ driver,
217
+ contractIR,
218
+ expectedTargetId: this.options.target.targetId,
219
+ contractPath: '',
220
+ });
221
+
222
+ onProgress?.({
223
+ action: 'verify',
224
+ kind: 'spanEnd',
225
+ spanId: 'verify',
226
+ outcome: result.ok ? 'ok' : 'error',
227
+ });
228
+
229
+ return result;
230
+ } catch (error) {
231
+ onProgress?.({
232
+ action: 'verify',
233
+ kind: 'spanEnd',
234
+ spanId: 'verify',
235
+ outcome: 'error',
236
+ });
237
+ throw error;
238
+ }
239
+ }
240
+
241
+ async schemaVerify(options: SchemaVerifyOptions): Promise<VerifyDatabaseSchemaResult> {
242
+ const { onProgress } = options;
243
+ await this.connectWithProgress(options.connection, 'schemaVerify', onProgress);
244
+ const { driver, familyInstance, frameworkComponents } = await this.ensureConnected();
245
+
246
+ // Validate contract using family instance
247
+ let contractIR: ContractIR;
248
+ try {
249
+ contractIR = familyInstance.validateContractIR(options.contractIR);
250
+ } catch (error) {
251
+ const message = error instanceof Error ? error.message : String(error);
252
+ throw new ContractValidationError(message, error);
253
+ }
254
+
255
+ // Emit schemaVerify span
256
+ onProgress?.({
257
+ action: 'schemaVerify',
258
+ kind: 'spanStart',
259
+ spanId: 'schemaVerify',
260
+ label: 'Verifying database schema...',
261
+ });
262
+
263
+ try {
264
+ // Delegate to family instance schemaVerify method
265
+ const result = await familyInstance.schemaVerify({
266
+ driver,
267
+ contractIR,
268
+ strict: options.strict ?? false,
269
+ contractPath: '',
270
+ frameworkComponents,
271
+ });
272
+
273
+ onProgress?.({
274
+ action: 'schemaVerify',
275
+ kind: 'spanEnd',
276
+ spanId: 'schemaVerify',
277
+ outcome: result.ok ? 'ok' : 'error',
278
+ });
279
+
280
+ return result;
281
+ } catch (error) {
282
+ onProgress?.({
283
+ action: 'schemaVerify',
284
+ kind: 'spanEnd',
285
+ spanId: 'schemaVerify',
286
+ outcome: 'error',
287
+ });
288
+ throw error;
289
+ }
290
+ }
291
+
292
+ async sign(options: SignOptions): Promise<SignDatabaseResult> {
293
+ const { onProgress } = options;
294
+ await this.connectWithProgress(options.connection, 'sign', onProgress);
295
+ const { driver, familyInstance } = await this.ensureConnected();
296
+
297
+ // Validate contract using family instance
298
+ let contractIR: ContractIR;
299
+ try {
300
+ contractIR = familyInstance.validateContractIR(options.contractIR);
301
+ } catch (error) {
302
+ const message = error instanceof Error ? error.message : String(error);
303
+ throw new ContractValidationError(message, error);
304
+ }
305
+
306
+ // Emit sign span
307
+ onProgress?.({
308
+ action: 'sign',
309
+ kind: 'spanStart',
310
+ spanId: 'sign',
311
+ label: 'Signing database...',
312
+ });
313
+
314
+ try {
315
+ // Delegate to family instance sign method
316
+ const result = await familyInstance.sign({
317
+ driver,
318
+ contractIR,
319
+ contractPath: options.contractPath ?? '',
320
+ ...ifDefined('configPath', options.configPath),
321
+ });
322
+
323
+ onProgress?.({
324
+ action: 'sign',
325
+ kind: 'spanEnd',
326
+ spanId: 'sign',
327
+ outcome: 'ok',
328
+ });
329
+
330
+ return result;
331
+ } catch (error) {
332
+ onProgress?.({
333
+ action: 'sign',
334
+ kind: 'spanEnd',
335
+ spanId: 'sign',
336
+ outcome: 'error',
337
+ });
338
+ throw error;
339
+ }
340
+ }
341
+
342
+ async dbInit(options: DbInitOptions): Promise<DbInitResult> {
343
+ const { onProgress } = options;
344
+ await this.connectWithProgress(options.connection, 'dbInit', onProgress);
345
+ const { driver, familyInstance, frameworkComponents } = await this.ensureConnected();
346
+
347
+ // Check target supports migrations
348
+ if (!this.options.target.migrations) {
349
+ throw new Error(`Target "${this.options.target.targetId}" does not support migrations`);
350
+ }
351
+
352
+ // Validate contract using family instance
353
+ let contractIR: ContractIR;
354
+ try {
355
+ contractIR = familyInstance.validateContractIR(options.contractIR);
356
+ } catch (error) {
357
+ const message = error instanceof Error ? error.message : String(error);
358
+ throw new ContractValidationError(message, error);
359
+ }
360
+
361
+ // Delegate to extracted dbInit operation
362
+ return executeDbInit({
363
+ driver,
364
+ familyInstance,
365
+ contractIR,
366
+ mode: options.mode,
367
+ migrations: this.options.target.migrations,
368
+ frameworkComponents,
369
+ ...ifDefined('onProgress', onProgress),
370
+ });
371
+ }
372
+
373
+ async dbUpdate(options: DbUpdateOptions): Promise<DbUpdateResult> {
374
+ const { onProgress } = options;
375
+ await this.connectWithProgress(options.connection, 'dbUpdate', onProgress);
376
+ const { driver, familyInstance, frameworkComponents } = await this.ensureConnected();
377
+
378
+ if (!this.options.target.migrations) {
379
+ throw new Error(`Target "${this.options.target.targetId}" does not support migrations`);
380
+ }
381
+
382
+ let contractIR: ContractIR;
383
+ try {
384
+ contractIR = familyInstance.validateContractIR(options.contractIR);
385
+ } catch (error) {
386
+ const message = error instanceof Error ? error.message : String(error);
387
+ throw new ContractValidationError(message, error);
388
+ }
389
+
390
+ return executeDbUpdate({
391
+ driver,
392
+ familyInstance,
393
+ contractIR,
394
+ mode: options.mode,
395
+ migrations: this.options.target.migrations,
396
+ frameworkComponents,
397
+ ...ifDefined('acceptDataLoss', options.acceptDataLoss),
398
+ ...ifDefined('onProgress', onProgress),
399
+ });
400
+ }
401
+
402
+ async readMarker(): Promise<ContractMarkerRecord | null> {
403
+ const { driver, familyInstance } = await this.ensureConnected();
404
+ return familyInstance.readMarker({ driver });
405
+ }
406
+
407
+ async migrationApply(options: MigrationApplyOptions): Promise<MigrationApplyResult> {
408
+ const { onProgress } = options;
409
+ await this.connectWithProgress(options.connection, 'migrationApply', onProgress);
410
+ const { driver, familyInstance, frameworkComponents } = await this.ensureConnected();
411
+
412
+ if (!this.options.target.migrations) {
413
+ throw new Error(`Target "${this.options.target.targetId}" does not support migrations`);
414
+ }
415
+
416
+ return executeMigrationApply({
417
+ driver,
418
+ familyInstance,
419
+ originHash: options.originHash,
420
+ destinationHash: options.destinationHash,
421
+ pendingMigrations: options.pendingMigrations,
422
+ migrations: this.options.target.migrations,
423
+ frameworkComponents,
424
+ targetId: this.options.target.targetId,
425
+ ...(onProgress ? { onProgress } : {}),
426
+ });
427
+ }
428
+
429
+ async introspect(options?: IntrospectOptions): Promise<unknown> {
430
+ const onProgress = options?.onProgress;
431
+ await this.connectWithProgress(options?.connection, 'introspect', onProgress);
432
+ const { driver, familyInstance } = await this.ensureConnected();
433
+
434
+ // TODO: Pass schema option to familyInstance.introspect when schema filtering is implemented
435
+ const _schema = options?.schema;
436
+ void _schema;
437
+
438
+ // Emit introspect span
439
+ onProgress?.({
440
+ action: 'introspect',
441
+ kind: 'spanStart',
442
+ spanId: 'introspect',
443
+ label: 'Introspecting database schema...',
444
+ });
445
+
446
+ try {
447
+ const result = await familyInstance.introspect({ driver });
448
+
449
+ onProgress?.({
450
+ action: 'introspect',
451
+ kind: 'spanEnd',
452
+ spanId: 'introspect',
453
+ outcome: 'ok',
454
+ });
455
+
456
+ return result;
457
+ } catch (error) {
458
+ onProgress?.({
459
+ action: 'introspect',
460
+ kind: 'spanEnd',
461
+ spanId: 'introspect',
462
+ outcome: 'error',
463
+ });
464
+ throw error;
465
+ }
466
+ }
467
+
468
+ toSchemaView(schemaIR: unknown): CoreSchemaView | undefined {
469
+ this.init();
470
+ if (this.familyInstance?.toSchemaView) {
471
+ return this.familyInstance.toSchemaView(schemaIR);
472
+ }
473
+ return undefined;
474
+ }
475
+
476
+ async emit(options: EmitOptions): Promise<EmitResult> {
477
+ const { onProgress, contractConfig } = options;
478
+
479
+ // Ensure initialized (creates stack and family instance)
480
+ // emit() does NOT require a database connection
481
+ this.init();
482
+
483
+ if (!this.familyInstance) {
484
+ throw new Error('Family instance was not initialized. This is a bug.');
485
+ }
486
+
487
+ let contractRaw: unknown;
488
+ onProgress?.({
489
+ action: 'emit',
490
+ kind: 'spanStart',
491
+ spanId: 'resolveSource',
492
+ label: 'Resolving contract source...',
493
+ });
494
+
495
+ try {
496
+ const sourceContext = {
497
+ composedExtensionPacks: (this.options.extensionPacks ?? []).map((p) => p.id),
498
+ };
499
+ const providerResult = await contractConfig.sourceProvider(sourceContext);
500
+ if (!providerResult.ok) {
501
+ onProgress?.({
502
+ action: 'emit',
503
+ kind: 'spanEnd',
504
+ spanId: 'resolveSource',
505
+ outcome: 'error',
506
+ });
507
+
508
+ return notOk({
509
+ code: 'CONTRACT_SOURCE_INVALID',
510
+ summary: providerResult.failure.summary,
511
+ why: providerResult.failure.summary,
512
+ meta: providerResult.failure.meta,
513
+ diagnostics: providerResult.failure,
514
+ });
515
+ }
516
+ contractRaw = providerResult.value;
517
+
518
+ onProgress?.({
519
+ action: 'emit',
520
+ kind: 'spanEnd',
521
+ spanId: 'resolveSource',
522
+ outcome: 'ok',
523
+ });
524
+ } catch (error) {
525
+ onProgress?.({
526
+ action: 'emit',
527
+ kind: 'spanEnd',
528
+ spanId: 'resolveSource',
529
+ outcome: 'error',
530
+ });
531
+
532
+ const message = error instanceof Error ? error.message : String(error);
533
+ return notOk({
534
+ code: 'CONTRACT_SOURCE_INVALID',
535
+ summary: 'Failed to resolve contract source',
536
+ why: message,
537
+ diagnostics: {
538
+ summary: 'Contract source provider threw an exception',
539
+ diagnostics: [
540
+ {
541
+ code: 'PROVIDER_THROW',
542
+ message,
543
+ },
544
+ ],
545
+ },
546
+ meta: undefined,
547
+ });
548
+ }
549
+
550
+ // Emit contract
551
+ onProgress?.({
552
+ action: 'emit',
553
+ kind: 'spanStart',
554
+ spanId: 'emit',
555
+ label: 'Emitting contract...',
556
+ });
557
+
558
+ try {
559
+ const enrichedIR = enrichContractIR(
560
+ contractRaw as ContractIR,
561
+ this.frameworkComponents ?? [],
562
+ );
563
+
564
+ try {
565
+ this.familyInstance.validateContractIR(enrichedIR);
566
+ } catch (error) {
567
+ onProgress?.({
568
+ action: 'emit',
569
+ kind: 'spanEnd',
570
+ spanId: 'emit',
571
+ outcome: 'error',
572
+ });
573
+ const message = error instanceof Error ? error.message : String(error);
574
+ return notOk({
575
+ code: 'CONTRACT_VALIDATION_FAILED',
576
+ summary: 'Contract validation failed',
577
+ why: message,
578
+ meta: undefined,
579
+ });
580
+ }
581
+
582
+ const emitResult = await this.familyInstance.emitContract({ contractIR: enrichedIR });
583
+
584
+ onProgress?.({
585
+ action: 'emit',
586
+ kind: 'spanEnd',
587
+ spanId: 'emit',
588
+ outcome: 'ok',
589
+ });
590
+
591
+ return ok({
592
+ storageHash: emitResult.storageHash,
593
+ ...ifDefined('executionHash', emitResult.executionHash),
594
+ profileHash: emitResult.profileHash,
595
+ contractJson: emitResult.contractJson,
596
+ contractDts: emitResult.contractDts,
597
+ });
598
+ } catch (error) {
599
+ onProgress?.({
600
+ action: 'emit',
601
+ kind: 'spanEnd',
602
+ spanId: 'emit',
603
+ outcome: 'error',
604
+ });
605
+
606
+ return notOk({
607
+ code: 'EMIT_FAILED',
608
+ summary: 'Failed to emit contract',
609
+ why: error instanceof Error ? error.message : String(error),
610
+ meta: undefined,
611
+ });
612
+ }
613
+ }
614
+ }