@prisma-next/cli 0.15.0 → 0.16.0-dev.1

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 (148) hide show
  1. package/dist/cli.d.mts +1 -1
  2. package/dist/cli.mjs +13 -13
  3. package/dist/{client-DigVI8zX.mjs → client-KuBQftxz.mjs} +67 -36
  4. package/dist/client-KuBQftxz.mjs.map +1 -0
  5. package/dist/{command-helpers-CUoKYTlG.mjs → command-helpers-Cpq44aVa.mjs} +21 -5
  6. package/dist/command-helpers-Cpq44aVa.mjs.map +1 -0
  7. package/dist/commands/contract-emit.d.mts +0 -1
  8. package/dist/commands/contract-emit.d.mts.map +1 -1
  9. package/dist/commands/contract-emit.mjs +1 -1
  10. package/dist/commands/contract-infer.d.mts +0 -1
  11. package/dist/commands/contract-infer.d.mts.map +1 -1
  12. package/dist/commands/contract-infer.mjs +1 -1
  13. package/dist/commands/db-init.d.mts +0 -1
  14. package/dist/commands/db-init.d.mts.map +1 -1
  15. package/dist/commands/db-init.mjs +6 -6
  16. package/dist/commands/db-init.mjs.map +1 -1
  17. package/dist/commands/db-schema.d.mts +0 -1
  18. package/dist/commands/db-schema.d.mts.map +1 -1
  19. package/dist/commands/db-schema.mjs +3 -3
  20. package/dist/commands/db-sign.d.mts +0 -1
  21. package/dist/commands/db-sign.d.mts.map +1 -1
  22. package/dist/commands/db-sign.mjs +4 -4
  23. package/dist/commands/db-update.d.mts +0 -1
  24. package/dist/commands/db-update.d.mts.map +1 -1
  25. package/dist/commands/db-update.mjs +5 -5
  26. package/dist/commands/db-update.mjs.map +1 -1
  27. package/dist/commands/db-verify.d.mts +0 -1
  28. package/dist/commands/db-verify.d.mts.map +1 -1
  29. package/dist/commands/db-verify.mjs +1 -1
  30. package/dist/commands/migrate.d.mts +1 -2
  31. package/dist/commands/migrate.d.mts.map +1 -1
  32. package/dist/commands/migrate.mjs +5 -5
  33. package/dist/commands/migration-check.d.mts +1 -2
  34. package/dist/commands/migration-check.d.mts.map +1 -1
  35. package/dist/commands/migration-check.mjs +1 -1
  36. package/dist/commands/migration-graph.d.mts +2 -3
  37. package/dist/commands/migration-graph.d.mts.map +1 -1
  38. package/dist/commands/migration-graph.mjs +3 -3
  39. package/dist/commands/migration-list.d.mts +2 -3
  40. package/dist/commands/migration-list.d.mts.map +1 -1
  41. package/dist/commands/migration-list.mjs +1 -1
  42. package/dist/commands/migration-log.d.mts +2 -3
  43. package/dist/commands/migration-log.d.mts.map +1 -1
  44. package/dist/commands/migration-log.mjs +1 -1
  45. package/dist/commands/migration-new.d.mts +0 -1
  46. package/dist/commands/migration-new.d.mts.map +1 -1
  47. package/dist/commands/migration-new.mjs +3 -3
  48. package/dist/commands/migration-plan.d.mts +0 -1
  49. package/dist/commands/migration-plan.d.mts.map +1 -1
  50. package/dist/commands/migration-plan.mjs +1 -1
  51. package/dist/commands/migration-show.d.mts +0 -1
  52. package/dist/commands/migration-show.d.mts.map +1 -1
  53. package/dist/commands/migration-show.mjs +3 -3
  54. package/dist/commands/migration-status.d.mts +2 -3
  55. package/dist/commands/migration-status.d.mts.map +1 -1
  56. package/dist/commands/migration-status.mjs +1 -1
  57. package/dist/commands/ref.d.mts +1 -2
  58. package/dist/commands/ref.d.mts.map +1 -1
  59. package/dist/commands/ref.mjs +2 -2
  60. package/dist/commands/telemetry/index.d.mts +0 -1
  61. package/dist/commands/telemetry/index.d.mts.map +1 -1
  62. package/dist/commands/telemetry/index.mjs +1 -1
  63. package/dist/{contract-at-errors-D8CNDD7g.mjs → contract-at-errors-D9gEjYtg.mjs} +2 -2
  64. package/dist/{contract-at-errors-D8CNDD7g.mjs.map → contract-at-errors-D9gEjYtg.mjs.map} +1 -1
  65. package/dist/{contract-emit-wgqrOXUI.mjs → contract-emit-CgnXSANN.mjs} +3 -3
  66. package/dist/{contract-emit-wgqrOXUI.mjs.map → contract-emit-CgnXSANN.mjs.map} +1 -1
  67. package/dist/{contract-emit-sYrqzsdN.mjs → contract-emit-cJQBlvGb.mjs} +3 -3
  68. package/dist/{contract-emit-sYrqzsdN.mjs.map → contract-emit-cJQBlvGb.mjs.map} +1 -1
  69. package/dist/{contract-infer-6SQnnVj7.mjs → contract-infer-Cg8evbeg.mjs} +3 -3
  70. package/dist/{contract-infer-6SQnnVj7.mjs.map → contract-infer-Cg8evbeg.mjs.map} +1 -1
  71. package/dist/{contract-space-aggregate-loader-DpB4x97a.mjs → contract-space-aggregate-loader-D_3VeHVb.mjs} +14 -15
  72. package/dist/contract-space-aggregate-loader-D_3VeHVb.mjs.map +1 -0
  73. package/dist/{db-verify-CEsG5Gek.mjs → db-verify-BEclUzki.mjs} +6 -7
  74. package/dist/db-verify-BEclUzki.mjs.map +1 -0
  75. package/dist/exports/control-api.d.mts +1 -2
  76. package/dist/exports/control-api.d.mts.map +1 -1
  77. package/dist/exports/control-api.mjs +2 -2
  78. package/dist/exports/index.d.mts +0 -1
  79. package/dist/exports/index.d.mts.map +1 -1
  80. package/dist/exports/index.mjs +2 -2
  81. package/dist/exports/init-output.d.mts +1 -0
  82. package/dist/exports/init-output.d.mts.map +1 -1
  83. package/dist/{format-CjongNb1.mjs → format-DAUAZqPt.mjs} +2 -2
  84. package/dist/{format-CjongNb1.mjs.map → format-DAUAZqPt.mjs.map} +1 -1
  85. package/dist/{framework-components-BbnM1RYm.mjs → framework-components-CnbUbiJw.mjs} +2 -2
  86. package/dist/{framework-components-BbnM1RYm.mjs.map → framework-components-CnbUbiJw.mjs.map} +1 -1
  87. package/dist/global-flags-BpoOYtNZ.d.mts.map +1 -1
  88. package/dist/{init-D8YFfaD6.mjs → init-C3JhkHg-.mjs} +33 -46
  89. package/dist/init-C3JhkHg-.mjs.map +1 -0
  90. package/dist/{inspect-live-schema-CEt_ztBJ.mjs → inspect-live-schema-CX7EA5C3.mjs} +3 -3
  91. package/dist/{inspect-live-schema-CEt_ztBJ.mjs.map → inspect-live-schema-CX7EA5C3.mjs.map} +1 -1
  92. package/dist/{migration-check-ByeQHOhK.mjs → migration-check-MHT3w7_v.mjs} +24 -24
  93. package/dist/migration-check-MHT3w7_v.mjs.map +1 -0
  94. package/dist/migration-cli.d.mts +0 -1
  95. package/dist/migration-cli.d.mts.map +1 -1
  96. package/dist/migration-cli.mjs +3 -3
  97. package/dist/migration-cli.mjs.map +1 -1
  98. package/dist/{migration-command-scaffold-CJO3tjiR.mjs → migration-command-scaffold-CRyYrHZ9.mjs} +3 -3
  99. package/dist/{migration-command-scaffold-CJO3tjiR.mjs.map → migration-command-scaffold-CRyYrHZ9.mjs.map} +1 -1
  100. package/dist/{migration-list-DTYM8xvm.mjs → migration-list-BAKujUjh.mjs} +3 -3
  101. package/dist/{migration-list-DTYM8xvm.mjs.map → migration-list-BAKujUjh.mjs.map} +1 -1
  102. package/dist/{migration-log-qZ8LpQIl.mjs → migration-log-C_xam2HZ.mjs} +3 -3
  103. package/dist/{migration-log-qZ8LpQIl.mjs.map → migration-log-C_xam2HZ.mjs.map} +1 -1
  104. package/dist/{migration-path-target-DmNwsDgs.mjs → migration-path-target-DpPTsRCg.mjs} +2 -2
  105. package/dist/{migration-path-target-DmNwsDgs.mjs.map → migration-path-target-DpPTsRCg.mjs.map} +1 -1
  106. package/dist/{migration-plan-CfPrB80t.mjs → migration-plan-CreSVtWZ.mjs} +6 -6
  107. package/dist/migration-plan-CreSVtWZ.mjs.map +1 -0
  108. package/dist/{migration-status-DRuS-rP5.mjs → migration-status-YgEvXf-u.mjs} +5 -5
  109. package/dist/{migration-status-DRuS-rP5.mjs.map → migration-status-YgEvXf-u.mjs.map} +1 -1
  110. package/dist/schemas-CeGMYFYX.d.mts.map +1 -1
  111. package/dist/{telemetry-DHBBq5wT.mjs → telemetry-DVr6RFnq.mjs} +2 -2
  112. package/dist/{telemetry-DHBBq5wT.mjs.map → telemetry-DVr6RFnq.mjs.map} +1 -1
  113. package/dist/{terminal-ui-DGRNFWna.d.mts → terminal-ui-BN4QOsm5.d.mts} +1 -2
  114. package/dist/terminal-ui-BN4QOsm5.d.mts.map +1 -0
  115. package/dist/{types-DFoqNIcQ.d.mts → types-D4r_HI9-.d.mts} +8 -4
  116. package/dist/types-D4r_HI9-.d.mts.map +1 -0
  117. package/dist/{verify-DDCb0R2r.mjs → verify-CgDVZjhc.mjs} +9 -8
  118. package/dist/verify-CgDVZjhc.mjs.map +1 -0
  119. package/package.json +25 -25
  120. package/src/commands/db-init.ts +3 -3
  121. package/src/commands/db-update.ts +1 -1
  122. package/src/commands/db-verify.ts +2 -3
  123. package/src/commands/init/errors.ts +62 -58
  124. package/src/commands/init/init.ts +29 -26
  125. package/src/commands/migration-check.ts +7 -7
  126. package/src/commands/migration-plan.ts +1 -1
  127. package/src/control-api/operations/db-run.ts +4 -4
  128. package/src/control-api/operations/db-verify.ts +12 -9
  129. package/src/control-api/operations/migrate.ts +70 -26
  130. package/src/control-api/types.ts +4 -1
  131. package/src/migration-cli.ts +5 -5
  132. package/src/utils/cli-errors.ts +15 -2
  133. package/src/utils/combine-verify-results.ts +1 -1
  134. package/src/utils/contract-space-aggregate-loader.ts +12 -13
  135. package/src/utils/formatters/errors.ts +24 -3
  136. package/src/utils/formatters/verify.ts +7 -6
  137. package/src/utils/integrity-violation-to-check-failure.ts +14 -14
  138. package/src/utils/result-handler.ts +4 -2
  139. package/dist/client-DigVI8zX.mjs.map +0 -1
  140. package/dist/command-helpers-CUoKYTlG.mjs.map +0 -1
  141. package/dist/contract-space-aggregate-loader-DpB4x97a.mjs.map +0 -1
  142. package/dist/db-verify-CEsG5Gek.mjs.map +0 -1
  143. package/dist/init-D8YFfaD6.mjs.map +0 -1
  144. package/dist/migration-check-ByeQHOhK.mjs.map +0 -1
  145. package/dist/migration-plan-CfPrB80t.mjs.map +0 -1
  146. package/dist/terminal-ui-DGRNFWna.d.mts.map +0 -1
  147. package/dist/types-DFoqNIcQ.d.mts.map +0 -1
  148. package/dist/verify-DDCb0R2r.mjs.map +0 -1
