@prisma-next/cli 0.14.0-dev.9 → 0.14.0-dev.90

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 (118) hide show
  1. package/dist/cli.mjs +17 -15
  2. package/dist/cli.mjs.map +1 -1
  3. package/dist/{client-EnAjSmZg.mjs → client-DigVI8zX.mjs} +90 -104
  4. package/dist/client-DigVI8zX.mjs.map +1 -0
  5. package/dist/{command-helpers-6cNJZ863.mjs → command-helpers-CUoKYTlG.mjs} +11 -8
  6. package/dist/command-helpers-CUoKYTlG.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.mjs +3 -3
  10. package/dist/commands/db-schema.mjs +3 -3
  11. package/dist/commands/db-sign.mjs +4 -4
  12. package/dist/commands/db-update.mjs +4 -4
  13. package/dist/commands/db-verify.d.mts.map +1 -1
  14. package/dist/commands/db-verify.mjs +1 -1
  15. package/dist/commands/migrate.d.mts +1 -1
  16. package/dist/commands/migrate.d.mts.map +1 -1
  17. package/dist/commands/migrate.mjs +36 -37
  18. package/dist/commands/migrate.mjs.map +1 -1
  19. package/dist/commands/migration-check.d.mts +1 -1
  20. package/dist/commands/migration-check.mjs +1 -1
  21. package/dist/commands/migration-graph.d.mts +1 -1
  22. package/dist/commands/migration-graph.mjs +8 -8
  23. package/dist/commands/migration-graph.mjs.map +1 -1
  24. package/dist/commands/migration-list.d.mts +6 -6
  25. package/dist/commands/migration-list.d.mts.map +1 -1
  26. package/dist/commands/migration-list.mjs +1 -1
  27. package/dist/commands/migration-log.d.mts +1 -1
  28. package/dist/commands/migration-log.mjs +1 -1
  29. package/dist/commands/migration-new.mjs +3 -3
  30. package/dist/commands/migration-plan.d.mts.map +1 -1
  31. package/dist/commands/migration-plan.mjs +1 -1
  32. package/dist/commands/migration-show.mjs +3 -3
  33. package/dist/commands/migration-status.d.mts +1 -1
  34. package/dist/commands/migration-status.mjs +1 -1
  35. package/dist/commands/ref.d.mts +1 -1
  36. package/dist/commands/ref.mjs +2 -2
  37. package/dist/commands/telemetry/index.mjs +1 -1
  38. package/dist/{contract-at-errors-DdxWRjMc.mjs → contract-at-errors-D8CNDD7g.mjs} +2 -2
  39. package/dist/{contract-at-errors-DdxWRjMc.mjs.map → contract-at-errors-D8CNDD7g.mjs.map} +1 -1
  40. package/dist/{contract-emit-fz6pDR2n.mjs → contract-emit-sYrqzsdN.mjs} +8 -10
  41. package/dist/contract-emit-sYrqzsdN.mjs.map +1 -0
  42. package/dist/{contract-emit-D4xDDFkO.mjs → contract-emit-wgqrOXUI.mjs} +3 -3
  43. package/dist/{contract-emit-D4xDDFkO.mjs.map → contract-emit-wgqrOXUI.mjs.map} +1 -1
  44. package/dist/{contract-infer-Ars4yjBv.mjs → contract-infer-6SQnnVj7.mjs} +3 -3
  45. package/dist/{contract-infer-Ars4yjBv.mjs.map → contract-infer-6SQnnVj7.mjs.map} +1 -1
  46. package/dist/{contract-space-aggregate-loader-Bu2fjSWV.mjs → contract-space-aggregate-loader-DpB4x97a.mjs} +63 -5
  47. package/dist/contract-space-aggregate-loader-DpB4x97a.mjs.map +1 -0
  48. package/dist/{db-verify-CZxRV_er.mjs → db-verify-CEsG5Gek.mjs} +56 -60
  49. package/dist/db-verify-CEsG5Gek.mjs.map +1 -0
  50. package/dist/exports/control-api.d.mts +3 -3
  51. package/dist/exports/control-api.d.mts.map +1 -1
  52. package/dist/exports/control-api.mjs +2 -2
  53. package/dist/exports/index.mjs +2 -2
  54. package/dist/{format-C3XdaDuA.mjs → format-CjongNb1.mjs} +2 -2
  55. package/dist/{format-C3XdaDuA.mjs.map → format-CjongNb1.mjs.map} +1 -1
  56. package/dist/{framework-components-5RljWT7Q.mjs → framework-components-BbnM1RYm.mjs} +2 -2
  57. package/dist/{framework-components-5RljWT7Q.mjs.map → framework-components-BbnM1RYm.mjs.map} +1 -1
  58. package/dist/{init-C79Yb97u.mjs → init-D8YFfaD6.mjs} +18 -14
  59. package/dist/{init-C79Yb97u.mjs.map → init-D8YFfaD6.mjs.map} +1 -1
  60. package/dist/{inspect-live-schema-DR8Eyt29.mjs → inspect-live-schema-CEt_ztBJ.mjs} +3 -3
  61. package/dist/{inspect-live-schema-DR8Eyt29.mjs.map → inspect-live-schema-CEt_ztBJ.mjs.map} +1 -1
  62. package/dist/{migration-check-DWX4qdOB.mjs → migration-check-BJVt-B22.mjs} +9 -10
  63. package/dist/migration-check-BJVt-B22.mjs.map +1 -0
  64. package/dist/{migration-command-scaffold-6OT_Q-3Y.mjs → migration-command-scaffold-CJO3tjiR.mjs} +3 -3
  65. package/dist/{migration-command-scaffold-6OT_Q-3Y.mjs.map → migration-command-scaffold-CJO3tjiR.mjs.map} +1 -1
  66. package/dist/{migration-graph-command-render-CEez7YUK.mjs → migration-graph-command-render-B83xrnNy.mjs} +24 -24
  67. package/dist/{migration-graph-command-render-CEez7YUK.mjs.map → migration-graph-command-render-B83xrnNy.mjs.map} +1 -1
  68. package/dist/{migration-list-DVHVmMrf.mjs → migration-list-DTYM8xvm.mjs} +15 -15
  69. package/dist/migration-list-DTYM8xvm.mjs.map +1 -0
  70. package/dist/{migration-log-CQpc9JoL.mjs → migration-log-qZ8LpQIl.mjs} +4 -4
  71. package/dist/{migration-log-CQpc9JoL.mjs.map → migration-log-qZ8LpQIl.mjs.map} +1 -1
  72. package/dist/{migration-path-target-zK6RG6JY.mjs → migration-path-target-DmNwsDgs.mjs} +2 -2
  73. package/dist/{migration-path-target-zK6RG6JY.mjs.map → migration-path-target-DmNwsDgs.mjs.map} +1 -1
  74. package/dist/{migration-plan-o2aqjnfd.mjs → migration-plan-CfPrB80t.mjs} +29 -29
  75. package/dist/migration-plan-CfPrB80t.mjs.map +1 -0
  76. package/dist/{migration-status-B5AArPMQ.mjs → migration-status-CJqH7kX9.mjs} +13 -13
  77. package/dist/migration-status-CJqH7kX9.mjs.map +1 -0
  78. package/dist/{telemetry-BckOSUWG.mjs → telemetry-DHBBq5wT.mjs} +2 -2
  79. package/dist/{telemetry-BckOSUWG.mjs.map → telemetry-DHBBq5wT.mjs.map} +1 -1
  80. package/dist/{types-qETCCNbe.d.mts → types-DFoqNIcQ.d.mts} +26 -20
  81. package/dist/types-DFoqNIcQ.d.mts.map +1 -0
  82. package/dist/{verify-DLqhL2LU.mjs → verify-DDCb0R2r.mjs} +57 -159
  83. package/dist/verify-DDCb0R2r.mjs.map +1 -0
  84. package/package.json +28 -28
  85. package/src/commands/db-verify.ts +33 -19
  86. package/src/commands/migrate.ts +46 -43
  87. package/src/commands/migration-check.ts +5 -5
  88. package/src/commands/migration-graph.ts +4 -4
  89. package/src/commands/migration-list.ts +12 -12
  90. package/src/commands/migration-plan.ts +14 -3
  91. package/src/commands/migration-status.ts +9 -9
  92. package/src/control-api/client.ts +8 -23
  93. package/src/control-api/operations/contract-emit.ts +8 -26
  94. package/src/control-api/operations/db-init.ts +2 -2
  95. package/src/control-api/operations/db-run.ts +18 -17
  96. package/src/control-api/operations/db-verify.ts +52 -44
  97. package/src/control-api/operations/migrate.ts +72 -72
  98. package/src/control-api/operations/run-migration.ts +6 -5
  99. package/src/control-api/types.ts +8 -8
  100. package/src/utils/combine-verify-results.ts +94 -0
  101. package/src/utils/extension-pack-inputs.ts +1 -1
  102. package/src/utils/formatters/errors.ts +13 -5
  103. package/src/utils/formatters/verify.ts +88 -227
  104. package/src/utils/plan-resolution.ts +18 -18
  105. package/dist/client-EnAjSmZg.mjs.map +0 -1
  106. package/dist/command-helpers-6cNJZ863.mjs.map +0 -1
  107. package/dist/contract-emit-fz6pDR2n.mjs.map +0 -1
  108. package/dist/contract-space-aggregate-loader-Bu2fjSWV.mjs.map +0 -1
  109. package/dist/db-verify-CZxRV_er.mjs.map +0 -1
  110. package/dist/extension-pack-inputs-1ySHqxKG.mjs +0 -62
  111. package/dist/extension-pack-inputs-1ySHqxKG.mjs.map +0 -1
  112. package/dist/migration-check-DWX4qdOB.mjs.map +0 -1
  113. package/dist/migration-list-DVHVmMrf.mjs.map +0 -1
  114. package/dist/migration-plan-o2aqjnfd.mjs.map +0 -1
  115. package/dist/migration-status-B5AArPMQ.mjs.map +0 -1
  116. package/dist/types-qETCCNbe.d.mts.map +0 -1
  117. package/dist/verify-DLqhL2LU.mjs.map +0 -1
  118. package/src/utils/combine-schema-results.ts +0 -84
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify-DDCb0R2r.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 { 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 failure reason, prefixed onto an issue's message for display. */\nconst REASON_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 `reason` (and the path) into prose is this\n * formatter's job, not the differ's — the differ's issue is data (`path` +\n * `reason` + nodes), not prose.\n */\nfunction formatIssueMessage(issue: SchemaDiffIssue): string {\n return `${REASON_LABEL[issue.reason]}: ${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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prisma-next/cli",
3
- "version": "0.14.0-dev.9",
3
+ "version": "0.14.0-dev.90",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -8,46 +8,46 @@
8
8
  "prisma-next": "./dist/cli.js"
9
9
  },
10
10
  "dependencies": {
11
- "@clack/prompts": "^1.4.0",
12
- "@prisma-next/config": "0.14.0-dev.9",
13
- "@prisma-next/config-loader": "0.14.0-dev.9",
14
- "@prisma-next/contract": "0.14.0-dev.9",
15
- "@prisma-next/emitter": "0.14.0-dev.9",
16
- "@prisma-next/errors": "0.14.0-dev.9",
17
- "@prisma-next/framework-components": "0.14.0-dev.9",
18
- "@prisma-next/language-server": "0.14.0-dev.9",
19
- "@prisma-next/migration-tools": "0.14.0-dev.9",
20
- "@prisma-next/psl-parser": "0.14.0-dev.9",
21
- "@prisma-next/psl-printer": "0.14.0-dev.9",
22
- "@prisma-next/cli-telemetry": "0.14.0-dev.9",
23
- "@prisma-next/utils": "0.14.0-dev.9",
24
- "arktype": "^2.2.0",
11
+ "@clack/prompts": "^1.6.0",
12
+ "@prisma-next/config": "0.14.0-dev.90",
13
+ "@prisma-next/config-loader": "0.14.0-dev.90",
14
+ "@prisma-next/contract": "0.14.0-dev.90",
15
+ "@prisma-next/emitter": "0.14.0-dev.90",
16
+ "@prisma-next/errors": "0.14.0-dev.90",
17
+ "@prisma-next/framework-components": "0.14.0-dev.90",
18
+ "@prisma-next/language-server": "0.14.0-dev.90",
19
+ "@prisma-next/migration-tools": "0.14.0-dev.90",
20
+ "@prisma-next/psl-parser": "0.14.0-dev.90",
21
+ "@prisma-next/psl-printer": "0.14.0-dev.90",
22
+ "@prisma-next/cli-telemetry": "0.14.0-dev.90",
23
+ "@prisma-next/utils": "0.14.0-dev.90",
24
+ "arktype": "^2.2.2",
25
25
  "ci-info": "^4.3.1",
26
26
  "clipanion": "4.0.0-rc.4",
27
27
  "closest-match": "^1.3.3",
28
28
  "colorette": "^2.0.20",
29
29
  "commander": "^14.0.3",
30
- "esbuild": "^0.28.0",
30
+ "esbuild": "^0.28.1",
31
31
  "jsonc-parser": "^3.3.1",
32
- "package-manager-detector": "^1.6.0",
32
+ "package-manager-detector": "^1.7.0",
33
33
  "pathe": "^2.0.3",
34
34
  "string-width": "^8.2.1",
35
35
  "strip-ansi": "^7.2.0",
36
36
  "wrap-ansi": "^10.0.0"
37
37
  },
38
38
  "devDependencies": {
39
- "@prisma-next/sql-contract": "0.14.0-dev.9",
40
- "@prisma-next/sql-contract-emitter": "0.14.0-dev.9",
41
- "@prisma-next/sql-contract-ts": "0.14.0-dev.9",
42
- "@prisma-next/sql-operations": "0.14.0-dev.9",
43
- "@prisma-next/sql-runtime": "0.14.0-dev.9",
44
- "@prisma-next/test-utils": "0.14.0-dev.9",
45
- "@prisma-next/tsconfig": "0.14.0-dev.9",
46
- "@prisma-next/tsdown": "0.14.0-dev.9",
47
- "@types/node": "25.9.1",
48
- "tsdown": "0.22.1",
39
+ "@prisma-next/sql-contract": "0.14.0-dev.90",
40
+ "@prisma-next/sql-contract-emitter": "0.14.0-dev.90",
41
+ "@prisma-next/sql-contract-ts": "0.14.0-dev.90",
42
+ "@prisma-next/sql-operations": "0.14.0-dev.90",
43
+ "@prisma-next/sql-runtime": "0.14.0-dev.90",
44
+ "@prisma-next/test-utils": "0.14.0-dev.90",
45
+ "@prisma-next/tsconfig": "0.14.0-dev.90",
46
+ "@prisma-next/tsdown": "0.14.0-dev.90",
47
+ "@types/node": "25.9.4",
48
+ "tsdown": "0.22.3",
49
49
  "typescript": "5.9.3",
50
- "vitest": "4.1.8"
50
+ "vitest": "4.1.10"
51
51
  },
52
52
  "peerDependencies": {
53
53
  "typescript": ">=5.9"
@@ -1,10 +1,7 @@
1
1
  import { readFile } from 'node:fs/promises';
2
2
  import { loadConfig } from '@prisma-next/config-loader';
3
3
  import type { Contract } from '@prisma-next/contract/types';
4
- import type {
5
- VerifyDatabaseResult,
6
- VerifyDatabaseSchemaResult,
7
- } from '@prisma-next/framework-components/control';
4
+ import type { VerifyDatabaseResult } from '@prisma-next/framework-components/control';
8
5
  import {
9
6
  createControlStack,
10
7
  VERIFY_CODE_HASH_MISMATCH,
@@ -29,7 +26,7 @@ import {
29
26
  errorTargetMismatch,
30
27
  errorUnexpected,
31
28
  } from '../utils/cli-errors';
32
- import { combineSchemaResults } from '../utils/combine-schema-results';
29
+ import { type CombinedVerifyResult, combineVerifyResults } from '../utils/combine-verify-results';
33
30
  import {
34
31
  addGlobalOptions,
35
32
  maskConnectionUrl,
@@ -103,7 +100,7 @@ function mapVerifyFailure(verifyResult: VerifyDatabaseResult): CliStructuredErro
103
100
  return errorRuntime(verifyResult.summary);
104
101
  }
105
102
 
106
- type DbVerifyFailure = CliStructuredError | VerifyDatabaseSchemaResult;
103
+ type DbVerifyFailure = CliStructuredError | CombinedVerifyResult;
107
104
 
108
105
  function errorInvalidVerifyMode(options: {
109
106
  readonly why: string;
@@ -419,12 +416,13 @@ async function executeDbVerifyCommand(
419
416
  });
420
417
  }
421
418
 
422
- const combined = combineSchemaResults(
419
+ const combined = combineVerifyResults(
423
420
  aggregateResult.value.schemaResults,
424
421
  aggregateResult.value.appSpaceId,
425
422
  options.strict ?? false,
423
+ aggregateResult.value.unclaimed,
426
424
  );
427
- if (!combined.ok) {
425
+ if (!combined.result.ok) {
428
426
  return notOk(combined);
429
427
  }
430
428
 
@@ -438,10 +436,13 @@ async function executeDbVerifyCommand(
438
436
  ...ifDefined('missingCodecs', verifyResult.missingCodecs),
439
437
  ...ifDefined('codecCoverageSkipped', verifyResult.codecCoverageSkipped),
440
438
  schema: {
441
- summary: combined.summary,
442
- counts: combined.schema.counts,
443
- strict: combined.meta?.strict ?? false,
439
+ summary: combined.result.summary,
440
+ strict: combined.result.meta?.strict ?? false,
441
+ warnings: (combined.result.schema.warnings?.issues ?? []).map((issue) =>
442
+ issue.path.join('/'),
443
+ ),
444
444
  },
445
+ unclaimed: combined.unclaimed,
445
446
  meta: {
446
447
  ...(verifyResult.meta ?? {}),
447
448
  schemaVerification: 'performed',
@@ -459,7 +460,7 @@ async function executeDbSchemaOnlyVerifyCommand(
459
460
  options: DbVerifyOptions,
460
461
  flags: GlobalFlags,
461
462
  ui: TerminalUI,
462
- ): Promise<Result<VerifyDatabaseSchemaResult, CliStructuredError>> {
463
+ ): Promise<Result<CombinedVerifyResult, CliStructuredError>> {
463
464
  const paths = await resolveVerifyPaths(options);
464
465
  renderVerifyHeader(paths, options, 'schema-only', flags, ui);
465
466
 
@@ -484,10 +485,11 @@ async function executeDbSchemaOnlyVerifyCommand(
484
485
  if (!aggregateResult.ok) return notOk(aggregateResult.failure);
485
486
 
486
487
  return ok(
487
- combineSchemaResults(
488
+ combineVerifyResults(
488
489
  aggregateResult.value.schemaResults,
489
490
  aggregateResult.value.appSpaceId,
490
491
  options.strict ?? false,
492
+ aggregateResult.value.unclaimed,
491
493
  ),
492
494
  );
493
495
  } catch (error) {
@@ -542,18 +544,26 @@ export function createDbVerifyCommand(): Command {
542
544
 
543
545
  if (mode === 'schema-only') {
544
546
  const result = await executeDbSchemaOnlyVerifyCommand(options, flags, ui);
545
- const exitCode = handleResult(result, flags, ui, (schemaVerifyResult) => {
547
+ const exitCode = handleResult(result, flags, ui, (combined) => {
546
548
  if (flags.json) {
547
- ui.output(formatSchemaVerifyJson(schemaVerifyResult));
549
+ ui.output(formatSchemaVerifyJson(combined.result, combined.unclaimed));
548
550
  } else {
549
- const output = formatSchemaVerifyOutput(schemaVerifyResult, flags);
551
+ // Always show schema-drift failures, even in quiet mode — exiting 1
552
+ // without diagnostics is unhelpful (same policy as the full-mode
553
+ // failure branch below).
554
+ const renderFlags = combined.result.ok ? flags : { ...flags, quiet: false };
555
+ const output = formatSchemaVerifyOutput(
556
+ combined.result,
557
+ renderFlags,
558
+ combined.unclaimed,
559
+ );
550
560
  if (output) {
551
561
  ui.log(output);
552
562
  }
553
563
  }
554
564
  });
555
565
 
556
- if (result.ok && !result.value.ok) {
566
+ if (result.ok && !result.value.result.ok) {
557
567
  process.exit(1);
558
568
  }
559
569
 
@@ -580,11 +590,15 @@ export function createDbVerifyCommand(): Command {
580
590
  }
581
591
 
582
592
  if (flags.json) {
583
- ui.output(formatSchemaVerifyJson(result.failure));
593
+ ui.output(formatSchemaVerifyJson(result.failure.result, result.failure.unclaimed));
584
594
  } else {
585
595
  // Always show schema-drift failures, even in quiet mode — exiting 1 without
586
596
  // diagnostics is unhelpful.
587
- const output = formatSchemaVerifyOutput(result.failure, { ...flags, quiet: false });
597
+ const output = formatSchemaVerifyOutput(
598
+ result.failure.result,
599
+ { ...flags, quiet: false },
600
+ result.failure.unclaimed,
601
+ );
588
602
  if (output) {
589
603
  ui.log(output);
590
604
  }
@@ -2,7 +2,10 @@ import { readFile } from 'node:fs/promises';
2
2
  import { loadConfig } from '@prisma-next/config-loader';
3
3
  import type { Contract } from '@prisma-next/contract/types';
4
4
  import { createControlStack } from '@prisma-next/framework-components/control';
5
- import { type ContractSpaceMember, requireHeadRef } from '@prisma-next/migration-tools/aggregate';
5
+ import {
6
+ type AggregateContractSpace,
7
+ requireHeadRef,
8
+ } from '@prisma-next/migration-tools/aggregate';
6
9
  import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';
7
10
  import { errorUnknownInvariant, MigrationToolsError } from '@prisma-next/migration-tools/errors';
8
11
  import { findLatestMigration, isGraphNode } from '@prisma-next/migration-tools/migration-graph';
@@ -13,7 +16,7 @@ import { ifDefined } from '@prisma-next/utils/defined';
13
16
  import { notOk, ok, type Result } from '@prisma-next/utils/result';
14
17
  import { Command } from 'commander';
15
18
  import { createControlClient } from '../control-api/client';
16
- import { planMemberPath } from '../control-api/operations/migrate';
19
+ import { planSpacePath } from '../control-api/operations/migrate';
17
20
  import type {
18
21
  MigrateFailure,
19
22
  MigratePathDecision,
@@ -149,8 +152,8 @@ export interface MigrateResult {
149
152
  * Computes the path through the SAME seam as `executeMigrate`:
150
153
  * - `readAllMarkers()` for the from-state (when no `--from` is given), preserving
151
154
  * the full marker including `invariants` (not just `storageHash`).
152
- * - `planMemberPath()` (shared with `executeMigrate`) for per-member path selection,
153
- * which feeds `graphWalkStrategy()` with the same target hash, target invariants,
155
+ * - `planSpacePath()` (shared with `executeMigrate`) for per-space path selection,
156
+ * which feeds `resolveRecordedPath()` with the same target hash, target invariants,
154
157
  * and current marker as the real apply path uses.
155
158
  *
156
159
  * Returns BEFORE any write boundary (`runMigration` / marker / DDL). No
@@ -203,7 +206,7 @@ async function executeMigrateShowCommand(
203
206
  const appGraph = aggregate.app.graph();
204
207
 
205
208
  // Resolve the --to target (defaults to the on-disk contract, same as migrate).
206
- // Also capture the ref's invariants so planMemberPath feeds graphWalkStrategy the
209
+ // Also capture the ref's invariants so planSpacePath feeds resolveRecordedPath the
207
210
  // same target invariants that real migrate would use (refInvariants ?? headRef.invariants).
208
211
  let targetHash: string = contractHash;
209
212
  let refInvariants: readonly string[] | undefined;
@@ -258,13 +261,13 @@ async function executeMigrateShowCommand(
258
261
  // - Explicit --from: parse it offline (no connection).
259
262
  // - Omitted: read the live DB marker via readAllMarkers() — the same source migrate uses.
260
263
  //
261
- // Full marker records (storageHash + invariants) are preserved so planMemberPath
262
- // can feed graphWalkStrategy the complete currentMarker — exactly as executeMigrate
264
+ // Full marker records (storageHash + invariants) are preserved so planSpacePath
265
+ // can feed resolveRecordedPath the complete currentMarker — exactly as executeMigrate
263
266
  // does via familyInstance.readAllMarkers(). A stripped { storageHash, invariants: [] }
264
267
  // marker would produce a different `required` set and a different (incorrect) path.
265
268
  type LiveMarker = { readonly storageHash: string; readonly invariants: readonly string[] };
266
269
  const markerBySpace = new Map<string, LiveMarker | null>();
267
- const allMembers: ReadonlyArray<ContractSpaceMember> = [aggregate.app, ...aggregate.extensions];
270
+ const allSpaces: ReadonlyArray<AggregateContractSpace> = [aggregate.app, ...aggregate.extensions];
268
271
 
269
272
  if (hasExplicitFrom) {
270
273
  // @db with explicit --from requires a connection
@@ -301,7 +304,7 @@ async function executeMigrateShowCommand(
301
304
  } else {
302
305
  // Offline hypothetical: the --from ref only carries a hash (no live invariants).
303
306
  // Apply the from-hash marker to the APP space only. Extension spaces are left
304
- // absent from markerBySpace (treated as null / greenfield by planMemberPath),
307
+ // absent from markerBySpace (treated as null / greenfield by planSpacePath),
305
308
  // so they plan from their own marker → own head — exactly as executeMigrate does.
306
309
  const fromHash = fromResult.value.hash;
307
310
  const offlineMarker: LiveMarker | null =
@@ -334,9 +337,9 @@ async function executeMigrateShowCommand(
334
337
  const allMarkers = await client.readAllMarkers();
335
338
  // Store the full marker record (storageHash + invariants) per space.
336
339
  // This is the same data executeMigrate uses via familyInstance.readAllMarkers().
337
- for (const member of allMembers) {
338
- const marker = allMarkers.get(member.spaceId);
339
- markerBySpace.set(member.spaceId, marker ?? null);
340
+ for (const space of allSpaces) {
341
+ const marker = allMarkers.get(space.spaceId);
342
+ markerBySpace.set(space.spaceId, marker ?? null);
340
343
  }
341
344
  } catch (error) {
342
345
  if (CliStructuredError.is(error)) {
@@ -355,35 +358,35 @@ async function executeMigrateShowCommand(
355
358
  }
356
359
  }
357
360
 
358
- // Walk the path via planMemberPath — the same helper executeMigrate uses.
359
- // planMemberPath feeds graphWalkStrategy identical inputs (targetHash, targetInvariants,
361
+ // Walk the path via planSpacePath — the same helper executeMigrate uses.
362
+ // planSpacePath feeds resolveRecordedPath identical inputs (targetHash, targetInvariants,
360
363
  // currentMarker with full invariants), so the preview path is always the path migrate runs.
361
364
  //
362
365
  // Canonical schedule order: extensions alphabetically first, then app — mirroring the
363
366
  // runner's `applyOrder` in operations/migrate.ts so the "Will run, in order:" list
364
367
  // reflects the actual execution sequence (extensions install first, app last).
365
- const canonicalOrderMembers: ReadonlyArray<ContractSpaceMember> = [
368
+ const canonicalOrderSpaces: ReadonlyArray<AggregateContractSpace> = [
366
369
  ...aggregate.extensions,
367
370
  aggregate.app,
368
371
  ];
369
372
  const orderedMigrations: MigrateShowMigration[] = [];
370
- for (const member of canonicalOrderMembers) {
371
- const isAppMember = member.spaceId === aggregate.app.spaceId;
372
- const headRef = requireHeadRef(member);
373
- const memberTargetHash = isAppMember ? targetHash : headRef.hash;
374
- const memberRefInvariants = isAppMember ? refInvariants : undefined;
375
- const liveMarker = markerBySpace.get(member.spaceId) ?? null;
376
-
377
- const outcome = planMemberPath({
378
- member,
373
+ for (const space of canonicalOrderSpaces) {
374
+ const isAppSpace = space.spaceId === aggregate.app.spaceId;
375
+ const headRef = requireHeadRef(space);
376
+ const spaceTargetHash = isAppSpace ? targetHash : headRef.hash;
377
+ const spaceRefInvariants = isAppSpace ? refInvariants : undefined;
378
+ const liveMarker = markerBySpace.get(space.spaceId) ?? null;
379
+
380
+ const outcome = planSpacePath({
381
+ space,
379
382
  aggregate,
380
- targetHash: memberTargetHash,
381
- refInvariants: memberRefInvariants,
383
+ targetHash: spaceTargetHash,
384
+ refInvariants: spaceRefInvariants,
382
385
  liveMarker,
383
386
  });
384
387
 
385
388
  if (outcome.kind === 'at-head') {
386
- // Empty-graph member already at target — nothing to run for this space.
389
+ // Empty-graph space already at target — nothing to run for this space.
387
390
  continue;
388
391
  }
389
392
  if (outcome.kind === 'never-planned') {
@@ -417,7 +420,7 @@ async function executeMigrateShowCommand(
417
420
 
418
421
  for (const edge of outcome.plan.migrationEdges) {
419
422
  orderedMigrations.push({
420
- spaceId: member.spaceId,
423
+ spaceId: space.spaceId,
421
424
  dirName: edge.dirName,
422
425
  migrationHash: edge.migrationHash,
423
426
  from: edge.from,
@@ -445,12 +448,12 @@ async function executeMigrateShowCommand(
445
448
  // before rendering. This ensures the name column, hash column, and ops column
446
449
  // start at the same horizontal offset across every space section AND the
447
450
  // "Will run, in order:" list below.
448
- const memberLayouts = allMembers.map((member) => {
449
- const isApp = member.spaceId === aggregate.app.spaceId;
450
- const memberGraph = member.graph();
451
- const rowModel = buildMigrationGraphRows(memberGraph, isApp ? { contractHash } : {});
451
+ const spaceLayouts = allSpaces.map((space) => {
452
+ const isApp = space.spaceId === aggregate.app.spaceId;
453
+ const spaceGraph = space.graph();
454
+ const rowModel = buildMigrationGraphRows(spaceGraph, isApp ? { contractHash } : {});
452
455
  const edgeAnnotations = new Map<string, MigrationEdgeAnnotation>();
453
- for (const edge of memberGraph.migrationByHash.values()) {
456
+ for (const edge of spaceGraph.migrationByHash.values()) {
454
457
  edgeAnnotations.set(edge.migrationHash, {
455
458
  pathHighlight: onPathHashes.has(edge.migrationHash) ? 'on-path' : 'off-path',
456
459
  });
@@ -459,17 +462,17 @@ async function executeMigrateShowCommand(
459
462
  // green/continuous; off-path lanes dim. Rows, gutter, and labels all come
460
463
  // from this one grid.
461
464
  const grid = buildGrid(rowModel, {}, highlightFromEdgeAnnotations(edgeAnnotations));
462
- return { member, isApp, memberGraph, rowModel, grid, edgeAnnotations };
465
+ return { space, isApp, spaceGraph, rowModel, grid, edgeAnnotations };
463
466
  });
464
467
 
465
468
  // Global max across all space grids so every section's labels share columns.
466
469
  const globalLabelColumn =
467
- memberLayouts.length > 1
468
- ? Math.max(...memberLayouts.map(({ grid }) => computeLabelColumn(grid, 'unicode')))
470
+ spaceLayouts.length > 1
471
+ ? Math.max(...spaceLayouts.map(({ grid }) => computeLabelColumn(grid, 'unicode')))
469
472
  : undefined;
470
473
  const globalMaxDirNameWidthFromLayouts =
471
- memberLayouts.length > 1
472
- ? Math.max(...memberLayouts.map(({ rowModel }) => computeMaxDirNameWidth(rowModel)))
474
+ spaceLayouts.length > 1
475
+ ? Math.max(...spaceLayouts.map(({ rowModel }) => computeMaxDirNameWidth(rowModel)))
473
476
  : undefined;
474
477
  // The run-list name column width must be at least as wide as the global tree dirName
475
478
  // width so that tree sections and the list align at the hash column.
@@ -485,10 +488,10 @@ async function executeMigrateShowCommand(
485
488
  runListLeftPad = globalLabelColumn;
486
489
 
487
490
  // Render each space section with globally computed widths.
488
- const showSpaceHeadings = allMembers.length > 1;
491
+ const showSpaceHeadings = allSpaces.length > 1;
489
492
  const sections: string[] = [];
490
- for (const { member, isApp, rowModel, grid, edgeAnnotations } of memberLayouts) {
491
- const liveMarker = markerBySpace.get(member.spaceId) ?? null;
493
+ for (const { space, isApp, rowModel, grid, edgeAnnotations } of spaceLayouts) {
494
+ const liveMarker = markerBySpace.get(space.spaceId) ?? null;
492
495
  const liveMarkerHash = liveMarker?.storageHash ?? EMPTY_CONTRACT_HASH;
493
496
  const tree = renderMigrationGraphCommand({
494
497
  grid,
@@ -496,7 +499,7 @@ async function executeMigrateShowCommand(
496
499
  contractHash,
497
500
  isAppSpace: isApp,
498
501
  ...(needsLiveMarker ? { dbHash: liveMarkerHash } : {}),
499
- refsByHash: listRefsByContractHash(member),
502
+ refsByHash: listRefsByContractHash(space),
500
503
  edgeAnnotationsByHash: edgeAnnotations,
501
504
  colorize,
502
505
  glyphMode: 'unicode',
@@ -505,7 +508,7 @@ async function executeMigrateShowCommand(
505
508
  });
506
509
  if (tree.length === 0) continue;
507
510
  if (showSpaceHeadings) {
508
- sections.push(`${member.spaceId}:\n${indentMigrationGraphTreeBlock(tree, ' ')}`);
511
+ sections.push(`${space.spaceId}:\n${indentMigrationGraphTreeBlock(tree, ' ')}`);
509
512
  } else {
510
513
  sections.push(tree);
511
514
  }
@@ -156,16 +156,16 @@ export async function enumerateCheckSpaces(
156
156
  candidateDirs.filter((name) => !RESERVED_SPACE_SUBDIR_NAMES.has(name)).filter(isValidSpaceId),
157
157
  );
158
158
  const spaces: CheckSpace[] = [];
159
- for (const member of aggregate.spaces()) {
160
- const spaceId = member.spaceId;
159
+ for (const space of aggregate.spaces()) {
160
+ const spaceId = space.spaceId;
161
161
  if (!isValidSpaceId(spaceId)) continue;
162
162
  if (!onDiskSpaceIds.has(spaceId)) continue;
163
163
  const migrationsDir = spaceMigrationDirectory(projectMigrationsDir, spaceId);
164
164
  spaces.push({
165
165
  spaceId,
166
- packages: member.packages,
167
- refs: member.refs,
168
- graph: member.graph(),
166
+ packages: space.packages,
167
+ refs: space.refs,
168
+ graph: space.graph(),
169
169
  migrationsDir,
170
170
  refsDir: spaceRefsDirectory(migrationsDir),
171
171
  });
@@ -154,13 +154,13 @@ export async function executeMigrationGraphCommand(
154
154
  const treeSections: MigrationGraphTreeSection[] = [];
155
155
  const spaces: MigrationSpaceGraphEntry[] = [];
156
156
  for (const spaceEntry of scopedSpaces) {
157
- const member = aggregate.space(spaceEntry.space);
158
- if (member === undefined) {
157
+ const space = aggregate.space(spaceEntry.space);
158
+ if (space === undefined) {
159
159
  continue;
160
160
  }
161
- const graph = member.graph();
161
+ const graph = space.graph();
162
162
  const isAppSpace = spaceEntry.space === aggregate.app.spaceId;
163
- const refsByHash = listRefsByContractHash(member);
163
+ const refsByHash = listRefsByContractHash(space);
164
164
  const tree =
165
165
  spaceEntry.migrations.length === 0
166
166
  ? ''
@@ -1,7 +1,7 @@
1
1
  import { loadConfig } from '@prisma-next/config-loader';
2
2
  import type {
3
+ AggregateContractSpace,
3
4
  ContractSpaceAggregate,
4
- ContractSpaceMember,
5
5
  } from '@prisma-next/migration-tools/aggregate';
6
6
  import type { MigrationGraph } from '@prisma-next/migration-tools/graph';
7
7
  import { HEAD_REF_NAME, refsByContractHash } from '@prisma-next/migration-tools/refs';
@@ -59,18 +59,18 @@ function compareDirNamesDescending(a: MigrationListEntry, b: MigrationListEntry)
59
59
 
60
60
  /**
61
61
  * Ref names decorating a space's destination contract hashes. The
62
- * tolerant `member.refs` deliberately omits the structural `head.json`;
62
+ * tolerant `space.refs` deliberately omits the structural `head.json`;
63
63
  * for extension spaces the old enumerator surfaced it as a `head`
64
- * decoration on the tip migration, so fold `member.headRef` back in to
64
+ * decoration on the tip migration, so fold `space.headRef` back in to
65
65
  * keep that output. The app space synthesises its head, so it carries
66
66
  * no on-disk `head` ref to restore.
67
67
  */
68
68
  export function listRefsByContractHash(
69
- member: ContractSpaceMember,
69
+ space: AggregateContractSpace,
70
70
  ): ReadonlyMap<string, readonly string[]> {
71
- const byHash = new Map(refsByContractHash(member.refs));
72
- if (member.spaceId !== APP_SPACE_ID && member.headRef !== null) {
73
- const hash = member.headRef.hash;
71
+ const byHash = new Map(refsByContractHash(space.refs));
72
+ if (space.spaceId !== APP_SPACE_ID && space.headRef !== null) {
73
+ const hash = space.headRef.hash;
74
74
  const bucket = byHash.get(hash) ?? [];
75
75
  if (!bucket.includes(HEAD_REF_NAME)) {
76
76
  byHash.set(hash, [...bucket, HEAD_REF_NAME].sort());
@@ -92,7 +92,7 @@ async function orderedOnDiskSpaceIds(projectMigrationsDir: string): Promise<read
92
92
  * {@link MigrationSpaceListEntry} rows `migration list` displays.
93
93
  *
94
94
  * Space membership matches the on-disk contract-space directories (not the
95
- * aggregate's always-present synthesized app member when `migrations/app/`
95
+ * aggregate's always-present synthesized app space when `migrations/app/`
96
96
  * is absent); package and ref data come from `aggregate.space(id)`.
97
97
  */
98
98
  export async function migrationSpaceListEntriesFromAggregate(
@@ -103,12 +103,12 @@ export async function migrationSpaceListEntriesFromAggregate(
103
103
  const spaces: MigrationSpaceListEntry[] = [];
104
104
 
105
105
  for (const spaceId of spaceIds) {
106
- const member = aggregate.space(spaceId);
107
- if (member === undefined) {
106
+ const space = aggregate.space(spaceId);
107
+ if (space === undefined) {
108
108
  continue;
109
109
  }
110
- const refsByHash = listRefsByContractHash(member);
111
- const migrations: MigrationListEntry[] = member.packages
110
+ const refsByHash = listRefsByContractHash(space);
111
+ const migrations: MigrationListEntry[] = space.packages
112
112
  .map((pkg) => ({
113
113
  name: pkg.dirName,
114
114
  hash: pkg.metadata.migrationHash,