@prisma-next/cli 0.3.0-dev.45 → 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.
@@ -506,7 +506,6 @@ class ControlClientImpl implements ControlClient {
506
506
  throw new Error('Family instance was not initialized. This is a bug.');
507
507
  }
508
508
 
509
- // Resolve contract source
510
509
  let contractRaw: unknown;
511
510
  onProgress?.({
512
511
  action: 'emit',
@@ -516,14 +515,24 @@ class ControlClientImpl implements ControlClient {
516
515
  });
517
516
 
518
517
  try {
519
- switch (contractConfig.source.kind) {
520
- case 'loader':
521
- contractRaw = await contractConfig.source.load();
522
- break;
523
- case 'value':
524
- contractRaw = contractConfig.source.value;
525
- break;
518
+ const providerResult = await contractConfig.sourceProvider();
519
+ if (!providerResult.ok) {
520
+ onProgress?.({
521
+ action: 'emit',
522
+ kind: 'spanEnd',
523
+ spanId: 'resolveSource',
524
+ outcome: 'error',
525
+ });
526
+
527
+ return notOk({
528
+ code: 'CONTRACT_SOURCE_INVALID',
529
+ summary: providerResult.failure.summary,
530
+ why: providerResult.failure.summary,
531
+ meta: providerResult.failure.meta,
532
+ diagnostics: providerResult.failure,
533
+ });
526
534
  }
535
+ contractRaw = providerResult.value;
527
536
 
528
537
  onProgress?.({
529
538
  action: 'emit',
@@ -539,10 +548,20 @@ class ControlClientImpl implements ControlClient {
539
548
  outcome: 'error',
540
549
  });
541
550
 
551
+ const message = error instanceof Error ? error.message : String(error);
542
552
  return notOk({
543
553
  code: 'CONTRACT_SOURCE_INVALID',
544
554
  summary: 'Failed to resolve contract source',
545
- why: error instanceof Error ? error.message : String(error),
555
+ why: message,
556
+ diagnostics: {
557
+ summary: 'Contract source provider threw an exception',
558
+ diagnostics: [
559
+ {
560
+ code: 'PROVIDER_THROW',
561
+ message,
562
+ },
563
+ ],
564
+ },
546
565
  meta: undefined,
547
566
  });
548
567
  }
@@ -4,9 +4,29 @@ import { abortable } from '@prisma-next/utils/abortable';
4
4
  import { ifDefined } from '@prisma-next/utils/defined';
5
5
  import { dirname, isAbsolute, join, resolve } from 'pathe';
6
6
  import { loadConfig } from '../../config-loader';
7
- import { errorContractConfigMissing } from '../../utils/cli-errors';
7
+ import { errorContractConfigMissing, errorRuntime } from '../../utils/cli-errors';
8
8
  import type { ContractEmitOptions, ContractEmitResult } from '../types';
9
9
 
10
+ interface ProviderFailureLike {
11
+ readonly summary: string;
12
+ readonly diagnostics: readonly unknown[];
13
+ readonly meta?: unknown;
14
+ }
15
+
16
+ function isRecord(value: unknown): value is Record<string, unknown> {
17
+ return typeof value === 'object' && value !== null;
18
+ }
19
+
20
+ function isAbortError(error: unknown): boolean {
21
+ return isRecord(error) && typeof error['name'] === 'string' && error['name'] === 'AbortError';
22
+ }
23
+
24
+ function isProviderFailureLike(value: unknown): value is ProviderFailureLike {
25
+ return (
26
+ isRecord(value) && typeof value['summary'] === 'string' && Array.isArray(value['diagnostics'])
27
+ );
28
+ }
29
+
10
30
  /**
11
31
  * Executes the contract emit operation.
12
32
  *
@@ -54,13 +74,10 @@ export async function executeContractEmit(
54
74
  });
55
75
  }
56
76
 
57
- // Validate source is defined and is either a function or a non-null object
58
- if (
59
- contractConfig.source === null ||
60
- (typeof contractConfig.source !== 'function' && typeof contractConfig.source !== 'object')
61
- ) {
77
+ // Validate source exists and is callable
78
+ if (typeof contractConfig.source !== 'function') {
62
79
  throw errorContractConfigMissing({
63
- why: 'Contract config must include a valid source (function or non-null object)',
80
+ why: 'Contract config must include a valid source provider function',
64
81
  });
65
82
  }
66
83
 
@@ -73,18 +90,59 @@ export async function executeContractEmit(
73
90
  // Colocate .d.ts with .json (contract.json → contract.d.ts)
74
91
  const outputDtsPath = `${outputJsonPath.slice(0, -5)}.d.ts`;
75
92
 
93
+ let providerResult: Awaited<ReturnType<typeof contractConfig.source>>;
94
+ try {
95
+ providerResult = await unlessAborted(contractConfig.source());
96
+ } catch (error) {
97
+ if (signal.aborted || isAbortError(error)) {
98
+ throw error;
99
+ }
100
+ throw errorRuntime('Failed to resolve contract source', {
101
+ why: error instanceof Error ? error.message : String(error),
102
+ fix: 'Ensure contract.source resolves to ok(contractIR) or returns structured diagnostics.',
103
+ });
104
+ }
105
+
106
+ if (!isRecord(providerResult) || typeof providerResult.ok !== 'boolean') {
107
+ throw errorRuntime('Failed to resolve contract source', {
108
+ why: 'Contract source provider returned malformed result shape.',
109
+ fix: 'Ensure contract.source resolves to ok(contractIR) or notOk({ summary, diagnostics }).',
110
+ });
111
+ }
112
+
113
+ if (providerResult.ok && !('value' in providerResult)) {
114
+ throw errorRuntime('Failed to resolve contract source', {
115
+ why: 'Contract source provider returned malformed success result: missing value.',
116
+ fix: 'Ensure contract.source success payload is ok(contractIR).',
117
+ });
118
+ }
119
+
120
+ if (!providerResult.ok && !isProviderFailureLike(providerResult.failure)) {
121
+ throw errorRuntime('Failed to resolve contract source', {
122
+ why: 'Contract source provider returned malformed failure result: expected summary and diagnostics.',
123
+ fix: 'Ensure contract.source failure payload is notOk({ summary, diagnostics, meta? }).',
124
+ });
125
+ }
126
+
127
+ if (!providerResult.ok) {
128
+ throw errorRuntime('Failed to resolve contract source', {
129
+ why: providerResult.failure.summary,
130
+ fix: 'Fix contract source diagnostics and return ok(contractIR).',
131
+ meta: {
132
+ diagnostics: providerResult.failure.diagnostics,
133
+ ...ifDefined('providerMeta', providerResult.failure.meta),
134
+ },
135
+ });
136
+ }
137
+
76
138
  // Create control plane stack from config
77
139
  const stack = createControlPlaneStack(config);
78
140
  const familyInstance = config.family.create(stack);
79
141
 
80
- // Resolve contract source from config
81
- const contractRaw =
82
- typeof contractConfig.source === 'function'
83
- ? await unlessAborted(contractConfig.source())
84
- : contractConfig.source;
85
-
86
142
  // Emit contract via family instance
87
- const emitResult = await unlessAborted(familyInstance.emitContract({ contractIR: contractRaw }));
143
+ const emitResult = await unlessAborted(
144
+ familyInstance.emitContract({ contractIR: providerResult.value }),
145
+ );
88
146
 
89
147
  // Create directory if needed and write files (both colocated)
90
148
  await unlessAborted(mkdir(dirname(outputJsonPath), { recursive: true }));
@@ -1,3 +1,7 @@
1
+ import type {
2
+ ContractSourceDiagnostics,
3
+ ContractSourceProvider,
4
+ } from '@prisma-next/core-control-plane/config-types';
1
5
  import type { CoreSchemaView } from '@prisma-next/core-control-plane/schema-view';
2
6
  import type {
3
7
  ControlAdapterDescriptor,
@@ -203,37 +207,14 @@ export interface IntrospectOptions {
203
207
  readonly onProgress?: OnControlProgress;
204
208
  }
205
209
 
206
- /**
207
- * Contract source as a raw value (any JSON-serializable value).
208
- */
209
- export interface ContractSourceValue {
210
- readonly kind: 'value';
211
- readonly value: unknown;
212
- }
213
-
214
- /**
215
- * Contract source as a lazy loader function.
216
- */
217
- export interface ContractSourceLoader {
218
- readonly kind: 'loader';
219
- readonly load: () => unknown | Promise<unknown>;
220
- }
221
-
222
- /**
223
- * Discriminated union for contract source.
224
- * Use `kind` to determine how to resolve the contract.
225
- */
226
- export type EmitContractSource = ContractSourceValue | ContractSourceLoader;
227
-
228
210
  /**
229
211
  * Contract configuration for emit operation.
230
212
  */
231
213
  export interface EmitContractConfig {
232
214
  /**
233
- * Contract source - either a raw value or a loader function.
234
- * Switch on `source.kind` to determine how to resolve.
215
+ * Contract source provider.
235
216
  */
236
- readonly source: EmitContractSource;
217
+ readonly sourceProvider: ContractSourceProvider;
237
218
  /**
238
219
  * Output path for contract.json.
239
220
  * The .d.ts types file will be colocated (e.g., contract.json → contract.d.ts).
@@ -340,6 +321,7 @@ export interface EmitFailure {
340
321
  readonly summary: string;
341
322
  readonly why: string | undefined;
342
323
  readonly meta: Record<string, unknown> | undefined;
324
+ readonly diagnostics?: ContractSourceDiagnostics;
343
325
  }
344
326
 
345
327
  /**
@@ -25,8 +25,6 @@ export { executeContractEmit } from '../control-api/operations/contract-emit';
25
25
  export type {
26
26
  ContractEmitOptions,
27
27
  ContractEmitResult,
28
- ContractSourceLoader,
29
- ContractSourceValue,
30
28
  ControlActionName,
31
29
  ControlClient,
32
30
  ControlClientOptions,
@@ -37,7 +35,6 @@ export type {
37
35
  DbInitResult,
38
36
  DbInitSuccess,
39
37
  EmitContractConfig,
40
- EmitContractSource,
41
38
  EmitFailure,
42
39
  EmitFailureCode,
43
40
  EmitOptions,