@@ -287,7 +287,18 @@ export function errorPathUnreachable(failure: MigrateFailure): CliStructuredErro
287
287
  // Plan-then-apply recovery. The planner destination is the missing edge's
288
288
  // target; `migration plan --to` (built for arbitrary targets) makes this a
289
289
  // real command, so the diagnostic that sends you here is now honest.
290
+ //
291
+ // Never-planned spaces have an EMPTY migration graph, and contract-ref
292
+ // resolution only resolves full hashes against graph nodes — a
293
+ // `--to <hash>` remediation would reject its own input. `migration plan`
294
+ // without `--to` targets the working contract (the same contract the app
295
+ // space's synthesized head ref carries), so the bare form is the one that
296
+ // runs verbatim.
297
+ const neverPlanned = meta['kind'] === 'neverPlanned';
290
298
  const planCommand = (() => {
299
+ if (neverPlanned) {
300
+ return 'prisma-next migration plan --name <slug>';
301
+ }
291
302
  if (planFromHash !== null && targetHash !== null) {
292
303
  return `prisma-next migration plan --from ${planFromHash} --to ${targetHash} --name <slug>`;
293
304
  }
@@ -300,7 +311,9 @@ export function errorPathUnreachable(failure: MigrateFailure): CliStructuredErro
300
311
  return 'prisma-next migration plan';
301
312
  })();
