@prisma-next/cli 0.5.0-dev.27 → 0.5.0-dev.29
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/dist/cli.mjs +3 -3
- package/dist/{client-CJT9lqBd.mjs → client-keSCAgjW.mjs} +14 -7
- package/dist/client-keSCAgjW.mjs.map +1 -0
- package/dist/commands/contract-infer.d.mts.map +1 -1
- package/dist/commands/contract-infer.mjs +1 -1
- package/dist/commands/db-init.d.mts.map +1 -1
- package/dist/commands/db-init.mjs +5 -4
- package/dist/commands/db-init.mjs.map +1 -1
- package/dist/commands/db-schema.mjs +2 -2
- package/dist/commands/db-sign.mjs +2 -2
- package/dist/commands/db-update.mjs +4 -4
- package/dist/commands/db-update.mjs.map +1 -1
- package/dist/commands/db-verify.mjs +2 -2
- package/dist/commands/migration-apply.mjs +2 -2
- package/dist/commands/migration-plan.d.mts +7 -1
- package/dist/commands/migration-plan.d.mts.map +1 -1
- package/dist/commands/migration-plan.mjs +9 -9
- package/dist/commands/migration-plan.mjs.map +1 -1
- package/dist/commands/migration-ref.d.mts +1 -1
- package/dist/commands/migration-show.d.mts +9 -2
- package/dist/commands/migration-show.d.mts.map +1 -1
- package/dist/commands/migration-show.mjs +11 -5
- package/dist/commands/migration-show.mjs.map +1 -1
- package/dist/commands/migration-status.mjs +1 -1
- package/dist/{contract-infer-CPQRrGmy.mjs → contract-infer-GztVCOCJ.mjs} +8 -16
- package/dist/contract-infer-GztVCOCJ.mjs.map +1 -0
- package/dist/exports/control-api.d.mts +35 -3
- package/dist/exports/control-api.d.mts.map +1 -1
- package/dist/exports/control-api.mjs +1 -1
- package/dist/exports/init-output.mjs +1 -1
- package/dist/{init-BKgjxw6r.mjs → init-C7dE9KOJ.mjs} +2 -2
- package/dist/{init-BKgjxw6r.mjs.map → init-C7dE9KOJ.mjs.map} +1 -1
- package/dist/{inspect-live-schema-CLchu1d-.mjs → inspect-live-schema-BaR9ISwa.mjs} +5 -5
- package/dist/inspect-live-schema-BaR9ISwa.mjs.map +1 -0
- package/dist/{migration-command-scaffold-BP50SzoG.mjs → migration-command-scaffold-D1dWuEWQ.mjs} +2 -2
- package/dist/{migration-command-scaffold-BP50SzoG.mjs.map → migration-command-scaffold-D1dWuEWQ.mjs.map} +1 -1
- package/dist/{migration-status-CMjpUefa.mjs → migration-status-CP5k8O5i.mjs} +2 -2
- package/dist/{migration-status-CMjpUefa.mjs.map → migration-status-CP5k8O5i.mjs.map} +1 -1
- package/dist/{migrations-CYYmURZx.mjs → migrations-MEoKMiV5.mjs} +39 -17
- package/dist/migrations-MEoKMiV5.mjs.map +1 -0
- package/dist/{output-BiO7kt87.mjs → output-BpcQrnnq.mjs} +1 -1
- package/dist/{output-BiO7kt87.mjs.map → output-BpcQrnnq.mjs.map} +1 -1
- package/dist/{verify-BumcH6Ry.mjs → verify-BT9tgCOH.mjs} +1 -1
- package/dist/{verify-BumcH6Ry.mjs.map → verify-BT9tgCOH.mjs.map} +1 -1
- package/package.json +14 -14
- package/src/commands/contract-infer.ts +7 -20
- package/src/commands/db-init.ts +1 -0
- package/src/commands/db-update.ts +1 -1
- package/src/commands/inspect-live-schema.ts +10 -5
- package/src/commands/migration-plan.ts +20 -9
- package/src/commands/migration-show.ts +26 -6
- package/src/control-api/client.ts +21 -0
- package/src/control-api/operations/db-init.ts +5 -3
- package/src/control-api/operations/db-update.ts +5 -3
- package/src/control-api/types.ts +37 -2
- package/src/utils/formatters/migrations.ts +58 -20
- package/dist/client-CJT9lqBd.mjs.map +0 -1
- package/dist/contract-infer-CPQRrGmy.mjs.map +0 -1
- package/dist/extract-operation-statements-CU-Pp4-N.mjs +0 -13
- package/dist/extract-operation-statements-CU-Pp4-N.mjs.map +0 -1
- package/dist/extract-sql-ddl-Bm0Mm0IT.mjs +0 -26
- package/dist/extract-sql-ddl-Bm0Mm0IT.mjs.map +0 -1
- package/dist/inspect-live-schema-CLchu1d-.mjs.map +0 -1
- package/dist/migrations-CYYmURZx.mjs.map +0 -1
- package/src/control-api/operations/extract-operation-statements.ts +0 -14
- package/src/control-api/operations/extract-sql-ddl.ts +0 -47
- /package/dist/{cli-errors-BJLUczXT.d.mts → cli-errors-DDeVsP2Y.d.mts} +0 -0
|
@@ -3,6 +3,32 @@ import { green, yellow } from "colorette";
|
|
|
3
3
|
|
|
4
4
|
//#region src/utils/formatters/migrations.ts
|
|
5
5
|
/**
|
|
6
|
+
* Render a single statement of an `OperationPreview` for the human-readable
|
|
7
|
+
* preview block. SQL statements get a trailing `;` if missing — matches the
|
|
8
|
+
* legacy `string[]`-based renderer byte-for-byte (per spec OQ-4). Other
|
|
9
|
+
* languages (`'mongodb-shell'`) render verbatim.
|
|
10
|
+
*/
|
|
11
|
+
function renderPreviewStatement(text, language) {
|
|
12
|
+
const trimmed = text.trim();
|
|
13
|
+
if (!trimmed) return void 0;
|
|
14
|
+
if (language === "sql") return trimmed.endsWith(";") ? trimmed : `${trimmed};`;
|
|
15
|
+
return trimmed;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Choose the header label for a preview block. SQL-only previews keep the
|
|
19
|
+
* legacy `DDL preview` label (preserves CLI byte-identity for SQL targets per
|
|
20
|
+
* spec OQ-4); previews from any other family — or a mix that includes any
|
|
21
|
+
* non-SQL language — use the family-agnostic `Operation preview` label.
|
|
22
|
+
*
|
|
23
|
+
* An empty `statements` array deliberately renders as `Operation preview`
|
|
24
|
+
* rather than `DDL preview`: `Array.prototype.every` is vacuously true for
|
|
25
|
+
* empty arrays, but we have no evidence the preview is SQL-only when no
|
|
26
|
+
* statements are present, so the family-agnostic label is the safer default.
|
|
27
|
+
*/
|
|
28
|
+
function previewBlockHeader(preview) {
|
|
29
|
+
return preview.statements.length > 0 && preview.statements.every((s) => s.language === "sql") ? "DDL preview" : "Operation preview";
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
6
32
|
* Formats human-readable output for migration commands (db init, db update) in plan mode.
|
|
7
33
|
*/
|
|
8
34
|
function formatMigrationPlanOutput(result, flags) {
|
|
@@ -32,18 +58,16 @@ function formatMigrationPlanOutput(result, flags) {
|
|
|
32
58
|
lines.push("");
|
|
33
59
|
lines.push(`${formatDimText(`Destination hash: ${result.plan.destination.storageHash}`)}`);
|
|
34
60
|
}
|
|
35
|
-
const
|
|
36
|
-
if (
|
|
61
|
+
const preview = result.plan?.preview;
|
|
62
|
+
if (preview) {
|
|
37
63
|
lines.push("");
|
|
38
|
-
lines.push(`${formatDimText(
|
|
39
|
-
if (
|
|
64
|
+
lines.push(`${formatDimText(previewBlockHeader(preview))}`);
|
|
65
|
+
if (preview.statements.length === 0) lines.push(`${formatDimText("No operations.")}`);
|
|
40
66
|
else {
|
|
41
67
|
lines.push("");
|
|
42
|
-
for (const statement of
|
|
43
|
-
const
|
|
44
|
-
if (
|
|
45
|
-
const line = trimmed.endsWith(";") ? trimmed : `${trimmed};`;
|
|
46
|
-
lines.push(`${line}`);
|
|
68
|
+
for (const statement of preview.statements) {
|
|
69
|
+
const rendered = renderPreviewStatement(statement.text, statement.language);
|
|
70
|
+
if (rendered) lines.push(rendered);
|
|
47
71
|
}
|
|
48
72
|
}
|
|
49
73
|
}
|
|
@@ -106,15 +130,13 @@ function formatMigrationShowOutput(result, flags) {
|
|
|
106
130
|
lines.push(`${formatYellow("⚠")} This migration contains destructive operations that may cause data loss.`);
|
|
107
131
|
}
|
|
108
132
|
}
|
|
109
|
-
if (result.
|
|
133
|
+
if (result.preview.statements.length > 0) {
|
|
110
134
|
lines.push("");
|
|
111
|
-
lines.push(`${formatDimText(
|
|
135
|
+
lines.push(`${formatDimText(previewBlockHeader(result.preview))}`);
|
|
112
136
|
lines.push("");
|
|
113
|
-
for (const statement of result.
|
|
114
|
-
const
|
|
115
|
-
if (
|
|
116
|
-
const line = trimmed.endsWith(";") ? trimmed : `${trimmed};`;
|
|
117
|
-
lines.push(`${line}`);
|
|
137
|
+
for (const statement of result.preview.statements) {
|
|
138
|
+
const rendered = renderPreviewStatement(statement.text, statement.language);
|
|
139
|
+
if (rendered) lines.push(rendered);
|
|
118
140
|
}
|
|
119
141
|
}
|
|
120
142
|
return lines.join("\n");
|
|
@@ -149,4 +171,4 @@ function formatMigrationJson(result) {
|
|
|
149
171
|
|
|
150
172
|
//#endregion
|
|
151
173
|
export { formatMigrationShowOutput as a, formatMigrationPlanOutput as i, formatMigrationApplyOutput as n, formatMigrationJson as r, formatMigrationApplyCommandOutput as t };
|
|
152
|
-
//# sourceMappingURL=migrations-
|
|
174
|
+
//# sourceMappingURL=migrations-MEoKMiV5.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrations-MEoKMiV5.mjs","names":["lines: string[]"],"sources":["../src/utils/formatters/migrations.ts"],"sourcesContent":["import type { OperationPreview } from '@prisma-next/framework-components/control';\nimport { green, yellow } from 'colorette';\n\nimport type { GlobalFlags } from '../global-flags';\nimport { createColorFormatter, formatDim, isVerbose } from './helpers';\n\n/**\n * Render a single statement of an `OperationPreview` for the human-readable\n * preview block. SQL statements get a trailing `;` if missing — matches the\n * legacy `string[]`-based renderer byte-for-byte (per spec OQ-4). Other\n * languages (`'mongodb-shell'`) render verbatim.\n */\nfunction renderPreviewStatement(text: string, language: string): string | undefined {\n const trimmed = text.trim();\n if (!trimmed) return undefined;\n if (language === 'sql') {\n return trimmed.endsWith(';') ? trimmed : `${trimmed};`;\n }\n return trimmed;\n}\n\n/**\n * Choose the header label for a preview block. SQL-only previews keep the\n * legacy `DDL preview` label (preserves CLI byte-identity for SQL targets per\n * spec OQ-4); previews from any other family — or a mix that includes any\n * non-SQL language — use the family-agnostic `Operation preview` label.\n *\n * An empty `statements` array deliberately renders as `Operation preview`\n * rather than `DDL preview`: `Array.prototype.every` is vacuously true for\n * empty arrays, but we have no evidence the preview is SQL-only when no\n * statements are present, so the family-agnostic label is the safer default.\n */\nexport function previewBlockHeader(preview: OperationPreview): string {\n const allSql =\n preview.statements.length > 0 && preview.statements.every((s) => s.language === 'sql');\n return allSql ? 'DDL preview' : 'Operation preview';\n}\n\n// ============================================================================\n// Migration Command Output Formatters (shared by db init and db update)\n// ============================================================================\n\n/**\n * Shared CLI output type for migration commands (db init, db update).\n */\nexport interface MigrationCommandResult {\n readonly ok: true;\n readonly mode: 'plan' | 'apply';\n readonly plan: {\n readonly targetId: string;\n readonly destination: {\n readonly storageHash: string;\n readonly profileHash?: string;\n };\n readonly operations: readonly {\n readonly id: string;\n readonly label: string;\n readonly operationClass: string;\n }[];\n /**\n * Family-agnostic textual preview of the planned operations. Replaces the\n * previous `sql?: readonly string[]`. Consumers should read\n * `plan.preview?.statements`.\n */\n readonly preview?: OperationPreview;\n };\n readonly execution?: {\n readonly operationsPlanned: number;\n readonly operationsExecuted: number;\n };\n readonly marker?: {\n readonly storageHash: string;\n readonly profileHash?: string;\n };\n readonly summary: string;\n readonly timings: {\n readonly total: number;\n };\n}\n\n/**\n * Formats human-readable output for migration commands (db init, db update) in plan mode.\n */\nexport function formatMigrationPlanOutput(\n result: MigrationCommandResult,\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 formatDimText = (text: string) => formatDim(useColor, text);\n\n // Plan summary\n const operationCount = result.plan?.operations.length ?? 0;\n lines.push(`${formatGreen('✔')} Planned ${operationCount} operation(s)`);\n\n // Show operations tree\n if (result.plan?.operations && result.plan.operations.length > 0) {\n const formatYellow = createColorFormatter(useColor, yellow);\n lines.push(`${formatDimText('│')}`);\n for (let i = 0; i < result.plan.operations.length; i++) {\n const op = result.plan.operations[i];\n if (!op) continue;\n const isLast = i === result.plan.operations.length - 1;\n const treeChar = isLast ? '└' : '├';\n const opClassLabel =\n op.operationClass === 'destructive'\n ? formatYellow(`[${op.operationClass}]`)\n : formatDimText(`[${op.operationClass}]`);\n lines.push(`${formatDimText(treeChar)}─ ${op.label} ${opClassLabel}`);\n }\n\n const hasDestructive = result.plan.operations.some((op) => op.operationClass === 'destructive');\n if (hasDestructive) {\n lines.push('');\n lines.push(\n `${formatYellow('⚠')} This migration contains destructive operations that may cause data loss.`,\n );\n }\n }\n\n // Destination hash\n if (result.plan?.destination) {\n lines.push('');\n lines.push(`${formatDimText(`Destination hash: ${result.plan.destination.storageHash}`)}`);\n }\n\n // Statement preview (any family that implements OperationPreviewCapable)\n const preview = result.plan?.preview;\n if (preview) {\n lines.push('');\n lines.push(`${formatDimText(previewBlockHeader(preview))}`);\n if (preview.statements.length === 0) {\n lines.push(`${formatDimText('No operations.')}`);\n } else {\n lines.push('');\n for (const statement of preview.statements) {\n const rendered = renderPreviewStatement(statement.text, statement.language);\n if (rendered) {\n lines.push(rendered);\n }\n }\n }\n }\n\n // Timings in verbose mode\n if (isVerbose(flags, 1)) {\n lines.push(`${formatDimText(`Total time: ${result.timings.total}ms`)}`);\n }\n\n // Note about dry run\n lines.push('');\n lines.push(`${formatDimText('This is a dry run. No changes were applied.')}`);\n lines.push(`${formatDimText('Run without --dry-run to apply changes.')}`);\n\n return lines.join('\\n');\n}\n\nexport interface MigrationApplyCommandOutputResult {\n readonly migrationsApplied: number;\n readonly markerHash: string;\n readonly applied: readonly {\n readonly dirName: string;\n readonly operationsExecuted: number;\n }[];\n readonly summary: string;\n readonly timings?: {\n readonly total: number;\n };\n}\n\nexport function formatMigrationApplyCommandOutput(\n result: MigrationApplyCommandOutputResult,\n flags: GlobalFlags,\n): string {\n if (flags.quiet) {\n return '';\n }\n\n const lines: string[] = [];\n const useColor = flags.color !== false;\n const formatGreen = createColorFormatter(useColor, green);\n const formatDimText = (text: string) => formatDim(useColor, text);\n\n if (result.migrationsApplied === 0) {\n lines.push(`${formatGreen('✔')} ${result.summary}`);\n lines.push(formatDimText(` marker: ${result.markerHash}`));\n return lines.join('\\n');\n }\n\n lines.push(`${formatGreen('✔')} ${result.summary}`);\n lines.push('');\n\n for (let i = 0; i < result.applied.length; i++) {\n const migration = result.applied[i]!;\n const isLast = i === result.applied.length - 1;\n const treeChar = isLast ? '└' : '├';\n lines.push(\n `${formatDimText(treeChar)}─ ${migration.dirName} ${formatDimText(`[${migration.operationsExecuted} op(s)]`)}`,\n );\n }\n\n lines.push('');\n lines.push(formatDimText(`marker: ${result.markerHash}`));\n\n if (isVerbose(flags, 1) && result.timings) {\n lines.push('');\n lines.push(formatDimText(`Total time: ${result.timings.total}ms`));\n }\n\n return lines.join('\\n');\n}\n\ninterface MigrationShowResult {\n readonly dirName: string;\n readonly dirPath: string;\n readonly from: string | null;\n readonly to: string;\n readonly migrationHash: string;\n readonly createdAt: string;\n readonly operations: readonly {\n readonly id: string;\n readonly label: string;\n readonly operationClass: string;\n }[];\n readonly preview: OperationPreview;\n readonly summary: string;\n}\n\nexport function formatMigrationShowOutput(result: MigrationShowResult, 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 formatYellow = createColorFormatter(useColor, yellow);\n const formatDimText = (text: string) => formatDim(useColor, text);\n\n lines.push(`${formatGreen('✔')} ${result.dirName}`);\n lines.push(`${formatDimText(` from: ${result.from ?? '(baseline)'}`)}`);\n lines.push(`${formatDimText(` to: ${result.to}`)}`);\n lines.push(`${formatDimText(` migrationHash: ${result.migrationHash}`)}`);\n lines.push(`${formatDimText(` created: ${result.createdAt}`)}`);\n\n lines.push('');\n lines.push(`${result.operations.length} operation(s)`);\n\n if (result.operations.length > 0) {\n lines.push(`${formatDimText('│')}`);\n for (let i = 0; i < result.operations.length; i++) {\n const op = result.operations[i]!;\n const isLast = i === result.operations.length - 1;\n const treeChar = isLast ? '└' : '├';\n const opClassLabel =\n op.operationClass === 'destructive'\n ? formatYellow(`[${op.operationClass}]`)\n : formatDimText(`[${op.operationClass}]`);\n lines.push(`${formatDimText(treeChar)}─ ${op.label} ${opClassLabel}`);\n }\n\n const hasDestructive = result.operations.some((op) => op.operationClass === 'destructive');\n if (hasDestructive) {\n lines.push('');\n lines.push(\n `${formatYellow('⚠')} This migration contains destructive operations that may cause data loss.`,\n );\n }\n }\n\n if (result.preview.statements.length > 0) {\n lines.push('');\n lines.push(`${formatDimText(previewBlockHeader(result.preview))}`);\n lines.push('');\n for (const statement of result.preview.statements) {\n const rendered = renderPreviewStatement(statement.text, statement.language);\n if (rendered) {\n lines.push(rendered);\n }\n }\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Formats human-readable output for migration commands (db init, db update) in apply mode.\n */\nexport function formatMigrationApplyOutput(\n result: MigrationCommandResult,\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 formatDimText = (text: string) => formatDim(useColor, text);\n\n if (result.ok) {\n // Success summary\n const executed = result.execution?.operationsExecuted ?? 0;\n if (executed === 0) {\n lines.push(`${formatGreen('✔')} Database already matches contract`);\n } else {\n lines.push(`${formatGreen('✔')} Applied ${executed} operation(s)`);\n }\n\n // Marker info\n if (result.marker) {\n lines.push(`${formatDimText(` Signature: ${result.marker.storageHash}`)}`);\n if (result.marker.profileHash) {\n lines.push(`${formatDimText(` Profile hash: ${result.marker.profileHash}`)}`);\n }\n }\n\n // Timings in verbose mode\n if (isVerbose(flags, 1)) {\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 migration commands (db init, db update).\n */\nexport function formatMigrationJson(result: MigrationCommandResult): string {\n return JSON.stringify(result, null, 2);\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"output-
|
|
1
|
+
{"version":3,"file":"output-BpcQrnnq.mjs","names":["lines: string[]","steps: string[]"],"sources":["../src/commands/init/output.ts"],"sourcesContent":["import { type } from 'arktype';\nimport type { GlobalFlags } from '../../utils/global-flags';\nimport type { TerminalUI } from '../../utils/terminal-ui';\n\n/**\n * arktype schema for the structured success document `init --json` writes\n * to stdout (FR1.5). The same shape backs the human-readable outro\n * renderer (FR10), so the two output modes carry identical information.\n *\n * `target` is normalised to the user-facing flag value (`mongodb` rather\n * than the internal `mongo`) so consumers can round-trip the document\n * straight into a follow-up `--target` invocation.\n *\n * The `ok: true` literal is the documented success/error discriminator —\n * see [Style Guide § JSON Semantics](../../../../../../../docs/CLI%20Style%20Guide.md#json-semantics).\n * Error envelopes (`CliErrorEnvelope`) carry `ok: false` so consumers can\n * branch with `if (doc.ok)` without inspecting the rest of the structure.\n */\nexport const InitOutputSchema = type({\n ok: 'true',\n target: \"'postgres'|'mongodb'\",\n authoring: \"'psl'|'typescript'\",\n schemaPath: 'string',\n filesWritten: 'string[]',\n /**\n * FR9.1 — files removed from disk during this run. Populated only on\n * re-init when previously-emitted contract artefacts (`contract.json`,\n * `contract.d.ts`, `start-/end-contract.*`, `ops.json`,\n * `migration.json`) were left behind by an earlier run. Empty on a\n * green-field init.\n */\n filesDeleted: 'string[]',\n packagesInstalled: {\n skipped: 'boolean',\n deps: 'string[]',\n devDeps: 'string[]',\n },\n contractEmitted: 'boolean',\n nextSteps: 'string[]',\n warnings: 'string[]',\n});\n\nexport type InitOutput = typeof InitOutputSchema.infer;\n\n/**\n * Serialises the output document for `--json`. Sorted keys are not enforced\n * — `JSON.stringify` preserves insertion order, and the schema field order\n * is the documented order, which matches what users will see when they\n * `jq .` the result.\n */\nexport function formatInitJson(output: InitOutput): string {\n return JSON.stringify(output, null, 2);\n}\n\n/**\n * Renders the human-readable outro on stderr (FR10.1). Re-uses the same\n * data structure as the JSON output so the two stay in lock-step.\n *\n * Warnings come before \"Next steps\" because they describe state the user\n * needs to be aware of before acting on the next-steps list.\n */\nexport function renderInitOutro(ui: TerminalUI, output: InitOutput, flags: GlobalFlags): void {\n if (flags.quiet || flags.json) {\n return;\n }\n\n for (const warning of output.warnings) {\n ui.warn(warning);\n }\n\n const lines: string[] = [];\n lines.push(`Target: ${output.target}`);\n lines.push(`Authoring: ${output.authoring}`);\n lines.push(`Schema: ${output.schemaPath}`);\n lines.push('');\n lines.push('Files written:');\n for (const file of output.filesWritten) {\n lines.push(` • ${file}`);\n }\n\n if (output.filesDeleted.length > 0) {\n lines.push('');\n lines.push('Files deleted (stale contract artefacts):');\n for (const file of output.filesDeleted) {\n lines.push(` • ${file}`);\n }\n }\n\n if (!output.packagesInstalled.skipped) {\n lines.push('');\n lines.push('Packages installed:');\n for (const dep of output.packagesInstalled.deps) {\n lines.push(` • ${dep}`);\n }\n for (const dep of output.packagesInstalled.devDeps) {\n lines.push(` • ${dep} (dev)`);\n }\n }\n\n lines.push('');\n lines.push('Next steps:');\n for (const step of output.nextSteps) {\n lines.push(` ${step}`);\n }\n\n ui.note(lines.join('\\n'), 'Done');\n}\n\n/**\n * Builds the `nextSteps` array from the resolved scaffold state. Steps are\n * ordered by the workflow a user needs to follow: configure connection →\n * (emit if not yet done) → run a starter query → docs / agent skill.\n *\n * The strings are stable and human-readable; agents wanting to act on them\n * should match on substrings (e.g. \"DATABASE_URL\") rather than exact text,\n * since copy may evolve.\n */\nexport function buildNextSteps(options: {\n readonly target: 'postgres' | 'mongodb';\n readonly contractEmitted: boolean;\n readonly emitCommand: string;\n readonly schemaPath: string;\n}): string[] {\n const steps: string[] = [];\n steps.push('1. Set DATABASE_URL in your environment (export it or add it to .env).');\n if (!options.contractEmitted) {\n steps.push(`2. Emit the contract: \\`${options.emitCommand}\\``);\n steps.push(`3. Edit your schema at ${options.schemaPath}, then re-run the emit command.`);\n steps.push(\n '4. Open prisma-next.md for a quick reference on how to write your first typed query.',\n );\n steps.push(\n '5. The .agents/skills/prisma-next/SKILL.md file is wired up for AI-coding agents in this project.',\n );\n } else {\n steps.push(\n `2. Edit your schema at ${options.schemaPath}, then re-run \\`${options.emitCommand}\\`.`,\n );\n steps.push(\n '3. Open prisma-next.md for a quick reference on how to write your first typed query.',\n );\n steps.push(\n '4. The .agents/skills/prisma-next/SKILL.md file is wired up for AI-coding agents in this project.',\n );\n }\n return steps;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAkBA,MAAa,mBAAmB,KAAK;CACnC,IAAI;CACJ,QAAQ;CACR,WAAW;CACX,YAAY;CACZ,cAAc;CAQd,cAAc;CACd,mBAAmB;EACjB,SAAS;EACT,MAAM;EACN,SAAS;EACV;CACD,iBAAiB;CACjB,WAAW;CACX,UAAU;CACX,CAAC;;;;;;;AAUF,SAAgB,eAAe,QAA4B;AACzD,QAAO,KAAK,UAAU,QAAQ,MAAM,EAAE;;;;;;;;;AAUxC,SAAgB,gBAAgB,IAAgB,QAAoB,OAA0B;AAC5F,KAAI,MAAM,SAAS,MAAM,KACvB;AAGF,MAAK,MAAM,WAAW,OAAO,SAC3B,IAAG,KAAK,QAAQ;CAGlB,MAAMA,QAAkB,EAAE;AAC1B,OAAM,KAAK,cAAc,OAAO,SAAS;AACzC,OAAM,KAAK,cAAc,OAAO,YAAY;AAC5C,OAAM,KAAK,cAAc,OAAO,aAAa;AAC7C,OAAM,KAAK,GAAG;AACd,OAAM,KAAK,iBAAiB;AAC5B,MAAK,MAAM,QAAQ,OAAO,aACxB,OAAM,KAAK,OAAO,OAAO;AAG3B,KAAI,OAAO,aAAa,SAAS,GAAG;AAClC,QAAM,KAAK,GAAG;AACd,QAAM,KAAK,4CAA4C;AACvD,OAAK,MAAM,QAAQ,OAAO,aACxB,OAAM,KAAK,OAAO,OAAO;;AAI7B,KAAI,CAAC,OAAO,kBAAkB,SAAS;AACrC,QAAM,KAAK,GAAG;AACd,QAAM,KAAK,sBAAsB;AACjC,OAAK,MAAM,OAAO,OAAO,kBAAkB,KACzC,OAAM,KAAK,OAAO,MAAM;AAE1B,OAAK,MAAM,OAAO,OAAO,kBAAkB,QACzC,OAAM,KAAK,OAAO,IAAI,QAAQ;;AAIlC,OAAM,KAAK,GAAG;AACd,OAAM,KAAK,cAAc;AACzB,MAAK,MAAM,QAAQ,OAAO,UACxB,OAAM,KAAK,KAAK,OAAO;AAGzB,IAAG,KAAK,MAAM,KAAK,KAAK,EAAE,OAAO;;;;;;;;;;;AAYnC,SAAgB,eAAe,SAKlB;CACX,MAAMC,QAAkB,EAAE;AAC1B,OAAM,KAAK,yEAAyE;AACpF,KAAI,CAAC,QAAQ,iBAAiB;AAC5B,QAAM,KAAK,2BAA2B,QAAQ,YAAY,IAAI;AAC9D,QAAM,KAAK,0BAA0B,QAAQ,WAAW,iCAAiC;AACzF,QAAM,KACJ,uFACD;AACD,QAAM,KACJ,oGACD;QACI;AACL,QAAM,KACJ,0BAA0B,QAAQ,WAAW,kBAAkB,QAAQ,YAAY,KACpF;AACD,QAAM,KACJ,uFACD;AACD,QAAM,KACJ,oGACD;;AAEH,QAAO"}
|
|
@@ -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-BT9tgCOH.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify-BumcH6Ry.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-BT9tgCOH.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":";;;;;;;;AAgDA,SAAgB,mBACd,QACA,OACQ;AACR,KAAI,MAAM,MACR,QAAO;CAGT,MAAMA,QAAkB,EAAE;CAE1B,MAAM,WAAW,MAAM,UAAU;CACjC,MAAM,cAAc,qBAAqB,UAAU,MAAM;CACzD,MAAM,eAAe,qBAAqB,UAAU,OAAO;CAC3D,MAAM,iBAAiB,SAAiB,UAAU,UAAU,KAAK;CACjE,MAAM,mBACJ,OAAO,SAAS,SACZ,kBAAkB,OAAO,QAAQ,SAAS,cAAc,kBACxD;AAEN,OAAM,KAAK,GAAG,YAAY,IAAI,CAAC,GAAG,OAAO,UAAU;AACnD,OAAM,KAAK,GAAG,cAAc,mBAAmB,mBAAmB,GAAG;AACrE,OAAM,KAAK,GAAG,cAAc,kBAAkB,OAAO,SAAS,cAAc,GAAG;AAC/E,KAAI,OAAO,SAAS,YAClB,OAAM,KAAK,GAAG,cAAc,kBAAkB,OAAO,SAAS,cAAc,GAAG;AAEjF,KAAI,OAAO,SAAS,UAAU,OAAO,UAAU,UAAU,OAAO,EAAE,CAChE,OAAM,KACJ,GAAG,cAAc,kBAAkB,OAAO,OAAO,OAAO,KAAK,QAAQ,OAAO,OAAO,OAAO,KAAK,QAAQ,OAAO,OAAO,OAAO,OAAO,GACpI;AAEH,KAAI,OAAO,SAAS;AAClB,QAAM,KAAK,GAAG;AACd,QAAM,KAAK,GAAG,aAAa,IAAI,CAAC,GAAG,OAAO,UAAU;;AAGtD,KAAI,UAAU,OAAO,EAAE,EAAE;AACvB,MAAI,OAAO,qBACT,OAAM,KACJ,GAAG,cAAc,sEAAsE,GACxF;AAEH,QAAM,KAAK,GAAG,cAAc,iBAAiB,OAAO,QAAQ,MAAM,IAAI,GAAG;;AAG3E,QAAO,MAAM,KAAK,KAAK;;;;;AAMzB,SAAgB,iBAAiB,QAA8C;CAC7E,MAAM,SAAS;EACb,IAAI,OAAO;EACX,SAAS,OAAO;EAChB,MAAM,OAAO;EACb,UAAU,OAAO;EACjB,GAAG,UAAU,UAAU,OAAO,OAAO;EACrC,QAAQ,OAAO;EACf,GAAG,UAAU,iBAAiB,OAAO,cAAc;EACnD,GAAG,UAAU,wBAAwB,OAAO,qBAAqB;EACjE,GAAG,UAAU,UAAU,OAAO,OAAO;EACrC,GAAG,UAAU,WAAW,OAAO,QAAQ;EACvC,GAAG,UAAU,QAAQ,OAAO,KAAK;EACjC,SAAS,OAAO;EACjB;AAED,QAAO,KAAK,UAAU,QAAQ,MAAM,EAAE;;;;;AAMxC,SAAgB,qBAAqB,QAAiD;AACpF,QAAO,KAAK,UAAU,QAAQ,MAAM,EAAE;;;;;;AAOxC,SAAS,iBACP,MACA,OACA,SAOU;CACV,MAAM,EAAE,QAAQ,QAAQ,UAAU,eAAe,SAAS,UAAU;CACpE,MAAMA,QAAkB,EAAE;CAG1B,IAAIC,iBAAyB,KAAK;AAElC,KAAI,SACF,SAAQ,KAAK,MAAb;EACE,KAAK;AACH,oBAAiB,KAAK,KAAK,MAAM;AACjC;EACF,KAAK,UAAU;GAEb,MAAM,aAAa,KAAK,MAAM,MAAM,iBAAiB;AACrD,OAAI,aAAa,IAAI;IACnB,MAAM,YAAY,WAAW;AAC7B,qBAAiB,GAAG,IAAI,QAAQ,CAAC,GAAG,KAAK,UAAU;SAGnD,kBAAiB,KAAK,KAAK,MAAM;AAEnC;;EAEF,KAAK;AAEH,oBAAiB,IAAI,KAAK,MAAM;AAChC;EAEF,KAAK,SAAS;GAGZ,MAAM,cAAc,KAAK,MAAM,MAAM,oBAAoB;AACzD,OAAI,cAAc,MAAM,YAAY,IAAI;IACtC,MAAM,aAAa,YAAY;IAC/B,MAAM,OAAO,YAAY;IAEzB,MAAM,YAAY,KAAK,MAAM,4BAA4B;AACzD,QAAI,YAAY,MAAM,UAAU,IAAI;KAClC,MAAM,cAAc,UAAU;KAC9B,MAAM,cAAc,UAAU;AAC9B,sBAAiB,GAAG,KAAK,WAAW,CAAC,IAAI,YAAY,GAAG,IAAI,YAAY;UAGxE,kBAAiB,GAAG,KAAK,WAAW,CAAC,IAAI;SAG3C,kBAAiB,KAAK;AAExB;;EAEF,KAAK,SAAS;GAGZ,MAAM,UAAU,KAAK,MAAM,MAAM,wBAAwB;AACzD,OAAI,UAAU,IAAI;IAChB,MAAM,cAAc,QAAQ;AAC5B,qBAAiB,GAAG,IAAI,cAAc,CAAC,IAAI,KAAK,YAAY;UACvD;IAEL,MAAM,cAAc,KAAK,MAAM,MAAM,kBAAkB;AACvD,QAAI,cAAc,IAAI;KACpB,MAAM,OAAO,YAAY;AACzB,sBAAiB,GAAG,IAAI,SAAS,CAAC,GAAG,KAAK,KAAK;WAC1C;KAEL,MAAM,aAAa,KAAK,MAAM,MAAM,6BAA6B;AACjE,SAAI,aAAa,IAAI;MACnB,MAAM,cAAc,WAAW,KAAK,GAAG,IAAI,SAAS,CAAC,KAAK;MAC1D,MAAM,OAAO,WAAW;AACxB,uBAAiB,GAAG,cAAc,IAAI,QAAQ,CAAC,GAAG,KAAK,KAAK;WAE5D,kBAAiB,IAAI,KAAK,MAAM;;;AAItC;;EAEF,KAAK,cAAc;GAGjB,MAAM,WAAW,KAAK,MAAM,MAAM,sCAAsC;AACxE,OAAI,WAAW,MAAM,SAAS,IAAI;IAChC,MAAM,UAAU,SAAS;IACzB,MAAM,OAAO,SAAS;AACtB,qBAAiB,GAAG,KAAK,QAAQ,CAAC,GAAG,IAAI,KAAK;SAG9C,kBAAiB,QAAQ,KAAK,MAAM;AAEtC;;EAEF;AACE,oBAAiB,KAAK;AACtB;;AAKN,KAAI,OACF,OAAM,KAAK,eAAe;MACrB;EAEL,MAAM,aAAa,GAAG,cADL,SAAS,MAAM,IACa,CAAC;AAC9C,QAAM,KAAK,GAAG,SAAS,aAAa,iBAAiB;;AAIvD,KAAI,KAAK,YAAY,KAAK,SAAS,SAAS,GAAG;EAC7C,MAAM,cAAc,SAAS,KAAK,GAAG,SAAS,SAAS,QAAQ,GAAG,cAAc,IAAI,CAAC;AACrF,OAAK,IAAI,IAAI,GAAG,IAAI,KAAK,SAAS,QAAQ,KAAK;GAC7C,MAAM,QAAQ,KAAK,SAAS;AAC5B,OAAI,CAAC,MAAO;GAEZ,MAAM,aAAa,iBAAiB,OAAO,OAAO;IAChD,QAFkB,MAAM,KAAK,SAAS,SAAS;IAG/C,QAAQ;IACR;IACA;IACA,QAAQ;IACT,CAAC;AACF,SAAM,KAAK,GAAG,WAAW;;;AAI7B,QAAO;;;;;AAMT,SAAgB,uBACd,QACA,YACA,OACQ;AACR,KAAI,MAAM,MACR,QAAO;CAGT,MAAMD,QAAkB,EAAE;CAE1B,MAAM,WAAW,MAAM,UAAU;CACjC,MAAM,iBAAiB,SAAiB,UAAU,UAAU,KAAK;AAEjE,KAAI,YAAY;EAEd,MAAM,YAAY,iBAAiB,WAAW,MAAM,OAAO;GACzD,QAAQ;GACR,QAAQ;GACR;GACA;GACA,QAAQ;GACT,CAAC;AACF,QAAM,KAAK,GAAG,UAAU;QACnB;AAEL,QAAM,KAAK,KAAK,OAAO,UAAU;AACjC,MAAI,UAAU,OAAO,EAAE,EAAE;AACvB,SAAM,KAAK,aAAa,OAAO,OAAO,SAAS,GAAG,OAAO,OAAO,KAAK;AACrE,OAAI,OAAO,MAAM,MACf,OAAM,KAAK,eAAe,OAAO,KAAK,QAAQ;;;AAMpD,KAAI,UAAU,OAAO,EAAE,CACrB,OAAM,KAAK,GAAG,cAAc,iBAAiB,OAAO,QAAQ,MAAM,IAAI,GAAG;AAG3E,QAAO,MAAM,KAAK,KAAK;;;;;;AAOzB,SAAS,6BACP,MACA,OACA,SAOU;CACV,MAAM,EAAE,QAAQ,QAAQ,UAAU,eAAe,SAAS,UAAU;CACpE,MAAMA,QAAkB,EAAE;CAG1B,IAAI,cAAc;CAClB,IAAIE,eAAyC,SAAS;AACtD,KAAI,SACF,SAAQ,KAAK,QAAb;EACE,KAAK;AACH,iBAAc;AACd,iBAAc;AACd;EACF,KAAK;AACH,iBAAc;AACd,kBAAe,SAAU,WAAW,OAAO,KAAK,GAAG;AACnD;EACF,KAAK;AACH,iBAAc;AACd,iBAAc;AACd;;KAGJ,SAAQ,KAAK,QAAb;EACE,KAAK;AACH,iBAAc;AACd;EACF,KAAK;AACH,iBAAc;AACd;EACF,KAAK;AACH,iBAAc;AACd;;CAMN,IAAIC,cAAwC,SAAS;CACrD,IAAIF,iBAAyB,KAAK;AAElC,KAAI,SACF,SAAQ,KAAK,MAAb;EACE,KAAK;EACL,KAAK;AACH,gBAAa;AACb,oBAAiB,WAAW,KAAK,KAAK;AACtC;EACF,KAAK,SAAS;GAEZ,MAAM,aAAa,KAAK,KAAK,MAAM,iBAAiB;AACpD,OAAI,aAAa,IAAI;IACnB,MAAM,YAAY,WAAW;AAC7B,qBAAiB,GAAG,IAAI,QAAQ,CAAC,GAAG,KAAK,UAAU;SAEnD,kBAAiB,IAAI,KAAK,KAAK;AAEjC;;EAEF,KAAK;AACH,gBAAa;AACb,oBAAiB,WAAW,KAAK,KAAK;AACtC;EACF,KAAK,UAAU;GAGb,MAAM,cAAc,KAAK,KAAK,MAAM,oBAAoB;AACxD,OAAI,cAAc,MAAM,YAAY,IAAI;IACtC,MAAM,aAAa,YAAY;IAC/B,MAAM,OAAO,YAAY;IAGzB,MAAM,YAAY,KAAK,MAAM,4CAA4C;AACzE,QAAI,YAAY,MAAM,UAAU,MAAM,UAAU,IAAI;KAClD,MAAM,eAAe,UAAU;KAC/B,MAAM,aAAa,UAAU;KAC7B,MAAM,cAAc,UAAU;AAC9B,sBAAiB,GAAG,KAAK,WAAW,CAAC,IAAI,aAAa,KAAK,IAAI,WAAW,CAAC,GAAG,IAAI,YAAY;UAG9F,kBAAiB,GAAG,KAAK,WAAW,CAAC,IAAI;SAG3C,kBAAiB,KAAK;AAExB;;EAEF,KAAK;EACL,KAAK;AACH,iBAAc,SAAS;AACvB,oBAAiB,WAAW,KAAK,KAAK;AACtC;EACF,KAAK,cAAc;GAEjB,MAAM,UAAU,KAAK,KAAK,MAAM,wBAAwB;AACxD,OAAI,UAAU,IAAI;IAChB,MAAM,cAAc,QAAQ;AAC5B,qBAAiB,GAAG,IAAI,cAAc,CAAC,IAAI,KAAK,YAAY;SAE5D,kBAAiB,IAAI,KAAK,KAAK;AAEjC;;EAEF,KAAK;EACL,KAAK;EACL,KAAK;AACH,gBAAa;AACb,oBAAiB,WAAW,KAAK,KAAK;AACtC;EACF,KAAK,cAAc;GAGjB,MAAM,UAAU,KAAK,KAAK,MAAM,uBAAuB;AACvD,OAAI,UAAU,IAAI;IAChB,MAAM,SAAS,QAAQ;AACvB,qBAAiB,GAAG,IAAI,cAAc,CAAC,GAAG,KAAK,OAAO;UACjD;IAGL,MAAM,WAAW,KAAK,KAAK,MAAM,sCAAsC;AACvE,QAAI,WAAW,MAAM,SAAS,IAAI;KAChC,MAAM,UAAU,SAAS;KACzB,MAAM,OAAO,SAAS;AACtB,sBAAiB,GAAG,KAAK,QAAQ,CAAC,GAAG,IAAI,KAAK;WACzC;AAEL,kBAAa;AACb,sBAAiB,WAAW,KAAK,KAAK;;;AAG1C;;EAEF;AACE,oBAAiB,KAAK;AACtB;;KAGJ,kBAAiB,KAAK;CAGxB,MAAM,qBAAqB,YAAY,YAAY;CAGnD,IAAI,YAAY;AAChB,MACG,KAAK,WAAW,UAAU,KAAK,WAAW,WAC3C,KAAK,WACL,KAAK,QAAQ,SAAS,GACtB;EAIA,MAAM,cAAc,cAAc,IAAI,KAAK,QAAQ,GAAG;AACtD,cAAY,GAAG,eAAe,GAAG;;AAKnC,KAAI,OACF,OAAM,KAAK,GAAG,mBAAmB,GAAG,YAAY;MAC3C;EAEL,MAAM,aAAa,GAAG,cADL,SAAS,MAAM,IACa,CAAC;AAC9C,QAAM,KAAK,GAAG,SAAS,aAAa,mBAAmB,GAAG,YAAY;;AAIxE,KAAI,KAAK,YAAY,KAAK,SAAS,SAAS,GAAG;EAC7C,MAAM,cAAc,SAAS,KAAK,GAAG,SAAS,SAAS,QAAQ,GAAG,cAAc,IAAI,CAAC;AACrF,OAAK,IAAI,IAAI,GAAG,IAAI,KAAK,SAAS,QAAQ,KAAK;GAC7C,MAAM,QAAQ,KAAK,SAAS;AAC5B,OAAI,CAAC,MAAO;GAEZ,MAAM,aAAa,6BAA6B,OAAO,OAAO;IAC5D,QAFkB,MAAM,KAAK,SAAS,SAAS;IAG/C,QAAQ;IACR;IACA;IACA,QAAQ;IACT,CAAC;AACF,SAAM,KAAK,GAAG,WAAW;;;AAI7B,QAAO;;;;;AAMT,SAAgB,yBACd,QACA,OACQ;AACR,KAAI,MAAM,MACR,QAAO;CAGT,MAAMD,QAAkB,EAAE;CAE1B,MAAM,WAAW,MAAM,UAAU;CACjC,MAAM,cAAc,qBAAqB,UAAU,MAAM;CACzD,MAAM,YAAY,qBAAqB,UAAU,IAAI;CACrD,MAAM,iBAAiB,SAAiB,UAAU,UAAU,KAAK;CAGjE,MAAM,YAAY,6BAA6B,OAAO,OAAO,MAAM,OAAO;EACxE,QAAQ;EACR,QAAQ;EACR;EACA;EACA,QAAQ;EACT,CAAC;AACF,OAAM,KAAK,GAAG,UAAU;AAGxB,KAAI,UAAU,OAAO,EAAE,EAAE;AACvB,QAAM,KAAK,GAAG,cAAc,iBAAiB,OAAO,QAAQ,MAAM,IAAI,GAAG;AACzE,QAAM,KACJ,GAAG,cAAc,UAAU,OAAO,OAAO,OAAO,KAAK,QAAQ,OAAO,OAAO,OAAO,KAAK,QAAQ,OAAO,OAAO,OAAO,OAAO,GAC5H;;AAIH,OAAM,KAAK,GAAG;AAGd,KAAI,OAAO,GACT,OAAM,KAAK,GAAG,YAAY,IAAI,CAAC,GAAG,OAAO,UAAU;MAC9C;EACL,MAAM,WAAW,OAAO,OAAO,KAAK,OAAO,KAAK,KAAK;AACrD,QAAM,KAAK,GAAG,UAAU,IAAI,CAAC,GAAG,OAAO,UAAU,WAAW;;AAG9D,QAAO,MAAM,KAAK,KAAK;;;;;AAMzB,SAAgB,uBAAuB,QAA4C;AACjF,QAAO,KAAK,UAAU,QAAQ,MAAM,EAAE;;;;;AAUxC,SAAgB,iBAAiB,QAA4B,OAA4B;AACvF,KAAI,MAAM,MACR,QAAO;CAGT,MAAMA,QAAkB,EAAE;CAE1B,MAAM,WAAW,MAAM,UAAU;CACjC,MAAM,cAAc,qBAAqB,UAAU,MAAM;CACzD,MAAM,iBAAiB,SAAiB,UAAU,UAAU,KAAK;AAEjE,KAAI,OAAO,IAAI;AAEb,QAAM,KAAK,GAAG,YAAY,IAAI,CAAC,kBAAkB;EAGjD,MAAM,eAAe,OAAO,OAAO,UAAU,eAAe;EAC5D,MAAM,cAAc,OAAO,SAAS;AAEpC,QAAM,KAAK,GAAG,cAAc,WAAW,eAAe,GAAG;AACzD,QAAM,KAAK,GAAG,cAAc,WAAW,cAAc,GAAG;AAExD,MAAI,UAAU,OAAO,EAAE,EAAE;AACvB,OAAI,OAAO,SAAS,YAClB,OAAM,KAAK,GAAG,cAAc,kBAAkB,OAAO,SAAS,cAAc,GAAG;AAEjF,OAAI,OAAO,OAAO,UAAU,YAC1B,OAAM,KACJ,GAAG,cAAc,2BAA2B,OAAO,OAAO,SAAS,cAAc,GAClF;AAEH,SAAM,KAAK,GAAG,cAAc,iBAAiB,OAAO,QAAQ,MAAM,IAAI,GAAG;;;AAI7E,QAAO,MAAM,KAAK,KAAK;;;;;AAMzB,SAAgB,eAAe,QAAoC;AACjE,QAAO,KAAK,UAAU,QAAQ,MAAM,EAAE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma-next/cli",
|
|
3
|
-
"version": "0.5.0-dev.
|
|
3
|
+
"version": "0.5.0-dev.29",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"files": [
|
|
@@ -26,25 +26,25 @@
|
|
|
26
26
|
"string-width": "^8.2.0",
|
|
27
27
|
"strip-ansi": "^7.1.2",
|
|
28
28
|
"wrap-ansi": "^10.0.0",
|
|
29
|
-
"@prisma-next/
|
|
30
|
-
"@prisma-next/
|
|
31
|
-
"@prisma-next/
|
|
32
|
-
"@prisma-next/framework-components": "0.5.0-dev.
|
|
33
|
-
"@prisma-next/
|
|
34
|
-
"@prisma-next/
|
|
35
|
-
"@prisma-next/
|
|
36
|
-
"@prisma-next/
|
|
29
|
+
"@prisma-next/contract": "0.5.0-dev.29",
|
|
30
|
+
"@prisma-next/config": "0.5.0-dev.29",
|
|
31
|
+
"@prisma-next/errors": "0.5.0-dev.29",
|
|
32
|
+
"@prisma-next/framework-components": "0.5.0-dev.29",
|
|
33
|
+
"@prisma-next/emitter": "0.5.0-dev.29",
|
|
34
|
+
"@prisma-next/migration-tools": "0.5.0-dev.29",
|
|
35
|
+
"@prisma-next/psl-printer": "0.5.0-dev.29",
|
|
36
|
+
"@prisma-next/utils": "0.5.0-dev.29"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@types/node": "24.10.4",
|
|
40
40
|
"tsdown": "0.18.4",
|
|
41
41
|
"typescript": "5.9.3",
|
|
42
42
|
"vitest": "4.0.17",
|
|
43
|
-
"@prisma-next/sql-contract": "0.5.0-dev.
|
|
44
|
-
"@prisma-next/sql-contract-emitter": "0.5.0-dev.
|
|
45
|
-
"@prisma-next/sql-
|
|
46
|
-
"@prisma-next/sql-
|
|
47
|
-
"@prisma-next/sql-runtime": "0.5.0-dev.
|
|
43
|
+
"@prisma-next/sql-contract": "0.5.0-dev.29",
|
|
44
|
+
"@prisma-next/sql-contract-emitter": "0.5.0-dev.29",
|
|
45
|
+
"@prisma-next/sql-contract-ts": "0.5.0-dev.29",
|
|
46
|
+
"@prisma-next/sql-operations": "0.5.0-dev.29",
|
|
47
|
+
"@prisma-next/sql-runtime": "0.5.0-dev.29",
|
|
48
48
|
"@prisma-next/test-utils": "0.0.1",
|
|
49
49
|
"@prisma-next/tsconfig": "0.0.0",
|
|
50
50
|
"@prisma-next/tsdown": "0.0.0"
|
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { errorRuntime } from '@prisma-next/errors/execution';
|
|
3
|
-
import { printPsl
|
|
4
|
-
import {
|
|
5
|
-
createPostgresDefaultMapping,
|
|
6
|
-
createPostgresTypeMap,
|
|
7
|
-
extractEnumInfo,
|
|
8
|
-
parseRawDefault,
|
|
9
|
-
} from '@prisma-next/psl-printer/postgres';
|
|
3
|
+
import { printPsl } from '@prisma-next/psl-printer';
|
|
10
4
|
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
11
5
|
import { Command } from 'commander';
|
|
12
6
|
import { dirname, relative } from 'pathe';
|
|
@@ -59,26 +53,19 @@ async function executeContractInferCommand(
|
|
|
59
53
|
return inspectResult;
|
|
60
54
|
}
|
|
61
55
|
|
|
62
|
-
const { config, target, meta } = inspectResult.value;
|
|
56
|
+
const { config, target, meta, pslContractAst } = inspectResult.value;
|
|
63
57
|
|
|
64
|
-
if (
|
|
58
|
+
if (!pslContractAst) {
|
|
65
59
|
return notOk(
|
|
66
|
-
errorRuntime(
|
|
67
|
-
why: 'contract
|
|
68
|
-
fix: 'Use
|
|
60
|
+
errorRuntime('contract infer is not supported for this family', {
|
|
61
|
+
why: 'The configured family does not implement the PslContractInferCapable capability, so an inferred PSL contract cannot be produced from the live database schema.',
|
|
62
|
+
fix: 'Use a family that supports contract inference (e.g. SQL/Postgres).',
|
|
69
63
|
}),
|
|
70
64
|
);
|
|
71
65
|
}
|
|
72
66
|
|
|
73
|
-
const schema = validatePrintableSqlSchemaIR(inspectResult.value.schema);
|
|
74
67
|
const outputPath = resolveContractInferOutputPath(options, config.contract?.output);
|
|
75
|
-
const
|
|
76
|
-
const pslContent = printPsl(schema, {
|
|
77
|
-
defaultMapping: createPostgresDefaultMapping(),
|
|
78
|
-
typeMap: createPostgresTypeMap(enumInfo.typeNames),
|
|
79
|
-
enumInfo,
|
|
80
|
-
parseRawDefault,
|
|
81
|
-
});
|
|
68
|
+
const pslContent = printPsl(pslContractAst);
|
|
82
69
|
|
|
83
70
|
if (existsSync(outputPath) && !flags.json && !flags.quiet) {
|
|
84
71
|
ui.stderr(`\u26A0 Overwriting existing file: ${relative(process.cwd(), outputPath)}`);
|
package/src/commands/db-init.ts
CHANGED
|
@@ -112,7 +112,7 @@ async function executeDbUpdateCommand(
|
|
|
112
112
|
label: op.label,
|
|
113
113
|
operationClass: op.operationClass,
|
|
114
114
|
})),
|
|
115
|
-
...ifDefined('
|
|
115
|
+
...ifDefined('preview', result.value.plan.preview),
|
|
116
116
|
},
|
|
117
117
|
...ifDefined(
|
|
118
118
|
'execution',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CoreSchemaView } from '@prisma-next/framework-components/control';
|
|
2
|
-
import {
|
|
2
|
+
import type { PslDocumentAst } from '@prisma-next/framework-components/psl-ast';
|
|
3
3
|
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
4
4
|
import { relative, resolve } from 'pathe';
|
|
5
5
|
import { loadConfig } from '../config-loader';
|
|
@@ -33,6 +33,12 @@ export interface InspectLiveSchemaResult {
|
|
|
33
33
|
readonly config: LoadedCliConfig;
|
|
34
34
|
readonly schema: unknown;
|
|
35
35
|
readonly schemaView: CoreSchemaView | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* PSL AST inferred from the introspected schema, when the configured family
|
|
38
|
+
* implements `PslContractInferCapable`. `undefined` for families that do not
|
|
39
|
+
* support inference (e.g. Mongo today).
|
|
40
|
+
*/
|
|
41
|
+
readonly pslContractAst: PslDocumentAst | undefined;
|
|
36
42
|
readonly target: {
|
|
37
43
|
readonly familyId: string;
|
|
38
44
|
readonly id: string;
|
|
@@ -122,14 +128,12 @@ export async function inspectLiveSchema(
|
|
|
122
128
|
const onProgress = createProgressAdapter({ ui, flags });
|
|
123
129
|
|
|
124
130
|
try {
|
|
125
|
-
const
|
|
131
|
+
const schema = await client.introspect({
|
|
126
132
|
connection: dbConnection,
|
|
127
133
|
onProgress,
|
|
128
134
|
});
|
|
129
|
-
// TODO(TML-2251): Remove SQL-specific branching — SQL should use the same family-agnostic path as Mongo.
|
|
130
|
-
const schema =
|
|
131
|
-
config.family.familyId === 'sql' ? validatePrintableSqlSchemaIR(schemaIR) : schemaIR;
|
|
132
135
|
const schemaView = client.toSchemaView(schema);
|
|
136
|
+
const pslContractAst = client.inferPslContract(schema);
|
|
133
137
|
|
|
134
138
|
const dbUrl = typeof dbConnection === 'string' ? maskConnectionUrl(dbConnection) : undefined;
|
|
135
139
|
|
|
@@ -137,6 +141,7 @@ export async function inspectLiveSchema(
|
|
|
137
141
|
config,
|
|
138
142
|
schema,
|
|
139
143
|
schemaView,
|
|
144
|
+
pslContractAst,
|
|
140
145
|
target: {
|
|
141
146
|
familyId: config.family.familyId,
|
|
142
147
|
id: config.target.targetId,
|
|
@@ -3,7 +3,9 @@ import type { Contract } from '@prisma-next/contract/types';
|
|
|
3
3
|
import { getEmittedArtifactPaths } from '@prisma-next/emitter';
|
|
4
4
|
import {
|
|
5
5
|
createControlStack,
|
|
6
|
+
hasOperationPreview,
|
|
6
7
|
type MigrationPlanOperation,
|
|
8
|
+
type OperationPreview,
|
|
7
9
|
} from '@prisma-next/framework-components/control';
|
|
8
10
|
import { MigrationToolsError } from '@prisma-next/migration-tools/errors';
|
|
9
11
|
import { computeMigrationHash } from '@prisma-next/migration-tools/hash';
|
|
@@ -20,7 +22,6 @@ import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
|
20
22
|
import { Command } from 'commander';
|
|
21
23
|
import { join, relative } from 'pathe';
|
|
22
24
|
import { loadConfig } from '../config-loader';
|
|
23
|
-
import { extractSqlDdl } from '../control-api/operations/extract-sql-ddl';
|
|
24
25
|
import {
|
|
25
26
|
type CliErrorConflict,
|
|
26
27
|
CliStructuredError,
|
|
@@ -65,7 +66,12 @@ export interface MigrationPlanResult {
|
|
|
65
66
|
readonly label: string;
|
|
66
67
|
readonly operationClass: string;
|
|
67
68
|
}[];
|
|
68
|
-
|
|
69
|
+
/**
|
|
70
|
+
* Family-agnostic textual preview of the migration plan operations.
|
|
71
|
+
* Replaces the previous `sql?: readonly string[]` field; consumers should
|
|
72
|
+
* read `result.preview?.statements`.
|
|
73
|
+
*/
|
|
74
|
+
readonly preview?: OperationPreview;
|
|
69
75
|
readonly summary: string;
|
|
70
76
|
/**
|
|
71
77
|
* When true, `migration.ts` was written but contains unfilled
|
|
@@ -364,7 +370,9 @@ async function executeMigrationPlanCommand(
|
|
|
364
370
|
return ok(result);
|
|
365
371
|
}
|
|
366
372
|
|
|
367
|
-
const
|
|
373
|
+
const preview = hasOperationPreview(familyInstance)
|
|
374
|
+
? familyInstance.toOperationPreview(plannedOps)
|
|
375
|
+
: undefined;
|
|
368
376
|
const result: MigrationPlanResult = {
|
|
369
377
|
ok: true,
|
|
370
378
|
noOp: false,
|
|
@@ -376,7 +384,7 @@ async function executeMigrationPlanCommand(
|
|
|
376
384
|
label: op.label,
|
|
377
385
|
operationClass: op.operationClass,
|
|
378
386
|
})),
|
|
379
|
-
|
|
387
|
+
...(preview !== undefined ? { preview } : {}),
|
|
380
388
|
summary: `Planned ${plannedOps.length} operation(s)`,
|
|
381
389
|
timings: { total: Date.now() - startTime },
|
|
382
390
|
};
|
|
@@ -503,14 +511,17 @@ function formatMigrationPlanOutput(result: MigrationPlanResult, flags: GlobalFla
|
|
|
503
511
|
`Next: review ${green_(result.dir ?? '<dir>')} if needed, then run ${green_('prisma-next migration apply')}.`,
|
|
504
512
|
);
|
|
505
513
|
|
|
506
|
-
if (result.
|
|
514
|
+
if (result.preview && result.preview.statements.length > 0) {
|
|
515
|
+
// The non-empty length is already guaranteed by the surrounding check, so
|
|
516
|
+
// a plain `every` here is equivalent to the helper in formatters/migrations.ts.
|
|
517
|
+
const allSql = result.preview.statements.every((s) => s.language === 'sql');
|
|
507
518
|
lines.push('');
|
|
508
|
-
lines.push(dim_('DDL preview'));
|
|
519
|
+
lines.push(dim_(allSql ? 'DDL preview' : 'Operation preview'));
|
|
509
520
|
lines.push('');
|
|
510
|
-
for (const statement of result.
|
|
511
|
-
const trimmed = statement.trim();
|
|
521
|
+
for (const statement of result.preview.statements) {
|
|
522
|
+
const trimmed = statement.text.trim();
|
|
512
523
|
if (!trimmed) continue;
|
|
513
|
-
const line = trimmed.endsWith(';') ? trimmed :
|
|
524
|
+
const line = statement.language === 'sql' && !trimmed.endsWith(';') ? `${trimmed};` : trimmed;
|
|
514
525
|
lines.push(line);
|
|
515
526
|
}
|
|
516
527
|
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
MigrationPlanOperation,
|
|
3
|
+
OperationPreview,
|
|
4
|
+
} from '@prisma-next/framework-components/control';
|
|
2
5
|
import { MigrationToolsError } from '@prisma-next/migration-tools/errors';
|
|
3
6
|
import { readMigrationPackage, readMigrationsDir } from '@prisma-next/migration-tools/io';
|
|
4
7
|
import {
|
|
@@ -10,7 +13,7 @@ import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
|
10
13
|
import { Command } from 'commander';
|
|
11
14
|
import { relative, resolve } from 'pathe';
|
|
12
15
|
import { loadConfig } from '../config-loader';
|
|
13
|
-
import {
|
|
16
|
+
import { createControlClient } from '../control-api/client';
|
|
14
17
|
import {
|
|
15
18
|
type CliStructuredError,
|
|
16
19
|
errorRuntime,
|
|
@@ -46,7 +49,13 @@ export interface MigrationShowResult {
|
|
|
46
49
|
readonly label: string;
|
|
47
50
|
readonly operationClass: string;
|
|
48
51
|
}[];
|
|
49
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Family-agnostic textual preview of the migration's operations. Replaces
|
|
54
|
+
* the previous string-array DDL field. Always defined; statements is empty
|
|
55
|
+
* for a no-op migration or a family that does not implement the
|
|
56
|
+
* `OperationPreviewCapable` capability.
|
|
57
|
+
*/
|
|
58
|
+
readonly preview: OperationPreview;
|
|
50
59
|
readonly summary: string;
|
|
51
60
|
}
|
|
52
61
|
|
|
@@ -174,7 +183,18 @@ async function executeMigrationShowCommand(
|
|
|
174
183
|
}
|
|
175
184
|
|
|
176
185
|
const ops = pkg.ops as readonly MigrationPlanOperation[];
|
|
177
|
-
|
|
186
|
+
|
|
187
|
+
// `migration show` is an offline command; the control client is constructed
|
|
188
|
+
// purely to dispatch the family-specific `toOperationPreview` capability and
|
|
189
|
+
// is not connected to a database.
|
|
190
|
+
const client = createControlClient({
|
|
191
|
+
family: config.family,
|
|
192
|
+
target: config.target,
|
|
193
|
+
adapter: config.adapter,
|
|
194
|
+
...(config.driver ? { driver: config.driver } : {}),
|
|
195
|
+
extensionPacks: config.extensionPacks ?? [],
|
|
196
|
+
});
|
|
197
|
+
const preview: OperationPreview = client.toOperationPreview(ops) ?? { statements: [] };
|
|
178
198
|
|
|
179
199
|
const result: MigrationShowResult = {
|
|
180
200
|
ok: true,
|
|
@@ -189,7 +209,7 @@ async function executeMigrationShowCommand(
|
|
|
189
209
|
label: op.label,
|
|
190
210
|
operationClass: op.operationClass,
|
|
191
211
|
})),
|
|
192
|
-
|
|
212
|
+
preview,
|
|
193
213
|
summary: `${ops.length} operation(s)`,
|
|
194
214
|
};
|
|
195
215
|
return ok(result);
|
|
@@ -200,7 +220,7 @@ export function createMigrationShowCommand(): Command {
|
|
|
200
220
|
setCommandDescriptions(
|
|
201
221
|
command,
|
|
202
222
|
'Display migration package contents',
|
|
203
|
-
'Shows the operations,
|
|
223
|
+
'Shows the operations, statement preview, and metadata for a migration package.\n' +
|
|
204
224
|
'Accepts a directory path, a hash prefix (git-style), or defaults to the\n' +
|
|
205
225
|
'latest migration.',
|
|
206
226
|
);
|