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