302
313
  const applyCommand =
303
- targetHash !== null ? `prisma-next migrate --to ${targetHash}` : 'prisma-next migrate';
314
+ targetHash !== null && !neverPlanned
315
+ ? `prisma-next migrate --to ${targetHash}`
316
+ : 'prisma-next migrate';
304
317
  return errorRuntime(failure.summary, {
305
318
  why:
306
319
  failure.why ??
@@ -347,7 +360,7 @@ export function mapMigrationToolsError(error: MigrationToolsError): CliStructure
347
360
  * Shared "needs a live database" precondition for read verbs that consult the
348
361
  * marker/ledger (`migration log`, `migration status`). A command needs both a
349
362
  * connection string and a control-plane driver; either missing yields the same
350
- * `PN-CLI-4005` envelope with `meta.missingFlags` (canonical long-form flags
363
+ * `CONFIG.DB_CONNECTION_REQUIRED` envelope with `meta.missingFlags` (canonical long-form flags
351
364
  * per CLI Style Guide §Errors) so callers can react programmatically. Returns
352
365
  * `null` when both are present.
353
366
  */
@@ -78,7 +78,7 @@ export function combineVerifyResults(
78
78
  return {
79
79
  result: {
80
80
  ok,
81
- ...(ok ? {} : { code: appResult.code ?? 'PN-RUN-3010' }),
81
+ ...(ok ? {} : { code: appResult.code ?? 'CONTRACT.MARKER_REQUIRED' }),
82
82
  summary,
83
83
  contract: appResult.contract,
84
84
  target: appResult.target,
@@ -20,7 +20,7 @@ import { toDeclaredExtensionsFromRaw } from './extension-pack-inputs';
20
20
 
21
21
  const CONTRACT_SPACES_DOCS_URL = 'https://pris.ly/contract-spaces';
22
22
 
23
- function contractSpaceError5002(
23
+ function contractSpaceViolationError(
24
24
  summary: string,
25
25
  options: {
26
26
  readonly why: string;
@@ -28,8 +28,7 @@ function contractSpaceError5002(
28
28
  readonly violations: readonly IntegrityViolation[];
29
29
  },
30
30
  ): CliStructuredError {
31
- return new CliStructuredError('5002', summary, {
32
- domain: 'MIG',
31
+ return new CliStructuredError('MIGRATION.CONTRACT_SPACE_VIOLATION', summary, {
33
32
  why: options.why,
34
33
  fix: options.fix,
35
34
  docsUrl: CONTRACT_SPACES_DOCS_URL,
@@ -38,7 +37,7 @@ function contractSpaceError5002(
38
37
  }
39
38
 
40
39
  /**
41
- * Build the `5002` structured-error envelope for a contract-space
40
+ * Build the `MIGRATION.CONTRACT_SPACE_VIOLATION` structured-error envelope for a contract-space
42
41
  * target mismatch. Shared between the declared-extension precheck (the
43
42
  * descriptor's configured target disagrees with the project target) and
44
43
  * the on-disk-contract check surfaced by `checkIntegrity`.
@@ -48,7 +47,7 @@ function targetMismatchError(
48
47
  expected: string,
49
48
  actual: string,
50
49
  ): CliStructuredError {
51
- return contractSpaceError5002(`Contract-space target mismatch for "${spaceId}"`, {
50
+ return contractSpaceViolationError(`Contract-space target mismatch for "${spaceId}"`, {
52
51
  why: `Space "${spaceId}" targets "${actual}" but the project's adapter targets "${expected}".`,
53
52
  fix: 'Update the extension descriptor to target the configured database, or change the project adapter.',
54
53
  violations: [{ kind: 'targetMismatch', spaceId, expected, actual }],
@@ -89,9 +88,10 @@ function describeIntegrityViolation(violation: IntegrityViolation): string {
89
88
  /**
90
89
  * Map the integrity violations `checkIntegrity` reports into a single
91
90
  * CLI structured-error envelope, preserving the error codes the prior
92
- * throw-on-load loader emitted: `5001` (layout drift, bundled) and
93
- * `5002` (target / disjointness / contract-validation / structural
94
- * integrity). Returns `null` when there is nothing to refuse on.
91
+ * throw-on-load loader emitted: `MIGRATION.CONTRACT_SPACE_LAYOUT_VIOLATION`
92
+ * (layout drift, bundled) and `MIGRATION.CONTRACT_SPACE_VIOLATION` (target /
93
+ * disjointness / contract-validation / structural integrity). Returns
94
+ * `null` when there is nothing to refuse on.
95
95
  *
96
96
  * Precedence reproduces the prior loader's first-failure ordering:
97
97
  * layout drift first (every offence bundled into one envelope), then
@@ -113,8 +113,7 @@ export function mapIntegrityViolations(
113
113
  layout.length === 1
114
114
  ? 'Contract-space layout violation detected'
115
115
  : `Contract-space layout violations detected (${layout.length})`;
116
- return new CliStructuredError('5001', summary, {
117
- domain: 'MIG',
116
+ return new CliStructuredError('MIGRATION.CONTRACT_SPACE_LAYOUT_VIOLATION', summary, {
118
117
  why: `The on-disk \`migrations/\` directory and your \`extensionPacks\` declaration are not in agreement.\n${lines.join('\n')}`,
119
118
  fix: 'Declare the extension in `extensionPacks` and re-emit its contract-space artefacts, or remove the orphan `migrations/<space>` directory.',
120
119
  docsUrl: CONTRACT_SPACES_DOCS_URL,
@@ -133,7 +132,7 @@ export function mapIntegrityViolations(
133
132
 
134
133
  const disjointness = violations.find((v) => v.kind === 'disjointness');
135
134
  if (disjointness && disjointness.kind === 'disjointness') {
136
- return contractSpaceError5002(
135
+ return contractSpaceViolationError(
137
136
  `Contract-space disjointness violation: storage element "${disjointness.element}" claimed by multiple spaces`,
138
137
  {
139
138
  why: `Spaces ${disjointness.claimedBy.map((s) => `"${s}"`).join(', ')} all claim the storage element "${disjointness.element}". Each storage element must be owned by exactly one contract space.`,
@@ -145,7 +144,7 @@ export function mapIntegrityViolations(
145
144
 
146
145
  const contractUnreadable = violations.find((v) => v.kind === 'contractUnreadable');
147
146
  if (contractUnreadable && contractUnreadable.kind === 'contractUnreadable') {
148
- return contractSpaceError5002(
147
+ return contractSpaceViolationError(
149
148
  `Contract-space contract validation failed for "${contractUnreadable.spaceId}"`,
150
149
  {
151
150
  why: contractUnreadable.detail,
@@ -160,7 +159,7 @@ export function mapIntegrityViolations(
160
159
  // is still computed; the gate just renders one envelope).
161
160
  const structural = violations[0]!;
162
161
  const spaceId = 'spaceId' in structural ? structural.spaceId : '*';
163
- return contractSpaceError5002(`Contract-space integrity failure for "${spaceId}"`, {
162
+ return contractSpaceViolationError(`Contract-space integrity failure for "${spaceId}"`, {
164
163
  why: describeIntegrityViolation(structural),
165
164
  fix: 'Re-emit the affected migration package(s) or restore the on-disk `migrations/` directory from version control.',
166
165
  violations: [structural],
@@ -7,6 +7,25 @@ import type { GlobalFlags } from '../global-flags';
7
7
  import { createColorFormatter, formatDim, isVerbose } from './helpers';
8
8
  import { formatPlannerWarningsBlock } from './migrations';
9
9
 
10
+ /**
11
+ * The display label for a schema-diff issue in the shared error envelope,
12
+ * derived from which sides are present: expected-only is a missing object,
13
+ * actual-only an extra one, both a mismatch. `undefined` when the entry is not
14
+ * a schema-diff issue (it carries neither side) so the caller can fall through
15
+ * to its generic label.
16
+ */
17
+ function schemaDiffIssueLabel(issue: {
18
+ readonly expected?: unknown;
19
+ readonly actual?: unknown;
20
+ }): 'missing' | 'extra' | 'mismatch' | undefined {
21
+ const hasExpected = issue.expected !== undefined;
22
+ const hasActual = issue.actual !== undefined;
23
+ if (hasExpected && hasActual) return 'mismatch';
24
+ if (hasExpected) return 'missing';
25
+ if (hasActual) return 'extra';
26
+ return undefined;
27
+ }
28
+
10
29
  /**
11
30
  * Formats error output for human-readable display.
12
31
  */
@@ -51,13 +70,15 @@ export function formatErrorOutput(error: CliErrorEnvelope, flags: GlobalFlags):
51
70
  // Show issues list if present (always show a short list; show full list when verbose).
52
71
  // `issues` is a shared error-envelope field: PSL interpretation diagnostics stamp
53
72
  // `kind` and `message` (their diagnostic code and prose); schema-diff issues
54
- // (`SchemaDiffIssue`) carry no `message` and stamp `reason` and `path` instead.
73
+ // (`SchemaDiffIssue`) carry no `message` and stamp `path` plus the
74
+ // `expected`/`actual` presence a label is derived from.
55
75
  if (error.meta?.['issues']) {
56
76
  const issues = error.meta['issues'] as readonly {
57
77
  kind?: string;
58
- reason?: string;
59
78
  message?: string;
60
79
  path?: readonly string[];
80
+ expected?: unknown;
81
+ actual?: unknown;
61
82
  }[];
62
83
  if (issues.length > 0) {
63
84
  const maxToShow = isVerbose(flags, 1) ? issues.length : Math.min(3, issues.length);
@@ -66,7 +87,7 @@ export function formatErrorOutput(error: CliErrorEnvelope, flags: GlobalFlags):
66
87
  : ` Issues (showing ${maxToShow} of ${issues.length}):`;
67
88
  lines.push(`${formatDimText(header)}`);
68
89
  for (const issue of issues.slice(0, maxToShow)) {
69
- const label = issue.kind ?? issue.reason ?? 'issue';
90
+ const label = issue.kind ?? schemaDiffIssueLabel(issue) ?? 'issue';
70
91
  const message = issue.message ?? issue.path?.join('/') ?? '';
71
92
  lines.push(`${formatDimText(` - [${label}] ${message}`)}`);
72
93
  }
@@ -8,13 +8,14 @@ import type {
8
8
  VerifyDatabaseResult,
9
9
  VerifyDatabaseSchemaResult,
10
10
  } from '@prisma-next/framework-components/control';
11
+ import { issueOutcome } from '@prisma-next/framework-components/control';
11
12
  import { ifDefined } from '@prisma-next/utils/defined';
12
13
  import { bold, cyan, dim, green, magenta, red, yellow } from 'colorette';
13
14
  import type { GlobalFlags } from '../global-flags';
14
15
  import { createColorFormatter, formatDim, isVerbose } from './helpers';
15
16
 
16
- /** Human-readable label for each failure reason, prefixed onto an issue's message for display. */
17
- const REASON_LABEL: Record<ExpectationFailureReason, string> = {
17
+ /** Human-readable label for each outcome, prefixed onto an issue's message for display. */
18
+ const OUTCOME_LABEL: Record<ExpectationFailureReason, string> = {
18
19
  'not-found': 'missing',
19
20
  'not-expected': 'extra',
20
21
  'not-equal': 'mismatch',
@@ -22,12 +23,12 @@ const REASON_LABEL: Record<ExpectationFailureReason, string> = {
22
23
 
23
24
  /**
24
25
  * The issue's display text: its own path, prefixed with a human label for
25
- * why it's flagged. Turning `reason` (and the path) into prose is this
26
- * formatter's job, not the differ's — the differ's issue is data (`path` +
27
- * `reason` + nodes), not prose.
26
+ * why it's flagged. Turning the presence-derived outcome (and the path) into
27
+ * prose is this formatter's job, not the differ's — the differ's issue is
28
+ * data (`path` + nodes), not prose.
28
29
  */
29
30
  function formatIssueMessage(issue: SchemaDiffIssue): string {
30
- return `${REASON_LABEL[issue.reason]}: ${issue.path.join('/')}`;
31
+ return `${OUTCOME_LABEL[issueOutcome(issue)]}: ${issue.path.join('/')}`;
31
32
  }
32
33
 
33
34
  // ============================================================================
@@ -14,7 +14,7 @@ function migrationFileRelative(dirPath: string, fileName: string): string {
14
14
 
15
15
  /**
16
16
  * Map one {@link IntegrityViolation} onto a `migration check` failure row.
17
- * Sole catalogue mapping from integrity violations to `PN-MIG-CHECK-*`.
17
+ * Sole catalogue mapping from integrity violations to `MIGRATION.CHECK_*` codes.
18
18
  */
19
19
  export function integrityViolationToCheckFailure(
20
20
  violation: IntegrityViolation,
@@ -31,7 +31,7 @@ export function integrityViolationToCheckFailure(
31
31
  case 'hashMismatch':
32
32
  return {
33
33
  space: violation.spaceId,
34
- code: 'PN-MIG-CHECK-001',
34
+ code: 'MIGRATION.CHECK_HASH_MISMATCH',
35
35
  where: migrationFileRelative(
36
36
  join(migrationsDir, violation.spaceId, violation.dirName),
37
37
  'migration.json',
@@ -42,7 +42,7 @@ export function integrityViolationToCheckFailure(
42
42
  case 'providedInvariantsMismatch':
43
43
  return {
44
44
  space: violation.spaceId,
45
- code: 'PN-MIG-CHECK-002',
45
+ code: 'MIGRATION.CHECK_PROVIDED_INVARIANTS_MISMATCH',
46
46
  where: packageRelative(violation.spaceId, violation.dirName),
47
47
  why: `Migration "${violation.dirName}" providedInvariants in migration.json disagrees with ops.json.`,
48
48
  fix: 'Re-emit the migration package so migration.json and ops.json agree.',
@@ -50,7 +50,7 @@ export function integrityViolationToCheckFailure(
50
50
  case 'packageUnloadable':
51
51
  return {
52
52
  space: violation.spaceId,
53
- code: 'PN-MIG-CHECK-002',
53
+ code: 'MIGRATION.CHECK_PACKAGE_UNLOADABLE',
54
54
  where: packageRelative(violation.spaceId, violation.dirName),
55
55
  why: `Migration "${violation.dirName}" could not be loaded: ${violation.detail}`,
56
56
  fix: 'Re-emit the migration package or restore from version control.',
@@ -58,7 +58,7 @@ export function integrityViolationToCheckFailure(
58
58
  case 'sameSourceAndTarget':
59
59
  return {
60
60
  space: violation.spaceId,
61
- code: 'PN-MIG-CHECK-007',
61
+ code: 'MIGRATION.CHECK_NOOP_SELF_EDGE',
62
62
  where: packageRelative(violation.spaceId, violation.dirName),
63
63
  why: `Migration "${violation.dirName}" in space "${violation.spaceId}" has source equal to target (${violation.hash}) with no data invariant — a true no-op self-edge.`,
64
64
  fix: 'Add a data operation if this self-edge was meant to carry a data invariant, or delete the migration if it is a true no-op.',
@@ -66,7 +66,7 @@ export function integrityViolationToCheckFailure(
66
66
  case 'orphanSpaceDir':
67
67
  return {
68
68
  space: violation.spaceId,
69
- code: 'PN-MIG-CHECK-008',
69
+ code: 'MIGRATION.CHECK_ORPHAN_SPACE_DIR',
70
70
  where: spaceRelative(violation.spaceId),
71
71
  why: `Contract-space directory "${violation.spaceId}" exists on disk but no extension declares it.`,
72
72
  fix: 'Remove the orphan directory, or declare the extension in `extensionPacks`.',
@@ -74,7 +74,7 @@ export function integrityViolationToCheckFailure(
74
74
  case 'declaredButUnmigrated':
75
75
  return {
76
76
  space: violation.spaceId,
77
- code: 'PN-MIG-CHECK-009',
77
+ code: 'MIGRATION.CHECK_DECLARED_BUT_UNMIGRATED',
78
78
  where: spaceRelative(violation.spaceId),
79
79
  why: `Extension "${violation.spaceId}" is declared in \`extensionPacks\` but has no on-disk migrations directory.`,
80
80
  fix: 'Re-emit the extension contract-space artefacts with `prisma-next contract emit` and migration planning, or remove the extension from `extensionPacks` if it is unused.',
@@ -82,7 +82,7 @@ export function integrityViolationToCheckFailure(
82
82
  case 'headRefMissing':
83
83
  return {
84
84
  space: violation.spaceId,
85
- code: 'PN-MIG-CHECK-010',
85
+ code: 'MIGRATION.CHECK_HEAD_REF_MISSING',
86
86
  where: refRelative(violation.spaceId, 'head'),
87
87
  why: `Head ref \`refs/head.json\` is missing for contract space "${violation.spaceId}".`,
88
88
  fix: 'Re-emit the contract-space migrations and head ref artefacts, or restore `refs/head.json` from version control.',
@@ -90,7 +90,7 @@ export function integrityViolationToCheckFailure(
90
90
  case 'headRefNotInGraph':
91
91
  return {
92
92
  space: violation.spaceId,
93
- code: 'PN-MIG-CHECK-011',
93
+ code: 'MIGRATION.CHECK_HEAD_REF_NOT_IN_GRAPH',
94
94
  where: refRelative(violation.spaceId, 'head'),
95
95
  why: `Head ref ${violation.hash} for contract space "${violation.spaceId}" is not present in its migration graph.`,
96
96
  fix: 'Re-emit the contract space migrations, or restore the missing migration package.',
@@ -98,7 +98,7 @@ export function integrityViolationToCheckFailure(
98
98
  case 'refUnreadable':
99
99
  return {
100
100
  space: violation.spaceId,
101
- code: 'PN-MIG-CHECK-012',
101
+ code: 'MIGRATION.CHECK_REF_UNREADABLE',
102
102
  where: refRelative(violation.spaceId, violation.refName),
103
103
  why: `Ref "${violation.refName}" for contract space "${violation.spaceId}" is unreadable: ${violation.detail}`,
104
104
  fix: 'Repair or remove the corrupt ref file.',
@@ -106,7 +106,7 @@ export function integrityViolationToCheckFailure(
106
106
  case 'targetMismatch':
107
107
  return {
108
108
  space: violation.spaceId,
109
- code: 'PN-MIG-CHECK-013',
109
+ code: 'MIGRATION.CHECK_TARGET_MISMATCH',
110
110
  where: spaceRelative(violation.spaceId),
111
111
  why: `Contract space "${violation.spaceId}" targets "${violation.actual}" but the project targets "${violation.expected}".`,
112
112
  fix: 'Update the extension to target the configured database, or change the project target.',
@@ -114,7 +114,7 @@ export function integrityViolationToCheckFailure(
114
114
  case 'disjointness':
115
115
  return {
116
116
  space: 'app',
117
- code: 'PN-MIG-CHECK-014',
117
+ code: 'MIGRATION.CHECK_SPACE_DISJOINTNESS_VIOLATION',
118
118
  where: migrationPathRelative(migrationsDir),
119
119
  why: `Storage element "${violation.element}" is claimed by multiple contract spaces: ${violation.claimedBy.join(', ')}.`,
120
120
  fix: 'Update the contracts so each storage element is owned by exactly one contract space.',
@@ -122,7 +122,7 @@ export function integrityViolationToCheckFailure(
122
122
  case 'contractUnreadable':
123
123
  return {
124
124
  space: violation.spaceId,
125
- code: 'PN-MIG-CHECK-015',
125
+ code: 'MIGRATION.CHECK_CONTRACT_UNREADABLE',
126
126
  where: migrationFileRelative(join(migrationsDir, violation.spaceId), 'contract.json'),
127
127
  why: `Contract for space "${violation.spaceId}" is unreadable: ${violation.detail}`,
128
128
  fix: 'Re-emit the extension contract artefacts, or fix the descriptor producing the invalid contract.',
@@ -130,7 +130,7 @@ export function integrityViolationToCheckFailure(
130
130
  case 'duplicateMigrationHash':
131
131
  return {
132
132
  space: violation.spaceId,
133
- code: 'PN-MIG-CHECK-016',
133
+ code: 'MIGRATION.CHECK_DUPLICATE_MIGRATION_HASH',
134
134
  where: spaceRelative(violation.spaceId),
135
135
  why: `Multiple migrations in space "${violation.spaceId}" share migrationHash "${violation.migrationHash}" (${violation.dirNames.join(', ')}).`,
136
136
  fix: 'Re-emit one of the conflicting packages so each migrationHash is unique.',
@@ -37,7 +37,9 @@ export function handleResult<T>(
37
37
  ui.error(formatErrorOutput(envelope, flags));
38
38
  }
39
39
 
40
- // Infer exit code from error domain: CLI errors = 2, RUN errors = 1
41
- const exitCode = result.failure.domain === 'CLI' ? 2 : 1;
40
+ // Every structured failure reaching this point is an expected failure and
41
+ // exits 2 (PRECONDITION), except a user-declined prompt, which exits 3
42
+ // (USER_ABORTED). See CLI Style Guide § Exit Codes / ADR 239 § Exit codes.
43
+ const exitCode = result.failure.code === 'CLI.INIT_USER_ABORTED' ? 3 : 2;
42
44
  return exitCode;
43
45
  }