@prisma-next/cli 0.4.0-dev.5 → 0.4.0-dev.6
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 +8 -7
- package/dist/cli-errors-BUuJr6py.mjs +5 -0
- package/dist/{cli-errors-DStABy9d.d.mts → cli-errors-Dic2eADK.d.mts} +1 -0
- package/dist/cli.mjs +9 -16
- package/dist/cli.mjs.map +1 -1
- package/dist/{client-tdnbk0OR.mjs → client-DUs1DH-1.mjs} +2 -2
- package/dist/{client-tdnbk0OR.mjs.map → client-DUs1DH-1.mjs.map} +1 -1
- package/dist/commands/contract-emit.mjs +1 -6
- package/dist/commands/contract-infer.mjs +1 -7
- package/dist/commands/db-init.mjs +5 -6
- package/dist/commands/db-init.mjs.map +1 -1
- package/dist/commands/db-schema.mjs +3 -6
- package/dist/commands/db-schema.mjs.map +1 -1
- package/dist/commands/db-sign.mjs +4 -5
- package/dist/commands/db-sign.mjs.map +1 -1
- package/dist/commands/db-update.mjs +5 -6
- package/dist/commands/db-update.mjs.map +1 -1
- package/dist/commands/db-verify.mjs +4 -5
- package/dist/commands/db-verify.mjs.map +1 -1
- package/dist/commands/migration-apply.mjs +6 -7
- package/dist/commands/migration-apply.mjs.map +1 -1
- package/dist/commands/migration-emit.d.mts +38 -0
- package/dist/commands/migration-emit.d.mts.map +1 -0
- package/dist/commands/migration-emit.mjs +81 -0
- package/dist/commands/migration-emit.mjs.map +1 -0
- package/dist/commands/migration-new.mjs +5 -5
- package/dist/commands/migration-new.mjs.map +1 -1
- package/dist/commands/migration-plan.d.mts.map +1 -1
- package/dist/commands/migration-plan.mjs +13 -32
- package/dist/commands/migration-plan.mjs.map +1 -1
- package/dist/commands/migration-ref.d.mts +1 -1
- package/dist/commands/migration-ref.mjs +2 -2
- package/dist/commands/migration-show.d.mts +1 -1
- package/dist/commands/migration-show.mjs +4 -4
- package/dist/commands/migration-show.mjs.map +1 -1
- package/dist/commands/migration-status.mjs +1 -6
- package/dist/{contract-emit-Ctn6mH9H.mjs → contract-emit-7jqH6dq9.mjs} +5 -5
- package/dist/{contract-emit-Ctn6mH9H.mjs.map → contract-emit-7jqH6dq9.mjs.map} +1 -1
- package/dist/contract-emit-C2_J2U7A.mjs +4 -0
- package/dist/{contract-emit-CRoS1nx5.mjs → contract-emit-CKig_Lra.mjs} +4 -4
- package/dist/{contract-emit-CRoS1nx5.mjs.map → contract-emit-CKig_Lra.mjs.map} +1 -1
- package/dist/{contract-infer-Ba1SE57Q.mjs → contract-infer-BFlIbyl9.mjs} +3 -3
- package/dist/{contract-infer-Ba1SE57Q.mjs.map → contract-infer-BFlIbyl9.mjs.map} +1 -1
- package/dist/exports/control-api.mjs +2 -4
- package/dist/exports/index.mjs +1 -6
- package/dist/exports/index.mjs.map +1 -1
- package/dist/{framework-components-BAsliT4V.mjs → framework-components-Bsr1GaIj.mjs} +2 -2
- package/dist/{framework-components-BAsliT4V.mjs.map → framework-components-Bsr1GaIj.mjs.map} +1 -1
- package/dist/{init-CYWnL7gq.mjs → init-DlFLMBaU.mjs} +2 -2
- package/dist/{init-CYWnL7gq.mjs.map → init-DlFLMBaU.mjs.map} +1 -1
- package/dist/{inspect-live-schema-gYQiWfpl.mjs → inspect-live-schema-gjmUZ8xm.mjs} +4 -4
- package/dist/{inspect-live-schema-gYQiWfpl.mjs.map → inspect-live-schema-gjmUZ8xm.mjs.map} +1 -1
- package/dist/{migration-command-scaffold-x4n_ZhAh.mjs → migration-command-scaffold-CfllKppa.mjs} +4 -4
- package/dist/{migration-command-scaffold-x4n_ZhAh.mjs.map → migration-command-scaffold-CfllKppa.mjs.map} +1 -1
- package/dist/migration-emit-dRXV6QSz.mjs +72 -0
- package/dist/migration-emit-dRXV6QSz.mjs.map +1 -0
- package/dist/{migration-status-DyVDf5NI.mjs → migration-status-BwKCQB_a.mjs} +5 -5
- package/dist/{migration-status-DyVDf5NI.mjs.map → migration-status-BwKCQB_a.mjs.map} +1 -1
- package/dist/{migrations-DTZBYXm1.mjs → migrations-BIsjFjSV.mjs} +6 -15
- package/dist/migrations-BIsjFjSV.mjs.map +1 -0
- package/dist/{result-handler-oK_vA-Fn.mjs → result-handler-AFK4hxyX.mjs} +2 -2
- package/dist/result-handler-AFK4hxyX.mjs.map +1 -0
- package/dist/{validate-contract-deps-esa-VQ0h.mjs → validate-contract-deps-DBH6iTAD.mjs} +1 -1
- package/dist/{validate-contract-deps-esa-VQ0h.mjs.map → validate-contract-deps-DBH6iTAD.mjs.map} +1 -1
- package/dist/{verify-DlFQ2FOw.mjs → verify-C56CuQc7.mjs} +2 -2
- package/dist/{verify-DlFQ2FOw.mjs.map → verify-C56CuQc7.mjs.map} +1 -1
- package/package.json +19 -19
- package/src/cli.ts +4 -4
- package/src/commands/migration-apply.ts +2 -2
- package/src/commands/migration-emit.ts +132 -0
- package/src/commands/migration-new.ts +3 -3
- package/src/commands/migration-plan.ts +15 -57
- package/src/commands/migration-show.ts +1 -1
- package/src/commands/migration-status.ts +1 -1
- package/src/lib/migration-emit.ts +113 -0
- package/src/utils/cli-errors.ts +5 -0
- package/src/utils/formatters/help.ts +1 -1
- package/src/utils/formatters/migrations.ts +6 -20
- package/dist/cli-errors-BDCYR5ap.mjs +0 -4
- package/dist/commands/migration-verify.d.mts +0 -16
- package/dist/commands/migration-verify.d.mts.map +0 -1
- package/dist/commands/migration-verify.mjs +0 -110
- package/dist/commands/migration-verify.mjs.map +0 -1
- package/dist/contract-emit-CVUWsFfx.mjs +0 -6
- package/dist/migrations-DTZBYXm1.mjs.map +0 -1
- package/dist/result-handler-oK_vA-Fn.mjs.map +0 -1
- package/src/commands/migration-verify.ts +0 -180
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { t as loadConfig } from "../config-loader-C4VXKl8f.mjs";
|
|
2
|
+
import { g as errorTargetMigrationNotSupported, h as errorRuntime, t as CliStructuredError, v as errorUnexpected } from "../cli-errors-BUuJr6py.mjs";
|
|
3
|
+
import { t as assertFrameworkComponentsCompatible } from "../framework-components-Bsr1GaIj.mjs";
|
|
4
|
+
import { _ as formatStyledHeader, d as setCommandExamples, m as parseGlobalFlags, n as addGlobalOptions, r as getTargetMigrations, t as handleResult, u as setCommandDescriptions } from "../result-handler-AFK4hxyX.mjs";
|
|
5
|
+
import { t as TerminalUI } from "../terminal-ui-C5k88MmW.mjs";
|
|
6
|
+
import { r as formatMigrationEmitCommandOutput } from "../migrations-BIsjFjSV.mjs";
|
|
7
|
+
import { t as emitMigration } from "../migration-emit-dRXV6QSz.mjs";
|
|
8
|
+
import { Command } from "commander";
|
|
9
|
+
import { notOk, ok } from "@prisma-next/utils/result";
|
|
10
|
+
import { MigrationToolsError } from "@prisma-next/migration-tools/types";
|
|
11
|
+
|
|
12
|
+
//#region src/commands/migration-emit.ts
|
|
13
|
+
async function executeMigrationEmitCommand(options, flags, ui) {
|
|
14
|
+
const dir = options.dir;
|
|
15
|
+
if (!flags.json && !flags.quiet) {
|
|
16
|
+
const header = formatStyledHeader({
|
|
17
|
+
command: "migration emit",
|
|
18
|
+
description: "Emit ops.json from migration.ts and compute migrationId",
|
|
19
|
+
details: [{
|
|
20
|
+
label: "dir",
|
|
21
|
+
value: dir
|
|
22
|
+
}],
|
|
23
|
+
flags
|
|
24
|
+
});
|
|
25
|
+
ui.stderr(header);
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
const config = await loadConfig(options.config);
|
|
29
|
+
const migrations = getTargetMigrations(config.target);
|
|
30
|
+
if (!migrations) throw errorTargetMigrationNotSupported({ why: `Target "${config.target.id}" does not support migrations` });
|
|
31
|
+
const frameworkComponents = assertFrameworkComponentsCompatible(config.family.familyId, config.target.targetId, [
|
|
32
|
+
config.target,
|
|
33
|
+
config.adapter,
|
|
34
|
+
...config.extensionPacks ?? []
|
|
35
|
+
]);
|
|
36
|
+
const { migrationId } = await emitMigration(dir, {
|
|
37
|
+
targetId: config.target.targetId,
|
|
38
|
+
migrations,
|
|
39
|
+
frameworkComponents
|
|
40
|
+
});
|
|
41
|
+
return ok({
|
|
42
|
+
ok: true,
|
|
43
|
+
dir,
|
|
44
|
+
migrationId,
|
|
45
|
+
summary: `Emitted ops.json and attested migrationId: ${migrationId}`
|
|
46
|
+
});
|
|
47
|
+
} catch (error) {
|
|
48
|
+
if (CliStructuredError.is(error)) return notOk(error);
|
|
49
|
+
if (MigrationToolsError.is(error)) return notOk(errorRuntime(error.message, {
|
|
50
|
+
why: error.why,
|
|
51
|
+
fix: error.fix,
|
|
52
|
+
meta: {
|
|
53
|
+
code: error.code,
|
|
54
|
+
...error.details ?? {}
|
|
55
|
+
}
|
|
56
|
+
}));
|
|
57
|
+
return notOk(errorUnexpected(error instanceof Error ? error.message : String(error), { why: `Failed to emit migration: ${error instanceof Error ? error.message : String(error)}` }));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function createMigrationEmitCommand() {
|
|
61
|
+
const command = new Command("emit");
|
|
62
|
+
setCommandDescriptions(command, "Emit ops.json from migration.ts and compute migrationId", "Evaluates migration.ts in the package directory, resolves it to ops.json,\nthen computes and persists the content-addressed migrationId in manifest.json.");
|
|
63
|
+
setCommandExamples(command, ["prisma-next migration emit --dir migrations/20250101-add-users"]);
|
|
64
|
+
addGlobalOptions(command).requiredOption("--dir <path>", "Path to the migration package directory").option("--config <path>", "Path to prisma-next.config.ts").action(async (options) => {
|
|
65
|
+
const flags = parseGlobalFlags(options);
|
|
66
|
+
const ui = new TerminalUI({
|
|
67
|
+
color: flags.color,
|
|
68
|
+
interactive: flags.interactive
|
|
69
|
+
});
|
|
70
|
+
const exitCode = handleResult(await executeMigrationEmitCommand(options, flags, ui), flags, ui, (emitResult) => {
|
|
71
|
+
if (flags.json) ui.output(JSON.stringify(emitResult, null, 2));
|
|
72
|
+
else if (!flags.quiet) ui.log(formatMigrationEmitCommandOutput(emitResult, flags));
|
|
73
|
+
});
|
|
74
|
+
process.exit(exitCode);
|
|
75
|
+
});
|
|
76
|
+
return command;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
//#endregion
|
|
80
|
+
export { createMigrationEmitCommand };
|
|
81
|
+
//# sourceMappingURL=migration-emit.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-emit.mjs","names":[],"sources":["../../src/commands/migration-emit.ts"],"sourcesContent":["import { MigrationToolsError } from '@prisma-next/migration-tools/types';\nimport { notOk, ok, type Result } from '@prisma-next/utils/result';\nimport { Command } from 'commander';\nimport { loadConfig } from '../config-loader';\nimport { emitMigration } from '../lib/migration-emit';\nimport {\n CliStructuredError,\n errorRuntime,\n errorTargetMigrationNotSupported,\n errorUnexpected,\n} from '../utils/cli-errors';\nimport {\n addGlobalOptions,\n getTargetMigrations,\n setCommandDescriptions,\n setCommandExamples,\n} from '../utils/command-helpers';\nimport { formatMigrationEmitCommandOutput } from '../utils/formatters/migrations';\nimport { formatStyledHeader } from '../utils/formatters/styled';\nimport { assertFrameworkComponentsCompatible } from '../utils/framework-components';\nimport type { CommonCommandOptions } from '../utils/global-flags';\nimport { type GlobalFlags, parseGlobalFlags } from '../utils/global-flags';\nimport { handleResult } from '../utils/result-handler';\nimport { TerminalUI } from '../utils/terminal-ui';\n\nexport interface MigrationEmitOptions extends CommonCommandOptions {\n readonly dir: string;\n readonly config?: string;\n}\n\nexport interface MigrationEmitResult {\n readonly ok: boolean;\n readonly dir: string;\n readonly migrationId: string;\n readonly summary: string;\n}\n\nasync function executeMigrationEmitCommand(\n options: MigrationEmitOptions,\n flags: GlobalFlags,\n ui: TerminalUI,\n): Promise<Result<MigrationEmitResult, CliStructuredError>> {\n const dir = options.dir;\n\n if (!flags.json && !flags.quiet) {\n const header = formatStyledHeader({\n command: 'migration emit',\n description: 'Emit ops.json from migration.ts and compute migrationId',\n details: [{ label: 'dir', value: dir }],\n flags,\n });\n ui.stderr(header);\n }\n\n try {\n const config = await loadConfig(options.config);\n const migrations = getTargetMigrations(config.target);\n if (!migrations) {\n throw errorTargetMigrationNotSupported({\n why: `Target \"${config.target.id}\" does not support migrations`,\n });\n }\n const frameworkComponents = assertFrameworkComponentsCompatible(\n config.family.familyId,\n config.target.targetId,\n [config.target, config.adapter, ...(config.extensionPacks ?? [])],\n );\n\n const { migrationId } = await emitMigration(dir, {\n targetId: config.target.targetId,\n migrations,\n frameworkComponents,\n });\n\n return ok({\n ok: true,\n dir,\n migrationId,\n summary: `Emitted ops.json and attested migrationId: ${migrationId}`,\n });\n } catch (error) {\n if (CliStructuredError.is(error)) {\n return notOk(error);\n }\n if (MigrationToolsError.is(error)) {\n return notOk(\n errorRuntime(error.message, {\n why: error.why,\n fix: error.fix,\n meta: { code: error.code, ...(error.details ?? {}) },\n }),\n );\n }\n return notOk(\n errorUnexpected(error instanceof Error ? error.message : String(error), {\n why: `Failed to emit migration: ${error instanceof Error ? error.message : String(error)}`,\n }),\n );\n }\n}\n\nexport function createMigrationEmitCommand(): Command {\n const command = new Command('emit');\n setCommandDescriptions(\n command,\n 'Emit ops.json from migration.ts and compute migrationId',\n 'Evaluates migration.ts in the package directory, resolves it to ops.json,\\n' +\n 'then computes and persists the content-addressed migrationId in manifest.json.',\n );\n setCommandExamples(command, ['prisma-next migration emit --dir migrations/20250101-add-users']);\n addGlobalOptions(command)\n .requiredOption('--dir <path>', 'Path to the migration package directory')\n .option('--config <path>', 'Path to prisma-next.config.ts')\n .action(async (options: MigrationEmitOptions) => {\n const flags = parseGlobalFlags(options);\n const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });\n\n const result = await executeMigrationEmitCommand(options, flags, ui);\n\n const exitCode = handleResult(result, flags, ui, (emitResult) => {\n if (flags.json) {\n ui.output(JSON.stringify(emitResult, null, 2));\n } else if (!flags.quiet) {\n ui.log(formatMigrationEmitCommandOutput(emitResult, flags));\n }\n });\n\n process.exit(exitCode);\n });\n\n return command;\n}\n"],"mappings":";;;;;;;;;;;;AAqCA,eAAe,4BACb,SACA,OACA,IAC0D;CAC1D,MAAM,MAAM,QAAQ;AAEpB,KAAI,CAAC,MAAM,QAAQ,CAAC,MAAM,OAAO;EAC/B,MAAM,SAAS,mBAAmB;GAChC,SAAS;GACT,aAAa;GACb,SAAS,CAAC;IAAE,OAAO;IAAO,OAAO;IAAK,CAAC;GACvC;GACD,CAAC;AACF,KAAG,OAAO,OAAO;;AAGnB,KAAI;EACF,MAAM,SAAS,MAAM,WAAW,QAAQ,OAAO;EAC/C,MAAM,aAAa,oBAAoB,OAAO,OAAO;AACrD,MAAI,CAAC,WACH,OAAM,iCAAiC,EACrC,KAAK,WAAW,OAAO,OAAO,GAAG,gCAClC,CAAC;EAEJ,MAAM,sBAAsB,oCAC1B,OAAO,OAAO,UACd,OAAO,OAAO,UACd;GAAC,OAAO;GAAQ,OAAO;GAAS,GAAI,OAAO,kBAAkB,EAAE;GAAE,CAClE;EAED,MAAM,EAAE,gBAAgB,MAAM,cAAc,KAAK;GAC/C,UAAU,OAAO,OAAO;GACxB;GACA;GACD,CAAC;AAEF,SAAO,GAAG;GACR,IAAI;GACJ;GACA;GACA,SAAS,8CAA8C;GACxD,CAAC;UACK,OAAO;AACd,MAAI,mBAAmB,GAAG,MAAM,CAC9B,QAAO,MAAM,MAAM;AAErB,MAAI,oBAAoB,GAAG,MAAM,CAC/B,QAAO,MACL,aAAa,MAAM,SAAS;GAC1B,KAAK,MAAM;GACX,KAAK,MAAM;GACX,MAAM;IAAE,MAAM,MAAM;IAAM,GAAI,MAAM,WAAW,EAAE;IAAG;GACrD,CAAC,CACH;AAEH,SAAO,MACL,gBAAgB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,EAAE,EACtE,KAAK,6BAA6B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,IACzF,CAAC,CACH;;;AAIL,SAAgB,6BAAsC;CACpD,MAAM,UAAU,IAAI,QAAQ,OAAO;AACnC,wBACE,SACA,2DACA,4JAED;AACD,oBAAmB,SAAS,CAAC,iEAAiE,CAAC;AAC/F,kBAAiB,QAAQ,CACtB,eAAe,gBAAgB,0CAA0C,CACzE,OAAO,mBAAmB,gCAAgC,CAC1D,OAAO,OAAO,YAAkC;EAC/C,MAAM,QAAQ,iBAAiB,QAAQ;EACvC,MAAM,KAAK,IAAI,WAAW;GAAE,OAAO,MAAM;GAAO,aAAa,MAAM;GAAa,CAAC;EAIjF,MAAM,WAAW,aAFF,MAAM,4BAA4B,SAAS,OAAO,GAAG,EAE9B,OAAO,KAAK,eAAe;AAC/D,OAAI,MAAM,KACR,IAAG,OAAO,KAAK,UAAU,YAAY,MAAM,EAAE,CAAC;YACrC,CAAC,MAAM,MAChB,IAAG,IAAI,iCAAiC,YAAY,MAAM,CAAC;IAE7D;AAEF,UAAQ,KAAK,SAAS;GACtB;AAEJ,QAAO"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as loadConfig } from "../config-loader-C4VXKl8f.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { _ as formatStyledHeader, c as resolveMigrationPaths, d as setCommandExamples, m as parseGlobalFlags, n as addGlobalOptions, t as handleResult, u as setCommandDescriptions } from "../result-handler-
|
|
2
|
+
import { h as errorRuntime, v as errorUnexpected } from "../cli-errors-BUuJr6py.mjs";
|
|
3
|
+
import { _ as formatStyledHeader, c as resolveMigrationPaths, d as setCommandExamples, m as parseGlobalFlags, n as addGlobalOptions, t as handleResult, u as setCommandDescriptions } from "../result-handler-AFK4hxyX.mjs";
|
|
4
4
|
import { t as TerminalUI } from "../terminal-ui-C5k88MmW.mjs";
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
import { readFileSync } from "node:fs";
|
|
@@ -16,7 +16,7 @@ import { scaffoldMigrationTs } from "@prisma-next/migration-tools/migration-ts";
|
|
|
16
16
|
/**
|
|
17
17
|
* `migration new` — scaffolds a migration package with a migration.ts file
|
|
18
18
|
* for manual authoring. The user writes operation descriptors and data
|
|
19
|
-
* transforms; `migration
|
|
19
|
+
* transforms; `migration emit` resolves them to ops.json.
|
|
20
20
|
*/
|
|
21
21
|
async function executeMigrationNewCommand(options) {
|
|
22
22
|
const config = await loadConfig(options.config);
|
|
@@ -116,7 +116,7 @@ async function executeMigrationNewCommand(options) {
|
|
|
116
116
|
}
|
|
117
117
|
function createMigrationNewCommand() {
|
|
118
118
|
const command = new Command("new");
|
|
119
|
-
setCommandDescriptions(command, "Scaffold a new migration for manual authoring", "Creates a migration package with a migration.ts file for manual authoring.\nWrite operation descriptors and data transforms in migration.ts, then run\n`migration
|
|
119
|
+
setCommandDescriptions(command, "Scaffold a new migration for manual authoring", "Creates a migration package with a migration.ts file for manual authoring.\nWrite operation descriptors and data transforms in migration.ts, then run\n`migration emit` to resolve and attest the package.");
|
|
120
120
|
setCommandExamples(command, ["prisma-next migration new --name split-name", "prisma-next migration new --name custom-fk --from sha256:abc..."]);
|
|
121
121
|
addGlobalOptions(command).option("--name <slug>", "Migration name (used in directory name)").option("--from <hash>", "Starting contract hash (default: latest migration target)").option("--config <path>", "Path to prisma-next.config.ts").action(async (options) => {
|
|
122
122
|
const flags = parseGlobalFlags(options);
|
|
@@ -139,7 +139,7 @@ function createMigrationNewCommand() {
|
|
|
139
139
|
ui.output(`\nScaffolded migration at ${value.dir}`);
|
|
140
140
|
ui.output(` from: ${value.from}`);
|
|
141
141
|
ui.output(` to: ${value.to}`);
|
|
142
|
-
ui.output(`\nEdit migration.ts, then run \`prisma-next migration
|
|
142
|
+
ui.output(`\nEdit migration.ts, then run \`prisma-next migration emit --dir "${value.dir}"\` to attest.`);
|
|
143
143
|
}
|
|
144
144
|
});
|
|
145
145
|
process.exit(exitCode);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migration-new.mjs","names":["contractJsonContent: string","toContractJson: Contract","fromContract: Contract | null","fromHash: string","manifest: MigrationManifest"],"sources":["../../src/commands/migration-new.ts"],"sourcesContent":["/**\n * `migration new` — scaffolds a migration package with a migration.ts file\n * for manual authoring. The user writes operation descriptors and data\n * transforms; `migration verify` resolves them to ops.json.\n */\n\nimport { readFileSync } from 'node:fs';\nimport type { Contract } from '@prisma-next/contract/types';\nimport { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';\nimport { findLatestMigration, reconstructGraph } from '@prisma-next/migration-tools/dag';\nimport {\n formatMigrationDirName,\n readMigrationsDir,\n writeMigrationPackage,\n} from '@prisma-next/migration-tools/io';\nimport { scaffoldMigrationTs } from '@prisma-next/migration-tools/migration-ts';\nimport type { MigrationManifest } from '@prisma-next/migration-tools/types';\nimport { isAttested, MigrationToolsError } from '@prisma-next/migration-tools/types';\nimport { notOk, ok, type Result } from '@prisma-next/utils/result';\nimport { Command } from 'commander';\nimport { join, relative, resolve } from 'pathe';\nimport { loadConfig } from '../config-loader';\nimport { type CliStructuredError, errorRuntime, errorUnexpected } from '../utils/cli-errors';\nimport {\n addGlobalOptions,\n resolveMigrationPaths,\n setCommandDescriptions,\n setCommandExamples,\n} from '../utils/command-helpers';\nimport { formatStyledHeader } from '../utils/formatters/styled';\nimport type { CommonCommandOptions } from '../utils/global-flags';\nimport { parseGlobalFlags } from '../utils/global-flags';\nimport { handleResult } from '../utils/result-handler';\nimport { TerminalUI } from '../utils/terminal-ui';\n\ninterface MigrationNewOptions extends CommonCommandOptions {\n readonly name?: string;\n readonly from?: string;\n readonly config?: string;\n}\n\ninterface MigrationNewResult {\n readonly ok: true;\n readonly dir: string;\n readonly from: string;\n readonly to: string;\n readonly summary: string;\n}\n\nasync function executeMigrationNewCommand(\n options: MigrationNewOptions,\n): Promise<Result<MigrationNewResult, CliStructuredError>> {\n const config = await loadConfig(options.config);\n const { migrationsDir, migrationsRelative } = resolveMigrationPaths(options.config, config);\n\n // Read the emitted contract (destination)\n const contractPath = config.contract?.output ?? 'contract.json';\n const contractPathAbsolute = resolve(\n options.config ? resolve(options.config, '..') : process.cwd(),\n contractPath,\n );\n\n let contractJsonContent: string;\n try {\n contractJsonContent = readFileSync(contractPathAbsolute, 'utf-8');\n } catch (error) {\n if (error instanceof Error && (error as { code?: string }).code === 'ENOENT') {\n return notOk(\n errorRuntime(`Contract file not found at ${contractPathAbsolute}`, {\n why: `Contract file not found at ${contractPathAbsolute}`,\n fix: 'Run `prisma-next contract emit` first to generate the contract',\n }),\n );\n }\n throw error;\n }\n\n let toContractJson: Contract;\n try {\n toContractJson = JSON.parse(contractJsonContent) as Contract;\n } catch (error) {\n return notOk(\n errorRuntime('Contract JSON is invalid', {\n why: `Failed to parse ${contractPathAbsolute}: ${error instanceof Error ? error.message : String(error)}`,\n fix: 'Run `prisma-next contract emit` to regenerate the contract',\n }),\n );\n }\n\n const toStorageHash = (\n (toContractJson as unknown as Record<string, unknown>)['storage'] as\n | Record<string, unknown>\n | undefined\n )?.['storageHash'] as string | undefined;\n if (!toStorageHash) {\n return notOk(\n errorRuntime('Contract is missing storageHash', {\n why: `Contract at ${contractPathAbsolute} has no storageHash`,\n fix: 'Run `prisma-next contract emit` to regenerate the contract',\n }),\n );\n }\n\n // Determine \"from\" hash\n let fromContract: Contract | null = null;\n let fromHash: string = EMPTY_CONTRACT_HASH;\n\n try {\n const packages = await readMigrationsDir(migrationsDir);\n const attested = packages.filter(isAttested);\n\n if (attested.length > 0) {\n const graph = reconstructGraph(attested);\n\n if (options.from) {\n // Explicit --from: find the migration with matching to hash\n const match = attested.find((p) => p.manifest.to.startsWith(options.from!));\n if (!match) {\n return notOk(\n errorRuntime('Starting contract not found', {\n why: `No migration with to hash matching \"${options.from}\" exists in ${migrationsRelative}`,\n fix: 'Check that the --from hash matches a known migration target hash.',\n }),\n );\n }\n fromHash = match.manifest.to;\n fromContract = match.manifest.toContract;\n } else {\n const latestMigration = findLatestMigration(graph);\n if (latestMigration) {\n fromHash = latestMigration.to;\n const leafPkg = attested.find(\n (p) => p.manifest.migrationId === latestMigration.migrationId,\n );\n if (leafPkg) {\n fromContract = leafPkg.manifest.toContract;\n }\n }\n }\n }\n } catch (error) {\n if (MigrationToolsError.is(error)) {\n return notOk(\n errorRuntime(error.message, {\n why: error.why,\n fix: error.fix,\n meta: { code: error.code },\n }),\n );\n }\n throw error;\n }\n\n // Check for no-op\n if (fromHash === toStorageHash) {\n return notOk(\n errorRuntime('No changes detected', {\n why: 'The from and to contract hashes are identical — there is nothing to migrate.',\n fix: 'Change the contract and run `prisma-next contract emit` before creating a new migration.',\n }),\n );\n }\n\n // Build manifest and write package\n const timestamp = new Date();\n const slug = options.name ?? 'migration';\n const dirName = formatMigrationDirName(timestamp, slug);\n const packageDir = join(migrationsDir, dirName);\n\n const manifest: MigrationManifest = {\n from: fromHash,\n to: toStorageHash,\n migrationId: null,\n kind: 'regular',\n fromContract,\n toContract: toContractJson,\n hints: {\n used: [],\n applied: [],\n plannerVersion: '1.0.0',\n planningStrategy: 'manual',\n },\n labels: [],\n createdAt: timestamp.toISOString(),\n };\n\n try {\n // Write package with empty ops (draft)\n await writeMigrationPackage(packageDir, manifest, []);\n\n // Scaffold migration.ts\n await scaffoldMigrationTs(packageDir);\n\n return ok({\n ok: true as const,\n dir: relative(process.cwd(), packageDir),\n from: fromHash,\n to: toStorageHash,\n summary: `Scaffolded migration at ${relative(process.cwd(), packageDir)}`,\n });\n } catch (error) {\n return notOk(\n errorUnexpected(error instanceof Error ? error.message : String(error), {\n why: `Failed to scaffold migration: ${error instanceof Error ? error.message : String(error)}`,\n }),\n );\n }\n}\n\nexport function createMigrationNewCommand(): Command {\n const command = new Command('new');\n setCommandDescriptions(\n command,\n 'Scaffold a new migration for manual authoring',\n 'Creates a migration package with a migration.ts file for manual authoring.\\n' +\n 'Write operation descriptors and data transforms in migration.ts, then run\\n' +\n '`migration verify` to resolve and attest the package.',\n );\n setCommandExamples(command, [\n 'prisma-next migration new --name split-name',\n 'prisma-next migration new --name custom-fk --from sha256:abc...',\n ]);\n addGlobalOptions(command)\n .option('--name <slug>', 'Migration name (used in directory name)')\n .option('--from <hash>', 'Starting contract hash (default: latest migration target)')\n .option('--config <path>', 'Path to prisma-next.config.ts')\n .action(async (options: MigrationNewOptions) => {\n const flags = parseGlobalFlags(options);\n const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });\n\n if (!flags.json && !flags.quiet) {\n const header = formatStyledHeader({\n command: 'migration new',\n description: 'Scaffold a new migration',\n details: [],\n flags,\n });\n ui.stderr(header);\n }\n\n const result = await executeMigrationNewCommand(options);\n\n const exitCode = handleResult(result, flags, ui, (value) => {\n if (flags.json) {\n ui.output(JSON.stringify(value, null, 2));\n } else if (!flags.quiet) {\n ui.output(`\\nScaffolded migration at ${value.dir}`);\n ui.output(` from: ${value.from}`);\n ui.output(` to: ${value.to}`);\n ui.output(\n `\\nEdit migration.ts, then run \\`prisma-next migration verify --dir ${value.dir}\\` to attest.`,\n );\n }\n });\n\n process.exit(exitCode);\n });\n\n return command;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"migration-new.mjs","names":["contractJsonContent: string","toContractJson: Contract","fromContract: Contract | null","fromHash: string","manifest: MigrationManifest"],"sources":["../../src/commands/migration-new.ts"],"sourcesContent":["/**\n * `migration new` — scaffolds a migration package with a migration.ts file\n * for manual authoring. The user writes operation descriptors and data\n * transforms; `migration emit` resolves them to ops.json.\n */\n\nimport { readFileSync } from 'node:fs';\nimport type { Contract } from '@prisma-next/contract/types';\nimport { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';\nimport { findLatestMigration, reconstructGraph } from '@prisma-next/migration-tools/dag';\nimport {\n formatMigrationDirName,\n readMigrationsDir,\n writeMigrationPackage,\n} from '@prisma-next/migration-tools/io';\nimport { scaffoldMigrationTs } from '@prisma-next/migration-tools/migration-ts';\nimport type { MigrationManifest } from '@prisma-next/migration-tools/types';\nimport { isAttested, MigrationToolsError } from '@prisma-next/migration-tools/types';\nimport { notOk, ok, type Result } from '@prisma-next/utils/result';\nimport { Command } from 'commander';\nimport { join, relative, resolve } from 'pathe';\nimport { loadConfig } from '../config-loader';\nimport { type CliStructuredError, errorRuntime, errorUnexpected } from '../utils/cli-errors';\nimport {\n addGlobalOptions,\n resolveMigrationPaths,\n setCommandDescriptions,\n setCommandExamples,\n} from '../utils/command-helpers';\nimport { formatStyledHeader } from '../utils/formatters/styled';\nimport type { CommonCommandOptions } from '../utils/global-flags';\nimport { parseGlobalFlags } from '../utils/global-flags';\nimport { handleResult } from '../utils/result-handler';\nimport { TerminalUI } from '../utils/terminal-ui';\n\ninterface MigrationNewOptions extends CommonCommandOptions {\n readonly name?: string;\n readonly from?: string;\n readonly config?: string;\n}\n\ninterface MigrationNewResult {\n readonly ok: true;\n readonly dir: string;\n readonly from: string;\n readonly to: string;\n readonly summary: string;\n}\n\nasync function executeMigrationNewCommand(\n options: MigrationNewOptions,\n): Promise<Result<MigrationNewResult, CliStructuredError>> {\n const config = await loadConfig(options.config);\n const { migrationsDir, migrationsRelative } = resolveMigrationPaths(options.config, config);\n\n // Read the emitted contract (destination)\n const contractPath = config.contract?.output ?? 'contract.json';\n const contractPathAbsolute = resolve(\n options.config ? resolve(options.config, '..') : process.cwd(),\n contractPath,\n );\n\n let contractJsonContent: string;\n try {\n contractJsonContent = readFileSync(contractPathAbsolute, 'utf-8');\n } catch (error) {\n if (error instanceof Error && (error as { code?: string }).code === 'ENOENT') {\n return notOk(\n errorRuntime(`Contract file not found at ${contractPathAbsolute}`, {\n why: `Contract file not found at ${contractPathAbsolute}`,\n fix: 'Run `prisma-next contract emit` first to generate the contract',\n }),\n );\n }\n throw error;\n }\n\n let toContractJson: Contract;\n try {\n toContractJson = JSON.parse(contractJsonContent) as Contract;\n } catch (error) {\n return notOk(\n errorRuntime('Contract JSON is invalid', {\n why: `Failed to parse ${contractPathAbsolute}: ${error instanceof Error ? error.message : String(error)}`,\n fix: 'Run `prisma-next contract emit` to regenerate the contract',\n }),\n );\n }\n\n const toStorageHash = (\n (toContractJson as unknown as Record<string, unknown>)['storage'] as\n | Record<string, unknown>\n | undefined\n )?.['storageHash'] as string | undefined;\n if (!toStorageHash) {\n return notOk(\n errorRuntime('Contract is missing storageHash', {\n why: `Contract at ${contractPathAbsolute} has no storageHash`,\n fix: 'Run `prisma-next contract emit` to regenerate the contract',\n }),\n );\n }\n\n // Determine \"from\" hash\n let fromContract: Contract | null = null;\n let fromHash: string = EMPTY_CONTRACT_HASH;\n\n try {\n const packages = await readMigrationsDir(migrationsDir);\n const attested = packages.filter(isAttested);\n\n if (attested.length > 0) {\n const graph = reconstructGraph(attested);\n\n if (options.from) {\n // Explicit --from: find the migration with matching to hash\n const match = attested.find((p) => p.manifest.to.startsWith(options.from!));\n if (!match) {\n return notOk(\n errorRuntime('Starting contract not found', {\n why: `No migration with to hash matching \"${options.from}\" exists in ${migrationsRelative}`,\n fix: 'Check that the --from hash matches a known migration target hash.',\n }),\n );\n }\n fromHash = match.manifest.to;\n fromContract = match.manifest.toContract;\n } else {\n const latestMigration = findLatestMigration(graph);\n if (latestMigration) {\n fromHash = latestMigration.to;\n const leafPkg = attested.find(\n (p) => p.manifest.migrationId === latestMigration.migrationId,\n );\n if (leafPkg) {\n fromContract = leafPkg.manifest.toContract;\n }\n }\n }\n }\n } catch (error) {\n if (MigrationToolsError.is(error)) {\n return notOk(\n errorRuntime(error.message, {\n why: error.why,\n fix: error.fix,\n meta: { code: error.code },\n }),\n );\n }\n throw error;\n }\n\n // Check for no-op\n if (fromHash === toStorageHash) {\n return notOk(\n errorRuntime('No changes detected', {\n why: 'The from and to contract hashes are identical — there is nothing to migrate.',\n fix: 'Change the contract and run `prisma-next contract emit` before creating a new migration.',\n }),\n );\n }\n\n // Build manifest and write package\n const timestamp = new Date();\n const slug = options.name ?? 'migration';\n const dirName = formatMigrationDirName(timestamp, slug);\n const packageDir = join(migrationsDir, dirName);\n\n const manifest: MigrationManifest = {\n from: fromHash,\n to: toStorageHash,\n migrationId: null,\n kind: 'regular',\n fromContract,\n toContract: toContractJson,\n hints: {\n used: [],\n applied: [],\n plannerVersion: '1.0.0',\n planningStrategy: 'manual',\n },\n labels: [],\n createdAt: timestamp.toISOString(),\n };\n\n try {\n // Write package with empty ops (draft)\n await writeMigrationPackage(packageDir, manifest, []);\n\n // Scaffold migration.ts\n await scaffoldMigrationTs(packageDir);\n\n return ok({\n ok: true as const,\n dir: relative(process.cwd(), packageDir),\n from: fromHash,\n to: toStorageHash,\n summary: `Scaffolded migration at ${relative(process.cwd(), packageDir)}`,\n });\n } catch (error) {\n return notOk(\n errorUnexpected(error instanceof Error ? error.message : String(error), {\n why: `Failed to scaffold migration: ${error instanceof Error ? error.message : String(error)}`,\n }),\n );\n }\n}\n\nexport function createMigrationNewCommand(): Command {\n const command = new Command('new');\n setCommandDescriptions(\n command,\n 'Scaffold a new migration for manual authoring',\n 'Creates a migration package with a migration.ts file for manual authoring.\\n' +\n 'Write operation descriptors and data transforms in migration.ts, then run\\n' +\n '`migration emit` to resolve and attest the package.',\n );\n setCommandExamples(command, [\n 'prisma-next migration new --name split-name',\n 'prisma-next migration new --name custom-fk --from sha256:abc...',\n ]);\n addGlobalOptions(command)\n .option('--name <slug>', 'Migration name (used in directory name)')\n .option('--from <hash>', 'Starting contract hash (default: latest migration target)')\n .option('--config <path>', 'Path to prisma-next.config.ts')\n .action(async (options: MigrationNewOptions) => {\n const flags = parseGlobalFlags(options);\n const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });\n\n if (!flags.json && !flags.quiet) {\n const header = formatStyledHeader({\n command: 'migration new',\n description: 'Scaffold a new migration',\n details: [],\n flags,\n });\n ui.stderr(header);\n }\n\n const result = await executeMigrationNewCommand(options);\n\n const exitCode = handleResult(result, flags, ui, (value) => {\n if (flags.json) {\n ui.output(JSON.stringify(value, null, 2));\n } else if (!flags.quiet) {\n ui.output(`\\nScaffolded migration at ${value.dir}`);\n ui.output(` from: ${value.from}`);\n ui.output(` to: ${value.to}`);\n ui.output(\n `\\nEdit migration.ts, then run \\`prisma-next migration emit --dir \"${value.dir}\"\\` to attest.`,\n );\n }\n });\n\n process.exit(exitCode);\n });\n\n return command;\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migration-plan.d.mts","names":[],"sources":["../../src/commands/migration-plan.ts"],"sourcesContent":[],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"migration-plan.d.mts","names":[],"sources":["../../src/commands/migration-plan.ts"],"sourcesContent":[],"mappings":";;;;UA6CiB,mBAAA;;EAAA,SAAA,IAAA,EAAA,OAAmB;EAwSpB,SAAA,IAAA,EAAA,MAAA;EA4GJ,SAAA,EAAA,EAAA,MAAA;EAcI,SAAA,WAAA,CAAA,EAAA,MAAqB;EACjB,SAAA,GAAA,CAAA,EAAA,MAAA;EAEV,SAAA,UAAA,EAAA,SAAA;IAAG,SAAA,EAAA,EAAA,MAAA;IAAV,SAAA,KAAA,EAAA,MAAA;IAAM,SAAA,cAAA,EAAA,MAAA;;;;;;;;iBA7HO,0BAAA,CAAA,GAA8B;KA4GlC,uBAAA;;;;;;;;;;;;;;;;iBAcI;;;;qBACI,sBAEjB,OAAO,GAAG"}
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { t as loadConfig } from "../config-loader-C4VXKl8f.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { t as assertFrameworkComponentsCompatible } from "../framework-components-
|
|
2
|
+
import { a as errorContractValidationFailed, g as errorTargetMigrationNotSupported, h as errorRuntime, l as errorFileNotFound, p as errorMigrationPlanningFailed, t as CliStructuredError, v as errorUnexpected } from "../cli-errors-BUuJr6py.mjs";
|
|
3
|
+
import { t as assertFrameworkComponentsCompatible } from "../framework-components-Bsr1GaIj.mjs";
|
|
4
4
|
import { t as extractSqlDdl } from "../extract-sql-ddl-DDMX-9mz.mjs";
|
|
5
|
-
import { _ as formatStyledHeader, c as resolveMigrationPaths, d as setCommandExamples, i as loadAllBundles, m as parseGlobalFlags, n as addGlobalOptions, r as getTargetMigrations, s as resolveContractPath, t as handleResult, u as setCommandDescriptions } from "../result-handler-
|
|
5
|
+
import { _ as formatStyledHeader, c as resolveMigrationPaths, d as setCommandExamples, i as loadAllBundles, m as parseGlobalFlags, n as addGlobalOptions, r as getTargetMigrations, s as resolveContractPath, t as handleResult, u as setCommandDescriptions } from "../result-handler-AFK4hxyX.mjs";
|
|
6
6
|
import { t as TerminalUI } from "../terminal-ui-C5k88MmW.mjs";
|
|
7
|
+
import { t as emitMigration } from "../migration-emit-dRXV6QSz.mjs";
|
|
7
8
|
import { Command } from "commander";
|
|
8
9
|
import { notOk, ok } from "@prisma-next/utils/result";
|
|
9
10
|
import { join, relative } from "pathe";
|
|
10
11
|
import { EMPTY_CONTRACT_HASH } from "@prisma-next/migration-tools/constants";
|
|
11
12
|
import { readFile } from "node:fs/promises";
|
|
12
13
|
import { findLatestMigration } from "@prisma-next/migration-tools/dag";
|
|
13
|
-
import { formatMigrationDirName,
|
|
14
|
+
import { formatMigrationDirName, writeMigrationPackage } from "@prisma-next/migration-tools/io";
|
|
14
15
|
import { MigrationToolsError } from "@prisma-next/migration-tools/types";
|
|
15
|
-
import {
|
|
16
|
-
import { attestMigration } from "@prisma-next/migration-tools/attestation";
|
|
16
|
+
import { scaffoldMigrationTs } from "@prisma-next/migration-tools/migration-ts";
|
|
17
17
|
|
|
18
18
|
//#region src/commands/migration-plan.ts
|
|
19
19
|
function mapMigrationToolsError(error) {
|
|
@@ -91,7 +91,7 @@ async function executeMigrationPlanCommand(options, flags, ui, startTime) {
|
|
|
91
91
|
const existingDraft = drafts.find((d) => d.manifest.to === toStorageHash);
|
|
92
92
|
if (existingDraft) return notOk(errorRuntime("A draft migration to this contract already exists", {
|
|
93
93
|
why: `Draft migration at "${existingDraft.dirName}" already targets ${toStorageHash}`,
|
|
94
|
-
fix: `Run 'prisma-next migration
|
|
94
|
+
fix: `Run 'prisma-next migration emit --dir ${migrationsRelative}/${existingDraft.dirName}' to attest it, or delete it and re-plan.`
|
|
95
95
|
}));
|
|
96
96
|
if (options.from) {
|
|
97
97
|
const resolved = resolveBundleByPrefix(bundles, options.from);
|
|
@@ -170,31 +170,12 @@ async function executeMigrationPlanCommand(options, flags, ui, startTime) {
|
|
|
170
170
|
descriptors: descriptorResult.descriptors,
|
|
171
171
|
contractJsonPath: contractPathAbsolute
|
|
172
172
|
});
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
from: fromHash,
|
|
177
|
-
to: toStorageHash,
|
|
178
|
-
dir: relative(process.cwd(), packageDir),
|
|
179
|
-
operations: descriptorResult.descriptors.map((d) => ({
|
|
180
|
-
id: d.kind,
|
|
181
|
-
label: d.kind,
|
|
182
|
-
operationClass: "data"
|
|
183
|
-
})),
|
|
184
|
-
sql: [],
|
|
185
|
-
summary: `Planned ${descriptorResult.descriptors.length} operation(s) — data migration required. Edit migration.ts and run \`migration verify --dir ${relative(process.cwd(), packageDir)}\` to attest.`,
|
|
186
|
-
timings: { total: Date.now() - startTime }
|
|
187
|
-
});
|
|
188
|
-
const evaluatedDescriptors = await evaluateMigrationTs(packageDir);
|
|
189
|
-
if (!migrations.resolveDescriptors) throw errorTargetMigrationNotSupported({ why: `Target "${config.target.targetId}" does not implement resolveDescriptors; cannot finalize migration plan with migration.ts` });
|
|
190
|
-
const resolvedOps = migrations.resolveDescriptors(evaluatedDescriptors, {
|
|
191
|
-
fromContract,
|
|
192
|
-
toContract: toContractJson,
|
|
173
|
+
const { operations, migrationId } = await emitMigration(packageDir, {
|
|
174
|
+
targetId: config.target.targetId,
|
|
175
|
+
migrations,
|
|
193
176
|
frameworkComponents
|
|
194
177
|
});
|
|
195
|
-
|
|
196
|
-
const migrationId = await attestMigration(packageDir);
|
|
197
|
-
const sql = extractSqlDdl(resolvedOps);
|
|
178
|
+
const sql = extractSqlDdl(operations);
|
|
198
179
|
return ok({
|
|
199
180
|
ok: true,
|
|
200
181
|
noOp: false,
|
|
@@ -202,13 +183,13 @@ async function executeMigrationPlanCommand(options, flags, ui, startTime) {
|
|
|
202
183
|
to: toStorageHash,
|
|
203
184
|
migrationId,
|
|
204
185
|
dir: relative(process.cwd(), packageDir),
|
|
205
|
-
operations:
|
|
186
|
+
operations: operations.map((op) => ({
|
|
206
187
|
id: op.id,
|
|
207
188
|
label: op.label,
|
|
208
189
|
operationClass: op.operationClass
|
|
209
190
|
})),
|
|
210
191
|
sql,
|
|
211
|
-
summary: `Planned ${
|
|
192
|
+
summary: `Planned ${operations.length} operation(s)`,
|
|
212
193
|
timings: { total: Date.now() - startTime }
|
|
213
194
|
});
|
|
214
195
|
} catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migration-plan.mjs","names":["details: Array<{ label: string; value: string }>","contractJsonContent: string","toContractJson: Contract","fromContract: Contract | null","fromHash: string","manifest: MigrationManifest","lines: string[]"],"sources":["../../src/commands/migration-plan.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport type { Contract } from '@prisma-next/contract/types';\nimport type { OperationDescriptor } from '@prisma-next/framework-components/control';\nimport { attestMigration } from '@prisma-next/migration-tools/attestation';\nimport { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';\nimport { findLatestMigration } from '@prisma-next/migration-tools/dag';\nimport {\n formatMigrationDirName,\n writeMigrationOps,\n writeMigrationPackage,\n} from '@prisma-next/migration-tools/io';\nimport {\n evaluateMigrationTs,\n scaffoldMigrationTs,\n} from '@prisma-next/migration-tools/migration-ts';\nimport { type MigrationManifest, MigrationToolsError } from '@prisma-next/migration-tools/types';\nimport { notOk, ok, type Result } from '@prisma-next/utils/result';\nimport { Command } from 'commander';\nimport { join, relative } from 'pathe';\nimport { loadConfig } from '../config-loader';\nimport { extractSqlDdl } from '../control-api/operations/extract-sql-ddl';\nimport {\n type CliErrorConflict,\n CliStructuredError,\n errorContractValidationFailed,\n errorFileNotFound,\n errorMigrationPlanningFailed,\n errorRuntime,\n errorTargetMigrationNotSupported,\n errorUnexpected,\n} from '../utils/cli-errors';\nimport {\n addGlobalOptions,\n getTargetMigrations,\n loadAllBundles,\n resolveContractPath,\n resolveMigrationPaths,\n setCommandDescriptions,\n setCommandExamples,\n} from '../utils/command-helpers';\nimport { formatStyledHeader } from '../utils/formatters/styled';\nimport { assertFrameworkComponentsCompatible } from '../utils/framework-components';\nimport type { CommonCommandOptions } from '../utils/global-flags';\nimport { type GlobalFlags, parseGlobalFlags } from '../utils/global-flags';\nimport { handleResult } from '../utils/result-handler';\nimport { TerminalUI } from '../utils/terminal-ui';\n\ninterface MigrationPlanOptions extends CommonCommandOptions {\n readonly config?: string;\n readonly name?: string;\n readonly from?: string;\n}\n\nexport interface MigrationPlanResult {\n readonly ok: boolean;\n readonly noOp: boolean;\n readonly from: string;\n readonly to: string;\n readonly migrationId?: string;\n readonly dir?: string;\n readonly operations: readonly {\n readonly id: string;\n readonly label: string;\n readonly operationClass: string;\n }[];\n readonly sql?: readonly string[];\n readonly summary: string;\n readonly timings: {\n readonly total: number;\n };\n}\n\nfunction mapMigrationToolsError(error: unknown): CliStructuredError {\n if (CliStructuredError.is(error)) {\n return error;\n }\n if (MigrationToolsError.is(error)) {\n return errorRuntime(error.message, {\n why: error.why,\n fix: error.fix,\n meta: { code: error.code, ...(error.details ?? {}) },\n });\n }\n return errorUnexpected(error instanceof Error ? error.message : String(error), {\n why: `Unexpected error during migration plan: ${error instanceof Error ? error.message : String(error)}`,\n });\n}\n\nasync function executeMigrationPlanCommand(\n options: MigrationPlanOptions,\n flags: GlobalFlags,\n ui: TerminalUI,\n startTime: number,\n): Promise<Result<MigrationPlanResult, CliStructuredError>> {\n const config = await loadConfig(options.config);\n const { configPath, migrationsDir, migrationsRelative } = resolveMigrationPaths(\n options.config,\n config,\n );\n\n const contractPathAbsolute = resolveContractPath(config);\n const contractPath = relative(process.cwd(), contractPathAbsolute);\n\n if (!flags.json && !flags.quiet) {\n const details: Array<{ label: string; value: string }> = [\n { label: 'config', value: configPath },\n { label: 'contract', value: contractPath },\n { label: 'migrations', value: migrationsRelative },\n ];\n if (options.from) {\n details.push({ label: 'from', value: options.from });\n }\n if (options.name) {\n details.push({ label: 'name', value: options.name });\n }\n const header = formatStyledHeader({\n command: 'migration plan',\n description: 'Plan a migration from contract changes',\n url: 'https://pris.ly/migration-plan',\n details,\n flags,\n });\n ui.stderr(header);\n }\n\n // Load contract file (the \"to\" contract)\n let contractJsonContent: string;\n try {\n contractJsonContent = await readFile(contractPathAbsolute, 'utf-8');\n } catch (error) {\n if (error instanceof Error && (error as { code?: string }).code === 'ENOENT') {\n return notOk(\n errorFileNotFound(contractPathAbsolute, {\n why: `Contract file not found at ${contractPathAbsolute}`,\n fix: `Run \\`prisma-next contract emit\\` to generate ${contractPath}, or update \\`config.contract.output\\` in ${configPath}`,\n }),\n );\n }\n return notOk(\n errorUnexpected(error instanceof Error ? error.message : String(error), {\n why: `Failed to read contract file: ${error instanceof Error ? error.message : String(error)}`,\n }),\n );\n }\n\n let toContractJson: Contract;\n try {\n toContractJson = JSON.parse(contractJsonContent) as Contract;\n } catch (error) {\n return notOk(\n errorContractValidationFailed(\n `Contract JSON is invalid: ${error instanceof Error ? error.message : String(error)}`,\n { where: { path: contractPathAbsolute } },\n ),\n );\n }\n\n const rawStorageHash = toContractJson.storage?.storageHash;\n if (typeof rawStorageHash !== 'string') {\n return notOk(\n errorContractValidationFailed('Contract is missing storageHash', {\n where: { path: contractPathAbsolute },\n }),\n );\n }\n const toStorageHash = rawStorageHash;\n\n // Read existing migrations and determine \"from\" contract\n let fromContract: Contract | null = null;\n let fromHash: string = EMPTY_CONTRACT_HASH;\n\n try {\n const { attested: bundles, drafts, graph } = await loadAllBundles(migrationsDir);\n\n // Check if a draft migration already targets this contract\n const existingDraft = drafts.find((d) => d.manifest.to === toStorageHash);\n if (existingDraft) {\n return notOk(\n errorRuntime('A draft migration to this contract already exists', {\n why: `Draft migration at \"${existingDraft.dirName}\" already targets ${toStorageHash}`,\n fix: `Run 'prisma-next migration verify --dir ${migrationsRelative}/${existingDraft.dirName}' to attest it, or delete it and re-plan.`,\n }),\n );\n }\n\n if (options.from) {\n const resolved = resolveBundleByPrefix(bundles, options.from);\n if (!resolved.ok) {\n const f = resolved.failure;\n return notOk(\n f.reason === 'ambiguous'\n ? errorRuntime('Multiple matching migrations found', {\n why: `Prefix \"${options.from}\" matches ${f.count} migrations in ${migrationsRelative}`,\n fix: 'Provide a longer prefix to disambiguate, or omit --from to use the latest migration target.',\n })\n : errorRuntime('Starting contract not found', {\n why: `No migration with to hash matching \"${options.from}\" exists in ${migrationsRelative}`,\n fix: 'Check that the --from hash matches a known migration target hash, or omit --from to use the latest migration target.',\n }),\n );\n }\n fromHash = resolved.value.manifest.to;\n fromContract = resolved.value.manifest.toContract;\n } else {\n const latestMigration = findLatestMigration(graph);\n if (latestMigration) {\n fromHash = latestMigration.to;\n const leafPkg = bundles.find((p) => p.manifest.migrationId === latestMigration.migrationId);\n if (leafPkg) {\n fromContract = leafPkg.manifest.toContract;\n }\n }\n }\n } catch (error) {\n if (MigrationToolsError.is(error)) {\n return notOk(mapMigrationToolsError(error));\n }\n throw error;\n }\n\n // Check for no-op (same hash means no changes)\n if (fromHash === toStorageHash) {\n const result: MigrationPlanResult = {\n ok: true,\n noOp: true,\n from: fromHash,\n to: toStorageHash,\n operations: [],\n summary: 'No changes detected between contracts',\n timings: { total: Date.now() - startTime },\n };\n return ok(result);\n }\n\n // Check target supports migrations\n const migrations = getTargetMigrations(config.target);\n if (!migrations) {\n return notOk(\n errorTargetMigrationNotSupported({\n why: `Target \"${config.target.id}\" does not support migrations`,\n }),\n );\n }\n const frameworkComponents = assertFrameworkComponentsCompatible(\n config.family.familyId,\n config.target.targetId,\n [config.target, config.adapter, ...(config.extensionPacks ?? [])],\n );\n\n // Use descriptor-based planner if available, fall back to old planner\n if (migrations.planWithDescriptors) {\n const descriptorResult = migrations.planWithDescriptors({\n fromContract,\n toContract: toContractJson,\n frameworkComponents,\n });\n\n if (!descriptorResult.ok) {\n return notOk(\n errorMigrationPlanningFailed({\n conflicts: descriptorResult.conflicts as readonly CliErrorConflict[],\n }),\n );\n }\n\n if (descriptorResult.descriptors.length === 0) {\n return notOk(\n errorMigrationPlanningFailed({\n conflicts: [\n {\n kind: 'unsupportedChange',\n summary:\n 'Contract changed but planner produced no operations. ' +\n 'This indicates unsupported or ignored changes.',\n },\n ],\n }),\n );\n }\n\n // Build manifest and write migration package\n const timestamp = new Date();\n const slug = options.name ?? 'migration';\n const dirName = formatMigrationDirName(timestamp, slug);\n const packageDir = join(migrationsDir, dirName);\n\n const manifest: MigrationManifest = {\n from: fromHash,\n to: toStorageHash,\n migrationId: null,\n kind: 'regular',\n fromContract,\n toContract: toContractJson,\n hints: {\n used: [],\n applied: [],\n plannerVersion: '2.0.0',\n planningStrategy: 'descriptors',\n },\n labels: [],\n createdAt: timestamp.toISOString(),\n };\n\n try {\n // Always write migration.ts with the descriptors\n // Write package with empty ops first (draft)\n await writeMigrationPackage(packageDir, manifest, []);\n await scaffoldMigrationTs(packageDir, {\n descriptors: descriptorResult.descriptors,\n contractJsonPath: contractPathAbsolute,\n });\n\n if (descriptorResult.needsDataMigration) {\n // Draft — user must fill in dataTransform and run verify\n const result: MigrationPlanResult = {\n ok: true,\n noOp: false,\n from: fromHash,\n to: toStorageHash,\n dir: relative(process.cwd(), packageDir),\n operations: descriptorResult.descriptors.map((d) => ({\n id: (d as { kind: string }).kind,\n label: (d as { kind: string }).kind,\n operationClass: 'data' as const,\n })),\n sql: [],\n summary: `Planned ${descriptorResult.descriptors.length} operation(s) — data migration required. Edit migration.ts and run \\`migration verify --dir ${relative(process.cwd(), packageDir)}\\` to attest.`,\n timings: { total: Date.now() - startTime },\n };\n return ok(result);\n }\n\n // No data migration — evaluate, resolve, write ops, attest\n const evaluatedDescriptors = await evaluateMigrationTs(packageDir);\n\n if (!migrations.resolveDescriptors) {\n throw errorTargetMigrationNotSupported({\n why: `Target \"${config.target.targetId}\" does not implement resolveDescriptors; cannot finalize migration plan with migration.ts`,\n });\n }\n\n const resolvedOps = migrations.resolveDescriptors(\n evaluatedDescriptors as OperationDescriptor[],\n {\n fromContract,\n toContract: toContractJson,\n frameworkComponents,\n },\n );\n\n await writeMigrationOps(packageDir, resolvedOps);\n const migrationId = await attestMigration(packageDir);\n\n const sql = extractSqlDdl(resolvedOps);\n const result: MigrationPlanResult = {\n ok: true,\n noOp: false,\n from: fromHash,\n to: toStorageHash,\n migrationId,\n dir: relative(process.cwd(), packageDir),\n operations: resolvedOps.map((op) => ({\n id: op.id,\n label: op.label,\n operationClass: op.operationClass,\n })),\n sql,\n summary: `Planned ${resolvedOps.length} operation(s)`,\n timings: { total: Date.now() - startTime },\n };\n return ok(result);\n } catch (error) {\n return notOk(mapMigrationToolsError(error));\n }\n }\n\n return notOk(\n errorTargetMigrationNotSupported({\n why: `Target \"${config.target.id}\" does not support planWithDescriptors`,\n }),\n );\n}\n\nexport function createMigrationPlanCommand(): Command {\n const command = new Command('plan');\n setCommandDescriptions(\n command,\n 'Plan a migration from contract changes',\n 'Compares the emitted contract against the latest on-disk migration state and\\n' +\n 'produces a new migration package with the required operations. No database\\n' +\n 'connection is needed — this is a fully offline operation.',\n );\n setCommandExamples(command, [\n 'prisma-next migration plan',\n 'prisma-next migration plan --name add-users-table',\n ]);\n addGlobalOptions(command)\n .option('--config <path>', 'Path to prisma-next.config.ts')\n .option('--name <slug>', 'Name slug for the migration directory', 'migration')\n .option('--from <hash>', 'Explicit starting contract hash (overrides latest migration target)')\n .action(async (options: MigrationPlanOptions) => {\n const flags = parseGlobalFlags(options);\n const startTime = Date.now();\n\n const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });\n const result = await executeMigrationPlanCommand(options, flags, ui, startTime);\n\n const exitCode = handleResult(result, flags, ui, (planResult) => {\n if (flags.json) {\n ui.output(JSON.stringify(planResult, null, 2));\n } else if (!flags.quiet) {\n ui.log(formatMigrationPlanOutput(planResult, flags));\n }\n });\n\n process.exit(exitCode);\n });\n\n return command;\n}\n\nfunction formatMigrationPlanOutput(result: MigrationPlanResult, flags: GlobalFlags): string {\n const lines: string[] = [];\n const useColor = flags.color !== false;\n\n const green_ = useColor ? (s: string) => `\\x1b[32m${s}\\x1b[0m` : (s: string) => s;\n const yellow_ = useColor ? (s: string) => `\\x1b[33m${s}\\x1b[0m` : (s: string) => s;\n const dim_ = useColor ? (s: string) => `\\x1b[2m${s}\\x1b[0m` : (s: string) => s;\n\n if (result.noOp) {\n lines.push(`${green_('✔')} No changes detected`);\n lines.push(dim_(` from: ${result.from}`));\n lines.push(dim_(` to: ${result.to}`));\n return lines.join('\\n');\n }\n\n lines.push(`${green_('✔')} ${result.summary}`);\n lines.push('');\n\n if (result.operations.length > 0) {\n lines.push(dim_('│'));\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 ? yellow_(`[${op.operationClass}]`)\n : dim_(`[${op.operationClass}]`);\n lines.push(`${dim_(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 `${yellow_('⚠')} This migration contains destructive operations that may cause data loss.`,\n );\n }\n lines.push('');\n }\n\n lines.push(dim_(`from: ${result.from}`));\n lines.push(dim_(`to: ${result.to}`));\n if (result.migrationId) {\n lines.push(dim_(`migrationId: ${result.migrationId}`));\n }\n if (result.dir) {\n lines.push(dim_(`dir: ${result.dir}`));\n }\n\n if (result.sql && result.sql.length > 0) {\n lines.push('');\n lines.push(dim_('DDL preview'));\n lines.push('');\n for (const statement of result.sql) {\n const trimmed = statement.trim();\n if (!trimmed) continue;\n const line = trimmed.endsWith(';') ? trimmed : `${trimmed};`;\n lines.push(line);\n }\n }\n\n if (flags.verbose && result.timings) {\n lines.push('');\n lines.push(dim_(`Total time: ${result.timings.total}ms`));\n }\n\n return lines.join('\\n');\n}\n\nexport type PrefixResolutionFailure =\n | { reason: 'ambiguous'; count: number }\n | { reason: 'not-found' };\n\n/**\n * Resolve a migration bundle by exact hash or prefix match.\n *\n * Tries exact match first, then prefix match (auto-prepending `sha256:` when\n * the needle omits the scheme). Returns the matched bundle on success, or a\n * discriminated failure indicating whether the prefix was ambiguous or simply\n * not found.\n *\n * @internal Exported for testing only.\n */\nexport function resolveBundleByPrefix<T extends { manifest: { to: string } }>(\n bundles: readonly T[],\n needle: string,\n): Result<T, PrefixResolutionFailure> {\n const exact = bundles.find((p) => p.manifest.to === needle);\n if (exact) return ok(exact);\n\n const prefixWithScheme = needle.startsWith('sha256:') ? needle : `sha256:${needle}`;\n const candidates = bundles.filter((p) => p.manifest.to.startsWith(prefixWithScheme));\n\n if (candidates.length === 1) return ok(candidates[0]!);\n if (candidates.length > 1) return notOk({ reason: 'ambiguous', count: candidates.length });\n return notOk({ reason: 'not-found' });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"migration-plan.mjs","names":["details: Array<{ label: string; value: string }>","contractJsonContent: string","toContractJson: Contract","fromContract: Contract | null","fromHash: string","manifest: MigrationManifest","lines: string[]"],"sources":["../../src/commands/migration-plan.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport type { Contract } from '@prisma-next/contract/types';\nimport { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';\nimport { findLatestMigration } from '@prisma-next/migration-tools/dag';\nimport { formatMigrationDirName, writeMigrationPackage } from '@prisma-next/migration-tools/io';\nimport { scaffoldMigrationTs } from '@prisma-next/migration-tools/migration-ts';\nimport { type MigrationManifest, MigrationToolsError } from '@prisma-next/migration-tools/types';\nimport { notOk, ok, type Result } from '@prisma-next/utils/result';\nimport { Command } from 'commander';\nimport { join, relative } from 'pathe';\nimport { loadConfig } from '../config-loader';\nimport { extractSqlDdl } from '../control-api/operations/extract-sql-ddl';\nimport { emitMigration } from '../lib/migration-emit';\nimport {\n type CliErrorConflict,\n CliStructuredError,\n errorContractValidationFailed,\n errorFileNotFound,\n errorMigrationPlanningFailed,\n errorRuntime,\n errorTargetMigrationNotSupported,\n errorUnexpected,\n} from '../utils/cli-errors';\nimport {\n addGlobalOptions,\n getTargetMigrations,\n loadAllBundles,\n resolveContractPath,\n resolveMigrationPaths,\n setCommandDescriptions,\n setCommandExamples,\n} from '../utils/command-helpers';\nimport { formatStyledHeader } from '../utils/formatters/styled';\nimport { assertFrameworkComponentsCompatible } from '../utils/framework-components';\nimport type { CommonCommandOptions } from '../utils/global-flags';\nimport { type GlobalFlags, parseGlobalFlags } from '../utils/global-flags';\nimport { handleResult } from '../utils/result-handler';\nimport { TerminalUI } from '../utils/terminal-ui';\n\ninterface MigrationPlanOptions extends CommonCommandOptions {\n readonly config?: string;\n readonly name?: string;\n readonly from?: string;\n}\n\nexport interface MigrationPlanResult {\n readonly ok: boolean;\n readonly noOp: boolean;\n readonly from: string;\n readonly to: string;\n readonly migrationId?: string;\n readonly dir?: string;\n readonly operations: readonly {\n readonly id: string;\n readonly label: string;\n readonly operationClass: string;\n }[];\n readonly sql?: readonly string[];\n readonly summary: string;\n readonly timings: {\n readonly total: number;\n };\n}\n\nfunction mapMigrationToolsError(error: unknown): CliStructuredError {\n if (CliStructuredError.is(error)) {\n return error;\n }\n if (MigrationToolsError.is(error)) {\n return errorRuntime(error.message, {\n why: error.why,\n fix: error.fix,\n meta: { code: error.code, ...(error.details ?? {}) },\n });\n }\n return errorUnexpected(error instanceof Error ? error.message : String(error), {\n why: `Unexpected error during migration plan: ${error instanceof Error ? error.message : String(error)}`,\n });\n}\n\nasync function executeMigrationPlanCommand(\n options: MigrationPlanOptions,\n flags: GlobalFlags,\n ui: TerminalUI,\n startTime: number,\n): Promise<Result<MigrationPlanResult, CliStructuredError>> {\n const config = await loadConfig(options.config);\n const { configPath, migrationsDir, migrationsRelative } = resolveMigrationPaths(\n options.config,\n config,\n );\n\n const contractPathAbsolute = resolveContractPath(config);\n const contractPath = relative(process.cwd(), contractPathAbsolute);\n\n if (!flags.json && !flags.quiet) {\n const details: Array<{ label: string; value: string }> = [\n { label: 'config', value: configPath },\n { label: 'contract', value: contractPath },\n { label: 'migrations', value: migrationsRelative },\n ];\n if (options.from) {\n details.push({ label: 'from', value: options.from });\n }\n if (options.name) {\n details.push({ label: 'name', value: options.name });\n }\n const header = formatStyledHeader({\n command: 'migration plan',\n description: 'Plan a migration from contract changes',\n url: 'https://pris.ly/migration-plan',\n details,\n flags,\n });\n ui.stderr(header);\n }\n\n // Load contract file (the \"to\" contract)\n let contractJsonContent: string;\n try {\n contractJsonContent = await readFile(contractPathAbsolute, 'utf-8');\n } catch (error) {\n if (error instanceof Error && (error as { code?: string }).code === 'ENOENT') {\n return notOk(\n errorFileNotFound(contractPathAbsolute, {\n why: `Contract file not found at ${contractPathAbsolute}`,\n fix: `Run \\`prisma-next contract emit\\` to generate ${contractPath}, or update \\`config.contract.output\\` in ${configPath}`,\n }),\n );\n }\n return notOk(\n errorUnexpected(error instanceof Error ? error.message : String(error), {\n why: `Failed to read contract file: ${error instanceof Error ? error.message : String(error)}`,\n }),\n );\n }\n\n let toContractJson: Contract;\n try {\n toContractJson = JSON.parse(contractJsonContent) as Contract;\n } catch (error) {\n return notOk(\n errorContractValidationFailed(\n `Contract JSON is invalid: ${error instanceof Error ? error.message : String(error)}`,\n { where: { path: contractPathAbsolute } },\n ),\n );\n }\n\n const rawStorageHash = toContractJson.storage?.storageHash;\n if (typeof rawStorageHash !== 'string') {\n return notOk(\n errorContractValidationFailed('Contract is missing storageHash', {\n where: { path: contractPathAbsolute },\n }),\n );\n }\n const toStorageHash = rawStorageHash;\n\n // Read existing migrations and determine \"from\" contract\n let fromContract: Contract | null = null;\n let fromHash: string = EMPTY_CONTRACT_HASH;\n\n try {\n const { attested: bundles, drafts, graph } = await loadAllBundles(migrationsDir);\n\n // Check if a draft migration already targets this contract\n const existingDraft = drafts.find((d) => d.manifest.to === toStorageHash);\n if (existingDraft) {\n return notOk(\n errorRuntime('A draft migration to this contract already exists', {\n why: `Draft migration at \"${existingDraft.dirName}\" already targets ${toStorageHash}`,\n fix: `Run 'prisma-next migration emit --dir ${migrationsRelative}/${existingDraft.dirName}' to attest it, or delete it and re-plan.`,\n }),\n );\n }\n\n if (options.from) {\n const resolved = resolveBundleByPrefix(bundles, options.from);\n if (!resolved.ok) {\n const f = resolved.failure;\n return notOk(\n f.reason === 'ambiguous'\n ? errorRuntime('Multiple matching migrations found', {\n why: `Prefix \"${options.from}\" matches ${f.count} migrations in ${migrationsRelative}`,\n fix: 'Provide a longer prefix to disambiguate, or omit --from to use the latest migration target.',\n })\n : errorRuntime('Starting contract not found', {\n why: `No migration with to hash matching \"${options.from}\" exists in ${migrationsRelative}`,\n fix: 'Check that the --from hash matches a known migration target hash, or omit --from to use the latest migration target.',\n }),\n );\n }\n fromHash = resolved.value.manifest.to;\n fromContract = resolved.value.manifest.toContract;\n } else {\n const latestMigration = findLatestMigration(graph);\n if (latestMigration) {\n fromHash = latestMigration.to;\n const leafPkg = bundles.find((p) => p.manifest.migrationId === latestMigration.migrationId);\n if (leafPkg) {\n fromContract = leafPkg.manifest.toContract;\n }\n }\n }\n } catch (error) {\n if (MigrationToolsError.is(error)) {\n return notOk(mapMigrationToolsError(error));\n }\n throw error;\n }\n\n // Check for no-op (same hash means no changes)\n if (fromHash === toStorageHash) {\n const result: MigrationPlanResult = {\n ok: true,\n noOp: true,\n from: fromHash,\n to: toStorageHash,\n operations: [],\n summary: 'No changes detected between contracts',\n timings: { total: Date.now() - startTime },\n };\n return ok(result);\n }\n\n // Check target supports migrations\n const migrations = getTargetMigrations(config.target);\n if (!migrations) {\n return notOk(\n errorTargetMigrationNotSupported({\n why: `Target \"${config.target.id}\" does not support migrations`,\n }),\n );\n }\n const frameworkComponents = assertFrameworkComponentsCompatible(\n config.family.familyId,\n config.target.targetId,\n [config.target, config.adapter, ...(config.extensionPacks ?? [])],\n );\n\n // Use descriptor-based planner if available, fall back to old planner\n if (migrations.planWithDescriptors) {\n const descriptorResult = migrations.planWithDescriptors({\n fromContract,\n toContract: toContractJson,\n frameworkComponents,\n });\n\n if (!descriptorResult.ok) {\n return notOk(\n errorMigrationPlanningFailed({\n conflicts: descriptorResult.conflicts as readonly CliErrorConflict[],\n }),\n );\n }\n\n if (descriptorResult.descriptors.length === 0) {\n return notOk(\n errorMigrationPlanningFailed({\n conflicts: [\n {\n kind: 'unsupportedChange',\n summary:\n 'Contract changed but planner produced no operations. ' +\n 'This indicates unsupported or ignored changes.',\n },\n ],\n }),\n );\n }\n\n // Build manifest and write migration package\n const timestamp = new Date();\n const slug = options.name ?? 'migration';\n const dirName = formatMigrationDirName(timestamp, slug);\n const packageDir = join(migrationsDir, dirName);\n\n const manifest: MigrationManifest = {\n from: fromHash,\n to: toStorageHash,\n migrationId: null,\n kind: 'regular',\n fromContract,\n toContract: toContractJson,\n hints: {\n used: [],\n applied: [],\n plannerVersion: '2.0.0',\n planningStrategy: 'descriptors',\n },\n labels: [],\n createdAt: timestamp.toISOString(),\n };\n\n try {\n await writeMigrationPackage(packageDir, manifest, []);\n await scaffoldMigrationTs(packageDir, {\n descriptors: descriptorResult.descriptors,\n contractJsonPath: contractPathAbsolute,\n });\n\n // Always run emit inline; structured errors thrown during evaluation\n // (e.g. invalid migration.ts shape, missing file) propagate through\n // emitMigration to the CLI envelope.\n const { operations, migrationId } = await emitMigration(packageDir, {\n targetId: config.target.targetId,\n migrations,\n frameworkComponents,\n });\n\n const sql = extractSqlDdl(operations);\n const result: MigrationPlanResult = {\n ok: true,\n noOp: false,\n from: fromHash,\n to: toStorageHash,\n migrationId,\n dir: relative(process.cwd(), packageDir),\n operations: operations.map((op) => ({\n id: op.id,\n label: op.label,\n operationClass: op.operationClass,\n })),\n sql,\n summary: `Planned ${operations.length} operation(s)`,\n timings: { total: Date.now() - startTime },\n };\n return ok(result);\n } catch (error) {\n return notOk(mapMigrationToolsError(error));\n }\n }\n\n return notOk(\n errorTargetMigrationNotSupported({\n why: `Target \"${config.target.id}\" does not support planWithDescriptors`,\n }),\n );\n}\n\nexport function createMigrationPlanCommand(): Command {\n const command = new Command('plan');\n setCommandDescriptions(\n command,\n 'Plan a migration from contract changes',\n 'Compares the emitted contract against the latest on-disk migration state and\\n' +\n 'produces a new migration package with the required operations. No database\\n' +\n 'connection is needed — this is a fully offline operation.',\n );\n setCommandExamples(command, [\n 'prisma-next migration plan',\n 'prisma-next migration plan --name add-users-table',\n ]);\n addGlobalOptions(command)\n .option('--config <path>', 'Path to prisma-next.config.ts')\n .option('--name <slug>', 'Name slug for the migration directory', 'migration')\n .option('--from <hash>', 'Explicit starting contract hash (overrides latest migration target)')\n .action(async (options: MigrationPlanOptions) => {\n const flags = parseGlobalFlags(options);\n const startTime = Date.now();\n\n const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });\n const result = await executeMigrationPlanCommand(options, flags, ui, startTime);\n\n const exitCode = handleResult(result, flags, ui, (planResult) => {\n if (flags.json) {\n ui.output(JSON.stringify(planResult, null, 2));\n } else if (!flags.quiet) {\n ui.log(formatMigrationPlanOutput(planResult, flags));\n }\n });\n\n process.exit(exitCode);\n });\n\n return command;\n}\n\nfunction formatMigrationPlanOutput(result: MigrationPlanResult, flags: GlobalFlags): string {\n const lines: string[] = [];\n const useColor = flags.color !== false;\n\n const green_ = useColor ? (s: string) => `\\x1b[32m${s}\\x1b[0m` : (s: string) => s;\n const yellow_ = useColor ? (s: string) => `\\x1b[33m${s}\\x1b[0m` : (s: string) => s;\n const dim_ = useColor ? (s: string) => `\\x1b[2m${s}\\x1b[0m` : (s: string) => s;\n\n if (result.noOp) {\n lines.push(`${green_('✔')} No changes detected`);\n lines.push(dim_(` from: ${result.from}`));\n lines.push(dim_(` to: ${result.to}`));\n return lines.join('\\n');\n }\n\n lines.push(`${green_('✔')} ${result.summary}`);\n lines.push('');\n\n if (result.operations.length > 0) {\n lines.push(dim_('│'));\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 ? yellow_(`[${op.operationClass}]`)\n : dim_(`[${op.operationClass}]`);\n lines.push(`${dim_(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 `${yellow_('⚠')} This migration contains destructive operations that may cause data loss.`,\n );\n }\n lines.push('');\n }\n\n lines.push(dim_(`from: ${result.from}`));\n lines.push(dim_(`to: ${result.to}`));\n if (result.migrationId) {\n lines.push(dim_(`migrationId: ${result.migrationId}`));\n }\n if (result.dir) {\n lines.push(dim_(`dir: ${result.dir}`));\n }\n\n if (result.sql && result.sql.length > 0) {\n lines.push('');\n lines.push(dim_('DDL preview'));\n lines.push('');\n for (const statement of result.sql) {\n const trimmed = statement.trim();\n if (!trimmed) continue;\n const line = trimmed.endsWith(';') ? trimmed : `${trimmed};`;\n lines.push(line);\n }\n }\n\n if (flags.verbose && result.timings) {\n lines.push('');\n lines.push(dim_(`Total time: ${result.timings.total}ms`));\n }\n\n return lines.join('\\n');\n}\n\nexport type PrefixResolutionFailure =\n | { reason: 'ambiguous'; count: number }\n | { reason: 'not-found' };\n\n/**\n * Resolve a migration bundle by exact hash or prefix match.\n *\n * Tries exact match first, then prefix match (auto-prepending `sha256:` when\n * the needle omits the scheme). Returns the matched bundle on success, or a\n * discriminated failure indicating whether the prefix was ambiguous or simply\n * not found.\n *\n * @internal Exported for testing only.\n */\nexport function resolveBundleByPrefix<T extends { manifest: { to: string } }>(\n bundles: readonly T[],\n needle: string,\n): Result<T, PrefixResolutionFailure> {\n const exact = bundles.find((p) => p.manifest.to === needle);\n if (exact) return ok(exact);\n\n const prefixWithScheme = needle.startsWith('sha256:') ? needle : `sha256:${needle}`;\n const candidates = bundles.filter((p) => p.manifest.to.startsWith(prefixWithScheme));\n\n if (candidates.length === 1) return ok(candidates[0]!);\n if (candidates.length > 1) return notOk({ reason: 'ambiguous', count: candidates.length });\n return notOk({ reason: 'not-found' });\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as loadConfig } from "../config-loader-C4VXKl8f.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { h as formatCommandHelp, m as parseGlobalFlags, n as addGlobalOptions, t as handleResult, u as setCommandDescriptions } from "../result-handler-
|
|
2
|
+
import { h as errorRuntime, t as CliStructuredError, v as errorUnexpected } from "../cli-errors-BUuJr6py.mjs";
|
|
3
|
+
import { h as formatCommandHelp, m as parseGlobalFlags, n as addGlobalOptions, t as handleResult, u as setCommandDescriptions } from "../result-handler-AFK4hxyX.mjs";
|
|
4
4
|
import { t as TerminalUI } from "../terminal-ui-C5k88MmW.mjs";
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
import { notOk, ok } from "@prisma-next/utils/result";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as CliStructuredError } from "../cli-errors-
|
|
1
|
+
import { t as CliStructuredError } from "../cli-errors-Dic2eADK.mjs";
|
|
2
2
|
import { Command } from "commander";
|
|
3
3
|
import { Result } from "@prisma-next/utils/result";
|
|
4
4
|
import { MigrationBundle } from "@prisma-next/migration-tools/types";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { t as loadConfig } from "../config-loader-C4VXKl8f.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { h as errorRuntime, v as errorUnexpected } from "../cli-errors-BUuJr6py.mjs";
|
|
3
3
|
import { t as extractOperationStatements } from "../extract-operation-statements-DZUJNmL3.mjs";
|
|
4
|
-
import { _ as formatStyledHeader, d as setCommandExamples, m as parseGlobalFlags, n as addGlobalOptions, t as handleResult, u as setCommandDescriptions } from "../result-handler-
|
|
4
|
+
import { _ as formatStyledHeader, d as setCommandExamples, m as parseGlobalFlags, n as addGlobalOptions, t as handleResult, u as setCommandDescriptions } from "../result-handler-AFK4hxyX.mjs";
|
|
5
5
|
import { t as TerminalUI } from "../terminal-ui-C5k88MmW.mjs";
|
|
6
|
-
import {
|
|
6
|
+
import { o as formatMigrationShowOutput } from "../migrations-BIsjFjSV.mjs";
|
|
7
7
|
import { Command } from "commander";
|
|
8
8
|
import { notOk, ok } from "@prisma-next/utils/result";
|
|
9
9
|
import { relative, resolve } from "pathe";
|
|
@@ -70,7 +70,7 @@ async function executeMigrationShowCommand(target, options, flags, ui) {
|
|
|
70
70
|
const attested = allPackages.filter(isAttested);
|
|
71
71
|
if (attested.length === 0) return notOk(errorRuntime("No attested migrations found", {
|
|
72
72
|
why: `All migrations in ${migrationsRelative} are drafts (migrationId: null)`,
|
|
73
|
-
fix: "Run `prisma-next migration
|
|
73
|
+
fix: "Run `prisma-next migration emit --dir <path>` to attest a draft migration."
|
|
74
74
|
}));
|
|
75
75
|
const latestMigration = findLatestMigration(reconstructGraph(attested));
|
|
76
76
|
if (!latestMigration) return notOk(errorRuntime("Could not resolve latest migration", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migration-show.mjs","names":["details: Array<{ label: string; value: string }>","pkg: MigrationBundle"],"sources":["../../src/commands/migration-show.ts"],"sourcesContent":["import type { MigrationPlanOperation } from '@prisma-next/framework-components/control';\nimport { findLatestMigration, reconstructGraph } from '@prisma-next/migration-tools/dag';\nimport { readMigrationPackage, readMigrationsDir } from '@prisma-next/migration-tools/io';\nimport type { MigrationBundle } from '@prisma-next/migration-tools/types';\nimport { isAttested, MigrationToolsError } from '@prisma-next/migration-tools/types';\nimport { notOk, ok, type Result } from '@prisma-next/utils/result';\nimport { Command } from 'commander';\nimport { relative, resolve } from 'pathe';\nimport { loadConfig } from '../config-loader';\nimport { extractOperationStatements } from '../control-api/operations/extract-operation-statements';\nimport { type CliStructuredError, errorRuntime, errorUnexpected } from '../utils/cli-errors';\nimport {\n addGlobalOptions,\n setCommandDescriptions,\n setCommandExamples,\n} from '../utils/command-helpers';\nimport { formatMigrationShowOutput } from '../utils/formatters/migrations';\nimport { formatStyledHeader } from '../utils/formatters/styled';\nimport type { CommonCommandOptions } from '../utils/global-flags';\nimport { type GlobalFlags, parseGlobalFlags } from '../utils/global-flags';\nimport { handleResult } from '../utils/result-handler';\nimport { TerminalUI } from '../utils/terminal-ui';\n\ninterface MigrationShowOptions extends CommonCommandOptions {\n readonly config?: string;\n}\n\nexport interface MigrationShowResult {\n readonly ok: true;\n readonly dirName: string;\n readonly dirPath: string;\n readonly from: string;\n readonly to: string;\n readonly migrationId: string | null;\n readonly kind: string;\n readonly createdAt: string;\n readonly operations: readonly {\n readonly id: string;\n readonly label: string;\n readonly operationClass: string;\n }[];\n readonly sql: readonly string[];\n readonly summary: string;\n}\n\nfunction looksLikePath(target: string): boolean {\n return target.includes('/') || target.includes('\\\\');\n}\n\nexport function resolveByHashPrefix(\n packages: readonly MigrationBundle[],\n prefix: string,\n): Result<MigrationBundle, CliStructuredError> {\n const normalizedPrefix = prefix.startsWith('sha256:') ? prefix : `sha256:${prefix}`;\n const attested = packages.filter((p) => typeof p.manifest.migrationId === 'string');\n const matches = attested.filter((p) => p.manifest.migrationId!.startsWith(normalizedPrefix));\n\n if (matches.length === 1) {\n return ok(matches[0]!);\n }\n\n if (matches.length === 0) {\n return notOk(\n errorRuntime('No migration found matching prefix', {\n why: `No attested migration has a migrationId starting with \"${normalizedPrefix}\"`,\n fix: 'Run `prisma-next migration show` (no argument) to see the latest migration, or check the migrations directory for available packages.',\n }),\n );\n }\n\n const candidates = matches.map((p) => ` ${p.dirName} ${p.manifest.migrationId}`).join('\\n');\n return notOk(\n errorRuntime('Ambiguous hash prefix', {\n why: `Multiple migrations match prefix \"${normalizedPrefix}\":\\n${candidates}`,\n fix: 'Provide a longer prefix to uniquely identify the migration.',\n }),\n );\n}\n\nasync function executeMigrationShowCommand(\n target: string | undefined,\n options: MigrationShowOptions,\n flags: GlobalFlags,\n ui: TerminalUI,\n): Promise<Result<MigrationShowResult, CliStructuredError>> {\n const config = await loadConfig(options.config);\n const configPath = options.config\n ? relative(process.cwd(), resolve(options.config))\n : 'prisma-next.config.ts';\n\n const migrationsDir = resolve(\n options.config ? resolve(options.config, '..') : process.cwd(),\n config.migrations?.dir ?? 'migrations',\n );\n const migrationsRelative = relative(process.cwd(), migrationsDir);\n\n if (!flags.json && !flags.quiet) {\n const details: Array<{ label: string; value: string }> = [\n { label: 'config', value: configPath },\n { label: 'migrations', value: migrationsRelative },\n ];\n if (target) {\n details.push({ label: 'target', value: target });\n }\n const header = formatStyledHeader({\n command: 'migration show',\n description: 'Display migration package contents',\n details,\n flags,\n });\n ui.stderr(header);\n }\n\n let pkg: MigrationBundle;\n\n try {\n if (target && looksLikePath(target)) {\n pkg = await readMigrationPackage(resolve(target));\n } else {\n const allPackages = await readMigrationsDir(migrationsDir);\n if (allPackages.length === 0) {\n return notOk(\n errorRuntime('No migrations found', {\n why: `No migration packages found in ${migrationsRelative}`,\n fix: 'Run `prisma-next migration plan` to create a migration first.',\n }),\n );\n }\n\n if (target) {\n const resolved = resolveByHashPrefix(allPackages, target);\n if (!resolved.ok) return resolved;\n pkg = resolved.value;\n } else {\n const attested = allPackages.filter(isAttested);\n if (attested.length === 0) {\n return notOk(\n errorRuntime('No attested migrations found', {\n why: `All migrations in ${migrationsRelative} are drafts (migrationId: null)`,\n fix: 'Run `prisma-next migration verify --dir <path>` to attest a draft migration.',\n }),\n );\n }\n const graph = reconstructGraph(attested);\n const latestMigration = findLatestMigration(graph);\n if (!latestMigration) {\n return notOk(\n errorRuntime('Could not resolve latest migration', {\n why: 'No latest migration found in the migration history',\n fix: 'The migrations directory may be corrupted. Inspect the migration.json files.',\n }),\n );\n }\n const leafPkg = attested.find(\n (p) => p.manifest.migrationId === latestMigration.migrationId,\n );\n if (!leafPkg) {\n return notOk(\n errorRuntime('Could not resolve latest migration', {\n why: `Latest migration ${latestMigration.dirName} does not match any package`,\n fix: 'The migrations directory may be corrupted. Inspect the migration.json files.',\n }),\n );\n }\n pkg = leafPkg;\n }\n }\n } catch (error) {\n if (MigrationToolsError.is(error)) {\n return notOk(\n errorRuntime(error.message, {\n why: error.why,\n fix: error.fix,\n meta: { code: error.code, ...(error.details ?? {}) },\n }),\n );\n }\n return notOk(\n errorUnexpected(error instanceof Error ? error.message : String(error), {\n why: `Failed to read migration: ${error instanceof Error ? error.message : String(error)}`,\n }),\n );\n }\n\n const ops = pkg.ops as readonly MigrationPlanOperation[];\n const sql = extractOperationStatements(config.family.familyId, ops) ?? [];\n\n const result: MigrationShowResult = {\n ok: true,\n dirName: pkg.dirName,\n dirPath: relative(process.cwd(), pkg.dirPath),\n from: pkg.manifest.from,\n to: pkg.manifest.to,\n migrationId: pkg.manifest.migrationId,\n kind: pkg.manifest.kind,\n createdAt: pkg.manifest.createdAt,\n operations: ops.map((op) => ({\n id: op.id,\n label: op.label,\n operationClass: op.operationClass,\n })),\n sql,\n summary: `${ops.length} operation(s)`,\n };\n return ok(result);\n}\n\nexport function createMigrationShowCommand(): Command {\n const command = new Command('show');\n setCommandDescriptions(\n command,\n 'Display migration package contents',\n 'Shows the operations, DDL preview, and metadata for a migration package.\\n' +\n 'Accepts a directory path, a hash prefix (git-style), or defaults to the\\n' +\n 'latest migration.',\n );\n setCommandExamples(command, [\n 'prisma-next migration show',\n 'prisma-next migration show sha256:a1b2c3',\n ]);\n addGlobalOptions(command)\n .argument(\n '[target]',\n 'Migration directory path or migrationId hash prefix (defaults to latest)',\n )\n .option('--config <path>', 'Path to prisma-next.config.ts')\n .action(async (target: string | undefined, options: MigrationShowOptions) => {\n const flags = parseGlobalFlags(options);\n\n const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });\n\n const result = await executeMigrationShowCommand(target, options, flags, ui);\n\n const exitCode = handleResult(result, flags, ui, (showResult) => {\n if (flags.json) {\n ui.output(JSON.stringify(showResult, null, 2));\n } else if (!flags.quiet) {\n ui.log(formatMigrationShowOutput(showResult, flags));\n }\n });\n\n process.exit(exitCode);\n });\n\n return command;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"migration-show.mjs","names":["details: Array<{ label: string; value: string }>","pkg: MigrationBundle"],"sources":["../../src/commands/migration-show.ts"],"sourcesContent":["import type { MigrationPlanOperation } from '@prisma-next/framework-components/control';\nimport { findLatestMigration, reconstructGraph } from '@prisma-next/migration-tools/dag';\nimport { readMigrationPackage, readMigrationsDir } from '@prisma-next/migration-tools/io';\nimport type { MigrationBundle } from '@prisma-next/migration-tools/types';\nimport { isAttested, MigrationToolsError } from '@prisma-next/migration-tools/types';\nimport { notOk, ok, type Result } from '@prisma-next/utils/result';\nimport { Command } from 'commander';\nimport { relative, resolve } from 'pathe';\nimport { loadConfig } from '../config-loader';\nimport { extractOperationStatements } from '../control-api/operations/extract-operation-statements';\nimport { type CliStructuredError, errorRuntime, errorUnexpected } from '../utils/cli-errors';\nimport {\n addGlobalOptions,\n setCommandDescriptions,\n setCommandExamples,\n} from '../utils/command-helpers';\nimport { formatMigrationShowOutput } from '../utils/formatters/migrations';\nimport { formatStyledHeader } from '../utils/formatters/styled';\nimport type { CommonCommandOptions } from '../utils/global-flags';\nimport { type GlobalFlags, parseGlobalFlags } from '../utils/global-flags';\nimport { handleResult } from '../utils/result-handler';\nimport { TerminalUI } from '../utils/terminal-ui';\n\ninterface MigrationShowOptions extends CommonCommandOptions {\n readonly config?: string;\n}\n\nexport interface MigrationShowResult {\n readonly ok: true;\n readonly dirName: string;\n readonly dirPath: string;\n readonly from: string;\n readonly to: string;\n readonly migrationId: string | null;\n readonly kind: string;\n readonly createdAt: string;\n readonly operations: readonly {\n readonly id: string;\n readonly label: string;\n readonly operationClass: string;\n }[];\n readonly sql: readonly string[];\n readonly summary: string;\n}\n\nfunction looksLikePath(target: string): boolean {\n return target.includes('/') || target.includes('\\\\');\n}\n\nexport function resolveByHashPrefix(\n packages: readonly MigrationBundle[],\n prefix: string,\n): Result<MigrationBundle, CliStructuredError> {\n const normalizedPrefix = prefix.startsWith('sha256:') ? prefix : `sha256:${prefix}`;\n const attested = packages.filter((p) => typeof p.manifest.migrationId === 'string');\n const matches = attested.filter((p) => p.manifest.migrationId!.startsWith(normalizedPrefix));\n\n if (matches.length === 1) {\n return ok(matches[0]!);\n }\n\n if (matches.length === 0) {\n return notOk(\n errorRuntime('No migration found matching prefix', {\n why: `No attested migration has a migrationId starting with \"${normalizedPrefix}\"`,\n fix: 'Run `prisma-next migration show` (no argument) to see the latest migration, or check the migrations directory for available packages.',\n }),\n );\n }\n\n const candidates = matches.map((p) => ` ${p.dirName} ${p.manifest.migrationId}`).join('\\n');\n return notOk(\n errorRuntime('Ambiguous hash prefix', {\n why: `Multiple migrations match prefix \"${normalizedPrefix}\":\\n${candidates}`,\n fix: 'Provide a longer prefix to uniquely identify the migration.',\n }),\n );\n}\n\nasync function executeMigrationShowCommand(\n target: string | undefined,\n options: MigrationShowOptions,\n flags: GlobalFlags,\n ui: TerminalUI,\n): Promise<Result<MigrationShowResult, CliStructuredError>> {\n const config = await loadConfig(options.config);\n const configPath = options.config\n ? relative(process.cwd(), resolve(options.config))\n : 'prisma-next.config.ts';\n\n const migrationsDir = resolve(\n options.config ? resolve(options.config, '..') : process.cwd(),\n config.migrations?.dir ?? 'migrations',\n );\n const migrationsRelative = relative(process.cwd(), migrationsDir);\n\n if (!flags.json && !flags.quiet) {\n const details: Array<{ label: string; value: string }> = [\n { label: 'config', value: configPath },\n { label: 'migrations', value: migrationsRelative },\n ];\n if (target) {\n details.push({ label: 'target', value: target });\n }\n const header = formatStyledHeader({\n command: 'migration show',\n description: 'Display migration package contents',\n details,\n flags,\n });\n ui.stderr(header);\n }\n\n let pkg: MigrationBundle;\n\n try {\n if (target && looksLikePath(target)) {\n pkg = await readMigrationPackage(resolve(target));\n } else {\n const allPackages = await readMigrationsDir(migrationsDir);\n if (allPackages.length === 0) {\n return notOk(\n errorRuntime('No migrations found', {\n why: `No migration packages found in ${migrationsRelative}`,\n fix: 'Run `prisma-next migration plan` to create a migration first.',\n }),\n );\n }\n\n if (target) {\n const resolved = resolveByHashPrefix(allPackages, target);\n if (!resolved.ok) return resolved;\n pkg = resolved.value;\n } else {\n const attested = allPackages.filter(isAttested);\n if (attested.length === 0) {\n return notOk(\n errorRuntime('No attested migrations found', {\n why: `All migrations in ${migrationsRelative} are drafts (migrationId: null)`,\n fix: 'Run `prisma-next migration emit --dir <path>` to attest a draft migration.',\n }),\n );\n }\n const graph = reconstructGraph(attested);\n const latestMigration = findLatestMigration(graph);\n if (!latestMigration) {\n return notOk(\n errorRuntime('Could not resolve latest migration', {\n why: 'No latest migration found in the migration history',\n fix: 'The migrations directory may be corrupted. Inspect the migration.json files.',\n }),\n );\n }\n const leafPkg = attested.find(\n (p) => p.manifest.migrationId === latestMigration.migrationId,\n );\n if (!leafPkg) {\n return notOk(\n errorRuntime('Could not resolve latest migration', {\n why: `Latest migration ${latestMigration.dirName} does not match any package`,\n fix: 'The migrations directory may be corrupted. Inspect the migration.json files.',\n }),\n );\n }\n pkg = leafPkg;\n }\n }\n } catch (error) {\n if (MigrationToolsError.is(error)) {\n return notOk(\n errorRuntime(error.message, {\n why: error.why,\n fix: error.fix,\n meta: { code: error.code, ...(error.details ?? {}) },\n }),\n );\n }\n return notOk(\n errorUnexpected(error instanceof Error ? error.message : String(error), {\n why: `Failed to read migration: ${error instanceof Error ? error.message : String(error)}`,\n }),\n );\n }\n\n const ops = pkg.ops as readonly MigrationPlanOperation[];\n const sql = extractOperationStatements(config.family.familyId, ops) ?? [];\n\n const result: MigrationShowResult = {\n ok: true,\n dirName: pkg.dirName,\n dirPath: relative(process.cwd(), pkg.dirPath),\n from: pkg.manifest.from,\n to: pkg.manifest.to,\n migrationId: pkg.manifest.migrationId,\n kind: pkg.manifest.kind,\n createdAt: pkg.manifest.createdAt,\n operations: ops.map((op) => ({\n id: op.id,\n label: op.label,\n operationClass: op.operationClass,\n })),\n sql,\n summary: `${ops.length} operation(s)`,\n };\n return ok(result);\n}\n\nexport function createMigrationShowCommand(): Command {\n const command = new Command('show');\n setCommandDescriptions(\n command,\n 'Display migration package contents',\n 'Shows the operations, DDL preview, and metadata for a migration package.\\n' +\n 'Accepts a directory path, a hash prefix (git-style), or defaults to the\\n' +\n 'latest migration.',\n );\n setCommandExamples(command, [\n 'prisma-next migration show',\n 'prisma-next migration show sha256:a1b2c3',\n ]);\n addGlobalOptions(command)\n .argument(\n '[target]',\n 'Migration directory path or migrationId hash prefix (defaults to latest)',\n )\n .option('--config <path>', 'Path to prisma-next.config.ts')\n .action(async (target: string | undefined, options: MigrationShowOptions) => {\n const flags = parseGlobalFlags(options);\n\n const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });\n\n const result = await executeMigrationShowCommand(target, options, flags, ui);\n\n const exitCode = handleResult(result, flags, ui, (showResult) => {\n if (flags.json) {\n ui.output(JSON.stringify(showResult, null, 2));\n } else if (!flags.quiet) {\n ui.log(formatMigrationShowOutput(showResult, flags));\n }\n });\n\n process.exit(exitCode);\n });\n\n return command;\n}\n"],"mappings":"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"}
|
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
import "../config-loader-C4VXKl8f.mjs";
|
|
2
|
-
import "../
|
|
3
|
-
import "../framework-components-BAsliT4V.mjs";
|
|
4
|
-
import "../client-tdnbk0OR.mjs";
|
|
5
|
-
import "../result-handler-oK_vA-Fn.mjs";
|
|
6
|
-
import "../terminal-ui-C5k88MmW.mjs";
|
|
7
|
-
import { n as deriveEdgeStatuses, t as createMigrationStatusCommand } from "../migration-status-DyVDf5NI.mjs";
|
|
2
|
+
import { n as deriveEdgeStatuses, t as createMigrationStatusCommand } from "../migration-status-BwKCQB_a.mjs";
|
|
8
3
|
|
|
9
4
|
export { createMigrationStatusCommand, deriveEdgeStatuses };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as loadConfig } from "./config-loader-C4VXKl8f.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { t as createControlClient } from "./client-
|
|
4
|
-
import { _ as formatStyledHeader, d as setCommandExamples, m as parseGlobalFlags, n as addGlobalOptions, t as handleResult, u as setCommandDescriptions, v as formatSuccessMessage, x as isVerbose } from "./result-handler-
|
|
2
|
+
import { a as errorContractValidationFailed, h as errorRuntime, t as CliStructuredError, v as errorUnexpected$1 } from "./cli-errors-BUuJr6py.mjs";
|
|
3
|
+
import { t as createControlClient } from "./client-DUs1DH-1.mjs";
|
|
4
|
+
import { _ as formatStyledHeader, d as setCommandExamples, m as parseGlobalFlags, n as addGlobalOptions, t as handleResult, u as setCommandDescriptions, v as formatSuccessMessage, x as isVerbose } from "./result-handler-AFK4hxyX.mjs";
|
|
5
5
|
import { t as createProgressAdapter } from "./progress-adapter-B-YvmcDu.mjs";
|
|
6
6
|
import { t as TerminalUI } from "./terminal-ui-C5k88MmW.mjs";
|
|
7
7
|
import { Command } from "commander";
|
|
@@ -142,7 +142,7 @@ async function executeContractEmitCommand(options, flags, ui, startTime) {
|
|
|
142
142
|
mkdirSync(dirname(outputDtsPath), { recursive: true });
|
|
143
143
|
writeFileSync(outputJsonPath, result.value.contractJson, "utf-8");
|
|
144
144
|
writeFileSync(outputDtsPath, result.value.contractDts, "utf-8");
|
|
145
|
-
const { validateContractDeps } = await import("./validate-contract-deps-
|
|
145
|
+
const { validateContractDeps } = await import("./validate-contract-deps-DBH6iTAD.mjs");
|
|
146
146
|
const depsValidation = validateContractDeps(result.value.contractDts, dirname(outputDtsPath));
|
|
147
147
|
if (depsValidation.warning) ui.warn(depsValidation.warning);
|
|
148
148
|
return ok({
|
|
@@ -188,4 +188,4 @@ function createContractEmitCommand() {
|
|
|
188
188
|
|
|
189
189
|
//#endregion
|
|
190
190
|
export { createContractEmitCommand as t };
|
|
191
|
-
//# sourceMappingURL=contract-emit-
|
|
191
|
+
//# sourceMappingURL=contract-emit-7jqH6dq9.mjs.map
|