@prisma-next/cli 0.4.0-dev.9 → 0.5.0-dev.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -18
- package/dist/cli-errors-C0JhVj0c.d.mts +4 -0
- package/dist/cli-errors-DHq6GQGu.mjs +5 -0
- package/dist/cli.mjs +7 -18
- package/dist/cli.mjs.map +1 -1
- package/dist/{client-CJxHfhze.mjs → client-TG7rbCWT.mjs} +7 -6
- package/dist/{client-CJxHfhze.mjs.map → client-TG7rbCWT.mjs.map} +1 -1
- package/dist/commands/contract-emit.d.mts.map +1 -1
- package/dist/commands/contract-emit.mjs +2 -7
- package/dist/commands/contract-infer.mjs +2 -8
- package/dist/commands/db-init.mjs +6 -7
- package/dist/commands/db-init.mjs.map +1 -1
- package/dist/commands/db-schema.mjs +4 -7
- package/dist/commands/db-schema.mjs.map +1 -1
- package/dist/commands/db-sign.mjs +5 -6
- package/dist/commands/db-sign.mjs.map +1 -1
- package/dist/commands/db-update.mjs +6 -7
- package/dist/commands/db-update.mjs.map +1 -1
- package/dist/commands/db-verify.mjs +6 -7
- package/dist/commands/db-verify.mjs.map +1 -1
- package/dist/commands/migration-apply.d.mts +1 -1
- package/dist/commands/migration-apply.d.mts.map +1 -1
- package/dist/commands/migration-apply.mjs +33 -25
- package/dist/commands/migration-apply.mjs.map +1 -1
- package/dist/commands/migration-new.d.mts.map +1 -1
- package/dist/commands/migration-new.mjs +47 -22
- package/dist/commands/migration-new.mjs.map +1 -1
- package/dist/commands/migration-plan.d.mts +6 -1
- package/dist/commands/migration-plan.d.mts.map +1 -1
- package/dist/commands/migration-plan.mjs +92 -69
- package/dist/commands/migration-plan.mjs.map +1 -1
- package/dist/commands/migration-ref.d.mts +1 -1
- package/dist/commands/migration-ref.mjs +4 -4
- package/dist/commands/migration-show.d.mts +2 -2
- package/dist/commands/migration-show.d.mts.map +1 -1
- package/dist/commands/migration-show.mjs +9 -14
- package/dist/commands/migration-show.mjs.map +1 -1
- package/dist/commands/migration-status.d.mts +4 -5
- package/dist/commands/migration-status.d.mts.map +1 -1
- package/dist/commands/migration-status.mjs +2 -7
- package/dist/config-loader-_W4T21X1.mjs +90 -0
- package/dist/config-loader-_W4T21X1.mjs.map +1 -0
- package/dist/config-loader.d.mts.map +1 -1
- package/dist/config-loader.mjs +1 -1
- package/dist/{contract-emit-gpJNLGs7.mjs → contract-emit-CNYyzJwF.mjs} +18 -14
- package/dist/contract-emit-CNYyzJwF.mjs.map +1 -0
- package/dist/{contract-emit-CKig_Lra.mjs → contract-emit-CQfj7xJn.mjs} +24 -20
- package/dist/contract-emit-CQfj7xJn.mjs.map +1 -0
- package/dist/contract-emit-fhNwwhkQ.mjs +4 -0
- package/dist/{contract-infer-BDJgg7Xb.mjs → contract-infer-BP3DrGgz.mjs} +3 -3
- package/dist/{contract-infer-BDJgg7Xb.mjs.map → contract-infer-BP3DrGgz.mjs.map} +1 -1
- package/dist/exports/control-api.d.mts +2 -2
- package/dist/exports/control-api.d.mts.map +1 -1
- package/dist/exports/control-api.mjs +3 -5
- package/dist/exports/index.mjs +2 -7
- package/dist/exports/index.mjs.map +1 -1
- package/dist/{framework-components-Bsr1GaIj.mjs → framework-components-DfZKQBQ2.mjs} +2 -2
- package/dist/{framework-components-Bsr1GaIj.mjs.map → framework-components-DfZKQBQ2.mjs.map} +1 -1
- package/dist/{init-DZWvhEP0.mjs → init-CQfo_4Ro.mjs} +2 -2
- package/dist/{init-DZWvhEP0.mjs.map → init-CQfo_4Ro.mjs.map} +1 -1
- package/dist/{inspect-live-schema-ChqrALmw.mjs → inspect-live-schema-DWzf4Q_m.mjs} +5 -5
- package/dist/{inspect-live-schema-ChqrALmw.mjs.map → inspect-live-schema-DWzf4Q_m.mjs.map} +1 -1
- package/dist/migration-cli.d.mts +50 -0
- package/dist/migration-cli.d.mts.map +1 -0
- package/dist/migration-cli.mjs +184 -0
- package/dist/migration-cli.mjs.map +1 -0
- package/dist/{migration-command-scaffold-B0oH_hyB.mjs → migration-command-scaffold-CLMD302g.mjs} +6 -6
- package/dist/{migration-command-scaffold-B0oH_hyB.mjs.map → migration-command-scaffold-CLMD302g.mjs.map} +1 -1
- package/dist/{migration-status-CPamfEPj.mjs → migration-status-B0HLF7So.mjs} +18 -34
- package/dist/migration-status-B0HLF7So.mjs.map +1 -0
- package/dist/{migrations-BIsjFjSV.mjs → migrations-B0dOQlk0.mjs} +4 -15
- package/dist/migrations-B0dOQlk0.mjs.map +1 -0
- package/dist/{result-handler-AFK4hxyX.mjs → result-handler-CIyu0Pdt.mjs} +22 -11
- package/dist/result-handler-CIyu0Pdt.mjs.map +1 -0
- package/dist/{validate-contract-deps-DBH6iTAD.mjs → validate-contract-deps-esa-VQ0h.mjs} +1 -1
- package/dist/{validate-contract-deps-DBH6iTAD.mjs.map → validate-contract-deps-esa-VQ0h.mjs.map} +1 -1
- package/dist/{verify-C56CuQc7.mjs → verify-BxiVp50b.mjs} +2 -2
- package/dist/{verify-C56CuQc7.mjs.map → verify-BxiVp50b.mjs.map} +1 -1
- package/package.json +19 -19
- package/src/cli.ts +1 -5
- package/src/commands/contract-emit.ts +9 -10
- package/src/commands/migration-apply.ts +34 -23
- package/src/commands/migration-new.ts +39 -17
- package/src/commands/migration-plan.ts +119 -104
- package/src/commands/migration-show.ts +6 -16
- package/src/commands/migration-status.ts +14 -34
- package/src/config-loader.ts +35 -29
- package/src/config-path-validation.ts +75 -0
- package/src/control-api/client.ts +2 -1
- package/src/control-api/operations/contract-emit.ts +24 -23
- package/src/control-api/types.ts +1 -1
- package/src/migration-cli.ts +254 -0
- package/src/utils/cli-errors.ts +1 -0
- package/src/utils/command-helpers.ts +15 -19
- package/src/utils/formatters/graph-migration-mapper.ts +5 -14
- package/src/utils/formatters/help.ts +0 -1
- package/src/utils/formatters/migrations.ts +2 -29
- package/dist/cli-errors-BUuJr6py.mjs +0 -5
- package/dist/cli-errors-Dic2eADK.d.mts +0 -4
- package/dist/commands/migration-emit.d.mts +0 -38
- package/dist/commands/migration-emit.d.mts.map +0 -1
- package/dist/commands/migration-emit.mjs +0 -81
- package/dist/commands/migration-emit.mjs.map +0 -1
- package/dist/config-loader-C4VXKl8f.mjs +0 -43
- package/dist/config-loader-C4VXKl8f.mjs.map +0 -1
- package/dist/contract-emit-CKig_Lra.mjs.map +0 -1
- package/dist/contract-emit-CU-SYNe4.mjs +0 -6
- package/dist/contract-emit-gpJNLGs7.mjs.map +0 -1
- package/dist/migration-emit-Du4DBMqz.mjs +0 -125
- package/dist/migration-emit-Du4DBMqz.mjs.map +0 -1
- package/dist/migration-status-CPamfEPj.mjs.map +0 -1
- package/dist/migrations-BIsjFjSV.mjs.map +0 -1
- package/dist/result-handler-AFK4hxyX.mjs.map +0 -1
- package/src/commands/migration-emit.ts +0 -134
- package/src/lib/migration-emit.ts +0 -125
- package/src/lib/migration-strategy.ts +0 -49
package/dist/{validate-contract-deps-DBH6iTAD.mjs.map → validate-contract-deps-esa-VQ0h.mjs.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-contract-deps-
|
|
1
|
+
{"version":3,"file":"validate-contract-deps-esa-VQ0h.mjs","names":["missing: string[]"],"sources":["../src/utils/validate-contract-deps.ts"],"sourcesContent":["import { createRequire } from 'node:module';\n\nconst IMPORT_PATTERN = /import\\s+type\\s+.*?\\s+from\\s+['\"](@[^/]+\\/[^/'\"]+)/g;\n\nexport function extractPackageSpecifiers(dtsContent: string): string[] {\n const packages = new Set<string>();\n for (const match of dtsContent.matchAll(IMPORT_PATTERN)) {\n const pkg = match[1];\n if (pkg) packages.add(pkg);\n }\n return [...packages];\n}\n\nexport interface ContractDepsValidation {\n readonly missing: readonly string[];\n readonly warning?: string;\n}\n\nexport function validateContractDeps(\n dtsContent: string,\n projectRoot: string,\n): ContractDepsValidation {\n const packages = extractPackageSpecifiers(dtsContent);\n const resolve = createRequire(`${projectRoot}/package.json`);\n\n const missing: string[] = [];\n for (const pkg of packages) {\n try {\n resolve.resolve(`${pkg}/package.json`);\n } catch {\n missing.push(pkg);\n }\n }\n\n if (missing.length === 0) {\n return { missing };\n }\n\n const list = missing.map((p) => ` - ${p}`).join('\\n');\n const warning = [\n 'contract.d.ts imports types from packages that are not installed:',\n list,\n '',\n 'Install them with your package manager:',\n ...missing.map((p) => ` ${p}`),\n ].join('\\n');\n\n return { missing, warning };\n}\n"],"mappings":";;;AAEA,MAAM,iBAAiB;AAEvB,SAAgB,yBAAyB,YAA8B;CACrE,MAAM,2BAAW,IAAI,KAAa;AAClC,MAAK,MAAM,SAAS,WAAW,SAAS,eAAe,EAAE;EACvD,MAAM,MAAM,MAAM;AAClB,MAAI,IAAK,UAAS,IAAI,IAAI;;AAE5B,QAAO,CAAC,GAAG,SAAS;;AAQtB,SAAgB,qBACd,YACA,aACwB;CACxB,MAAM,WAAW,yBAAyB,WAAW;CACrD,MAAM,UAAU,cAAc,GAAG,YAAY,eAAe;CAE5D,MAAMA,UAAoB,EAAE;AAC5B,MAAK,MAAM,OAAO,SAChB,KAAI;AACF,UAAQ,QAAQ,GAAG,IAAI,eAAe;SAChC;AACN,UAAQ,KAAK,IAAI;;AAIrB,KAAI,QAAQ,WAAW,EACrB,QAAO,EAAE,SAAS;AAYpB,QAAO;EAAE;EAAS,SARF;GACd;GAFW,QAAQ,KAAK,MAAM,OAAO,IAAI,CAAC,KAAK,KAAK;GAIpD;GACA;GACA,GAAG,QAAQ,KAAK,MAAM,KAAK,IAAI;GAChC,CAAC,KAAK,KAAK;EAEe"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { b as formatDim, x as isVerbose, y as createColorFormatter } from "./result-handler-
|
|
1
|
+
import { b as formatDim, x as isVerbose, y as createColorFormatter } from "./result-handler-CIyu0Pdt.mjs";
|
|
2
2
|
import { ifDefined } from "@prisma-next/utils/defined";
|
|
3
3
|
import { bold, cyan, dim, green, magenta, red, yellow } from "colorette";
|
|
4
4
|
|
|
@@ -382,4 +382,4 @@ function formatSignJson(result) {
|
|
|
382
382
|
|
|
383
383
|
//#endregion
|
|
384
384
|
export { formatSignJson as a, formatVerifyOutput as c, formatSchemaVerifyOutput as i, formatIntrospectOutput as n, formatSignOutput as o, formatSchemaVerifyJson as r, formatVerifyJson as s, formatIntrospectJson as t };
|
|
385
|
-
//# sourceMappingURL=verify-
|
|
385
|
+
//# sourceMappingURL=verify-BxiVp50b.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify-C56CuQc7.mjs","names":["lines: string[]","formattedLabel: string","statusColor: (text: string) => string","labelColor: (text: string) => string"],"sources":["../src/utils/formatters/verify.ts"],"sourcesContent":["import type {\n CoreSchemaView,\n IntrospectSchemaResult,\n SchemaTreeNode,\n SchemaVerificationNode,\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// ============================================================================\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 counts: VerifyDatabaseSchemaResult['schema']['counts'];\n readonly strict: boolean;\n };\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.mode === 'full' && result.schema && isVerbose(flags, 1)) {\n lines.push(\n `${formatDimText(` schema: pass=${result.schema.counts.pass} warn=${result.schema.counts.warn} fail=${result.schema.counts.fail}`)}`,\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 ...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 * Matches the style of renderSchemaVerificationTree for consistency.\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 * Renders a schema verification tree structure from SchemaVerificationNode.\n * Similar to renderSchemaTree but for verification nodes with status-based colors and glyphs.\n */\nfunction renderSchemaVerificationTree(\n node: SchemaVerificationNode,\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 status glyph and color based on status\n let statusGlyph = '';\n let statusColor: (text: string) => string = (text) => text;\n if (useColor) {\n switch (node.status) {\n case 'pass':\n statusGlyph = '✔';\n statusColor = green;\n break;\n case 'warn':\n statusGlyph = '⚠';\n statusColor = (text) => (useColor ? yellow(text) : text);\n break;\n case 'fail':\n statusGlyph = '✖';\n statusColor = red;\n break;\n }\n } else {\n switch (node.status) {\n case 'pass':\n statusGlyph = '✔';\n break;\n case 'warn':\n statusGlyph = '⚠';\n break;\n case 'fail':\n statusGlyph = '✖';\n break;\n }\n }\n\n // Format node label with color based on kind\n // For column nodes, we need to parse the name to color code different parts\n let labelColor: (text: string) => string = (text) => text;\n let formattedLabel: string = node.name;\n\n if (useColor) {\n switch (node.kind) {\n case 'contract':\n case 'schema':\n labelColor = bold;\n formattedLabel = labelColor(node.name);\n break;\n case 'table': {\n // Parse \"table tableName\" format - color \"table\" dim, tableName cyan\n const tableMatch = node.name.match(/^table\\s+(.+)$/);\n if (tableMatch?.[1]) {\n const tableName = tableMatch[1];\n formattedLabel = `${dim('table')} ${cyan(tableName)}`;\n } else {\n formattedLabel = dim(node.name);\n }\n break;\n }\n case 'columns':\n labelColor = dim;\n formattedLabel = labelColor(node.name);\n break;\n case 'column': {\n // Parse column name format: \"columnName: contractType -> nativeType (nullability)\"\n // Color code: column name (cyan), contract type (default), native type (dim), nullability (dim)\n const columnMatch = node.name.match(/^([^:]+):\\s*(.+)$/);\n if (columnMatch?.[1] && columnMatch[2]) {\n const columnName = columnMatch[1];\n const rest = columnMatch[2];\n // Parse rest: \"contractType -> nativeType (nullability)\"\n // Match contract type (can contain /, @, etc.), arrow, native type, then nullability in parentheses\n const typeMatch = rest.match(/^([^\\s→]+)\\s*→\\s*([^\\s(]+)\\s*(\\([^)]+\\))$/);\n if (typeMatch?.[1] && typeMatch[2] && typeMatch[3]) {\n const contractType = typeMatch[1];\n const nativeType = typeMatch[2];\n const nullability = typeMatch[3];\n formattedLabel = `${cyan(columnName)}: ${contractType} → ${dim(nativeType)} ${dim(nullability)}`;\n } else {\n // Fallback if format doesn't match (e.g., no native type or no nullability)\n formattedLabel = `${cyan(columnName)}: ${rest}`;\n }\n } else {\n formattedLabel = node.name;\n }\n break;\n }\n case 'type':\n case 'nullability':\n labelColor = (text) => text; // Default color\n formattedLabel = labelColor(node.name);\n break;\n case 'primaryKey': {\n // Parse \"primary key: columnName\" format - color \"primary key\" dim, columnName cyan\n const pkMatch = node.name.match(/^primary key:\\s*(.+)$/);\n if (pkMatch?.[1]) {\n const columnNames = pkMatch[1];\n formattedLabel = `${dim('primary key')}: ${cyan(columnNames)}`;\n } else {\n formattedLabel = dim(node.name);\n }\n break;\n }\n case 'foreignKey':\n case 'unique':\n case 'index':\n labelColor = dim;\n formattedLabel = labelColor(node.name);\n break;\n case 'dependency': {\n // Parse specific extension message formats\n // \"database is postgres\" -> dim \"database is\", cyan \"postgres\"\n const dbMatch = node.name.match(/^database is\\s+(.+)$/);\n if (dbMatch?.[1]) {\n const dbName = dbMatch[1];\n formattedLabel = `${dim('database is')} ${cyan(dbName)}`;\n } else {\n // \"vector extension is enabled\" -> dim everything except extension name\n // Match pattern: \"extensionName extension is enabled\"\n const extMatch = node.name.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 name with magenta\n labelColor = magenta;\n formattedLabel = labelColor(node.name);\n }\n }\n break;\n }\n default:\n formattedLabel = node.name;\n break;\n }\n } else {\n formattedLabel = node.name;\n }\n\n const statusGlyphColored = statusColor(statusGlyph);\n\n // Build the label with optional message for failure/warn nodes\n let nodeLabel = formattedLabel;\n if (\n (node.status === 'fail' || node.status === 'warn') &&\n node.message &&\n node.message.length > 0\n ) {\n // Always show message for failure/warn nodes - it provides crucial context\n // For parent nodes, the message summarizes child failures\n // For leaf nodes, the message explains the specific issue\n const messageText = formatDimText(`(${node.message})`);\n nodeLabel = `${formattedLabel} ${messageText}`;\n }\n\n // Root node renders without tree characters or | prefix\n // Root node renders without tree characters or prefix\n if (isRoot) {\n lines.push(`${statusGlyphColored} ${nodeLabel}`);\n } else {\n const treeChar = isLast ? '└' : '├';\n const treePrefix = `${formatDimText(treeChar)}─ `;\n lines.push(`${prefix}${treePrefix}${statusGlyphColored} ${nodeLabel}`);\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 = renderSchemaVerificationTree(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 schema verification.\n */\nexport function formatSchemaVerifyOutput(\n result: VerifyDatabaseSchemaResult,\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 formatRed = createColorFormatter(useColor, red);\n const formatDimText = (text: string) => formatDim(useColor, text);\n\n // Render verification tree first\n const treeLines = renderSchemaVerificationTree(result.schema.root, flags, {\n isLast: true,\n prefix: '',\n useColor,\n formatDimText,\n isRoot: true,\n });\n lines.push(...treeLines);\n\n // Add counts and timings in verbose mode\n if (isVerbose(flags, 1)) {\n lines.push(`${formatDimText(` Total time: ${result.timings.total}ms`)}`);\n lines.push(\n `${formatDimText(` pass=${result.schema.counts.pass} warn=${result.schema.counts.warn} fail=${result.schema.counts.fail}`)}`,\n );\n }\n\n // Blank line before summary\n lines.push('');\n\n // Summary line at the end: summary 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.\n */\nexport function formatSchemaVerifyJson(result: VerifyDatabaseSchemaResult): string {\n return JSON.stringify(result, 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-BxiVp50b.mjs","names":["lines: string[]","formattedLabel: string","statusColor: (text: string) => string","labelColor: (text: string) => string"],"sources":["../src/utils/formatters/verify.ts"],"sourcesContent":["import type {\n CoreSchemaView,\n IntrospectSchemaResult,\n SchemaTreeNode,\n SchemaVerificationNode,\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// ============================================================================\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 counts: VerifyDatabaseSchemaResult['schema']['counts'];\n readonly strict: boolean;\n };\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.mode === 'full' && result.schema && isVerbose(flags, 1)) {\n lines.push(\n `${formatDimText(` schema: pass=${result.schema.counts.pass} warn=${result.schema.counts.warn} fail=${result.schema.counts.fail}`)}`,\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 ...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 * Matches the style of renderSchemaVerificationTree for consistency.\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 * Renders a schema verification tree structure from SchemaVerificationNode.\n * Similar to renderSchemaTree but for verification nodes with status-based colors and glyphs.\n */\nfunction renderSchemaVerificationTree(\n node: SchemaVerificationNode,\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 status glyph and color based on status\n let statusGlyph = '';\n let statusColor: (text: string) => string = (text) => text;\n if (useColor) {\n switch (node.status) {\n case 'pass':\n statusGlyph = '✔';\n statusColor = green;\n break;\n case 'warn':\n statusGlyph = '⚠';\n statusColor = (text) => (useColor ? yellow(text) : text);\n break;\n case 'fail':\n statusGlyph = '✖';\n statusColor = red;\n break;\n }\n } else {\n switch (node.status) {\n case 'pass':\n statusGlyph = '✔';\n break;\n case 'warn':\n statusGlyph = '⚠';\n break;\n case 'fail':\n statusGlyph = '✖';\n break;\n }\n }\n\n // Format node label with color based on kind\n // For column nodes, we need to parse the name to color code different parts\n let labelColor: (text: string) => string = (text) => text;\n let formattedLabel: string = node.name;\n\n if (useColor) {\n switch (node.kind) {\n case 'contract':\n case 'schema':\n labelColor = bold;\n formattedLabel = labelColor(node.name);\n break;\n case 'table': {\n // Parse \"table tableName\" format - color \"table\" dim, tableName cyan\n const tableMatch = node.name.match(/^table\\s+(.+)$/);\n if (tableMatch?.[1]) {\n const tableName = tableMatch[1];\n formattedLabel = `${dim('table')} ${cyan(tableName)}`;\n } else {\n formattedLabel = dim(node.name);\n }\n break;\n }\n case 'columns':\n labelColor = dim;\n formattedLabel = labelColor(node.name);\n break;\n case 'column': {\n // Parse column name format: \"columnName: contractType -> nativeType (nullability)\"\n // Color code: column name (cyan), contract type (default), native type (dim), nullability (dim)\n const columnMatch = node.name.match(/^([^:]+):\\s*(.+)$/);\n if (columnMatch?.[1] && columnMatch[2]) {\n const columnName = columnMatch[1];\n const rest = columnMatch[2];\n // Parse rest: \"contractType -> nativeType (nullability)\"\n // Match contract type (can contain /, @, etc.), arrow, native type, then nullability in parentheses\n const typeMatch = rest.match(/^([^\\s→]+)\\s*→\\s*([^\\s(]+)\\s*(\\([^)]+\\))$/);\n if (typeMatch?.[1] && typeMatch[2] && typeMatch[3]) {\n const contractType = typeMatch[1];\n const nativeType = typeMatch[2];\n const nullability = typeMatch[3];\n formattedLabel = `${cyan(columnName)}: ${contractType} → ${dim(nativeType)} ${dim(nullability)}`;\n } else {\n // Fallback if format doesn't match (e.g., no native type or no nullability)\n formattedLabel = `${cyan(columnName)}: ${rest}`;\n }\n } else {\n formattedLabel = node.name;\n }\n break;\n }\n case 'type':\n case 'nullability':\n labelColor = (text) => text; // Default color\n formattedLabel = labelColor(node.name);\n break;\n case 'primaryKey': {\n // Parse \"primary key: columnName\" format - color \"primary key\" dim, columnName cyan\n const pkMatch = node.name.match(/^primary key:\\s*(.+)$/);\n if (pkMatch?.[1]) {\n const columnNames = pkMatch[1];\n formattedLabel = `${dim('primary key')}: ${cyan(columnNames)}`;\n } else {\n formattedLabel = dim(node.name);\n }\n break;\n }\n case 'foreignKey':\n case 'unique':\n case 'index':\n labelColor = dim;\n formattedLabel = labelColor(node.name);\n break;\n case 'dependency': {\n // Parse specific extension message formats\n // \"database is postgres\" -> dim \"database is\", cyan \"postgres\"\n const dbMatch = node.name.match(/^database is\\s+(.+)$/);\n if (dbMatch?.[1]) {\n const dbName = dbMatch[1];\n formattedLabel = `${dim('database is')} ${cyan(dbName)}`;\n } else {\n // \"vector extension is enabled\" -> dim everything except extension name\n // Match pattern: \"extensionName extension is enabled\"\n const extMatch = node.name.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 name with magenta\n labelColor = magenta;\n formattedLabel = labelColor(node.name);\n }\n }\n break;\n }\n default:\n formattedLabel = node.name;\n break;\n }\n } else {\n formattedLabel = node.name;\n }\n\n const statusGlyphColored = statusColor(statusGlyph);\n\n // Build the label with optional message for failure/warn nodes\n let nodeLabel = formattedLabel;\n if (\n (node.status === 'fail' || node.status === 'warn') &&\n node.message &&\n node.message.length > 0\n ) {\n // Always show message for failure/warn nodes - it provides crucial context\n // For parent nodes, the message summarizes child failures\n // For leaf nodes, the message explains the specific issue\n const messageText = formatDimText(`(${node.message})`);\n nodeLabel = `${formattedLabel} ${messageText}`;\n }\n\n // Root node renders without tree characters or | prefix\n // Root node renders without tree characters or prefix\n if (isRoot) {\n lines.push(`${statusGlyphColored} ${nodeLabel}`);\n } else {\n const treeChar = isLast ? '└' : '├';\n const treePrefix = `${formatDimText(treeChar)}─ `;\n lines.push(`${prefix}${treePrefix}${statusGlyphColored} ${nodeLabel}`);\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 = renderSchemaVerificationTree(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 schema verification.\n */\nexport function formatSchemaVerifyOutput(\n result: VerifyDatabaseSchemaResult,\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 formatRed = createColorFormatter(useColor, red);\n const formatDimText = (text: string) => formatDim(useColor, text);\n\n // Render verification tree first\n const treeLines = renderSchemaVerificationTree(result.schema.root, flags, {\n isLast: true,\n prefix: '',\n useColor,\n formatDimText,\n isRoot: true,\n });\n lines.push(...treeLines);\n\n // Add counts and timings in verbose mode\n if (isVerbose(flags, 1)) {\n lines.push(`${formatDimText(` Total time: ${result.timings.total}ms`)}`);\n lines.push(\n `${formatDimText(` pass=${result.schema.counts.pass} warn=${result.schema.counts.warn} fail=${result.schema.counts.fail}`)}`,\n );\n }\n\n // Blank line before summary\n lines.push('');\n\n // Summary line at the end: summary 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.\n */\nexport function formatSchemaVerifyJson(result: VerifyDatabaseSchemaResult): string {\n return JSON.stringify(result, 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.
|
|
3
|
+
"version": "0.5.0-dev.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"files": [
|
|
@@ -24,27 +24,27 @@
|
|
|
24
24
|
"string-width": "^8.2.0",
|
|
25
25
|
"strip-ansi": "^7.1.2",
|
|
26
26
|
"wrap-ansi": "^10.0.0",
|
|
27
|
-
"@prisma-next/config": "0.
|
|
28
|
-
"@prisma-next/
|
|
29
|
-
"@prisma-next/
|
|
30
|
-
"@prisma-next/errors": "0.
|
|
31
|
-
"@prisma-next/
|
|
32
|
-
"@prisma-next/psl-printer": "0.
|
|
33
|
-
"@prisma-next/
|
|
34
|
-
"@prisma-next/utils": "0.
|
|
27
|
+
"@prisma-next/config": "0.5.0-dev.1",
|
|
28
|
+
"@prisma-next/contract": "0.5.0-dev.1",
|
|
29
|
+
"@prisma-next/emitter": "0.5.0-dev.1",
|
|
30
|
+
"@prisma-next/errors": "0.5.0-dev.1",
|
|
31
|
+
"@prisma-next/migration-tools": "0.5.0-dev.1",
|
|
32
|
+
"@prisma-next/psl-printer": "0.5.0-dev.1",
|
|
33
|
+
"@prisma-next/framework-components": "0.5.0-dev.1",
|
|
34
|
+
"@prisma-next/utils": "0.5.0-dev.1"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/node": "24.10.4",
|
|
38
38
|
"tsdown": "0.18.4",
|
|
39
39
|
"typescript": "5.9.3",
|
|
40
40
|
"vitest": "4.0.17",
|
|
41
|
-
"@prisma-next/sql-contract": "0.
|
|
42
|
-
"@prisma-next/sql-contract-
|
|
43
|
-
"@prisma-next/sql-contract
|
|
44
|
-
"@prisma-next/sql-runtime": "0.
|
|
45
|
-
"@prisma-next/sql-operations": "0.4.0-dev.9",
|
|
46
|
-
"@prisma-next/tsconfig": "0.0.0",
|
|
41
|
+
"@prisma-next/sql-contract-emitter": "0.5.0-dev.1",
|
|
42
|
+
"@prisma-next/sql-contract-ts": "0.5.0-dev.1",
|
|
43
|
+
"@prisma-next/sql-contract": "0.5.0-dev.1",
|
|
44
|
+
"@prisma-next/sql-runtime": "0.5.0-dev.1",
|
|
47
45
|
"@prisma-next/test-utils": "0.0.1",
|
|
46
|
+
"@prisma-next/sql-operations": "0.5.0-dev.1",
|
|
47
|
+
"@prisma-next/tsconfig": "0.0.0",
|
|
48
48
|
"@prisma-next/tsdown": "0.0.0"
|
|
49
49
|
},
|
|
50
50
|
"exports": {
|
|
@@ -100,10 +100,6 @@
|
|
|
100
100
|
"types": "./dist/commands/migration-status.d.mts",
|
|
101
101
|
"import": "./dist/commands/migration-status.mjs"
|
|
102
102
|
},
|
|
103
|
-
"./commands/migration-emit": {
|
|
104
|
-
"types": "./dist/commands/migration-emit.d.mts",
|
|
105
|
-
"import": "./dist/commands/migration-emit.mjs"
|
|
106
|
-
},
|
|
107
103
|
"./commands/migration-apply": {
|
|
108
104
|
"types": "./dist/commands/migration-apply.d.mts",
|
|
109
105
|
"import": "./dist/commands/migration-apply.mjs"
|
|
@@ -116,6 +112,10 @@
|
|
|
116
112
|
"types": "./dist/config-loader.d.mts",
|
|
117
113
|
"import": "./dist/config-loader.mjs"
|
|
118
114
|
},
|
|
115
|
+
"./migration-cli": {
|
|
116
|
+
"types": "./dist/migration-cli.d.mts",
|
|
117
|
+
"import": "./dist/migration-cli.mjs"
|
|
118
|
+
},
|
|
119
119
|
"./control-api": {
|
|
120
120
|
"types": "./dist/exports/control-api.d.mts",
|
|
121
121
|
"import": "./dist/exports/control-api.mjs"
|
package/src/cli.ts
CHANGED
|
@@ -13,7 +13,6 @@ import { createDbSignCommand } from './commands/db-sign';
|
|
|
13
13
|
import { createDbUpdateCommand } from './commands/db-update';
|
|
14
14
|
import { createDbVerifyCommand } from './commands/db-verify';
|
|
15
15
|
import { createMigrationApplyCommand } from './commands/migration-apply';
|
|
16
|
-
import { createMigrationEmitCommand } from './commands/migration-emit';
|
|
17
16
|
import { createMigrationNewCommand } from './commands/migration-new';
|
|
18
17
|
import { createMigrationPlanCommand } from './commands/migration-plan';
|
|
19
18
|
import { createMigrationRefCommand } from './commands/migration-ref';
|
|
@@ -215,7 +214,7 @@ const migrationCommand = new Command('migration');
|
|
|
215
214
|
setCommandDescriptions(
|
|
216
215
|
migrationCommand,
|
|
217
216
|
'On-disk migration management commands',
|
|
218
|
-
'Plan, apply,
|
|
217
|
+
'Plan, apply, and scaffold on-disk migration packages. Migrations are\n' +
|
|
219
218
|
'contract-to-contract edges stored as versioned directories under migrations/.',
|
|
220
219
|
);
|
|
221
220
|
migrationCommand.configureHelp({
|
|
@@ -238,9 +237,6 @@ migrationCommand.addCommand(migrationShowCommand);
|
|
|
238
237
|
const migrationStatusCommand = createMigrationStatusCommand();
|
|
239
238
|
migrationCommand.addCommand(migrationStatusCommand);
|
|
240
239
|
|
|
241
|
-
const migrationEmitCommand = createMigrationEmitCommand();
|
|
242
|
-
migrationCommand.addCommand(migrationEmitCommand);
|
|
243
|
-
|
|
244
240
|
const migrationApplyCommand = createMigrationApplyCommand();
|
|
245
241
|
migrationCommand.addCommand(migrationApplyCommand);
|
|
246
242
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { getEmittedArtifactPaths } from '@prisma-next/emitter';
|
|
2
3
|
import { errorContractConfigMissing } from '@prisma-next/errors/control';
|
|
3
4
|
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
4
5
|
import { Command } from 'commander';
|
|
5
|
-
import { dirname,
|
|
6
|
+
import { dirname, relative, resolve } from 'pathe';
|
|
6
7
|
import { loadConfig } from '../config-loader';
|
|
7
8
|
import { createControlClient } from '../control-api/client';
|
|
8
9
|
import type { EmitFailure } from '../control-api/types';
|
|
@@ -135,19 +136,17 @@ async function executeContractEmitCommand(
|
|
|
135
136
|
}),
|
|
136
137
|
);
|
|
137
138
|
}
|
|
138
|
-
|
|
139
|
+
let outputPaths: ReturnType<typeof getEmittedArtifactPaths>;
|
|
140
|
+
try {
|
|
141
|
+
outputPaths = getEmittedArtifactPaths(contractConfig.output);
|
|
142
|
+
} catch (error) {
|
|
139
143
|
return notOk(
|
|
140
144
|
errorContractConfigMissing({
|
|
141
|
-
why:
|
|
145
|
+
why: error instanceof Error ? error.message : String(error),
|
|
142
146
|
}),
|
|
143
147
|
);
|
|
144
148
|
}
|
|
145
|
-
const
|
|
146
|
-
const outputJsonPath = isAbsolute(contractConfig.output)
|
|
147
|
-
? contractConfig.output
|
|
148
|
-
: join(configDir, contractConfig.output);
|
|
149
|
-
// Colocate .d.ts with .json (contract.json → contract.d.ts)
|
|
150
|
-
const outputDtsPath = `${outputJsonPath.slice(0, -5)}.d.ts`;
|
|
149
|
+
const { jsonPath: outputJsonPath, dtsPath: outputDtsPath } = outputPaths;
|
|
151
150
|
|
|
152
151
|
// Output header to stderr (decoration)
|
|
153
152
|
if (!flags.json && !flags.quiet) {
|
|
@@ -182,7 +181,7 @@ async function executeContractEmitCommand(
|
|
|
182
181
|
// Call emit with progress callback
|
|
183
182
|
const result = await client.emit({
|
|
184
183
|
contractConfig: {
|
|
185
|
-
|
|
184
|
+
source: contractConfig.source,
|
|
186
185
|
output: outputJsonPath,
|
|
187
186
|
},
|
|
188
187
|
onProgress,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { verifyMigrationBundle } from '@prisma-next/migration-tools/attestation';
|
|
1
2
|
import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';
|
|
2
3
|
import { findPathWithDecision } from '@prisma-next/migration-tools/dag';
|
|
3
4
|
import { readRefs, resolveRef } from '@prisma-next/migration-tools/refs';
|
|
4
|
-
import type {
|
|
5
|
+
import type { MigrationBundle } from '@prisma-next/migration-tools/types';
|
|
5
6
|
import { MigrationToolsError } from '@prisma-next/migration-tools/types';
|
|
6
7
|
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
7
8
|
import { Command } from 'commander';
|
|
@@ -63,7 +64,7 @@ export interface MigrationApplyResult {
|
|
|
63
64
|
readonly refName?: string;
|
|
64
65
|
readonly selectedPath: readonly {
|
|
65
66
|
readonly dirName: string;
|
|
66
|
-
readonly migrationId: string
|
|
67
|
+
readonly migrationId: string;
|
|
67
68
|
readonly from: string;
|
|
68
69
|
readonly to: string;
|
|
69
70
|
}[];
|
|
@@ -94,7 +95,7 @@ function mapApplyFailure(failure: MigrationApplyFailure): CliStructuredErrorType
|
|
|
94
95
|
});
|
|
95
96
|
}
|
|
96
97
|
|
|
97
|
-
function packageToStep(pkg:
|
|
98
|
+
function packageToStep(pkg: MigrationBundle): MigrationApplyStep {
|
|
98
99
|
return {
|
|
99
100
|
dirName: pkg.dirName,
|
|
100
101
|
from: pkg.manifest.from,
|
|
@@ -198,12 +199,6 @@ async function executeMigrationApplyCommand(
|
|
|
198
199
|
let migrations: MigrationBundleSet;
|
|
199
200
|
try {
|
|
200
201
|
migrations = await loadAllBundles(migrationsDir);
|
|
201
|
-
if (migrations.drafts.length > 0 && !flags.quiet) {
|
|
202
|
-
ui.warn(
|
|
203
|
-
`${migrations.drafts.length} draft migration(s) found: ${migrations.drafts.map((d) => d.dirName).join(', ')}. ` +
|
|
204
|
-
"Run 'prisma-next migration emit --dir <path>' to attest before applying.",
|
|
205
|
-
);
|
|
206
|
-
}
|
|
207
202
|
} catch (error) {
|
|
208
203
|
if (MigrationToolsError.is(error)) {
|
|
209
204
|
return notOk(mapMigrationToolsError(error));
|
|
@@ -211,6 +206,27 @@ async function executeMigrationApplyCommand(
|
|
|
211
206
|
throw error;
|
|
212
207
|
}
|
|
213
208
|
|
|
209
|
+
// Defense in depth: re-hash every bundle and confirm the recorded
|
|
210
|
+
// `migrationId` matches the on-disk `(manifest, ops)`. Catches FS
|
|
211
|
+
// corruption, partial writes, and post-emit hand edits before we
|
|
212
|
+
// start touching the database.
|
|
213
|
+
for (const bundle of migrations.bundles) {
|
|
214
|
+
const verified = verifyMigrationBundle(bundle);
|
|
215
|
+
if (!verified.ok) {
|
|
216
|
+
return notOk(
|
|
217
|
+
errorRuntime(`Migration package is corrupt: ${bundle.dirName}`, {
|
|
218
|
+
why: `Stored migrationId "${verified.storedMigrationId}" does not match the recomputed hash "${verified.computedMigrationId}" for ${migrationsRelative}/${bundle.dirName}. The migration.json or ops.json has been edited or partially written since emit.`,
|
|
219
|
+
fix: `Re-emit the package by running \`node "${migrationsRelative}/${bundle.dirName}/migration.ts"\`, or restore the directory from version control.`,
|
|
220
|
+
meta: {
|
|
221
|
+
dirName: bundle.dirName,
|
|
222
|
+
storedMigrationId: verified.storedMigrationId,
|
|
223
|
+
computedMigrationId: verified.computedMigrationId,
|
|
224
|
+
},
|
|
225
|
+
}),
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
214
230
|
const client = createControlClient({
|
|
215
231
|
family: config.family,
|
|
216
232
|
target: config.target,
|
|
@@ -223,12 +239,12 @@ async function executeMigrationApplyCommand(
|
|
|
223
239
|
await client.connect(dbConnection);
|
|
224
240
|
const marker = await client.readMarker();
|
|
225
241
|
|
|
226
|
-
// --- No
|
|
227
|
-
if (migrations.
|
|
242
|
+
// --- No migrations on disk ---
|
|
243
|
+
if (migrations.bundles.length === 0) {
|
|
228
244
|
if (marker?.storageHash) {
|
|
229
245
|
return notOk(
|
|
230
246
|
errorRuntime('Database has state but no migrations exist', {
|
|
231
|
-
why: `The database marker hash "${marker.storageHash}" exists but no
|
|
247
|
+
why: `The database marker hash "${marker.storageHash}" exists but no migrations were found in ${migrationsRelative}`,
|
|
232
248
|
fix: 'Ensure the migrations directory is correct. If the database was managed with `db init` or `db update`, run `prisma-next db sign` to update the marker.',
|
|
233
249
|
meta: { markerHash: marker.storageHash, migrationsDir: migrationsRelative },
|
|
234
250
|
}),
|
|
@@ -238,8 +254,8 @@ async function executeMigrationApplyCommand(
|
|
|
238
254
|
if (destinationHash !== EMPTY_CONTRACT_HASH) {
|
|
239
255
|
return notOk(
|
|
240
256
|
errorRuntime('Current contract has no planned migrations', {
|
|
241
|
-
why: `No
|
|
242
|
-
fix: 'Run `prisma-next migration plan` to create
|
|
257
|
+
why: `No migrations were found in ${migrationsRelative}, but current contract hash is "${destinationHash}"`,
|
|
258
|
+
fix: 'Run `prisma-next migration plan` to create a migration for the current contract.',
|
|
243
259
|
meta: { destinationHash, migrationsDir: migrationsRelative },
|
|
244
260
|
}),
|
|
245
261
|
);
|
|
@@ -251,7 +267,7 @@ async function executeMigrationApplyCommand(
|
|
|
251
267
|
migrationsTotal: 0,
|
|
252
268
|
markerHash: EMPTY_CONTRACT_HASH,
|
|
253
269
|
applied: [],
|
|
254
|
-
summary: 'No
|
|
270
|
+
summary: 'No migrations found',
|
|
255
271
|
timings: { total: Date.now() - startTime },
|
|
256
272
|
});
|
|
257
273
|
}
|
|
@@ -283,15 +299,10 @@ async function executeMigrationApplyCommand(
|
|
|
283
299
|
}
|
|
284
300
|
|
|
285
301
|
if (!migrations.graph.nodes.has(destinationHash)) {
|
|
286
|
-
const matchingDraft = migrations.drafts.find((d) => d.manifest.to === destinationHash);
|
|
287
302
|
return notOk(
|
|
288
303
|
errorRuntime('Current contract has no planned migration path', {
|
|
289
|
-
why:
|
|
290
|
-
|
|
291
|
-
: `Current contract hash "${destinationHash}" is not present in the migration history at ${migrationsRelative}`,
|
|
292
|
-
fix: matchingDraft
|
|
293
|
-
? `Run 'prisma-next migration emit --dir "${migrationsRelative}/${matchingDraft.dirName}"' to attest, then re-run apply.`
|
|
294
|
-
: 'Run `prisma-next migration plan` to create a migration for the current contract, then re-run apply.',
|
|
304
|
+
why: `Current contract hash "${destinationHash}" is not present in the migration history at ${migrationsRelative}`,
|
|
305
|
+
fix: 'Run `prisma-next migration plan` to create a migration for the current contract, then re-run apply.',
|
|
295
306
|
meta: { destinationHash, knownNodes: [...migrations.graph.nodes] },
|
|
296
307
|
}),
|
|
297
308
|
);
|
|
@@ -329,7 +340,7 @@ async function executeMigrationApplyCommand(
|
|
|
329
340
|
});
|
|
330
341
|
}
|
|
331
342
|
|
|
332
|
-
const bundleByDir = new Map(migrations.
|
|
343
|
+
const bundleByDir = new Map(migrations.bundles.map((b) => [b.dirName, b]));
|
|
333
344
|
const pendingMigrations: MigrationApplyStep[] = [];
|
|
334
345
|
for (const migration of pendingPath) {
|
|
335
346
|
const pkg = bundleByDir.get(migration.dirName);
|
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
* `migration new` — scaffolds a migration package with a `migration.ts` file
|
|
3
3
|
* for manual authoring.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
* the same path here: the planner's `emptyMigration(context)` returns a
|
|
5
|
+
* The planner's `emptyMigration(context)` returns a
|
|
7
6
|
* `MigrationPlanWithAuthoringSurface`, whose `renderTypeScript()` produces
|
|
8
7
|
* the target-appropriate empty stub. The CLI writes the returned source
|
|
9
8
|
* verbatim.
|
|
@@ -11,18 +10,20 @@
|
|
|
11
10
|
|
|
12
11
|
import { readFileSync } from 'node:fs';
|
|
13
12
|
import type { Contract } from '@prisma-next/contract/types';
|
|
13
|
+
import { getEmittedArtifactPaths } from '@prisma-next/emitter';
|
|
14
14
|
import { createControlStack } from '@prisma-next/framework-components/control';
|
|
15
|
+
import { computeMigrationId } from '@prisma-next/migration-tools/attestation';
|
|
15
16
|
import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';
|
|
16
17
|
import { findLatestMigration, reconstructGraph } from '@prisma-next/migration-tools/dag';
|
|
17
18
|
import {
|
|
18
|
-
|
|
19
|
+
copyFilesWithRename,
|
|
19
20
|
formatMigrationDirName,
|
|
20
21
|
readMigrationsDir,
|
|
21
22
|
writeMigrationPackage,
|
|
22
23
|
} from '@prisma-next/migration-tools/io';
|
|
23
24
|
import { writeMigrationTs } from '@prisma-next/migration-tools/migration-ts';
|
|
24
25
|
import type { MigrationManifest } from '@prisma-next/migration-tools/types';
|
|
25
|
-
import {
|
|
26
|
+
import { MigrationToolsError } from '@prisma-next/migration-tools/types';
|
|
26
27
|
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
27
28
|
import { Command } from 'commander';
|
|
28
29
|
import { join, relative, resolve } from 'pathe';
|
|
@@ -116,16 +117,16 @@ async function executeMigrationNewCommand(
|
|
|
116
117
|
|
|
117
118
|
let fromContract: Contract | null = null;
|
|
118
119
|
let fromHash: string = EMPTY_CONTRACT_HASH;
|
|
120
|
+
let fromContractSourceDir: string | null = null;
|
|
119
121
|
|
|
120
122
|
try {
|
|
121
123
|
const packages = await readMigrationsDir(migrationsDir);
|
|
122
|
-
const attested = packages.filter(isAttested);
|
|
123
124
|
|
|
124
|
-
if (
|
|
125
|
-
const graph = reconstructGraph(
|
|
125
|
+
if (packages.length > 0) {
|
|
126
|
+
const graph = reconstructGraph(packages);
|
|
126
127
|
|
|
127
128
|
if (options.from) {
|
|
128
|
-
const match =
|
|
129
|
+
const match = packages.find((p) => p.manifest.to.startsWith(options.from!));
|
|
129
130
|
if (!match) {
|
|
130
131
|
return notOk(
|
|
131
132
|
errorRuntime('Starting contract not found', {
|
|
@@ -136,15 +137,17 @@ async function executeMigrationNewCommand(
|
|
|
136
137
|
}
|
|
137
138
|
fromHash = match.manifest.to;
|
|
138
139
|
fromContract = match.manifest.toContract;
|
|
140
|
+
fromContractSourceDir = match.dirPath;
|
|
139
141
|
} else {
|
|
140
142
|
const latestMigration = findLatestMigration(graph);
|
|
141
143
|
if (latestMigration) {
|
|
142
144
|
fromHash = latestMigration.to;
|
|
143
|
-
const leafPkg =
|
|
145
|
+
const leafPkg = packages.find(
|
|
144
146
|
(p) => p.manifest.migrationId === latestMigration.migrationId,
|
|
145
147
|
);
|
|
146
148
|
if (leafPkg) {
|
|
147
149
|
fromContract = leafPkg.manifest.toContract;
|
|
150
|
+
fromContractSourceDir = leafPkg.dirPath;
|
|
148
151
|
}
|
|
149
152
|
}
|
|
150
153
|
}
|
|
@@ -176,10 +179,13 @@ async function executeMigrationNewCommand(
|
|
|
176
179
|
const dirName = formatMigrationDirName(timestamp, slug);
|
|
177
180
|
const packageDir = join(migrationsDir, dirName);
|
|
178
181
|
|
|
179
|
-
|
|
182
|
+
// `migration new` scaffolds an empty `migration.ts` for the user to
|
|
183
|
+
// fill, so we attest over `ops: []`. Re-running self-emit after the
|
|
184
|
+
// user adds operations will produce a different `migrationId` (over
|
|
185
|
+
// the real ops). This is intentional — there is no on-disk draft.
|
|
186
|
+
const baseManifest: Omit<MigrationManifest, 'migrationId'> = {
|
|
180
187
|
from: fromHash,
|
|
181
188
|
to: toStorageHash,
|
|
182
|
-
migrationId: null,
|
|
183
189
|
kind: 'regular',
|
|
184
190
|
fromContract,
|
|
185
191
|
toContract: toContractJson,
|
|
@@ -187,11 +193,14 @@ async function executeMigrationNewCommand(
|
|
|
187
193
|
used: [],
|
|
188
194
|
applied: [],
|
|
189
195
|
plannerVersion: '1.0.0',
|
|
190
|
-
planningStrategy: 'manual',
|
|
191
196
|
},
|
|
192
197
|
labels: [],
|
|
193
198
|
createdAt: timestamp.toISOString(),
|
|
194
199
|
};
|
|
200
|
+
const manifest: MigrationManifest = {
|
|
201
|
+
...baseManifest,
|
|
202
|
+
migrationId: computeMigrationId(baseManifest, []),
|
|
203
|
+
};
|
|
195
204
|
|
|
196
205
|
const migrations = getTargetMigrations(config.target);
|
|
197
206
|
if (!migrations) {
|
|
@@ -210,14 +219,27 @@ async function executeMigrationNewCommand(
|
|
|
210
219
|
]);
|
|
211
220
|
|
|
212
221
|
await writeMigrationPackage(packageDir, manifest, []);
|
|
213
|
-
|
|
222
|
+
const destinationArtifacts = getEmittedArtifactPaths(contractPathAbsolute);
|
|
223
|
+
await copyFilesWithRename(packageDir, [
|
|
224
|
+
{ sourcePath: destinationArtifacts.jsonPath, destName: 'end-contract.json' },
|
|
225
|
+
{ sourcePath: destinationArtifacts.dtsPath, destName: 'end-contract.d.ts' },
|
|
226
|
+
]);
|
|
227
|
+
if (fromContractSourceDir !== null) {
|
|
228
|
+
const sourceArtifacts = getEmittedArtifactPaths(
|
|
229
|
+
join(fromContractSourceDir, 'end-contract.json'),
|
|
230
|
+
);
|
|
231
|
+
await copyFilesWithRename(packageDir, [
|
|
232
|
+
{ sourcePath: sourceArtifacts.jsonPath, destName: 'start-contract.json' },
|
|
233
|
+
{ sourcePath: sourceArtifacts.dtsPath, destName: 'start-contract.d.ts' },
|
|
234
|
+
]);
|
|
235
|
+
}
|
|
214
236
|
|
|
215
237
|
const stack = createControlStack(config);
|
|
216
238
|
const familyInstance = config.family.create(stack);
|
|
217
239
|
const planner = migrations.createPlanner(familyInstance);
|
|
218
240
|
const emptyPlan = planner.emptyMigration({
|
|
219
241
|
packageDir,
|
|
220
|
-
contractJsonPath: join(packageDir, 'contract.json'),
|
|
242
|
+
contractJsonPath: join(packageDir, 'end-contract.json'),
|
|
221
243
|
fromHash,
|
|
222
244
|
toHash: toStorageHash,
|
|
223
245
|
});
|
|
@@ -248,8 +270,8 @@ export function createMigrationNewCommand(): Command {
|
|
|
248
270
|
command,
|
|
249
271
|
'Scaffold a new migration for manual authoring',
|
|
250
272
|
'Creates a migration package with a migration.ts file for manual authoring.\n' +
|
|
251
|
-
'Write
|
|
252
|
-
'`migration
|
|
273
|
+
'Write the migration body in migration.ts, then run the file with Node\n' +
|
|
274
|
+
'(`node migration.ts`) to self-emit ops.json and attest the package.',
|
|
253
275
|
);
|
|
254
276
|
setCommandExamples(command, [
|
|
255
277
|
'prisma-next migration new --name split-name',
|
|
@@ -283,7 +305,7 @@ export function createMigrationNewCommand(): Command {
|
|
|
283
305
|
ui.output(` from: ${value.from}`);
|
|
284
306
|
ui.output(` to: ${value.to}`);
|
|
285
307
|
ui.output(
|
|
286
|
-
`\nEdit migration.ts, then run
|
|
308
|
+
`\nEdit migration.ts, then run it directly (\`node "${value.dir}/migration.ts"\`) to self-emit and attest.`,
|
|
287
309
|
);
|
|
288
310
|
}
|
|
289
311
|
});
|