@prisma-next/cli 0.16.0-dev.3 → 0.16.0-dev.31

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 (173) hide show
  1. package/README.md +44 -45
  2. package/dist/cli.mjs +13 -13
  3. package/dist/{client-KuBQftxz.mjs → client-B1UjQc_x.mjs} +50 -58
  4. package/dist/client-B1UjQc_x.mjs.map +1 -0
  5. package/dist/{command-helpers-Cpq44aVa.mjs → command-helpers-D-jBl0Qt.mjs} +16 -8
  6. package/dist/command-helpers-D-jBl0Qt.mjs.map +1 -0
  7. package/dist/commands/contract-emit.mjs +1 -1
  8. package/dist/commands/contract-infer.mjs +1 -1
  9. package/dist/commands/db-init.d.mts.map +1 -1
  10. package/dist/commands/db-init.mjs +8 -6
  11. package/dist/commands/db-init.mjs.map +1 -1
  12. package/dist/commands/db-schema.mjs +3 -3
  13. package/dist/commands/db-sign.d.mts.map +1 -1
  14. package/dist/commands/db-sign.mjs +11 -12
  15. package/dist/commands/db-sign.mjs.map +1 -1
  16. package/dist/commands/db-update.d.mts.map +1 -1
  17. package/dist/commands/db-update.mjs +14 -15
  18. package/dist/commands/db-update.mjs.map +1 -1
  19. package/dist/commands/db-verify.mjs +1 -1
  20. package/dist/commands/migrate.d.mts +1 -1
  21. package/dist/commands/migrate.d.mts.map +1 -1
  22. package/dist/commands/migrate.mjs +13 -13
  23. package/dist/commands/migrate.mjs.map +1 -1
  24. package/dist/commands/migration-check.d.mts +4 -2
  25. package/dist/commands/migration-check.d.mts.map +1 -1
  26. package/dist/commands/migration-check.mjs +1 -1
  27. package/dist/commands/migration-graph.d.mts +1 -1
  28. package/dist/commands/migration-graph.mjs +4 -4
  29. package/dist/commands/migration-list.d.mts +1 -1
  30. package/dist/commands/migration-list.d.mts.map +1 -1
  31. package/dist/commands/migration-list.mjs +1 -1
  32. package/dist/commands/migration-log.d.mts +1 -1
  33. package/dist/commands/migration-log.mjs +1 -1
  34. package/dist/commands/migration-new.d.mts.map +1 -1
  35. package/dist/commands/migration-new.mjs +16 -40
  36. package/dist/commands/migration-new.mjs.map +1 -1
  37. package/dist/commands/migration-plan.d.mts +3 -4
  38. package/dist/commands/migration-plan.d.mts.map +1 -1
  39. package/dist/commands/migration-plan.mjs +1 -1
  40. package/dist/commands/migration-show.mjs +5 -5
  41. package/dist/commands/migration-show.mjs.map +1 -1
  42. package/dist/commands/migration-status.d.mts +1 -1
  43. package/dist/commands/migration-status.d.mts.map +1 -1
  44. package/dist/commands/migration-status.mjs +1 -1
  45. package/dist/commands/ref.d.mts +1 -1
  46. package/dist/commands/ref.d.mts.map +1 -1
  47. package/dist/commands/ref.mjs +13 -18
  48. package/dist/commands/ref.mjs.map +1 -1
  49. package/dist/commands/telemetry/index.mjs +1 -1
  50. package/dist/contract-at-errors-DvZ_DzJK.mjs +36 -0
  51. package/dist/contract-at-errors-DvZ_DzJK.mjs.map +1 -0
  52. package/dist/{contract-emit-CgnXSANN.mjs → contract-emit-BT46Mu8Q.mjs} +3 -3
  53. package/dist/{contract-emit-CgnXSANN.mjs.map → contract-emit-BT46Mu8Q.mjs.map} +1 -1
  54. package/dist/{contract-emit-cJQBlvGb.mjs → contract-emit-hVdlaF2U.mjs} +6 -7
  55. package/dist/{contract-emit-cJQBlvGb.mjs.map → contract-emit-hVdlaF2U.mjs.map} +1 -1
  56. package/dist/{contract-enrichment-gn9sWbPw.mjs → contract-enrichment-JUhLv-BX.mjs} +8 -8
  57. package/dist/{contract-enrichment-gn9sWbPw.mjs.map → contract-enrichment-JUhLv-BX.mjs.map} +1 -1
  58. package/dist/{contract-infer-Cg8evbeg.mjs → contract-infer-DpqTQECL.mjs} +3 -3
  59. package/dist/{contract-infer-Cg8evbeg.mjs.map → contract-infer-DpqTQECL.mjs.map} +1 -1
  60. package/dist/{contract-space-aggregate-loader-D_3VeHVb.mjs → contract-space-aggregate-loader-CNd-ZB6C.mjs} +14 -14
  61. package/dist/contract-space-aggregate-loader-CNd-ZB6C.mjs.map +1 -0
  62. package/dist/{db-verify-BEclUzki.mjs → db-verify-FEN6k1m7.mjs} +10 -8
  63. package/dist/db-verify-FEN6k1m7.mjs.map +1 -0
  64. package/dist/exports/control-api.d.mts +4 -4
  65. package/dist/exports/control-api.d.mts.map +1 -1
  66. package/dist/exports/control-api.mjs +3 -3
  67. package/dist/exports/index.d.mts +2 -1
  68. package/dist/exports/index.d.mts.map +1 -1
  69. package/dist/exports/index.mjs +61 -21
  70. package/dist/exports/index.mjs.map +1 -1
  71. package/dist/exports/init-output.d.mts.map +1 -1
  72. package/dist/exports/init-output.mjs +1 -1
  73. package/dist/{format-DAUAZqPt.mjs → format-DI5qVr22.mjs} +8 -7
  74. package/dist/format-DI5qVr22.mjs.map +1 -0
  75. package/dist/{framework-components-CnbUbiJw.mjs → framework-components-C4XACYNB.mjs} +3 -3
  76. package/dist/framework-components-C4XACYNB.mjs.map +1 -0
  77. package/dist/{init-C3JhkHg-.mjs → init-ClrRkbwd.mjs} +77 -67
  78. package/dist/init-ClrRkbwd.mjs.map +1 -0
  79. package/dist/{inspect-live-schema-CX7EA5C3.mjs → inspect-live-schema-C0Rgdtw_.mjs} +4 -4
  80. package/dist/{inspect-live-schema-CX7EA5C3.mjs.map → inspect-live-schema-C0Rgdtw_.mjs.map} +1 -1
  81. package/dist/{migration-check-MHT3w7_v.mjs → migration-check-zEkHpPDT.mjs} +58 -30
  82. package/dist/migration-check-zEkHpPDT.mjs.map +1 -0
  83. package/dist/{migration-command-scaffold-CRyYrHZ9.mjs → migration-command-scaffold-CkNsp9g1.mjs} +4 -4
  84. package/dist/{migration-command-scaffold-CRyYrHZ9.mjs.map → migration-command-scaffold-CkNsp9g1.mjs.map} +1 -1
  85. package/dist/{migration-graph-command-render-B83xrnNy.mjs → migration-graph-command-render-DEwFpx-w.mjs} +5 -4
  86. package/dist/migration-graph-command-render-DEwFpx-w.mjs.map +1 -0
  87. package/dist/{migration-list-BAKujUjh.mjs → migration-list-BkafFVXS.mjs} +6 -6
  88. package/dist/migration-list-BkafFVXS.mjs.map +1 -0
  89. package/dist/{migration-log-C_xam2HZ.mjs → migration-log-BxhAtbhA.mjs} +5 -5
  90. package/dist/{migration-log-C_xam2HZ.mjs.map → migration-log-BxhAtbhA.mjs.map} +1 -1
  91. package/dist/{migration-path-target-DpPTsRCg.mjs → migration-path-target-B5HkjSTO.mjs} +2 -2
  92. package/dist/{migration-path-target-DpPTsRCg.mjs.map → migration-path-target-B5HkjSTO.mjs.map} +1 -1
  93. package/dist/{migration-plan-CreSVtWZ.mjs → migration-plan-Bk2pbPQH.mjs} +64 -74
  94. package/dist/migration-plan-Bk2pbPQH.mjs.map +1 -0
  95. package/dist/{migration-status-YgEvXf-u.mjs → migration-status-BzlARseq.mjs} +13 -12
  96. package/dist/migration-status-BzlARseq.mjs.map +1 -0
  97. package/dist/{output-mEQ74_nd.mjs → output-DWNYqZGc.mjs} +5 -6
  98. package/dist/output-DWNYqZGc.mjs.map +1 -0
  99. package/dist/{ref-advancement-BkXlikCA.mjs → ref-advancement-BCz7X7qJ.mjs} +13 -6
  100. package/dist/ref-advancement-BCz7X7qJ.mjs.map +1 -0
  101. package/dist/{telemetry-DVr6RFnq.mjs → telemetry-BbniBUbP.mjs} +2 -2
  102. package/dist/{telemetry-DVr6RFnq.mjs.map → telemetry-BbniBUbP.mjs.map} +1 -1
  103. package/dist/{types-D4r_HI9-.d.mts → types-DgXprE3w.d.mts} +3 -3
  104. package/dist/types-DgXprE3w.d.mts.map +1 -0
  105. package/dist/{verify-CgDVZjhc.mjs → verify-BSaNeaSL.mjs} +2 -2
  106. package/dist/{verify-CgDVZjhc.mjs.map → verify-BSaNeaSL.mjs.map} +1 -1
  107. package/package.json +21 -21
  108. package/src/commands/db-init.ts +5 -3
  109. package/src/commands/db-sign.ts +9 -7
  110. package/src/commands/db-update.ts +18 -9
  111. package/src/commands/db-verify.ts +4 -4
  112. package/src/commands/format.ts +1 -1
  113. package/src/commands/init/hygiene-gitattributes.ts +37 -21
  114. package/src/commands/init/init.ts +6 -6
  115. package/src/commands/init/output.ts +4 -5
  116. package/src/commands/init/probe-db.ts +9 -2
  117. package/src/commands/init/reinit-cleanup.ts +16 -17
  118. package/src/commands/init/templates/render.ts +2 -1
  119. package/src/commands/inspect-live-schema.ts +1 -1
  120. package/src/commands/migrate.ts +6 -5
  121. package/src/commands/migration-check.ts +72 -34
  122. package/src/commands/migration-list.ts +1 -5
  123. package/src/commands/migration-log.ts +1 -1
  124. package/src/commands/migration-new.ts +17 -41
  125. package/src/commands/migration-plan.ts +64 -88
  126. package/src/commands/migration-show.ts +2 -2
  127. package/src/commands/migration-status.ts +6 -7
  128. package/src/commands/ref.ts +19 -14
  129. package/src/control-api/client.ts +43 -25
  130. package/src/control-api/contract-enrichment.ts +7 -7
  131. package/src/control-api/operations/contract-emit.ts +2 -3
  132. package/src/control-api/operations/db-init.ts +3 -3
  133. package/src/control-api/operations/db-run.ts +8 -7
  134. package/src/control-api/operations/db-update.ts +2 -2
  135. package/src/control-api/operations/db-verify.ts +4 -4
  136. package/src/control-api/operations/format.ts +5 -4
  137. package/src/control-api/operations/migrate.ts +6 -5
  138. package/src/control-api/operations/run-migration.ts +2 -1
  139. package/src/control-api/types.ts +1 -1
  140. package/src/load-ts-contract.ts +81 -16
  141. package/src/utils/cli-errors.ts +15 -7
  142. package/src/utils/combine-verify-results.ts +2 -1
  143. package/src/utils/command-helpers.ts +15 -5
  144. package/src/utils/contract-at-errors.ts +13 -22
  145. package/src/utils/contract-space-aggregate-loader.ts +8 -8
  146. package/src/utils/contract-space-seed-phase.ts +11 -9
  147. package/src/utils/extension-pack-inputs.ts +8 -8
  148. package/src/utils/formatters/migration-graph-grid-layout.ts +2 -1
  149. package/src/utils/formatters/migration-graph-labels.ts +1 -2
  150. package/src/utils/formatters/migration-list-data-column.ts +1 -2
  151. package/src/utils/framework-components.ts +9 -9
  152. package/src/utils/integrity-violation-to-check-failure.ts +3 -3
  153. package/src/utils/migration-command-scaffold.ts +1 -1
  154. package/src/utils/plan-resolution.ts +7 -10
  155. package/src/utils/ref-advancement.ts +22 -3
  156. package/dist/client-KuBQftxz.mjs.map +0 -1
  157. package/dist/command-helpers-Cpq44aVa.mjs.map +0 -1
  158. package/dist/contract-at-errors-D9gEjYtg.mjs +0 -42
  159. package/dist/contract-at-errors-D9gEjYtg.mjs.map +0 -1
  160. package/dist/contract-space-aggregate-loader-D_3VeHVb.mjs.map +0 -1
  161. package/dist/db-verify-BEclUzki.mjs.map +0 -1
  162. package/dist/format-DAUAZqPt.mjs.map +0 -1
  163. package/dist/framework-components-CnbUbiJw.mjs.map +0 -1
  164. package/dist/init-C3JhkHg-.mjs.map +0 -1
  165. package/dist/migration-check-MHT3w7_v.mjs.map +0 -1
  166. package/dist/migration-graph-command-render-B83xrnNy.mjs.map +0 -1
  167. package/dist/migration-list-BAKujUjh.mjs.map +0 -1
  168. package/dist/migration-plan-CreSVtWZ.mjs.map +0 -1
  169. package/dist/migration-status-YgEvXf-u.mjs.map +0 -1
  170. package/dist/output-mEQ74_nd.mjs.map +0 -1
  171. package/dist/ref-advancement-BkXlikCA.mjs.map +0 -1
  172. package/dist/types-D4r_HI9-.d.mts.map +0 -1
  173. package/src/control-api/errors.ts +0 -9
@@ -1 +1 @@
1
- {"version":3,"file":"verify-CgDVZjhc.mjs","names":[],"sources":["../src/utils/formatters/verify.ts"],"sourcesContent":["import type {\n CoreSchemaView,\n ExpectationFailureReason,\n IntrospectSchemaResult,\n SchemaDiffIssue,\n SchemaTreeNode,\n SignDatabaseResult,\n VerifyDatabaseResult,\n VerifyDatabaseSchemaResult,\n} from '@prisma-next/framework-components/control';\nimport { issueOutcome } from '@prisma-next/framework-components/control';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { bold, cyan, dim, green, magenta, red, yellow } from 'colorette';\nimport type { GlobalFlags } from '../global-flags';\nimport { createColorFormatter, formatDim, isVerbose } from './helpers';\n\n/** Human-readable label for each outcome, prefixed onto an issue's message for display. */\nconst OUTCOME_LABEL: Record<ExpectationFailureReason, string> = {\n 'not-found': 'missing',\n 'not-expected': 'extra',\n 'not-equal': 'mismatch',\n};\n\n/**\n * The issue's display text: its own path, prefixed with a human label for\n * why it's flagged. Turning the presence-derived outcome (and the path) into\n * prose is this formatter's job, not the differ's — the differ's issue is\n * data (`path` + nodes), not prose.\n */\nfunction formatIssueMessage(issue: SchemaDiffIssue): string {\n return `${OUTCOME_LABEL[issueOutcome(issue)]}: ${issue.path.join('/')}`;\n}\n\n// ============================================================================\n// Verify Output Formatters\n// ============================================================================\n\nexport interface DbVerifyCommandSuccessResult {\n readonly ok: true;\n readonly mode: 'full' | 'marker-only';\n readonly summary: string;\n readonly contract: VerifyDatabaseResult['contract'];\n readonly marker?: VerifyDatabaseResult['marker'];\n readonly target: VerifyDatabaseResult['target'];\n readonly missingCodecs?: VerifyDatabaseResult['missingCodecs'];\n readonly codecCoverageSkipped?: VerifyDatabaseResult['codecCoverageSkipped'];\n readonly schema?: {\n readonly summary: string;\n readonly strict: boolean;\n /**\n * Warn-graded finding messages (observed-policy drift). Informational —\n * present on a passing verify; the full-mode result summarizes them as a\n * flat message list.\n */\n readonly warnings?: readonly string[];\n };\n /**\n * Live element names no contract space declares. In full success this is\n * only ever non-empty in lenient mode — strict mode fails on it — and is\n * rendered informationally.\n */\n readonly unclaimed?: readonly string[];\n readonly warning?: string;\n readonly meta?:\n | (NonNullable<VerifyDatabaseResult['meta']> & {\n readonly schemaVerification: 'performed' | 'skipped';\n })\n | {\n readonly schemaVerification: 'performed' | 'skipped';\n };\n readonly timings: {\n readonly total: number;\n };\n}\n\n/**\n * Formats human-readable output for database verify.\n */\nexport function formatVerifyOutput(\n result: DbVerifyCommandSuccessResult,\n flags: GlobalFlags,\n): string {\n if (flags.quiet) {\n return '';\n }\n\n const lines: string[] = [];\n\n const useColor = flags.color !== false;\n const formatGreen = createColorFormatter(useColor, green);\n const formatYellow = createColorFormatter(useColor, yellow);\n const formatDimText = (text: string) => formatDim(useColor, text);\n const verificationMode =\n result.mode === 'full'\n ? `marker + schema${result.schema?.strict ? ' (strict)' : ' (tolerant)'}`\n : 'marker only (--marker-only)';\n\n lines.push(`${formatGreen('✔')} ${result.summary}`);\n lines.push(`${formatDimText(` verification: ${verificationMode}`)}`);\n lines.push(`${formatDimText(` storageHash: ${result.contract.storageHash}`)}`);\n if (result.contract.profileHash) {\n lines.push(`${formatDimText(` profileHash: ${result.contract.profileHash}`)}`);\n }\n if (result.schema?.warnings && result.schema.warnings.length > 0) {\n lines.push('');\n lines.push(formatYellow('Schema warnings:'));\n for (const message of result.schema.warnings) {\n lines.push(` ${formatYellow('⚠')} ${message}`);\n }\n }\n\n if (result.unclaimed && result.unclaimed.length > 0) {\n lines.push('');\n lines.push(formatYellow('Unclaimed elements (declared by no contract):'));\n for (const name of result.unclaimed) {\n lines.push(` ${formatYellow('⚠')} ${name}`);\n }\n }\n\n if (result.warning) {\n lines.push('');\n lines.push(`${formatYellow('⚠')} ${result.warning}`);\n }\n\n if (isVerbose(flags, 1)) {\n if (result.codecCoverageSkipped) {\n lines.push(\n `${formatDimText(' Codec coverage check skipped (helper returned no supported types)')}`,\n );\n }\n lines.push(`${formatDimText(` Total time: ${result.timings.total}ms`)}`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Formats JSON output for database verify.\n */\nexport function formatVerifyJson(result: DbVerifyCommandSuccessResult): string {\n const output = {\n ok: result.ok,\n summary: result.summary,\n mode: result.mode,\n contract: result.contract,\n ...ifDefined('marker', result.marker),\n target: result.target,\n ...ifDefined('missingCodecs', result.missingCodecs),\n ...ifDefined('codecCoverageSkipped', result.codecCoverageSkipped),\n ...ifDefined('schema', result.schema),\n unclaimed: result.unclaimed ?? [],\n ...ifDefined('warning', result.warning),\n ...ifDefined('meta', result.meta),\n timings: result.timings,\n };\n\n return JSON.stringify(output, null, 2);\n}\n\n/**\n * Formats JSON output for database introspection.\n */\nexport function formatIntrospectJson(result: IntrospectSchemaResult<unknown>): string {\n return JSON.stringify(result, null, 2);\n}\n\n/**\n * Renders a schema tree structure from CoreSchemaView.\n * Status-glyph tree styling shared with the retired verification-tree renderer.\n */\nfunction renderSchemaTree(\n node: SchemaTreeNode,\n flags: GlobalFlags,\n options: {\n readonly isLast: boolean;\n readonly prefix: string;\n readonly useColor: boolean;\n readonly formatDimText: (text: string) => string;\n readonly isRoot?: boolean;\n },\n): string[] {\n const { isLast, prefix, useColor, formatDimText, isRoot = false } = options;\n const lines: string[] = [];\n\n // Format node label with color based on kind (matching schema-verify style)\n let formattedLabel: string = node.label;\n\n if (useColor) {\n switch (node.kind) {\n case 'root':\n formattedLabel = bold(node.label);\n break;\n case 'entity': {\n // Parse \"table tableName\" format - color \"table\" dim, tableName cyan\n const tableMatch = node.label.match(/^table\\s+(.+)$/);\n if (tableMatch?.[1]) {\n const tableName = tableMatch[1];\n formattedLabel = `${dim('table')} ${cyan(tableName)}`;\n } else {\n // Fallback: color entire label with cyan\n formattedLabel = cyan(node.label);\n }\n break;\n }\n case 'collection': {\n // \"columns\" grouping node - dim the label\n formattedLabel = dim(node.label);\n break;\n }\n case 'field': {\n // Parse column name format: \"columnName: typeDisplay (nullability)\"\n // Color code: column name (cyan), type (default), nullability (dim)\n const columnMatch = node.label.match(/^([^:]+):\\s*(.+)$/);\n if (columnMatch?.[1] && columnMatch[2]) {\n const columnName = columnMatch[1];\n const rest = columnMatch[2];\n // Parse rest: \"typeDisplay (nullability)\"\n const typeMatch = rest.match(/^([^\\s(]+)\\s*(\\([^)]+\\))$/);\n if (typeMatch?.[1] && typeMatch[2]) {\n const typeDisplay = typeMatch[1];\n const nullability = typeMatch[2];\n formattedLabel = `${cyan(columnName)}: ${typeDisplay} ${dim(nullability)}`;\n } else {\n // Fallback if format doesn't match\n formattedLabel = `${cyan(columnName)}: ${rest}`;\n }\n } else {\n formattedLabel = node.label;\n }\n break;\n }\n case 'index': {\n // Parse index/unique constraint/primary key formats\n // \"primary key: columnName\" -> dim \"primary key\", cyan columnName\n const pkMatch = node.label.match(/^primary key:\\s*(.+)$/);\n if (pkMatch?.[1]) {\n const columnNames = pkMatch[1];\n formattedLabel = `${dim('primary key')}: ${cyan(columnNames)}`;\n } else {\n // \"unique name\" -> dim \"unique\", cyan \"name\"\n const uniqueMatch = node.label.match(/^unique\\s+(.+)$/);\n if (uniqueMatch?.[1]) {\n const name = uniqueMatch[1];\n formattedLabel = `${dim('unique')} ${cyan(name)}`;\n } else {\n // \"index name\" or \"unique index name\" -> dim label prefix, cyan name\n const indexMatch = node.label.match(/^(unique\\s+)?index\\s+(.+)$/);\n if (indexMatch?.[2]) {\n const indexPrefix = indexMatch[1] ? `${dim('unique')} ` : '';\n const name = indexMatch[2];\n formattedLabel = `${indexPrefix}${dim('index')} ${cyan(name)}`;\n } else {\n formattedLabel = dim(node.label);\n }\n }\n }\n break;\n }\n case 'dependency': {\n // Parse extension message formats similar to schema-verify\n // \"extensionName extension is enabled\" -> cyan extensionName, dim rest\n const extMatch = node.label.match(/^([^\\s]+)\\s+(extension is enabled)$/);\n if (extMatch?.[1] && extMatch[2]) {\n const extName = extMatch[1];\n const rest = extMatch[2];\n formattedLabel = `${cyan(extName)} ${dim(rest)}`;\n } else {\n // Fallback: color entire label with magenta\n formattedLabel = magenta(node.label);\n }\n break;\n }\n default:\n formattedLabel = node.label;\n break;\n }\n }\n\n // Root node renders without tree characters or prefix\n if (isRoot) {\n lines.push(formattedLabel);\n } else {\n const treeChar = isLast ? '└' : '├';\n const treePrefix = `${formatDimText(treeChar)}─ `;\n lines.push(`${prefix}${treePrefix}${formattedLabel}`);\n }\n\n // Render children if present\n if (node.children && node.children.length > 0) {\n const childPrefix = isRoot ? '' : `${prefix}${isLast ? ' ' : `${formatDimText('│')} `}`;\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i];\n if (!child) continue;\n const isLastChild = i === node.children.length - 1;\n const childLines = renderSchemaTree(child, flags, {\n isLast: isLastChild,\n prefix: childPrefix,\n useColor,\n formatDimText,\n isRoot: false,\n });\n lines.push(...childLines);\n }\n }\n\n return lines;\n}\n\n/**\n * Formats human-readable output for database introspection.\n */\nexport function formatIntrospectOutput(\n result: IntrospectSchemaResult<unknown>,\n schemaView: CoreSchemaView | undefined,\n flags: GlobalFlags,\n): string {\n if (flags.quiet) {\n return '';\n }\n\n const lines: string[] = [];\n\n const useColor = flags.color !== false;\n const formatDimText = (text: string) => formatDim(useColor, text);\n\n if (schemaView) {\n // Render tree structure - root node is special (no tree characters)\n const treeLines = renderSchemaTree(schemaView.root, flags, {\n isLast: true,\n prefix: '',\n useColor,\n formatDimText,\n isRoot: true,\n });\n lines.push(...treeLines);\n } else {\n // Fallback: print summary when toSchemaView is not available\n lines.push(`✔ ${result.summary}`);\n if (isVerbose(flags, 1)) {\n lines.push(` Target: ${result.target.familyId}/${result.target.id}`);\n if (result.meta?.dbUrl) {\n lines.push(` Database: ${result.meta.dbUrl}`);\n }\n }\n }\n\n // Add timings in verbose mode\n if (isVerbose(flags, 1)) {\n lines.push(`${formatDimText(` Total time: ${result.timings.total}ms`)}`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Formats human-readable output for database schema verification.\n */\nexport function formatSchemaVerifyOutput(\n result: VerifyDatabaseSchemaResult,\n flags: GlobalFlags,\n unclaimed: readonly string[] = [],\n): string {\n if (flags.quiet) {\n return '';\n }\n\n const lines: string[] = [];\n\n const useColor = flags.color !== false;\n const formatGreen = createColorFormatter(useColor, green);\n const formatRed = createColorFormatter(useColor, red);\n const formatYellow = createColorFormatter(useColor, yellow);\n const formatDimText = (text: string) => formatDim(useColor, text);\n\n const issueMessages = result.schema.issues.map(formatIssueMessage);\n if (issueMessages.length > 0) {\n lines.push(formatRed('Schema issues:'));\n for (const message of issueMessages) {\n lines.push(` ${formatRed('✖')} ${message}`);\n }\n }\n\n const warningMessages = (result.schema.warnings?.issues ?? []).map(formatIssueMessage);\n if (warningMessages.length > 0) {\n if (lines.length > 0) lines.push('');\n lines.push(formatYellow('Schema warnings:'));\n for (const message of warningMessages) {\n lines.push(` ${formatYellow('⚠')} ${message}`);\n }\n }\n\n if (unclaimed.length > 0) {\n const strict = result.meta?.strict ?? false;\n if (lines.length > 0) lines.push('');\n lines.push(\n (strict ? formatRed : formatYellow)('Unclaimed elements (declared by no contract):'),\n );\n for (const name of unclaimed) {\n lines.push(` ${(strict ? formatRed : formatYellow)(strict ? '✖' : '⚠')} ${name}`);\n }\n }\n\n if (isVerbose(flags, 1)) {\n lines.push(`${formatDimText(` Total time: ${result.timings.total}ms`)}`);\n }\n\n // Blank line before summary\n if (lines.length > 0) lines.push('');\n\n // Summary line at the end: verdict with status glyph\n if (result.ok) {\n lines.push(`${formatGreen('✔')} ${result.summary}`);\n } else {\n const codeText = result.code ? ` (${result.code})` : '';\n lines.push(`${formatRed('✖')} ${result.summary}${codeText}`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Formats JSON output for database schema verification. The unclaimed-elements\n * list is a top-level field alongside the combined result, reported once for\n * the whole database.\n */\nexport function formatSchemaVerifyJson(\n result: VerifyDatabaseSchemaResult,\n unclaimed: readonly string[] = [],\n): string {\n return JSON.stringify({ ...result, unclaimed }, null, 2);\n}\n\n// ============================================================================\n// Sign Output Formatters\n// ============================================================================\n\n/**\n * Formats human-readable output for database sign.\n */\nexport function formatSignOutput(result: SignDatabaseResult, flags: GlobalFlags): string {\n if (flags.quiet) {\n return '';\n }\n\n const lines: string[] = [];\n\n const useColor = flags.color !== false;\n const formatGreen = createColorFormatter(useColor, green);\n const formatDimText = (text: string) => formatDim(useColor, text);\n\n if (result.ok) {\n // Main success message in white (not dimmed)\n lines.push(`${formatGreen('✔')} Database signed`);\n\n // Show from -> to hashes with clear labels\n const previousHash = result.marker.previous?.storageHash ?? 'none';\n const currentHash = result.contract.storageHash;\n\n lines.push(`${formatDimText(` from: ${previousHash}`)}`);\n lines.push(`${formatDimText(` to: ${currentHash}`)}`);\n\n if (isVerbose(flags, 1)) {\n if (result.contract.profileHash) {\n lines.push(`${formatDimText(` profileHash: ${result.contract.profileHash}`)}`);\n }\n if (result.marker.previous?.profileHash) {\n lines.push(\n `${formatDimText(` previous profileHash: ${result.marker.previous.profileHash}`)}`,\n );\n }\n lines.push(`${formatDimText(` Total time: ${result.timings.total}ms`)}`);\n }\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Formats JSON output for database sign.\n */\nexport function formatSignJson(result: SignDatabaseResult): string {\n return JSON.stringify(result, null, 2);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"verify-BSaNeaSL.mjs","names":[],"sources":["../src/utils/formatters/verify.ts"],"sourcesContent":["import type {\n CoreSchemaView,\n ExpectationFailureReason,\n IntrospectSchemaResult,\n SchemaDiffIssue,\n SchemaTreeNode,\n SignDatabaseResult,\n VerifyDatabaseResult,\n VerifyDatabaseSchemaResult,\n} from '@prisma-next/framework-components/control';\nimport { issueOutcome } from '@prisma-next/framework-components/control';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { bold, cyan, dim, green, magenta, red, yellow } from 'colorette';\nimport type { GlobalFlags } from '../global-flags';\nimport { createColorFormatter, formatDim, isVerbose } from './helpers';\n\n/** Human-readable label for each outcome, prefixed onto an issue's message for display. */\nconst OUTCOME_LABEL: Record<ExpectationFailureReason, string> = {\n 'not-found': 'missing',\n 'not-expected': 'extra',\n 'not-equal': 'mismatch',\n};\n\n/**\n * The issue's display text: its own path, prefixed with a human label for\n * why it's flagged. Turning the presence-derived outcome (and the path) into\n * prose is this formatter's job, not the differ's — the differ's issue is\n * data (`path` + nodes), not prose.\n */\nfunction formatIssueMessage(issue: SchemaDiffIssue): string {\n return `${OUTCOME_LABEL[issueOutcome(issue)]}: ${issue.path.join('/')}`;\n}\n\n// ============================================================================\n// Verify Output Formatters\n// ============================================================================\n\nexport interface DbVerifyCommandSuccessResult {\n readonly ok: true;\n readonly mode: 'full' | 'marker-only';\n readonly summary: string;\n readonly contract: VerifyDatabaseResult['contract'];\n readonly marker?: VerifyDatabaseResult['marker'];\n readonly target: VerifyDatabaseResult['target'];\n readonly missingCodecs?: VerifyDatabaseResult['missingCodecs'];\n readonly codecCoverageSkipped?: VerifyDatabaseResult['codecCoverageSkipped'];\n readonly schema?: {\n readonly summary: string;\n readonly strict: boolean;\n /**\n * Warn-graded finding messages (observed-policy drift). Informational —\n * present on a passing verify; the full-mode result summarizes them as a\n * flat message list.\n */\n readonly warnings?: readonly string[];\n };\n /**\n * Live element names no contract space declares. In full success this is\n * only ever non-empty in lenient mode — strict mode fails on it — and is\n * rendered informationally.\n */\n readonly unclaimed?: readonly string[];\n readonly warning?: string;\n readonly meta?:\n | (NonNullable<VerifyDatabaseResult['meta']> & {\n readonly schemaVerification: 'performed' | 'skipped';\n })\n | {\n readonly schemaVerification: 'performed' | 'skipped';\n };\n readonly timings: {\n readonly total: number;\n };\n}\n\n/**\n * Formats human-readable output for database verify.\n */\nexport function formatVerifyOutput(\n result: DbVerifyCommandSuccessResult,\n flags: GlobalFlags,\n): string {\n if (flags.quiet) {\n return '';\n }\n\n const lines: string[] = [];\n\n const useColor = flags.color !== false;\n const formatGreen = createColorFormatter(useColor, green);\n const formatYellow = createColorFormatter(useColor, yellow);\n const formatDimText = (text: string) => formatDim(useColor, text);\n const verificationMode =\n result.mode === 'full'\n ? `marker + schema${result.schema?.strict ? ' (strict)' : ' (tolerant)'}`\n : 'marker only (--marker-only)';\n\n lines.push(`${formatGreen('✔')} ${result.summary}`);\n lines.push(`${formatDimText(` verification: ${verificationMode}`)}`);\n lines.push(`${formatDimText(` storageHash: ${result.contract.storageHash}`)}`);\n if (result.contract.profileHash) {\n lines.push(`${formatDimText(` profileHash: ${result.contract.profileHash}`)}`);\n }\n if (result.schema?.warnings && result.schema.warnings.length > 0) {\n lines.push('');\n lines.push(formatYellow('Schema warnings:'));\n for (const message of result.schema.warnings) {\n lines.push(` ${formatYellow('⚠')} ${message}`);\n }\n }\n\n if (result.unclaimed && result.unclaimed.length > 0) {\n lines.push('');\n lines.push(formatYellow('Unclaimed elements (declared by no contract):'));\n for (const name of result.unclaimed) {\n lines.push(` ${formatYellow('⚠')} ${name}`);\n }\n }\n\n if (result.warning) {\n lines.push('');\n lines.push(`${formatYellow('⚠')} ${result.warning}`);\n }\n\n if (isVerbose(flags, 1)) {\n if (result.codecCoverageSkipped) {\n lines.push(\n `${formatDimText(' Codec coverage check skipped (helper returned no supported types)')}`,\n );\n }\n lines.push(`${formatDimText(` Total time: ${result.timings.total}ms`)}`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Formats JSON output for database verify.\n */\nexport function formatVerifyJson(result: DbVerifyCommandSuccessResult): string {\n const output = {\n ok: result.ok,\n summary: result.summary,\n mode: result.mode,\n contract: result.contract,\n ...ifDefined('marker', result.marker),\n target: result.target,\n ...ifDefined('missingCodecs', result.missingCodecs),\n ...ifDefined('codecCoverageSkipped', result.codecCoverageSkipped),\n ...ifDefined('schema', result.schema),\n unclaimed: result.unclaimed ?? [],\n ...ifDefined('warning', result.warning),\n ...ifDefined('meta', result.meta),\n timings: result.timings,\n };\n\n return JSON.stringify(output, null, 2);\n}\n\n/**\n * Formats JSON output for database introspection.\n */\nexport function formatIntrospectJson(result: IntrospectSchemaResult<unknown>): string {\n return JSON.stringify(result, null, 2);\n}\n\n/**\n * Renders a schema tree structure from CoreSchemaView.\n * Status-glyph tree styling shared with the retired verification-tree renderer.\n */\nfunction renderSchemaTree(\n node: SchemaTreeNode,\n flags: GlobalFlags,\n options: {\n readonly isLast: boolean;\n readonly prefix: string;\n readonly useColor: boolean;\n readonly formatDimText: (text: string) => string;\n readonly isRoot?: boolean;\n },\n): string[] {\n const { isLast, prefix, useColor, formatDimText, isRoot = false } = options;\n const lines: string[] = [];\n\n // Format node label with color based on kind (matching schema-verify style)\n let formattedLabel: string = node.label;\n\n if (useColor) {\n switch (node.kind) {\n case 'root':\n formattedLabel = bold(node.label);\n break;\n case 'entity': {\n // Parse \"table tableName\" format - color \"table\" dim, tableName cyan\n const tableMatch = node.label.match(/^table\\s+(.+)$/);\n if (tableMatch?.[1]) {\n const tableName = tableMatch[1];\n formattedLabel = `${dim('table')} ${cyan(tableName)}`;\n } else {\n // Fallback: color entire label with cyan\n formattedLabel = cyan(node.label);\n }\n break;\n }\n case 'collection': {\n // \"columns\" grouping node - dim the label\n formattedLabel = dim(node.label);\n break;\n }\n case 'field': {\n // Parse column name format: \"columnName: typeDisplay (nullability)\"\n // Color code: column name (cyan), type (default), nullability (dim)\n const columnMatch = node.label.match(/^([^:]+):\\s*(.+)$/);\n if (columnMatch?.[1] && columnMatch[2]) {\n const columnName = columnMatch[1];\n const rest = columnMatch[2];\n // Parse rest: \"typeDisplay (nullability)\"\n const typeMatch = rest.match(/^([^\\s(]+)\\s*(\\([^)]+\\))$/);\n if (typeMatch?.[1] && typeMatch[2]) {\n const typeDisplay = typeMatch[1];\n const nullability = typeMatch[2];\n formattedLabel = `${cyan(columnName)}: ${typeDisplay} ${dim(nullability)}`;\n } else {\n // Fallback if format doesn't match\n formattedLabel = `${cyan(columnName)}: ${rest}`;\n }\n } else {\n formattedLabel = node.label;\n }\n break;\n }\n case 'index': {\n // Parse index/unique constraint/primary key formats\n // \"primary key: columnName\" -> dim \"primary key\", cyan columnName\n const pkMatch = node.label.match(/^primary key:\\s*(.+)$/);\n if (pkMatch?.[1]) {\n const columnNames = pkMatch[1];\n formattedLabel = `${dim('primary key')}: ${cyan(columnNames)}`;\n } else {\n // \"unique name\" -> dim \"unique\", cyan \"name\"\n const uniqueMatch = node.label.match(/^unique\\s+(.+)$/);\n if (uniqueMatch?.[1]) {\n const name = uniqueMatch[1];\n formattedLabel = `${dim('unique')} ${cyan(name)}`;\n } else {\n // \"index name\" or \"unique index name\" -> dim label prefix, cyan name\n const indexMatch = node.label.match(/^(unique\\s+)?index\\s+(.+)$/);\n if (indexMatch?.[2]) {\n const indexPrefix = indexMatch[1] ? `${dim('unique')} ` : '';\n const name = indexMatch[2];\n formattedLabel = `${indexPrefix}${dim('index')} ${cyan(name)}`;\n } else {\n formattedLabel = dim(node.label);\n }\n }\n }\n break;\n }\n case 'dependency': {\n // Parse extension message formats similar to schema-verify\n // \"extensionName extension is enabled\" -> cyan extensionName, dim rest\n const extMatch = node.label.match(/^([^\\s]+)\\s+(extension is enabled)$/);\n if (extMatch?.[1] && extMatch[2]) {\n const extName = extMatch[1];\n const rest = extMatch[2];\n formattedLabel = `${cyan(extName)} ${dim(rest)}`;\n } else {\n // Fallback: color entire label with magenta\n formattedLabel = magenta(node.label);\n }\n break;\n }\n default:\n formattedLabel = node.label;\n break;\n }\n }\n\n // Root node renders without tree characters or prefix\n if (isRoot) {\n lines.push(formattedLabel);\n } else {\n const treeChar = isLast ? '└' : '├';\n const treePrefix = `${formatDimText(treeChar)}─ `;\n lines.push(`${prefix}${treePrefix}${formattedLabel}`);\n }\n\n // Render children if present\n if (node.children && node.children.length > 0) {\n const childPrefix = isRoot ? '' : `${prefix}${isLast ? ' ' : `${formatDimText('│')} `}`;\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i];\n if (!child) continue;\n const isLastChild = i === node.children.length - 1;\n const childLines = renderSchemaTree(child, flags, {\n isLast: isLastChild,\n prefix: childPrefix,\n useColor,\n formatDimText,\n isRoot: false,\n });\n lines.push(...childLines);\n }\n }\n\n return lines;\n}\n\n/**\n * Formats human-readable output for database introspection.\n */\nexport function formatIntrospectOutput(\n result: IntrospectSchemaResult<unknown>,\n schemaView: CoreSchemaView | undefined,\n flags: GlobalFlags,\n): string {\n if (flags.quiet) {\n return '';\n }\n\n const lines: string[] = [];\n\n const useColor = flags.color !== false;\n const formatDimText = (text: string) => formatDim(useColor, text);\n\n if (schemaView) {\n // Render tree structure - root node is special (no tree characters)\n const treeLines = renderSchemaTree(schemaView.root, flags, {\n isLast: true,\n prefix: '',\n useColor,\n formatDimText,\n isRoot: true,\n });\n lines.push(...treeLines);\n } else {\n // Fallback: print summary when toSchemaView is not available\n lines.push(`✔ ${result.summary}`);\n if (isVerbose(flags, 1)) {\n lines.push(` Target: ${result.target.familyId}/${result.target.id}`);\n if (result.meta?.dbUrl) {\n lines.push(` Database: ${result.meta.dbUrl}`);\n }\n }\n }\n\n // Add timings in verbose mode\n if (isVerbose(flags, 1)) {\n lines.push(`${formatDimText(` Total time: ${result.timings.total}ms`)}`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Formats human-readable output for database schema verification.\n */\nexport function formatSchemaVerifyOutput(\n result: VerifyDatabaseSchemaResult,\n flags: GlobalFlags,\n unclaimed: readonly string[] = [],\n): string {\n if (flags.quiet) {\n return '';\n }\n\n const lines: string[] = [];\n\n const useColor = flags.color !== false;\n const formatGreen = createColorFormatter(useColor, green);\n const formatRed = createColorFormatter(useColor, red);\n const formatYellow = createColorFormatter(useColor, yellow);\n const formatDimText = (text: string) => formatDim(useColor, text);\n\n const issueMessages = result.schema.issues.map(formatIssueMessage);\n if (issueMessages.length > 0) {\n lines.push(formatRed('Schema issues:'));\n for (const message of issueMessages) {\n lines.push(` ${formatRed('✖')} ${message}`);\n }\n }\n\n const warningMessages = (result.schema.warnings?.issues ?? []).map(formatIssueMessage);\n if (warningMessages.length > 0) {\n if (lines.length > 0) lines.push('');\n lines.push(formatYellow('Schema warnings:'));\n for (const message of warningMessages) {\n lines.push(` ${formatYellow('⚠')} ${message}`);\n }\n }\n\n if (unclaimed.length > 0) {\n const strict = result.meta?.strict ?? false;\n if (lines.length > 0) lines.push('');\n lines.push(\n (strict ? formatRed : formatYellow)('Unclaimed elements (declared by no contract):'),\n );\n for (const name of unclaimed) {\n lines.push(` ${(strict ? formatRed : formatYellow)(strict ? '✖' : '⚠')} ${name}`);\n }\n }\n\n if (isVerbose(flags, 1)) {\n lines.push(`${formatDimText(` Total time: ${result.timings.total}ms`)}`);\n }\n\n // Blank line before summary\n if (lines.length > 0) lines.push('');\n\n // Summary line at the end: verdict with status glyph\n if (result.ok) {\n lines.push(`${formatGreen('✔')} ${result.summary}`);\n } else {\n const codeText = result.code ? ` (${result.code})` : '';\n lines.push(`${formatRed('✖')} ${result.summary}${codeText}`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Formats JSON output for database schema verification. The unclaimed-elements\n * list is a top-level field alongside the combined result, reported once for\n * the whole database.\n */\nexport function formatSchemaVerifyJson(\n result: VerifyDatabaseSchemaResult,\n unclaimed: readonly string[] = [],\n): string {\n return JSON.stringify({ ...result, unclaimed }, null, 2);\n}\n\n// ============================================================================\n// Sign Output Formatters\n// ============================================================================\n\n/**\n * Formats human-readable output for database sign.\n */\nexport function formatSignOutput(result: SignDatabaseResult, flags: GlobalFlags): string {\n if (flags.quiet) {\n return '';\n }\n\n const lines: string[] = [];\n\n const useColor = flags.color !== false;\n const formatGreen = createColorFormatter(useColor, green);\n const formatDimText = (text: string) => formatDim(useColor, text);\n\n if (result.ok) {\n // Main success message in white (not dimmed)\n lines.push(`${formatGreen('✔')} Database signed`);\n\n // Show from -> to hashes with clear labels\n const previousHash = result.marker.previous?.storageHash ?? 'none';\n const currentHash = result.contract.storageHash;\n\n lines.push(`${formatDimText(` from: ${previousHash}`)}`);\n lines.push(`${formatDimText(` to: ${currentHash}`)}`);\n\n if (isVerbose(flags, 1)) {\n if (result.contract.profileHash) {\n lines.push(`${formatDimText(` profileHash: ${result.contract.profileHash}`)}`);\n }\n if (result.marker.previous?.profileHash) {\n lines.push(\n `${formatDimText(` previous profileHash: ${result.marker.previous.profileHash}`)}`,\n );\n }\n lines.push(`${formatDimText(` Total time: ${result.timings.total}ms`)}`);\n }\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Formats JSON output for database sign.\n */\nexport function formatSignJson(result: SignDatabaseResult): string {\n return JSON.stringify(result, null, 2);\n}\n"],"mappings":";;;;;;AAiBA,MAAM,gBAA0D;CAC9D,aAAa;CACb,gBAAgB;CAChB,aAAa;AACf;;;;;;;AAQA,SAAS,mBAAmB,OAAgC;CAC1D,OAAO,GAAG,cAAc,aAAa,KAAK,GAAG,IAAI,MAAM,KAAK,KAAK,GAAG;AACtE;;;;AA+CA,SAAgB,mBACd,QACA,OACQ;CACR,IAAI,MAAM,OACR,OAAO;CAGT,MAAM,QAAkB,CAAC;CAEzB,MAAM,WAAW,MAAM,UAAU;CACjC,MAAM,cAAc,qBAAqB,UAAU,KAAK;CACxD,MAAM,eAAe,qBAAqB,UAAU,MAAM;CAC1D,MAAM,iBAAiB,SAAiB,UAAU,UAAU,IAAI;CAChE,MAAM,mBACJ,OAAO,SAAS,SACZ,kBAAkB,OAAO,QAAQ,SAAS,cAAc,kBACxD;CAEN,MAAM,KAAK,GAAG,YAAY,GAAG,EAAE,GAAG,OAAO,SAAS;CAClD,MAAM,KAAK,GAAG,cAAc,mBAAmB,kBAAkB,GAAG;CACpE,MAAM,KAAK,GAAG,cAAc,kBAAkB,OAAO,SAAS,aAAa,GAAG;CAC9E,IAAI,OAAO,SAAS,aAClB,MAAM,KAAK,GAAG,cAAc,kBAAkB,OAAO,SAAS,aAAa,GAAG;CAEhF,IAAI,OAAO,QAAQ,YAAY,OAAO,OAAO,SAAS,SAAS,GAAG;EAChE,MAAM,KAAK,EAAE;EACb,MAAM,KAAK,aAAa,kBAAkB,CAAC;EAC3C,KAAK,MAAM,WAAW,OAAO,OAAO,UAClC,MAAM,KAAK,KAAK,aAAa,GAAG,EAAE,GAAG,SAAS;CAElD;CAEA,IAAI,OAAO,aAAa,OAAO,UAAU,SAAS,GAAG;EACnD,MAAM,KAAK,EAAE;EACb,MAAM,KAAK,aAAa,+CAA+C,CAAC;EACxE,KAAK,MAAM,QAAQ,OAAO,WACxB,MAAM,KAAK,KAAK,aAAa,GAAG,EAAE,GAAG,MAAM;CAE/C;CAEA,IAAI,OAAO,SAAS;EAClB,MAAM,KAAK,EAAE;EACb,MAAM,KAAK,GAAG,aAAa,GAAG,EAAE,GAAG,OAAO,SAAS;CACrD;CAEA,IAAI,UAAU,OAAO,CAAC,GAAG;EACvB,IAAI,OAAO,sBACT,MAAM,KACJ,GAAG,cAAc,qEAAqE,GACxF;EAEF,MAAM,KAAK,GAAG,cAAc,iBAAiB,OAAO,QAAQ,MAAM,GAAG,GAAG;CAC1E;CAEA,OAAO,MAAM,KAAK,IAAI;AACxB;;;;AAKA,SAAgB,iBAAiB,QAA8C;CAC7E,MAAM,SAAS;EACb,IAAI,OAAO;EACX,SAAS,OAAO;EAChB,MAAM,OAAO;EACb,UAAU,OAAO;EACjB,GAAG,UAAU,UAAU,OAAO,MAAM;EACpC,QAAQ,OAAO;EACf,GAAG,UAAU,iBAAiB,OAAO,aAAa;EAClD,GAAG,UAAU,wBAAwB,OAAO,oBAAoB;EAChE,GAAG,UAAU,UAAU,OAAO,MAAM;EACpC,WAAW,OAAO,aAAa,CAAC;EAChC,GAAG,UAAU,WAAW,OAAO,OAAO;EACtC,GAAG,UAAU,QAAQ,OAAO,IAAI;EAChC,SAAS,OAAO;CAClB;CAEA,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;AACvC;;;;AAKA,SAAgB,qBAAqB,QAAiD;CACpF,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;AACvC;;;;;AAMA,SAAS,iBACP,MACA,OACA,SAOU;CACV,MAAM,EAAE,QAAQ,QAAQ,UAAU,eAAe,SAAS,UAAU;CACpE,MAAM,QAAkB,CAAC;CAGzB,IAAI,iBAAyB,KAAK;CAElC,IAAI,UACF,QAAQ,KAAK,MAAb;EACE,KAAK;GACH,iBAAiB,KAAK,KAAK,KAAK;GAChC;EACF,KAAK,UAAU;GAEb,MAAM,aAAa,KAAK,MAAM,MAAM,gBAAgB;GACpD,IAAI,aAAa,IAAI;IACnB,MAAM,YAAY,WAAW;IAC7B,iBAAiB,GAAG,IAAI,OAAO,EAAE,GAAG,KAAK,SAAS;GACpD,OAEE,iBAAiB,KAAK,KAAK,KAAK;GAElC;EACF;EACA,KAAK;GAEH,iBAAiB,IAAI,KAAK,KAAK;GAC/B;EAEF,KAAK,SAAS;GAGZ,MAAM,cAAc,KAAK,MAAM,MAAM,mBAAmB;GACxD,IAAI,cAAc,MAAM,YAAY,IAAI;IACtC,MAAM,aAAa,YAAY;IAC/B,MAAM,OAAO,YAAY;IAEzB,MAAM,YAAY,KAAK,MAAM,2BAA2B;IACxD,IAAI,YAAY,MAAM,UAAU,IAAI;KAClC,MAAM,cAAc,UAAU;KAC9B,MAAM,cAAc,UAAU;KAC9B,iBAAiB,GAAG,KAAK,UAAU,EAAE,IAAI,YAAY,GAAG,IAAI,WAAW;IACzE,OAEE,iBAAiB,GAAG,KAAK,UAAU,EAAE,IAAI;GAE7C,OACE,iBAAiB,KAAK;GAExB;EACF;EACA,KAAK,SAAS;GAGZ,MAAM,UAAU,KAAK,MAAM,MAAM,uBAAuB;GACxD,IAAI,UAAU,IAAI;IAChB,MAAM,cAAc,QAAQ;IAC5B,iBAAiB,GAAG,IAAI,aAAa,EAAE,IAAI,KAAK,WAAW;GAC7D,OAAO;IAEL,MAAM,cAAc,KAAK,MAAM,MAAM,iBAAiB;IACtD,IAAI,cAAc,IAAI;KACpB,MAAM,OAAO,YAAY;KACzB,iBAAiB,GAAG,IAAI,QAAQ,EAAE,GAAG,KAAK,IAAI;IAChD,OAAO;KAEL,MAAM,aAAa,KAAK,MAAM,MAAM,4BAA4B;KAChE,IAAI,aAAa,IAAI;MACnB,MAAM,cAAc,WAAW,KAAK,GAAG,IAAI,QAAQ,EAAE,KAAK;MAC1D,MAAM,OAAO,WAAW;MACxB,iBAAiB,GAAG,cAAc,IAAI,OAAO,EAAE,GAAG,KAAK,IAAI;KAC7D,OACE,iBAAiB,IAAI,KAAK,KAAK;IAEnC;GACF;GACA;EACF;EACA,KAAK,cAAc;GAGjB,MAAM,WAAW,KAAK,MAAM,MAAM,qCAAqC;GACvE,IAAI,WAAW,MAAM,SAAS,IAAI;IAChC,MAAM,UAAU,SAAS;IACzB,MAAM,OAAO,SAAS;IACtB,iBAAiB,GAAG,KAAK,OAAO,EAAE,GAAG,IAAI,IAAI;GAC/C,OAEE,iBAAiB,QAAQ,KAAK,KAAK;GAErC;EACF;EACA;GACE,iBAAiB,KAAK;GACtB;CACJ;CAIF,IAAI,QACF,MAAM,KAAK,cAAc;MACpB;EAEL,MAAM,aAAa,GAAG,cADL,SAAS,MAAM,GACY,EAAE;EAC9C,MAAM,KAAK,GAAG,SAAS,aAAa,gBAAgB;CACtD;CAGA,IAAI,KAAK,YAAY,KAAK,SAAS,SAAS,GAAG;EAC7C,MAAM,cAAc,SAAS,KAAK,GAAG,SAAS,SAAS,QAAQ,GAAG,cAAc,GAAG,EAAE;EACrF,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,SAAS,QAAQ,KAAK;GAC7C,MAAM,QAAQ,KAAK,SAAS;GAC5B,IAAI,CAAC,OAAO;GAEZ,MAAM,aAAa,iBAAiB,OAAO,OAAO;IAChD,QAFkB,MAAM,KAAK,SAAS,SAAS;IAG/C,QAAQ;IACR;IACA;IACA,QAAQ;GACV,CAAC;GACD,MAAM,KAAK,GAAG,UAAU;EAC1B;CACF;CAEA,OAAO;AACT;;;;AAKA,SAAgB,uBACd,QACA,YACA,OACQ;CACR,IAAI,MAAM,OACR,OAAO;CAGT,MAAM,QAAkB,CAAC;CAEzB,MAAM,WAAW,MAAM,UAAU;CACjC,MAAM,iBAAiB,SAAiB,UAAU,UAAU,IAAI;CAEhE,IAAI,YAAY;EAEd,MAAM,YAAY,iBAAiB,WAAW,MAAM,OAAO;GACzD,QAAQ;GACR,QAAQ;GACR;GACA;GACA,QAAQ;EACV,CAAC;EACD,MAAM,KAAK,GAAG,SAAS;CACzB,OAAO;EAEL,MAAM,KAAK,KAAK,OAAO,SAAS;EAChC,IAAI,UAAU,OAAO,CAAC,GAAG;GACvB,MAAM,KAAK,aAAa,OAAO,OAAO,SAAS,GAAG,OAAO,OAAO,IAAI;GACpE,IAAI,OAAO,MAAM,OACf,MAAM,KAAK,eAAe,OAAO,KAAK,OAAO;EAEjD;CACF;CAGA,IAAI,UAAU,OAAO,CAAC,GACpB,MAAM,KAAK,GAAG,cAAc,iBAAiB,OAAO,QAAQ,MAAM,GAAG,GAAG;CAG1E,OAAO,MAAM,KAAK,IAAI;AACxB;;;;AAKA,SAAgB,yBACd,QACA,OACA,YAA+B,CAAC,GACxB;CACR,IAAI,MAAM,OACR,OAAO;CAGT,MAAM,QAAkB,CAAC;CAEzB,MAAM,WAAW,MAAM,UAAU;CACjC,MAAM,cAAc,qBAAqB,UAAU,KAAK;CACxD,MAAM,YAAY,qBAAqB,UAAU,GAAG;CACpD,MAAM,eAAe,qBAAqB,UAAU,MAAM;CAC1D,MAAM,iBAAiB,SAAiB,UAAU,UAAU,IAAI;CAEhE,MAAM,gBAAgB,OAAO,OAAO,OAAO,IAAI,kBAAkB;CACjE,IAAI,cAAc,SAAS,GAAG;EAC5B,MAAM,KAAK,UAAU,gBAAgB,CAAC;EACtC,KAAK,MAAM,WAAW,eACpB,MAAM,KAAK,KAAK,UAAU,GAAG,EAAE,GAAG,SAAS;CAE/C;CAEA,MAAM,mBAAmB,OAAO,OAAO,UAAU,UAAU,CAAC,EAAA,CAAG,IAAI,kBAAkB;CACrF,IAAI,gBAAgB,SAAS,GAAG;EAC9B,IAAI,MAAM,SAAS,GAAG,MAAM,KAAK,EAAE;EACnC,MAAM,KAAK,aAAa,kBAAkB,CAAC;EAC3C,KAAK,MAAM,WAAW,iBACpB,MAAM,KAAK,KAAK,aAAa,GAAG,EAAE,GAAG,SAAS;CAElD;CAEA,IAAI,UAAU,SAAS,GAAG;EACxB,MAAM,SAAS,OAAO,MAAM,UAAU;EACtC,IAAI,MAAM,SAAS,GAAG,MAAM,KAAK,EAAE;EACnC,MAAM,MACH,SAAS,YAAY,aAAA,CAAc,+CAA+C,CACrF;EACA,KAAK,MAAM,QAAQ,WACjB,MAAM,KAAK,MAAM,SAAS,YAAY,aAAA,CAAc,SAAS,MAAM,GAAG,EAAE,GAAG,MAAM;CAErF;CAEA,IAAI,UAAU,OAAO,CAAC,GACpB,MAAM,KAAK,GAAG,cAAc,iBAAiB,OAAO,QAAQ,MAAM,GAAG,GAAG;CAI1E,IAAI,MAAM,SAAS,GAAG,MAAM,KAAK,EAAE;CAGnC,IAAI,OAAO,IACT,MAAM,KAAK,GAAG,YAAY,GAAG,EAAE,GAAG,OAAO,SAAS;MAC7C;EACL,MAAM,WAAW,OAAO,OAAO,KAAK,OAAO,KAAK,KAAK;EACrD,MAAM,KAAK,GAAG,UAAU,GAAG,EAAE,GAAG,OAAO,UAAU,UAAU;CAC7D;CAEA,OAAO,MAAM,KAAK,IAAI;AACxB;;;;;;AAOA,SAAgB,uBACd,QACA,YAA+B,CAAC,GACxB;CACR,OAAO,KAAK,UAAU;EAAE,GAAG;EAAQ;CAAU,GAAG,MAAM,CAAC;AACzD;;;;AASA,SAAgB,iBAAiB,QAA4B,OAA4B;CACvF,IAAI,MAAM,OACR,OAAO;CAGT,MAAM,QAAkB,CAAC;CAEzB,MAAM,WAAW,MAAM,UAAU;CACjC,MAAM,cAAc,qBAAqB,UAAU,KAAK;CACxD,MAAM,iBAAiB,SAAiB,UAAU,UAAU,IAAI;CAEhE,IAAI,OAAO,IAAI;EAEb,MAAM,KAAK,GAAG,YAAY,GAAG,EAAE,iBAAiB;EAGhD,MAAM,eAAe,OAAO,OAAO,UAAU,eAAe;EAC5D,MAAM,cAAc,OAAO,SAAS;EAEpC,MAAM,KAAK,GAAG,cAAc,WAAW,cAAc,GAAG;EACxD,MAAM,KAAK,GAAG,cAAc,WAAW,aAAa,GAAG;EAEvD,IAAI,UAAU,OAAO,CAAC,GAAG;GACvB,IAAI,OAAO,SAAS,aAClB,MAAM,KAAK,GAAG,cAAc,kBAAkB,OAAO,SAAS,aAAa,GAAG;GAEhF,IAAI,OAAO,OAAO,UAAU,aAC1B,MAAM,KACJ,GAAG,cAAc,2BAA2B,OAAO,OAAO,SAAS,aAAa,GAClF;GAEF,MAAM,KAAK,GAAG,cAAc,iBAAiB,OAAO,QAAQ,MAAM,GAAG,GAAG;EAC1E;CACF;CAEA,OAAO,MAAM,KAAK,IAAI;AACxB;;;;AAKA,SAAgB,eAAe,QAAoC;CACjE,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;AACvC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prisma-next/cli",
3
- "version": "0.16.0-dev.3",
3
+ "version": "0.16.0-dev.31",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -9,18 +9,18 @@
9
9
  },
10
10
  "dependencies": {
11
11
  "@clack/prompts": "^1.7.0",
12
- "@prisma-next/config": "0.16.0-dev.3",
13
- "@prisma-next/config-loader": "0.16.0-dev.3",
14
- "@prisma-next/contract": "0.16.0-dev.3",
15
- "@prisma-next/emitter": "0.16.0-dev.3",
16
- "@prisma-next/errors": "0.16.0-dev.3",
17
- "@prisma-next/framework-components": "0.16.0-dev.3",
18
- "@prisma-next/language-server": "0.16.0-dev.3",
19
- "@prisma-next/migration-tools": "0.16.0-dev.3",
20
- "@prisma-next/psl-parser": "0.16.0-dev.3",
21
- "@prisma-next/psl-printer": "0.16.0-dev.3",
22
- "@prisma-next/cli-telemetry": "0.16.0-dev.3",
23
- "@prisma-next/utils": "0.16.0-dev.3",
12
+ "@prisma-next/config": "0.16.0-dev.31",
13
+ "@prisma-next/config-loader": "0.16.0-dev.31",
14
+ "@prisma-next/contract": "0.16.0-dev.31",
15
+ "@prisma-next/emitter": "0.16.0-dev.31",
16
+ "@prisma-next/errors": "0.16.0-dev.31",
17
+ "@prisma-next/framework-components": "0.16.0-dev.31",
18
+ "@prisma-next/language-server": "0.16.0-dev.31",
19
+ "@prisma-next/migration-tools": "0.16.0-dev.31",
20
+ "@prisma-next/psl-parser": "0.16.0-dev.31",
21
+ "@prisma-next/psl-printer": "0.16.0-dev.31",
22
+ "@prisma-next/cli-telemetry": "0.16.0-dev.31",
23
+ "@prisma-next/utils": "0.16.0-dev.31",
24
24
  "arktype": "^2.2.2",
25
25
  "ci-info": "^4.3.1",
26
26
  "clipanion": "4.0.0-rc.4",
@@ -36,14 +36,14 @@
36
36
  "wrap-ansi": "^10.0.0"
37
37
  },
38
38
  "devDependencies": {
39
- "@prisma-next/sql-contract": "0.16.0-dev.3",
40
- "@prisma-next/sql-contract-emitter": "0.16.0-dev.3",
41
- "@prisma-next/sql-contract-ts": "0.16.0-dev.3",
42
- "@prisma-next/sql-operations": "0.16.0-dev.3",
43
- "@prisma-next/sql-runtime": "0.16.0-dev.3",
44
- "@prisma-next/test-utils": "0.16.0-dev.3",
45
- "@prisma-next/tsconfig": "0.16.0-dev.3",
46
- "@prisma-next/tsdown": "0.16.0-dev.3",
39
+ "@prisma-next/sql-contract": "0.16.0-dev.31",
40
+ "@prisma-next/sql-contract-emitter": "0.16.0-dev.31",
41
+ "@prisma-next/sql-contract-ts": "0.16.0-dev.31",
42
+ "@prisma-next/sql-operations": "0.16.0-dev.31",
43
+ "@prisma-next/sql-runtime": "0.16.0-dev.31",
44
+ "@prisma-next/test-utils": "0.16.0-dev.31",
45
+ "@prisma-next/tsconfig": "0.16.0-dev.31",
46
+ "@prisma-next/tsdown": "0.16.0-dev.31",
47
47
  "@types/node": "25.9.4",
48
48
  "tsdown": "0.22.8",
49
49
  "typescript": "5.9.3",
@@ -1,8 +1,9 @@
1
1
  import { MigrationToolsError } from '@prisma-next/migration-tools/errors';
2
2
  import { ifDefined } from '@prisma-next/utils/defined';
3
+ import { assertNever } from '@prisma-next/utils/internal-error';
3
4
  import { notOk, ok, type Result } from '@prisma-next/utils/result';
5
+ import { isStructuredError } from '@prisma-next/utils/structured-error';
4
6
  import { Command } from 'commander';
5
- import { ContractValidationError } from '../control-api/errors';
6
7
  import type { DbInitFailure } from '../control-api/types';
7
8
  import {
8
9
  CliStructuredError,
@@ -109,7 +110,7 @@ function mapDbInitFailure(failure: DbInitFailure): CliStructuredError {
109
110
 
110
111
  // Exhaustive check - TypeScript will error if a new code is added but not handled
111
112
  const exhaustive: never = failure.code;
112
- throw new Error(`Unhandled DbInitFailure code: ${exhaustive}`);
113
+ return assertNever(exhaustive, `Unhandled DbInitFailure code: ${String(exhaustive)}`);
113
114
  }
114
115
 
115
116
  /**
@@ -176,6 +177,7 @@ async function executeDbInitCommand(
176
177
  ...ifDefined('advanceRef', options.advanceRef),
177
178
  ...ifDefined('db', options.db),
178
179
  refsDir,
180
+ migrationsDir,
179
181
  contractIR,
180
182
  mode: result.value.mode,
181
183
  hash: advancementHash,
@@ -235,7 +237,7 @@ async function executeDbInitCommand(
235
237
  return notOk(error);
236
238
  }
237
239
 
238
- if (error instanceof ContractValidationError) {
240
+ if (isStructuredError(error) && error.code === 'CONTRACT.VALIDATION_FAILED') {
239
241
  return notOk(
240
242
  errorContractValidationFailed(`Contract validation failed: ${error.message}`, {
241
243
  where: { path: contractPathAbsolute },
@@ -4,13 +4,14 @@ import type {
4
4
  SignDatabaseResult,
5
5
  VerifyDatabaseSchemaResult,
6
6
  } from '@prisma-next/framework-components/control';
7
+ import { readContractSnapshotJson } from '@prisma-next/migration-tools/contract-snapshot-store';
7
8
  import { MigrationToolsError } from '@prisma-next/migration-tools/errors';
8
9
  import { parseContractRef } from '@prisma-next/migration-tools/ref-resolution';
9
10
  import { notOk, ok, type Result } from '@prisma-next/utils/result';
11
+ import { isStructuredError } from '@prisma-next/utils/structured-error';
10
12
  import { Command } from 'commander';
11
- import { join, relative, resolve } from 'pathe';
13
+ import { relative, resolve } from 'pathe';
12
14
  import { createControlClient } from '../control-api/client';
13
- import { ContractValidationError } from '../control-api/errors';
14
15
  import {
15
16
  CliStructuredError,
16
17
  errorContractValidationFailed,
@@ -113,9 +114,10 @@ async function executeDbSignCommand(
113
114
  const targetHash = refResult.value.hash;
114
115
  const matchingBundle = bundles.find((p) => p.metadata.to === targetHash);
115
116
  if (matchingBundle) {
116
- const endContractPath = join(matchingBundle.dirPath, 'end-contract.json');
117
- const raw = await readFile(endContractPath, 'utf-8');
118
- contractJson = JSON.parse(raw) as Record<string, unknown>;
117
+ contractJson = (await readContractSnapshotJson(migrationsDir, targetHash)) as Record<
118
+ string,
119
+ unknown
120
+ >;
119
121
  } else {
120
122
  const defaultRaw = await readFile(contractPathAbsolute, 'utf-8');
121
123
  const defaultContract = JSON.parse(defaultRaw) as Record<string, unknown>;
@@ -196,7 +198,7 @@ async function executeDbSignCommand(
196
198
  target: config.target,
197
199
  adapter: config.adapter,
198
200
  driver: config.driver,
199
- extensionPacks: config.extensionPacks ?? [],
201
+ extensions: config.extensions ?? [],
200
202
  });
201
203
 
202
204
  // Create progress adapter
@@ -231,7 +233,7 @@ async function executeDbSignCommand(
231
233
  return notOk(error);
232
234
  }
233
235
 
234
- if (error instanceof ContractValidationError) {
236
+ if (isStructuredError(error) && error.code === 'CONTRACT.VALIDATION_FAILED') {
235
237
  return notOk(
236
238
  errorContractValidationFailed(`Contract validation failed: ${error.message}`, {
237
239
  where: { path: contractPathAbsolute },
@@ -1,11 +1,15 @@
1
- import { readFile } from 'node:fs/promises';
1
+ import {
2
+ contractSnapshotDir,
3
+ readContractSnapshotJson,
4
+ } from '@prisma-next/migration-tools/contract-snapshot-store';
2
5
  import { MigrationToolsError } from '@prisma-next/migration-tools/errors';
3
6
  import { parseContractRef } from '@prisma-next/migration-tools/ref-resolution';
4
7
  import { ifDefined } from '@prisma-next/utils/defined';
8
+ import { assertNever } from '@prisma-next/utils/internal-error';
5
9
  import { notOk, ok, type Result } from '@prisma-next/utils/result';
10
+ import { isStructuredError } from '@prisma-next/utils/structured-error';
6
11
  import { Command } from 'commander';
7
12
  import { join } from 'pathe';
8
- import { ContractValidationError } from '../control-api/errors';
9
13
  import type { DbUpdateFailure } from '../control-api/types';
10
14
  import {
11
15
  CliStructuredError,
@@ -89,7 +93,7 @@ function mapDbUpdateFailure(failure: DbUpdateFailure): CliStructuredError {
89
93
  }
90
94
 
91
95
  const exhaustive: never = failure.code;
92
- throw new Error(`Unhandled DbUpdateFailure code: ${exhaustive}`);
96
+ return assertNever(exhaustive, `Unhandled DbUpdateFailure code: ${String(exhaustive)}`);
93
97
  }
94
98
 
95
99
  /**
@@ -135,16 +139,20 @@ async function executeDbUpdateCommand(
135
139
  errorUnexpected(
136
140
  `No migration bundle found for --to "${options.to}" (resolved hash: ${targetHash})`,
137
141
  {
138
- why: `The ref resolved successfully but no on-disk migration package has an end-contract hash matching ${targetHash}.`,
142
+ why: `The ref resolved successfully but no on-disk migration package has a destination (\`to\`) hash matching ${targetHash}.`,
139
143
  fix: 'Provide a ref or hash that corresponds to an existing migration package, or run `migration list` to see available migrations.',
140
144
  },
141
145
  ),
142
146
  );
143
147
  }
144
- const endContractPath = join(matchingBundle.dirPath, 'end-contract.json');
145
- const raw = await readFile(endContractPath, 'utf-8');
146
- contractJson = JSON.parse(raw) as Record<string, unknown>;
147
- contractJsonPathForSnapshot = endContractPath;
148
+ contractJson = (await readContractSnapshotJson(migrationsDir, targetHash)) as Record<
149
+ string,
150
+ unknown
151
+ >;
152
+ contractJsonPathForSnapshot = join(
153
+ contractSnapshotDir(migrationsDir, targetHash),
154
+ 'contract.json',
155
+ );
148
156
  } catch (error) {
149
157
  if (MigrationToolsError.is(error)) {
150
158
  return notOk(mapMigrationToolsError(error));
@@ -193,6 +201,7 @@ async function executeDbUpdateCommand(
193
201
  ...ifDefined('advanceRef', options.advanceRef),
194
202
  ...ifDefined('db', options.db),
195
203
  refsDir,
204
+ migrationsDir,
196
205
  contractIR,
197
206
  mode: result.value.mode,
198
207
  hash: advancementHash,
@@ -254,7 +263,7 @@ async function executeDbUpdateCommand(
254
263
  return notOk(error);
255
264
  }
256
265
 
257
- if (error instanceof ContractValidationError) {
266
+ if (isStructuredError(error) && error.code === 'CONTRACT.VALIDATION_FAILED') {
258
267
  return notOk(
259
268
  errorContractValidationFailed(`Contract validation failed: ${error.message}`, {
260
269
  where: { path: contractPathAbsolute },
@@ -10,10 +10,10 @@ import {
10
10
  } from '@prisma-next/framework-components/control';
11
11
  import { ifDefined } from '@prisma-next/utils/defined';
12
12
  import { notOk, ok, type Result } from '@prisma-next/utils/result';
13
+ import { isStructuredError } from '@prisma-next/utils/structured-error';
13
14
  import { Command } from 'commander';
14
15
  import { relative, resolve } from 'pathe';
15
16
  import { createControlClient } from '../control-api/client';
16
- import { ContractValidationError } from '../control-api/errors';
17
17
  import {
18
18
  CliStructuredError,
19
19
  errorContractValidationFailed,
@@ -275,7 +275,7 @@ async function resolveVerifySetup(
275
275
  try {
276
276
  contractJson = familyInstance.deserializeContract(JSON.parse(contractJsonContent) as unknown);
277
277
  } catch (error) {
278
- if (error instanceof ContractValidationError) {
278
+ if (isStructuredError(error) && error.code === 'CONTRACT.VALIDATION_FAILED') {
279
279
  return notOk(
280
280
  errorContractValidationFailed(`Contract validation failed: ${error.message}`, {
281
281
  where: { path: contractPathAbsolute },
@@ -318,7 +318,7 @@ function createVerifyClient(setup: VerifySetup) {
318
318
  target: setup.config.target,
319
319
  adapter: setup.config.adapter,
320
320
  driver: setup.config.driver!,
321
- extensionPacks: setup.config.extensionPacks ?? [],
321
+ extensions: setup.config.extensions ?? [],
322
322
  });
323
323
  }
324
324
 
@@ -330,7 +330,7 @@ function wrapVerifyError(
330
330
  if (CliStructuredError.is(error)) {
331
331
  return notOk(error);
332
332
  }
333
- if (error instanceof ContractValidationError) {
333
+ if (isStructuredError(error) && error.code === 'CONTRACT.VALIDATION_FAILED') {
334
334
  return notOk(
335
335
  errorContractValidationFailed(`Contract validation failed: ${error.message}`, {
336
336
  where: { path: contractPathAbsolute },
@@ -23,7 +23,7 @@ export function createFormatCommand(): Command {
23
23
  command,
24
24
  'Format your PSL contract source',
25
25
  'Formats the Prisma schema (PSL) contract source declared in your config\n' +
26
- '(contract.source.inputs[0]) in place. Only runs when contract.source.sourceFormat\n' +
26
+ '(contract.source.inputs[0]) in place. Only runs when contract.source.format\n' +
27
27
  "is 'psl'; a TypeScript or unset source is left untouched. Indent and newline are\n" +
28
28
  'read from the optional formatter config section, defaulting to two spaces and the\n' +
29
29
  'system newline.',
@@ -1,47 +1,60 @@
1
1
  import type { TargetId } from './templates/code-templates';
2
2
 
3
3
  /**
4
- * The schema-relative `.gitattributes` entries written for a freshly
5
- * initialised project (FR3.4). Mirrors the relevant subset of the
6
- * repo-root [`.gitattributes`](../../../../../../../../.gitattributes):
4
+ * The `.gitattributes` entries written for a freshly initialised project
5
+ * (FR3.4). Mirrors the relevant subset of the repo-root
6
+ * [`.gitattributes`](../../../../../../../../.gitattributes):
7
7
  *
8
8
  * - **Today**: `contract.json`, `contract.d.ts` are emitted on every
9
9
  * `prisma-next contract emit`. Marking them `linguist-generated`
10
10
  * keeps GitHub's diff stats honest and collapses the file in code
11
11
  * review by default.
12
- * - **Forward-looking**: `end-contract.*`, `start-contract.*`, `ops.json`,
13
- * `migration.json` are not yet emitted by `init` flows but will be
14
- * produced by adjacent commands (lower / migration tooling). Adding
15
- * them now matches Decision 5 (forward-looking subset) so the file
16
- * does not need to be amended every time a new artefact lands.
12
+ * - **Forward-looking**: `ops.json`, `migration.json` are not yet emitted
13
+ * by `init` flows but will be produced by adjacent commands (lower /
14
+ * migration tooling). Adding them now matches Decision 5
15
+ * (forward-looking subset) so the file does not need to be amended
16
+ * every time a new artifact lands.
17
17
  *
18
- * Patterns are written relative to the schema directory so a user
19
- * who runs `init --schema-path db/contract.prisma` gets
20
- * `db/contract.json linguist-generated` — not the workspace-glob form
21
- * `<glob>/contract.json` (which would over-match any unrelated
18
+ * `ARTIFACT_FILENAMES` entries are written relative to the schema
19
+ * directory so a user who runs `init --schema-path db/contract.prisma`
20
+ * gets `db/contract.json linguist-generated` — not the workspace-glob
21
+ * form `<glob>/contract.json` (which would over-match any unrelated
22
22
  * `contract.json` the user has elsewhere) and not the absolute
23
- * `DEFAULT_CONTRACT_SOURCE_DIR/contract.json` (which would silently break for a non-default
24
- * schema path).
23
+ * `DEFAULT_CONTRACT_SOURCE_DIR/contract.json` (which would silently
24
+ * break for a non-default schema path).
25
+ *
26
+ * The migration contract snapshot store (`migrations/snapshots/<hex>/`)
27
+ * is anchored to the migrations root instead, not the schema directory:
28
+ * migration package depth under `migrations/` varies (`app/<pkg>`,
29
+ * `<space>/<pkg>`, or a bare `<pkg>` in extension source repos), so no
30
+ * single schema-dir-relative pattern can reach every snapshot. See
31
+ * `STORE_GITATTRIBUTES_LINES` below.
25
32
  */
26
- const ARTEFACT_FILENAMES: readonly string[] = [
33
+ const ARTIFACT_FILENAMES: readonly string[] = [
27
34
  'contract.json',
28
35
  'contract.d.ts',
29
- 'end-contract.json',
30
- 'end-contract.d.ts',
31
- 'start-contract.json',
32
- 'start-contract.d.ts',
33
36
  'ops.json',
34
37
  'migration.json',
35
38
  ];
36
39
 
37
40
  const ATTRIBUTE = 'linguist-generated';
38
41
 
42
+ /**
43
+ * Full `.gitattributes` lines for the migration contract snapshot store,
44
+ * already anchored to the migrations root — unlike `ARTIFACT_FILENAMES`,
45
+ * these are not combined with the schema-relative prefix.
46
+ */
47
+ const STORE_GITATTRIBUTES_LINES: readonly string[] = [
48
+ `migrations/snapshots/**/contract.json ${ATTRIBUTE}`,
49
+ `migrations/snapshots/**/contract.d.ts ${ATTRIBUTE}`,
50
+ ];
51
+
39
52
  /**
40
53
  * Computes the `.gitattributes` lines this scaffold expects to own. Each
41
54
  * line has the shape `<path> linguist-generated`. The `target` parameter
42
55
  * is currently unused but accepted for symmetry with the other hygiene
43
56
  * helpers and to leave room for target-specific entries (e.g. a future
44
- * Mongo-only artefact) without a signature break.
57
+ * family-specific artifact) without a signature break.
45
58
  */
46
59
  export function requiredGitattributesLines(
47
60
  schemaDir: string,
@@ -49,7 +62,10 @@ export function requiredGitattributesLines(
49
62
  ): readonly string[] {
50
63
  const dir = schemaDir === '.' ? '' : schemaDir.replace(/\/+$/, '');
51
64
  const prefix = dir === '' ? '' : `${dir}/`;
52
- return ARTEFACT_FILENAMES.map((file) => `${prefix}${file} ${ATTRIBUTE}`);
65
+ return [
66
+ ...ARTIFACT_FILENAMES.map((file) => `${prefix}${file} ${ATTRIBUTE}`),
67
+ ...STORE_GITATTRIBUTES_LINES,
68
+ ];
53
69
  }
54
70
 
55
71
  /**
@@ -48,7 +48,7 @@ import {
48
48
  renderInitOutro,
49
49
  } from './output';
50
50
  import { type ProbeOutcome, type ProbeOverrides, probeServerVersion } from './probe-db';
51
- import { findStaleArtefacts, removeDependency } from './reinit-cleanup';
51
+ import { findStaleArtifacts, removeDependency } from './reinit-cleanup';
52
52
  import {
53
53
  DEFAULT_SKILL_SOURCES,
54
54
  formatSkillInstallCommand,
@@ -175,14 +175,14 @@ export async function runInit(
175
175
  { path: '.env.example', content: envExampleContent(inputs.target) },
176
176
  ];
177
177
 
178
- // FR9.1 — on re-init, queue the previously-emitted contract artefacts
178
+ // FR9.1 — on re-init, queue the previously-emitted contract artifacts
179
179
  // for deletion so a target switch (or schema-shape change) does not
180
180
  // leave a stale `contract.json` / `contract.d.ts` next to the new
181
181
  // schema source. Detection is filesystem-only (no parsing of the
182
182
  // previous config) so the cleanup is safe to run before the write
183
183
  // phase: each path is checked for existence in the precondition,
184
184
  // and missing-on-disk-at-write-time is tolerated.
185
- const filesToDelete: string[] = inputs.reinit ? [...findStaleArtefacts(baseDir, schemaDir)] : [];
185
+ const filesToDelete: string[] = inputs.reinit ? [...findStaleArtifacts(baseDir, schemaDir)] : [];
186
186
 
187
187
  // `init` delegates the skill to `npx skills add prisma/prisma-next#v<version>`,
188
188
  // so a hand-rolled `.agents/skills/prisma-next/SKILL.md` in the project
@@ -245,7 +245,7 @@ export async function runInit(
245
245
  }
246
246
 
247
247
  // FR3.4: idempotent .gitattributes — linguist-generated entries for
248
- // the emitted artefacts so GitHub diff stats / code review collapse
248
+ // the emitted artifacts so GitHub diff stats / code review collapse
249
249
  // them by default.
250
250
  const gitattributesPath = join(baseDir, '.gitattributes');
251
251
  const existingGitattributes = existsSync(gitattributesPath)
@@ -373,7 +373,7 @@ export async function runInit(
373
373
  }
374
374
  }
375
375
 
376
- // FR9.1 — delete stale artefacts after the new templates are written.
376
+ // FR9.1 — delete stale artifacts after the new templates are written.
377
377
  // Order is intentional: the names do not collide with `filesToWrite`
378
378
  // (we never write `contract.json` from this command — that's `contract
379
379
  // emit`'s job), so deletion *after* the writes guarantees we never
@@ -847,7 +847,7 @@ function formatCatalogWarning(
847
847
  /**
848
848
  * Recognised pnpm error signatures that justify a fallback to npm.
849
849
  *
850
- * These patterns indicate the published artefact itself is at fault
850
+ * These patterns indicate the published artifact itself is at fault
851
851
  * (a leaked `workspace:*` or `catalog:` specifier), not the user's
852
852
  * environment — pnpm is faithfully reporting "I cannot resolve this
853
853
  * registry version", and npm is willing to install it because npm
@@ -24,10 +24,9 @@ export const InitOutputSchema = type({
24
24
  filesWritten: 'string[]',
25
25
  /**
26
26
  * FR9.1 — files removed from disk during this run. Populated only on
27
- * re-init when previously-emitted contract artefacts (`contract.json`,
28
- * `contract.d.ts`, `start-/end-contract.*`, `ops.json`,
29
- * `migration.json`) were left behind by an earlier run. Empty on a
30
- * green-field init.
27
+ * re-init when previously-emitted contract artifacts (`contract.json`,
28
+ * `contract.d.ts`, `ops.json`, `migration.json`) were left behind by an
29
+ * earlier run. Empty on a green-field init.
31
30
  */
32
31
  filesDeleted: 'string[]',
33
32
  packagesInstalled: {
@@ -80,7 +79,7 @@ export function renderInitOutro(ui: TerminalUI, output: InitOutput, flags: Globa
80
79
 
81
80
  if (output.filesDeleted.length > 0) {
82
81
  lines.push('');
83
- lines.push('Files deleted (stale contract artefacts):');
82
+ lines.push('Files deleted (stale contract artifacts):');
84
83
  for (const file of output.filesDeleted) {
85
84
  lines.push(` • ${file}`);
86
85
  }
@@ -1,4 +1,5 @@
1
1
  import { createRequire } from 'node:module';
2
+ import { CliStructuredError } from '@prisma-next/errors/control';
2
3
  import { join } from 'pathe';
3
4
  import type { TargetId } from './templates/code-templates';
4
5
 
@@ -248,7 +249,10 @@ interface PgClient {
248
249
  export function parsePostgresVersion(versionString: string): string {
249
250
  const match = versionString.match(/PostgreSQL\s+(\d+(?:\.\d+)?)/i);
250
251
  if (match === null || match[1] === undefined) {
251
- throw new Error(`Could not parse PostgreSQL version from \`${versionString}\``);
252
+ throw new CliStructuredError(
253
+ 'CLI.INIT_PROBE_FAILED',
254
+ `Could not parse PostgreSQL version from \`${versionString}\``,
255
+ );
252
256
  }
253
257
  return match[1];
254
258
  }
@@ -275,7 +279,10 @@ async function defaultProbeMongo(
275
279
  const buildInfo = await client.db().admin().command({ buildInfo: 1 });
276
280
  const versionString = String(buildInfo.version ?? '');
277
281
  if (versionString.length === 0) {
278
- throw new Error('buildInfo did not include a `version` field');
282
+ throw new CliStructuredError(
283
+ 'CLI.INIT_PROBE_FAILED',
284
+ 'buildInfo did not include a `version` field',
285
+ );
279
286
  }
280
287
  return { serverVersion: versionString };
281
288
  } finally {