@prisma-next/cli 0.3.0-dev.6 → 0.3.0-dev.64
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/LICENSE +201 -0
- package/README.md +314 -80
- package/dist/cli-errors-JlPTsazx.mjs +3 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.js +1 -2376
- package/dist/cli.mjs +203 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/client-PimzSD1f.mjs +981 -0
- package/dist/client-PimzSD1f.mjs.map +1 -0
- package/dist/commands/contract-emit.d.mts +7 -0
- package/dist/commands/contract-emit.d.mts.map +1 -0
- package/dist/commands/contract-emit.mjs +151 -0
- package/dist/commands/contract-emit.mjs.map +1 -0
- package/dist/commands/db-init.d.mts +7 -0
- package/dist/commands/db-init.d.mts.map +1 -0
- package/dist/commands/db-init.mjs +134 -0
- package/dist/commands/db-init.mjs.map +1 -0
- package/dist/commands/db-introspect.d.mts +7 -0
- package/dist/commands/db-introspect.d.mts.map +1 -0
- package/dist/commands/db-introspect.mjs +118 -0
- package/dist/commands/db-introspect.mjs.map +1 -0
- package/dist/commands/db-schema-verify.d.mts +7 -0
- package/dist/commands/db-schema-verify.d.mts.map +1 -0
- package/dist/commands/db-schema-verify.mjs +120 -0
- package/dist/commands/db-schema-verify.mjs.map +1 -0
- package/dist/commands/db-sign.d.mts +7 -0
- package/dist/commands/db-sign.d.mts.map +1 -0
- package/dist/commands/db-sign.mjs +142 -0
- package/dist/commands/db-sign.mjs.map +1 -0
- package/dist/commands/db-update.d.mts +7 -0
- package/dist/commands/db-update.d.mts.map +1 -0
- package/dist/commands/db-update.mjs +123 -0
- package/dist/commands/db-update.mjs.map +1 -0
- package/dist/commands/db-verify.d.mts +7 -0
- package/dist/commands/db-verify.d.mts.map +1 -0
- package/dist/commands/db-verify.mjs +133 -0
- package/dist/commands/db-verify.mjs.map +1 -0
- package/dist/commands/migration-apply.d.mts +23 -0
- package/dist/commands/migration-apply.d.mts.map +1 -0
- package/dist/commands/migration-apply.mjs +250 -0
- package/dist/commands/migration-apply.mjs.map +1 -0
- package/dist/commands/migration-plan.d.mts +25 -0
- package/dist/commands/migration-plan.d.mts.map +1 -0
- package/dist/commands/migration-plan.mjs +266 -0
- package/dist/commands/migration-plan.mjs.map +1 -0
- package/dist/commands/migration-show.d.mts +28 -0
- package/dist/commands/migration-show.d.mts.map +1 -0
- package/dist/commands/migration-show.mjs +138 -0
- package/dist/commands/migration-show.mjs.map +1 -0
- package/dist/commands/migration-status.d.mts +35 -0
- package/dist/commands/migration-status.d.mts.map +1 -0
- package/dist/commands/migration-status.mjs +260 -0
- package/dist/commands/migration-status.mjs.map +1 -0
- package/dist/commands/migration-verify.d.mts +16 -0
- package/dist/commands/migration-verify.d.mts.map +1 -0
- package/dist/commands/migration-verify.mjs +86 -0
- package/dist/commands/migration-verify.mjs.map +1 -0
- package/dist/config-loader-PPf4CtDj.mjs +43 -0
- package/dist/config-loader-PPf4CtDj.mjs.map +1 -0
- package/dist/{config-loader.d.ts → config-loader.d.mts} +8 -3
- package/dist/config-loader.d.mts.map +1 -0
- package/dist/config-loader.mjs +3 -0
- package/dist/exports/config-types.d.mts +2 -0
- package/dist/exports/config-types.mjs +3 -0
- package/dist/exports/control-api.d.mts +621 -0
- package/dist/exports/control-api.d.mts.map +1 -0
- package/dist/exports/control-api.mjs +98 -0
- package/dist/exports/control-api.mjs.map +1 -0
- package/dist/{load-ts-contract.d.ts → exports/index.d.mts} +10 -5
- package/dist/exports/index.d.mts.map +1 -0
- package/dist/exports/index.mjs +135 -0
- package/dist/exports/index.mjs.map +1 -0
- package/dist/extract-sql-ddl-BmlKvk4o.mjs +26 -0
- package/dist/extract-sql-ddl-BmlKvk4o.mjs.map +1 -0
- package/dist/framework-components-CjV_jD8f.mjs +59 -0
- package/dist/framework-components-CjV_jD8f.mjs.map +1 -0
- package/dist/migration-command-scaffold-DfY_F3ev.mjs +97 -0
- package/dist/migration-command-scaffold-DfY_F3ev.mjs.map +1 -0
- package/dist/progress-adapter-DENrzF6I.mjs +49 -0
- package/dist/progress-adapter-DENrzF6I.mjs.map +1 -0
- package/dist/result-handler-iA9JtUC7.mjs +1186 -0
- package/dist/result-handler-iA9JtUC7.mjs.map +1 -0
- package/package.json +75 -38
- package/src/cli.ts +43 -0
- package/src/commands/contract-emit.ts +221 -111
- package/src/commands/db-init.ts +217 -426
- package/src/commands/db-introspect.ts +148 -185
- package/src/commands/db-schema-verify.ts +162 -149
- package/src/commands/db-sign.ts +215 -202
- package/src/commands/db-update.ts +220 -0
- package/src/commands/db-verify.ts +193 -156
- package/src/commands/migration-apply.ts +431 -0
- package/src/commands/migration-plan.ts +446 -0
- package/src/commands/migration-show.ts +255 -0
- package/src/commands/migration-status.ts +436 -0
- package/src/commands/migration-verify.ts +151 -0
- package/src/config-loader.ts +13 -3
- package/src/control-api/client.ts +605 -0
- package/src/control-api/errors.ts +9 -0
- package/src/control-api/operations/contract-emit.ts +161 -0
- package/src/control-api/operations/db-init.ts +286 -0
- package/src/control-api/operations/db-update.ts +221 -0
- package/src/control-api/operations/extract-sql-ddl.ts +47 -0
- package/src/control-api/operations/migration-apply.ts +195 -0
- package/src/control-api/operations/migration-helpers.ts +49 -0
- package/src/control-api/types.ts +687 -0
- package/src/exports/config-types.ts +3 -3
- package/src/exports/control-api.ts +53 -0
- package/src/load-ts-contract.ts +16 -11
- package/src/utils/cli-errors.ts +3 -1
- package/src/utils/command-helpers.ts +92 -3
- package/src/utils/framework-components.ts +11 -30
- package/src/utils/migration-command-scaffold.ts +190 -0
- package/src/utils/output.ts +363 -25
- package/src/utils/progress-adapter.ts +86 -0
- package/dist/chunk-464LNZCE.js +0 -134
- package/dist/chunk-464LNZCE.js.map +0 -1
- package/dist/chunk-BZMBKEEQ.js +0 -997
- package/dist/chunk-BZMBKEEQ.js.map +0 -1
- package/dist/chunk-HWYQOCAJ.js +0 -47
- package/dist/chunk-HWYQOCAJ.js.map +0 -1
- package/dist/chunk-ZKYEJROM.js +0 -94
- package/dist/chunk-ZKYEJROM.js.map +0 -1
- package/dist/cli.d.ts +0 -2
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/commands/contract-emit.d.ts +0 -3
- package/dist/commands/contract-emit.d.ts.map +0 -1
- package/dist/commands/contract-emit.js +0 -9
- package/dist/commands/contract-emit.js.map +0 -1
- package/dist/commands/db-init.d.ts +0 -3
- package/dist/commands/db-init.d.ts.map +0 -1
- package/dist/commands/db-init.js +0 -341
- package/dist/commands/db-init.js.map +0 -1
- package/dist/commands/db-introspect.d.ts +0 -3
- package/dist/commands/db-introspect.d.ts.map +0 -1
- package/dist/commands/db-introspect.js +0 -190
- package/dist/commands/db-introspect.js.map +0 -1
- package/dist/commands/db-schema-verify.d.ts +0 -3
- package/dist/commands/db-schema-verify.d.ts.map +0 -1
- package/dist/commands/db-schema-verify.js +0 -164
- package/dist/commands/db-schema-verify.js.map +0 -1
- package/dist/commands/db-sign.d.ts +0 -3
- package/dist/commands/db-sign.d.ts.map +0 -1
- package/dist/commands/db-sign.js +0 -199
- package/dist/commands/db-sign.js.map +0 -1
- package/dist/commands/db-verify.d.ts +0 -3
- package/dist/commands/db-verify.d.ts.map +0 -1
- package/dist/commands/db-verify.js +0 -173
- package/dist/commands/db-verify.js.map +0 -1
- package/dist/config-loader.d.ts.map +0 -1
- package/dist/config-loader.js +0 -7
- package/dist/config-loader.js.map +0 -1
- package/dist/exports/config-types.d.ts +0 -3
- package/dist/exports/config-types.d.ts.map +0 -1
- package/dist/exports/config-types.js +0 -6
- package/dist/exports/config-types.js.map +0 -1
- package/dist/exports/index.d.ts +0 -4
- package/dist/exports/index.d.ts.map +0 -1
- package/dist/exports/index.js +0 -175
- package/dist/exports/index.js.map +0 -1
- package/dist/load-ts-contract.d.ts.map +0 -1
- package/dist/utils/action.d.ts +0 -16
- package/dist/utils/action.d.ts.map +0 -1
- package/dist/utils/cli-errors.d.ts +0 -7
- package/dist/utils/cli-errors.d.ts.map +0 -1
- package/dist/utils/command-helpers.d.ts +0 -12
- package/dist/utils/command-helpers.d.ts.map +0 -1
- package/dist/utils/framework-components.d.ts +0 -81
- package/dist/utils/framework-components.d.ts.map +0 -1
- package/dist/utils/global-flags.d.ts +0 -25
- package/dist/utils/global-flags.d.ts.map +0 -1
- package/dist/utils/output.d.ts +0 -142
- package/dist/utils/output.d.ts.map +0 -1
- package/dist/utils/result-handler.d.ts +0 -15
- package/dist/utils/result-handler.d.ts.map +0 -1
- package/dist/utils/spinner.d.ts +0 -29
- package/dist/utils/spinner.d.ts.map +0 -1
- package/src/utils/action.ts +0 -43
- package/src/utils/spinner.ts +0 -67
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { t as loadConfig } from "../config-loader-PPf4CtDj.mjs";
|
|
2
|
+
import { _ as errorUnexpected, c as errorFileNotFound, f as errorMigrationPlanningFailed, h as errorTargetMigrationNotSupported, i as errorContractValidationFailed, m as errorRuntime } from "../cli-errors-JlPTsazx.mjs";
|
|
3
|
+
import { t as assertFrameworkComponentsCompatible } from "../framework-components-CjV_jD8f.mjs";
|
|
4
|
+
import { t as extractSqlDdl } from "../extract-sql-ddl-BmlKvk4o.mjs";
|
|
5
|
+
import { C as parseGlobalFlags, D as setCommandDescriptions, T as resolveContractPath, n as formatCommandHelp, t as handleResult, y as formatStyledHeader } from "../result-handler-iA9JtUC7.mjs";
|
|
6
|
+
import { Command } from "commander";
|
|
7
|
+
import { notOk, ok } from "@prisma-next/utils/result";
|
|
8
|
+
import { join, relative, resolve } from "pathe";
|
|
9
|
+
import { createControlPlaneStack } from "@prisma-next/core-control-plane/stack";
|
|
10
|
+
import { EMPTY_CONTRACT_HASH } from "@prisma-next/core-control-plane/constants";
|
|
11
|
+
import { readFile } from "node:fs/promises";
|
|
12
|
+
import { findLatestMigration, reconstructGraph } from "@prisma-next/migration-tools/dag";
|
|
13
|
+
import { formatMigrationDirName, readMigrationsDir, writeMigrationPackage } from "@prisma-next/migration-tools/io";
|
|
14
|
+
import { MigrationToolsError } from "@prisma-next/migration-tools/types";
|
|
15
|
+
import { attestMigration } from "@prisma-next/migration-tools/attestation";
|
|
16
|
+
|
|
17
|
+
//#region src/commands/migration-plan.ts
|
|
18
|
+
function mapMigrationToolsError(error) {
|
|
19
|
+
if (MigrationToolsError.is(error)) return errorRuntime(error.message, {
|
|
20
|
+
why: error.why,
|
|
21
|
+
fix: error.fix,
|
|
22
|
+
meta: {
|
|
23
|
+
code: error.code,
|
|
24
|
+
...error.details ?? {}
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
return errorUnexpected(error instanceof Error ? error.message : String(error), { why: `Unexpected error during migration plan: ${error instanceof Error ? error.message : String(error)}` });
|
|
28
|
+
}
|
|
29
|
+
async function executeMigrationPlanCommand(options, flags, startTime) {
|
|
30
|
+
const config = await loadConfig(options.config);
|
|
31
|
+
const configPath = options.config ? relative(process.cwd(), resolve(options.config)) : "prisma-next.config.ts";
|
|
32
|
+
const migrationsDir = resolve(options.config ? resolve(options.config, "..") : process.cwd(), config.migrations?.dir ?? "migrations");
|
|
33
|
+
const migrationsRelative = relative(process.cwd(), migrationsDir);
|
|
34
|
+
const contractPathAbsolute = resolveContractPath(config);
|
|
35
|
+
const contractPath = relative(process.cwd(), contractPathAbsolute);
|
|
36
|
+
if (flags.json !== "object" && !flags.quiet) {
|
|
37
|
+
const details = [
|
|
38
|
+
{
|
|
39
|
+
label: "config",
|
|
40
|
+
value: configPath
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
label: "contract",
|
|
44
|
+
value: contractPath
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
label: "migrations",
|
|
48
|
+
value: migrationsRelative
|
|
49
|
+
}
|
|
50
|
+
];
|
|
51
|
+
if (options.from) details.push({
|
|
52
|
+
label: "from",
|
|
53
|
+
value: options.from
|
|
54
|
+
});
|
|
55
|
+
if (options.name) details.push({
|
|
56
|
+
label: "name",
|
|
57
|
+
value: options.name
|
|
58
|
+
});
|
|
59
|
+
const header = formatStyledHeader({
|
|
60
|
+
command: "migration plan",
|
|
61
|
+
description: "Plan a migration from contract changes",
|
|
62
|
+
url: "https://pris.ly/migration-plan",
|
|
63
|
+
details,
|
|
64
|
+
flags
|
|
65
|
+
});
|
|
66
|
+
console.log(header);
|
|
67
|
+
}
|
|
68
|
+
let contractJsonContent;
|
|
69
|
+
try {
|
|
70
|
+
contractJsonContent = await readFile(contractPathAbsolute, "utf-8");
|
|
71
|
+
} catch (error) {
|
|
72
|
+
if (error instanceof Error && error.code === "ENOENT") return notOk(errorFileNotFound(contractPathAbsolute, {
|
|
73
|
+
why: `Contract file not found at ${contractPathAbsolute}`,
|
|
74
|
+
fix: `Run \`prisma-next contract emit\` to generate ${contractPath}, or update \`config.contract.output\` in ${configPath}`
|
|
75
|
+
}));
|
|
76
|
+
return notOk(errorUnexpected(error instanceof Error ? error.message : String(error), { why: `Failed to read contract file: ${error instanceof Error ? error.message : String(error)}` }));
|
|
77
|
+
}
|
|
78
|
+
let toContractJson;
|
|
79
|
+
try {
|
|
80
|
+
toContractJson = JSON.parse(contractJsonContent);
|
|
81
|
+
} catch (error) {
|
|
82
|
+
return notOk(errorContractValidationFailed(`Contract JSON is invalid: ${error instanceof Error ? error.message : String(error)}`, { where: { path: contractPathAbsolute } }));
|
|
83
|
+
}
|
|
84
|
+
const toStorageHash = toContractJson["storageHash"];
|
|
85
|
+
if (!toStorageHash) return notOk(errorContractValidationFailed("Contract is missing storageHash", { where: { path: contractPathAbsolute } }));
|
|
86
|
+
let fromContract = null;
|
|
87
|
+
let fromHash = EMPTY_CONTRACT_HASH;
|
|
88
|
+
let parentMigrationId = null;
|
|
89
|
+
try {
|
|
90
|
+
const packages = (await readMigrationsDir(migrationsDir)).filter((p) => typeof p.manifest.migrationId === "string");
|
|
91
|
+
const latestMigration = findLatestMigration(reconstructGraph(packages));
|
|
92
|
+
const leafHash = latestMigration ? latestMigration.to : EMPTY_CONTRACT_HASH;
|
|
93
|
+
if (options.from) {
|
|
94
|
+
fromHash = options.from;
|
|
95
|
+
const sourcePkg = packages.find((p) => p.manifest.to === fromHash);
|
|
96
|
+
if (!sourcePkg) return notOk(errorRuntime("Starting contract not found", {
|
|
97
|
+
why: `No migration with to="${fromHash}" exists in ${migrationsRelative}`,
|
|
98
|
+
fix: "Check that the --from hash matches a known migration target hash, or omit --from to use the latest migration leaf."
|
|
99
|
+
}));
|
|
100
|
+
fromContract = sourcePkg.manifest.toContract;
|
|
101
|
+
parentMigrationId = sourcePkg.manifest.migrationId;
|
|
102
|
+
} else if (leafHash !== EMPTY_CONTRACT_HASH && latestMigration) {
|
|
103
|
+
fromHash = leafHash;
|
|
104
|
+
parentMigrationId = latestMigration.migrationId;
|
|
105
|
+
const leafPkg = packages.find((p) => p.manifest.migrationId === latestMigration.migrationId);
|
|
106
|
+
if (leafPkg) fromContract = leafPkg.manifest.toContract;
|
|
107
|
+
}
|
|
108
|
+
} catch (error) {
|
|
109
|
+
if (MigrationToolsError.is(error)) return notOk(mapMigrationToolsError(error));
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
if (fromHash === toStorageHash) return ok({
|
|
113
|
+
ok: true,
|
|
114
|
+
noOp: true,
|
|
115
|
+
from: fromHash,
|
|
116
|
+
to: toStorageHash,
|
|
117
|
+
operations: [],
|
|
118
|
+
summary: "No changes detected between contracts",
|
|
119
|
+
timings: { total: Date.now() - startTime }
|
|
120
|
+
});
|
|
121
|
+
const targetWithMigrations = config.target;
|
|
122
|
+
if (!targetWithMigrations.migrations) return notOk(errorTargetMigrationNotSupported({ why: `Target "${config.target.id}" does not support migrations` }));
|
|
123
|
+
const { migrations } = targetWithMigrations;
|
|
124
|
+
const stack = createControlPlaneStack({
|
|
125
|
+
target: config.target,
|
|
126
|
+
adapter: config.adapter,
|
|
127
|
+
extensionPacks: config.extensionPacks ?? []
|
|
128
|
+
});
|
|
129
|
+
const familyInstance = config.family.create(stack);
|
|
130
|
+
const frameworkComponents = assertFrameworkComponentsCompatible(config.family.familyId, config.target.targetId, [
|
|
131
|
+
config.target,
|
|
132
|
+
config.adapter,
|
|
133
|
+
...config.extensionPacks ?? []
|
|
134
|
+
]);
|
|
135
|
+
const planner = migrations.createPlanner(familyInstance);
|
|
136
|
+
const fromSchemaIR = migrations.contractToSchema(fromContract);
|
|
137
|
+
const plannerResult = planner.plan({
|
|
138
|
+
contract: toContractJson,
|
|
139
|
+
schema: fromSchemaIR,
|
|
140
|
+
policy: { allowedOperationClasses: [
|
|
141
|
+
"additive",
|
|
142
|
+
"widening",
|
|
143
|
+
"destructive"
|
|
144
|
+
] },
|
|
145
|
+
frameworkComponents
|
|
146
|
+
});
|
|
147
|
+
if (plannerResult.kind === "failure") return notOk(errorMigrationPlanningFailed({ conflicts: plannerResult.conflicts }));
|
|
148
|
+
const ops = plannerResult.plan.operations;
|
|
149
|
+
if (ops.length === 0) return notOk(errorMigrationPlanningFailed({ conflicts: [{
|
|
150
|
+
kind: "unsupportedChange",
|
|
151
|
+
summary: "Contract changed but planner produced no operations. This indicates unsupported or ignored changes (e.g. removals, type changes, or a planner/contract mismatch)."
|
|
152
|
+
}] }));
|
|
153
|
+
const timestamp = /* @__PURE__ */ new Date();
|
|
154
|
+
const packageDir = join(migrationsDir, formatMigrationDirName(timestamp, options.name ?? "migration"));
|
|
155
|
+
const manifest = {
|
|
156
|
+
from: fromHash,
|
|
157
|
+
to: toStorageHash,
|
|
158
|
+
migrationId: null,
|
|
159
|
+
parentMigrationId,
|
|
160
|
+
kind: "regular",
|
|
161
|
+
fromContract,
|
|
162
|
+
toContract: toContractJson,
|
|
163
|
+
hints: {
|
|
164
|
+
used: [],
|
|
165
|
+
applied: [],
|
|
166
|
+
plannerVersion: "1.0.0",
|
|
167
|
+
planningStrategy: "diff"
|
|
168
|
+
},
|
|
169
|
+
labels: [],
|
|
170
|
+
createdAt: timestamp.toISOString()
|
|
171
|
+
};
|
|
172
|
+
try {
|
|
173
|
+
await writeMigrationPackage(packageDir, manifest, ops);
|
|
174
|
+
const migrationId = await attestMigration(packageDir);
|
|
175
|
+
const sql = extractSqlDdl(ops);
|
|
176
|
+
return ok({
|
|
177
|
+
ok: true,
|
|
178
|
+
noOp: false,
|
|
179
|
+
from: fromHash,
|
|
180
|
+
to: toStorageHash,
|
|
181
|
+
migrationId,
|
|
182
|
+
dir: relative(process.cwd(), packageDir),
|
|
183
|
+
operations: ops.map((op) => ({
|
|
184
|
+
id: op.id,
|
|
185
|
+
label: op.label,
|
|
186
|
+
operationClass: op.operationClass
|
|
187
|
+
})),
|
|
188
|
+
sql,
|
|
189
|
+
summary: `Planned ${ops.length} operation(s)`,
|
|
190
|
+
timings: { total: Date.now() - startTime }
|
|
191
|
+
});
|
|
192
|
+
} catch (error) {
|
|
193
|
+
return notOk(mapMigrationToolsError(error));
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function createMigrationPlanCommand() {
|
|
197
|
+
const command = new Command("plan");
|
|
198
|
+
setCommandDescriptions(command, "Plan a migration from contract changes", "Compares the emitted contract against the latest on-disk migration state and\nproduces a new migration package with the required operations. No database\nconnection is needed — this is a fully offline operation.");
|
|
199
|
+
command.configureHelp({ formatHelp: (cmd) => {
|
|
200
|
+
return formatCommandHelp({
|
|
201
|
+
command: cmd,
|
|
202
|
+
flags: parseGlobalFlags({})
|
|
203
|
+
});
|
|
204
|
+
} }).option("--config <path>", "Path to prisma-next.config.ts").option("--name <slug>", "Name slug for the migration directory", "migration").option("--from <hash>", "Explicit starting contract hash (overrides migration chain leaf)").option("--json [format]", "Output as JSON (object)", false).option("-q, --quiet", "Quiet mode: errors only").option("-v, --verbose", "Verbose output: debug info, timings").option("-vv, --trace", "Trace output: deep internals, stack traces").option("--timestamps", "Add timestamps to output").option("--color", "Force color output").option("--no-color", "Disable color output").action(async (options) => {
|
|
205
|
+
const flags = parseGlobalFlags(options);
|
|
206
|
+
const exitCode = handleResult(await executeMigrationPlanCommand(options, flags, Date.now()), flags, (planResult) => {
|
|
207
|
+
if (flags.json === "object") console.log(JSON.stringify(planResult, null, 2));
|
|
208
|
+
else if (!flags.quiet) console.log(formatMigrationPlanOutput(planResult, flags));
|
|
209
|
+
});
|
|
210
|
+
process.exit(exitCode);
|
|
211
|
+
});
|
|
212
|
+
return command;
|
|
213
|
+
}
|
|
214
|
+
function formatMigrationPlanOutput(result, flags) {
|
|
215
|
+
const lines = [];
|
|
216
|
+
const useColor = flags.color !== false;
|
|
217
|
+
const green_ = useColor ? (s) => `\x1b[32m${s}\x1b[0m` : (s) => s;
|
|
218
|
+
const yellow_ = useColor ? (s) => `\x1b[33m${s}\x1b[0m` : (s) => s;
|
|
219
|
+
const dim_ = useColor ? (s) => `\x1b[2m${s}\x1b[0m` : (s) => s;
|
|
220
|
+
if (result.noOp) {
|
|
221
|
+
lines.push(`${green_("✔")} No changes detected`);
|
|
222
|
+
lines.push(dim_(` from: ${result.from}`));
|
|
223
|
+
lines.push(dim_(` to: ${result.to}`));
|
|
224
|
+
return lines.join("\n");
|
|
225
|
+
}
|
|
226
|
+
lines.push(`${green_("✔")} ${result.summary}`);
|
|
227
|
+
lines.push("");
|
|
228
|
+
if (result.operations.length > 0) {
|
|
229
|
+
lines.push(dim_("│"));
|
|
230
|
+
for (let i = 0; i < result.operations.length; i++) {
|
|
231
|
+
const op = result.operations[i];
|
|
232
|
+
const treeChar = i === result.operations.length - 1 ? "└" : "├";
|
|
233
|
+
const opClassLabel = op.operationClass === "destructive" ? yellow_(`[${op.operationClass}]`) : dim_(`[${op.operationClass}]`);
|
|
234
|
+
lines.push(`${dim_(treeChar)}─ ${op.label} ${opClassLabel}`);
|
|
235
|
+
}
|
|
236
|
+
if (result.operations.some((op) => op.operationClass === "destructive")) {
|
|
237
|
+
lines.push("");
|
|
238
|
+
lines.push(`${yellow_("⚠")} This migration contains destructive operations that may cause data loss.`);
|
|
239
|
+
}
|
|
240
|
+
lines.push("");
|
|
241
|
+
}
|
|
242
|
+
lines.push(dim_(`from: ${result.from}`));
|
|
243
|
+
lines.push(dim_(`to: ${result.to}`));
|
|
244
|
+
if (result.migrationId) lines.push(dim_(`migrationId: ${result.migrationId}`));
|
|
245
|
+
if (result.dir) lines.push(dim_(`dir: ${result.dir}`));
|
|
246
|
+
if (result.sql && result.sql.length > 0) {
|
|
247
|
+
lines.push("");
|
|
248
|
+
lines.push(dim_("DDL preview"));
|
|
249
|
+
lines.push("");
|
|
250
|
+
for (const statement of result.sql) {
|
|
251
|
+
const trimmed = statement.trim();
|
|
252
|
+
if (!trimmed) continue;
|
|
253
|
+
const line = trimmed.endsWith(";") ? trimmed : `${trimmed};`;
|
|
254
|
+
lines.push(line);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (flags.verbose && result.timings) {
|
|
258
|
+
lines.push("");
|
|
259
|
+
lines.push(dim_(`Total time: ${result.timings.total}ms`));
|
|
260
|
+
}
|
|
261
|
+
return lines.join("\n");
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
//#endregion
|
|
265
|
+
export { createMigrationPlanCommand };
|
|
266
|
+
//# sourceMappingURL=migration-plan.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-plan.mjs","names":["details: Array<{ label: string; value: string }>","contractJsonContent: string","toContractJson: ContractIR","fromContract: ContractIR | null","fromHash: string","parentMigrationId: string | null","ops: readonly MigrationPlanOperation[]","manifest: MigrationManifest","lines: string[]"],"sources":["../../src/commands/migration-plan.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport type { ContractIR } from '@prisma-next/contract/ir';\nimport { EMPTY_CONTRACT_HASH } from '@prisma-next/core-control-plane/constants';\nimport { createControlPlaneStack } from '@prisma-next/core-control-plane/stack';\nimport type {\n ControlTargetDescriptor,\n MigrationPlanOperation,\n} from '@prisma-next/core-control-plane/types';\nimport { attestMigration } from '@prisma-next/migration-tools/attestation';\nimport { findLatestMigration, reconstructGraph } from '@prisma-next/migration-tools/dag';\nimport {\n formatMigrationDirName,\n readMigrationsDir,\n writeMigrationPackage,\n} from '@prisma-next/migration-tools/io';\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, resolve } from 'pathe';\nimport { loadConfig } from '../config-loader';\nimport { extractSqlDdl } from '../control-api/operations/extract-sql-ddl';\nimport {\n type CliErrorConflict,\n type CliStructuredError,\n errorContractValidationFailed,\n errorFileNotFound,\n errorMigrationPlanningFailed,\n errorRuntime,\n errorTargetMigrationNotSupported,\n errorUnexpected,\n} from '../utils/cli-errors';\nimport { resolveContractPath, setCommandDescriptions } from '../utils/command-helpers';\nimport { assertFrameworkComponentsCompatible } from '../utils/framework-components';\nimport { type GlobalFlags, parseGlobalFlags } from '../utils/global-flags';\nimport { formatCommandHelp, formatStyledHeader } from '../utils/output';\nimport { handleResult } from '../utils/result-handler';\n\ninterface MigrationPlanOptions {\n readonly config?: string;\n readonly name?: string;\n readonly from?: string;\n readonly json?: string | boolean;\n readonly quiet?: boolean;\n readonly q?: boolean;\n readonly verbose?: boolean;\n readonly v?: boolean;\n readonly vv?: boolean;\n readonly trace?: boolean;\n readonly timestamps?: boolean;\n readonly color?: boolean;\n readonly 'no-color'?: boolean;\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 (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 startTime: number,\n): Promise<Result<MigrationPlanResult, 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 const contractPathAbsolute = resolveContractPath(config);\n const contractPath = relative(process.cwd(), contractPathAbsolute);\n\n if (flags.json !== 'object' && !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 console.log(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: ContractIR;\n try {\n toContractJson = JSON.parse(contractJsonContent) as ContractIR;\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 toStorageHash = (toContractJson as unknown as Record<string, unknown>)['storageHash'] as\n | string\n | undefined;\n if (!toStorageHash) {\n return notOk(\n errorContractValidationFailed('Contract is missing storageHash', {\n where: { path: contractPathAbsolute },\n }),\n );\n }\n\n // Read existing migrations and determine \"from\" contract\n let fromContract: ContractIR | null = null;\n let fromHash: string = EMPTY_CONTRACT_HASH;\n let parentMigrationId: string | null = null;\n\n try {\n const allPackages = await readMigrationsDir(migrationsDir);\n const packages = allPackages.filter((p) => typeof p.manifest.migrationId === 'string');\n const graph = reconstructGraph(packages);\n const latestMigration = findLatestMigration(graph);\n const leafHash = latestMigration ? latestMigration.to : EMPTY_CONTRACT_HASH;\n\n if (options.from) {\n fromHash = options.from;\n const sourcePkg = packages.find((p) => p.manifest.to === fromHash);\n if (!sourcePkg) {\n return notOk(\n errorRuntime('Starting contract not found', {\n why: `No migration with to=\"${fromHash}\" exists in ${migrationsRelative}`,\n fix: 'Check that the --from hash matches a known migration target hash, or omit --from to use the latest migration leaf.',\n }),\n );\n }\n fromContract = sourcePkg.manifest.toContract;\n parentMigrationId = sourcePkg.manifest.migrationId;\n } else if (leafHash !== EMPTY_CONTRACT_HASH && latestMigration) {\n fromHash = leafHash;\n parentMigrationId = latestMigration.migrationId;\n const leafPkg = packages.find((p) => p.manifest.migrationId === latestMigration.migrationId);\n if (leafPkg) {\n fromContract = leafPkg.manifest.toContract;\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 targetWithMigrations = config.target as ControlTargetDescriptor<string, string>;\n if (!targetWithMigrations.migrations) {\n return notOk(\n errorTargetMigrationNotSupported({\n why: `Target \"${config.target.id}\" does not support migrations`,\n }),\n );\n }\n\n // Plan migration using the same planner as db init\n const { migrations } = targetWithMigrations;\n const stack = createControlPlaneStack({\n target: config.target,\n adapter: config.adapter,\n extensionPacks: config.extensionPacks ?? [],\n });\n const familyInstance = config.family.create(stack);\n const frameworkComponents = assertFrameworkComponentsCompatible(\n config.family.familyId,\n config.target.targetId,\n [config.target, config.adapter, ...(config.extensionPacks ?? [])],\n );\n const planner = migrations.createPlanner(familyInstance);\n const fromSchemaIR = migrations.contractToSchema(fromContract);\n const plannerResult = planner.plan({\n contract: toContractJson,\n schema: fromSchemaIR,\n policy: { allowedOperationClasses: ['additive', 'widening', 'destructive'] },\n frameworkComponents,\n });\n\n if (plannerResult.kind === 'failure') {\n return notOk(\n errorMigrationPlanningFailed({\n conflicts: plannerResult.conflicts as readonly CliErrorConflict[],\n }),\n );\n }\n\n const ops: readonly MigrationPlanOperation[] = plannerResult.plan.operations;\n\n if (ops.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 (e.g. removals, type changes, or a planner/contract mismatch).',\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 parentMigrationId,\n kind: 'regular',\n fromContract,\n toContract: toContractJson,\n hints: {\n used: [],\n applied: [],\n plannerVersion: '1.0.0',\n planningStrategy: 'diff',\n },\n labels: [],\n createdAt: timestamp.toISOString(),\n };\n\n try {\n await writeMigrationPackage(packageDir, manifest, ops);\n const migrationId = await attestMigration(packageDir);\n\n const sql = extractSqlDdl(ops);\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: ops.map((op) => ({\n id: op.id,\n label: op.label,\n operationClass: op.operationClass,\n })),\n sql,\n summary: `Planned ${ops.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\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 command\n .configureHelp({\n formatHelp: (cmd) => {\n const flags = parseGlobalFlags({});\n return formatCommandHelp({ command: cmd, flags });\n },\n })\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 migration chain leaf)')\n .option('--json [format]', 'Output as JSON (object)', false)\n .option('-q, --quiet', 'Quiet mode: errors only')\n .option('-v, --verbose', 'Verbose output: debug info, timings')\n .option('-vv, --trace', 'Trace output: deep internals, stack traces')\n .option('--timestamps', 'Add timestamps to output')\n .option('--color', 'Force color output')\n .option('--no-color', 'Disable color output')\n .action(async (options: MigrationPlanOptions) => {\n const flags = parseGlobalFlags(options);\n const startTime = Date.now();\n\n const result = await executeMigrationPlanCommand(options, flags, startTime);\n\n const exitCode = handleResult(result, flags, (planResult) => {\n if (flags.json === 'object') {\n console.log(JSON.stringify(planResult, null, 2));\n } else if (!flags.quiet) {\n console.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"],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { CliStructuredError } from "@prisma-next/core-control-plane/errors";
|
|
3
|
+
import { Result } from "@prisma-next/utils/result";
|
|
4
|
+
import { MigrationPackage } from "@prisma-next/migration-tools/types";
|
|
5
|
+
|
|
6
|
+
//#region src/commands/migration-show.d.ts
|
|
7
|
+
interface MigrationShowResult {
|
|
8
|
+
readonly ok: true;
|
|
9
|
+
readonly dirName: string;
|
|
10
|
+
readonly dirPath: string;
|
|
11
|
+
readonly from: string;
|
|
12
|
+
readonly to: string;
|
|
13
|
+
readonly migrationId: string | null;
|
|
14
|
+
readonly kind: string;
|
|
15
|
+
readonly createdAt: string;
|
|
16
|
+
readonly operations: readonly {
|
|
17
|
+
readonly id: string;
|
|
18
|
+
readonly label: string;
|
|
19
|
+
readonly operationClass: string;
|
|
20
|
+
}[];
|
|
21
|
+
readonly sql: readonly string[];
|
|
22
|
+
readonly summary: string;
|
|
23
|
+
}
|
|
24
|
+
declare function resolveByHashPrefix(packages: readonly MigrationPackage[], prefix: string): Result<MigrationPackage, CliStructuredError>;
|
|
25
|
+
declare function createMigrationShowCommand(): Command;
|
|
26
|
+
//#endregion
|
|
27
|
+
export { MigrationShowResult, createMigrationShowCommand, resolveByHashPrefix };
|
|
28
|
+
//# sourceMappingURL=migration-show.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-show.d.mts","names":[],"sources":["../../src/commands/migration-show.ts"],"sourcesContent":[],"mappings":";;;;;;UA8BiB,mBAAA;;EAAA,SAAA,OAAA,EAAA,MAAmB;EAsBpB,SAAA,OAAA,EAAA,MAAmB;EACd,SAAA,IAAA,EAAA,MAAA;EAEX,SAAA,EAAA,EAAA,MAAA;EAAkB,SAAA,WAAA,EAAA,MAAA,GAAA,IAAA;EAAzB,SAAA,IAAA,EAAA,MAAA;EAAM,SAAA,SAAA,EAAA,MAAA;EA0JO,SAAA,UAAA,EAAA,SAA0B;;;;;;;;iBA7J1B,mBAAA,oBACK,qCAElB,OAAO,kBAAkB;iBA0JZ,0BAAA,CAAA,GAA8B"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { t as loadConfig } from "../config-loader-PPf4CtDj.mjs";
|
|
2
|
+
import { _ as errorUnexpected, m as errorRuntime } from "../cli-errors-JlPTsazx.mjs";
|
|
3
|
+
import { t as extractSqlDdl } from "../extract-sql-ddl-BmlKvk4o.mjs";
|
|
4
|
+
import { C as parseGlobalFlags, D as setCommandDescriptions, d as formatMigrationShowOutput, n as formatCommandHelp, t as handleResult, y as formatStyledHeader } from "../result-handler-iA9JtUC7.mjs";
|
|
5
|
+
import { Command } from "commander";
|
|
6
|
+
import { notOk, ok } from "@prisma-next/utils/result";
|
|
7
|
+
import { relative, resolve } from "pathe";
|
|
8
|
+
import { findLatestMigration, reconstructGraph } from "@prisma-next/migration-tools/dag";
|
|
9
|
+
import { readMigrationPackage, readMigrationsDir } from "@prisma-next/migration-tools/io";
|
|
10
|
+
import { MigrationToolsError } from "@prisma-next/migration-tools/types";
|
|
11
|
+
|
|
12
|
+
//#region src/commands/migration-show.ts
|
|
13
|
+
function looksLikePath(target) {
|
|
14
|
+
return target.includes("/") || target.includes("\\");
|
|
15
|
+
}
|
|
16
|
+
function resolveByHashPrefix(packages, prefix) {
|
|
17
|
+
const normalizedPrefix = prefix.startsWith("sha256:") ? prefix : `sha256:${prefix}`;
|
|
18
|
+
const matches = packages.filter((p) => typeof p.manifest.migrationId === "string").filter((p) => p.manifest.migrationId.startsWith(normalizedPrefix));
|
|
19
|
+
if (matches.length === 1) return ok(matches[0]);
|
|
20
|
+
if (matches.length === 0) return notOk(errorRuntime("No migration found matching prefix", {
|
|
21
|
+
why: `No attested migration has a migrationId starting with "${normalizedPrefix}"`,
|
|
22
|
+
fix: "Run `prisma-next migration show` (no argument) to see the latest migration, or check the migrations directory for available packages."
|
|
23
|
+
}));
|
|
24
|
+
return notOk(errorRuntime("Ambiguous hash prefix", {
|
|
25
|
+
why: `Multiple migrations match prefix "${normalizedPrefix}":\n${matches.map((p) => ` ${p.dirName} ${p.manifest.migrationId}`).join("\n")}`,
|
|
26
|
+
fix: "Provide a longer prefix to uniquely identify the migration."
|
|
27
|
+
}));
|
|
28
|
+
}
|
|
29
|
+
async function executeMigrationShowCommand(target, options, flags) {
|
|
30
|
+
const config = await loadConfig(options.config);
|
|
31
|
+
const configPath = options.config ? relative(process.cwd(), resolve(options.config)) : "prisma-next.config.ts";
|
|
32
|
+
const migrationsDir = resolve(options.config ? resolve(options.config, "..") : process.cwd(), config.migrations?.dir ?? "migrations");
|
|
33
|
+
const migrationsRelative = relative(process.cwd(), migrationsDir);
|
|
34
|
+
if (flags.json !== "object" && !flags.quiet) {
|
|
35
|
+
const details = [{
|
|
36
|
+
label: "config",
|
|
37
|
+
value: configPath
|
|
38
|
+
}, {
|
|
39
|
+
label: "migrations",
|
|
40
|
+
value: migrationsRelative
|
|
41
|
+
}];
|
|
42
|
+
if (target) details.push({
|
|
43
|
+
label: "target",
|
|
44
|
+
value: target
|
|
45
|
+
});
|
|
46
|
+
const header = formatStyledHeader({
|
|
47
|
+
command: "migration show",
|
|
48
|
+
description: "Display migration package contents",
|
|
49
|
+
details,
|
|
50
|
+
flags
|
|
51
|
+
});
|
|
52
|
+
console.log(header);
|
|
53
|
+
}
|
|
54
|
+
let pkg;
|
|
55
|
+
try {
|
|
56
|
+
if (target && looksLikePath(target)) pkg = await readMigrationPackage(resolve(target));
|
|
57
|
+
else {
|
|
58
|
+
const allPackages = await readMigrationsDir(migrationsDir);
|
|
59
|
+
if (allPackages.length === 0) return notOk(errorRuntime("No migrations found", {
|
|
60
|
+
why: `No migration packages found in ${migrationsRelative}`,
|
|
61
|
+
fix: "Run `prisma-next migration plan` to create a migration first."
|
|
62
|
+
}));
|
|
63
|
+
if (target) {
|
|
64
|
+
const resolved = resolveByHashPrefix(allPackages, target);
|
|
65
|
+
if (!resolved.ok) return resolved;
|
|
66
|
+
pkg = resolved.value;
|
|
67
|
+
} else {
|
|
68
|
+
const attested = allPackages.filter((p) => typeof p.manifest.migrationId === "string");
|
|
69
|
+
if (attested.length === 0) return notOk(errorRuntime("No attested migrations found", {
|
|
70
|
+
why: `All migrations in ${migrationsRelative} are drafts (migrationId: null)`,
|
|
71
|
+
fix: "Run `prisma-next migration verify --dir <path>` to attest a draft migration."
|
|
72
|
+
}));
|
|
73
|
+
const latestMigration = findLatestMigration(reconstructGraph(attested));
|
|
74
|
+
if (!latestMigration) return notOk(errorRuntime("Could not resolve latest migration", {
|
|
75
|
+
why: "No latest migration found in the migration chain",
|
|
76
|
+
fix: "The migrations directory may be corrupted. Inspect the migration.json files."
|
|
77
|
+
}));
|
|
78
|
+
const leafPkg = attested.find((p) => p.manifest.migrationId === latestMigration.migrationId);
|
|
79
|
+
if (!leafPkg) return notOk(errorRuntime("Could not resolve latest migration", {
|
|
80
|
+
why: `Latest migration ${latestMigration.dirName} does not match any package`,
|
|
81
|
+
fix: "The migrations directory may be corrupted. Inspect the migration.json files."
|
|
82
|
+
}));
|
|
83
|
+
pkg = leafPkg;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
} catch (error) {
|
|
87
|
+
if (MigrationToolsError.is(error)) return notOk(errorRuntime(error.message, {
|
|
88
|
+
why: error.why,
|
|
89
|
+
fix: error.fix,
|
|
90
|
+
meta: {
|
|
91
|
+
code: error.code,
|
|
92
|
+
...error.details ?? {}
|
|
93
|
+
}
|
|
94
|
+
}));
|
|
95
|
+
return notOk(errorUnexpected(error instanceof Error ? error.message : String(error), { why: `Failed to read migration: ${error instanceof Error ? error.message : String(error)}` }));
|
|
96
|
+
}
|
|
97
|
+
const ops = pkg.ops;
|
|
98
|
+
const sql = extractSqlDdl(ops);
|
|
99
|
+
return ok({
|
|
100
|
+
ok: true,
|
|
101
|
+
dirName: pkg.dirName,
|
|
102
|
+
dirPath: relative(process.cwd(), pkg.dirPath),
|
|
103
|
+
from: pkg.manifest.from,
|
|
104
|
+
to: pkg.manifest.to,
|
|
105
|
+
migrationId: pkg.manifest.migrationId,
|
|
106
|
+
kind: pkg.manifest.kind,
|
|
107
|
+
createdAt: pkg.manifest.createdAt,
|
|
108
|
+
operations: ops.map((op) => ({
|
|
109
|
+
id: op.id,
|
|
110
|
+
label: op.label,
|
|
111
|
+
operationClass: op.operationClass
|
|
112
|
+
})),
|
|
113
|
+
sql,
|
|
114
|
+
summary: `${ops.length} operation(s)`
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
function createMigrationShowCommand() {
|
|
118
|
+
const command = new Command("show");
|
|
119
|
+
setCommandDescriptions(command, "Display migration package contents", "Shows the operations, DDL preview, and metadata for a migration package.\nAccepts a directory path, a hash prefix (git-style), or defaults to the\nlatest migration.");
|
|
120
|
+
command.configureHelp({ formatHelp: (cmd) => {
|
|
121
|
+
return formatCommandHelp({
|
|
122
|
+
command: cmd,
|
|
123
|
+
flags: parseGlobalFlags({})
|
|
124
|
+
});
|
|
125
|
+
} }).argument("[target]", "Migration directory path or migrationId hash prefix (defaults to latest)").option("--config <path>", "Path to prisma-next.config.ts").option("--json [format]", "Output as JSON (object)", false).option("-q, --quiet", "Quiet mode: errors only").option("-v, --verbose", "Verbose output").option("-vv, --trace", "Trace output").option("--timestamps", "Add timestamps to output").option("--color", "Force color output").option("--no-color", "Disable color output").action(async (target, options) => {
|
|
126
|
+
const flags = parseGlobalFlags(options);
|
|
127
|
+
const exitCode = handleResult(await executeMigrationShowCommand(target, options, flags), flags, (showResult) => {
|
|
128
|
+
if (flags.json === "object") console.log(JSON.stringify(showResult, null, 2));
|
|
129
|
+
else if (!flags.quiet) console.log(formatMigrationShowOutput(showResult, flags));
|
|
130
|
+
});
|
|
131
|
+
process.exit(exitCode);
|
|
132
|
+
});
|
|
133
|
+
return command;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
//#endregion
|
|
137
|
+
export { createMigrationShowCommand, resolveByHashPrefix };
|
|
138
|
+
//# sourceMappingURL=migration-show.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-show.mjs","names":["details: Array<{ label: string; value: string }>","pkg: MigrationPackage"],"sources":["../../src/commands/migration-show.ts"],"sourcesContent":["import type { MigrationPlanOperation } from '@prisma-next/core-control-plane/types';\nimport { findLatestMigration, reconstructGraph } from '@prisma-next/migration-tools/dag';\nimport { readMigrationPackage, readMigrationsDir } from '@prisma-next/migration-tools/io';\nimport type { MigrationPackage } from '@prisma-next/migration-tools/types';\nimport { 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 { extractSqlDdl } from '../control-api/operations/extract-sql-ddl';\nimport { type CliStructuredError, errorRuntime, errorUnexpected } from '../utils/cli-errors';\nimport { setCommandDescriptions } from '../utils/command-helpers';\nimport { type GlobalFlags, parseGlobalFlags } from '../utils/global-flags';\nimport { formatCommandHelp, formatMigrationShowOutput, formatStyledHeader } from '../utils/output';\nimport { handleResult } from '../utils/result-handler';\n\ninterface MigrationShowOptions {\n readonly config?: string;\n readonly json?: string | boolean;\n readonly quiet?: boolean;\n readonly q?: boolean;\n readonly verbose?: boolean;\n readonly v?: boolean;\n readonly vv?: boolean;\n readonly trace?: boolean;\n readonly timestamps?: boolean;\n readonly color?: boolean;\n readonly 'no-color'?: boolean;\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 MigrationPackage[],\n prefix: string,\n): Result<MigrationPackage, 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): 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 !== 'object' && !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 console.log(header);\n }\n\n let pkg: MigrationPackage;\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((p) => typeof p.manifest.migrationId === 'string');\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 chain',\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 = extractSqlDdl(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 command\n .configureHelp({\n formatHelp: (cmd) => {\n const defaultFlags = parseGlobalFlags({});\n return formatCommandHelp({ command: cmd, flags: defaultFlags });\n },\n })\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 .option('--json [format]', 'Output as JSON (object)', false)\n .option('-q, --quiet', 'Quiet mode: errors only')\n .option('-v, --verbose', 'Verbose output')\n .option('-vv, --trace', 'Trace output')\n .option('--timestamps', 'Add timestamps to output')\n .option('--color', 'Force color output')\n .option('--no-color', 'Disable color output')\n .action(async (target: string | undefined, options: MigrationShowOptions) => {\n const flags = parseGlobalFlags(options);\n\n const result = await executeMigrationShowCommand(target, options, flags);\n\n const exitCode = handleResult(result, flags, (showResult) => {\n if (flags.json === 'object') {\n console.log(JSON.stringify(showResult, null, 2));\n } else if (!flags.quiet) {\n console.log(formatMigrationShowOutput(showResult, flags));\n }\n });\n\n process.exit(exitCode);\n });\n\n return command;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { MigrationChainEntry, MigrationPackage } from "@prisma-next/migration-tools/types";
|
|
3
|
+
|
|
4
|
+
//#region src/commands/migration-status.d.ts
|
|
5
|
+
interface MigrationStatusEntry {
|
|
6
|
+
readonly dirName: string;
|
|
7
|
+
readonly from: string;
|
|
8
|
+
readonly to: string;
|
|
9
|
+
readonly migrationId: string | null;
|
|
10
|
+
readonly operationCount: number;
|
|
11
|
+
readonly operationSummary: string;
|
|
12
|
+
readonly hasDestructive: boolean;
|
|
13
|
+
readonly status: 'applied' | 'pending' | 'unknown';
|
|
14
|
+
}
|
|
15
|
+
interface StatusDiagnostic {
|
|
16
|
+
readonly code: string;
|
|
17
|
+
readonly severity: 'warn' | 'info';
|
|
18
|
+
readonly message: string;
|
|
19
|
+
readonly hints: readonly string[];
|
|
20
|
+
}
|
|
21
|
+
interface MigrationStatusResult {
|
|
22
|
+
readonly ok: true;
|
|
23
|
+
readonly mode: 'online' | 'offline';
|
|
24
|
+
readonly migrations: readonly MigrationStatusEntry[];
|
|
25
|
+
readonly markerHash?: string;
|
|
26
|
+
readonly leafHash: string;
|
|
27
|
+
readonly contractHash: string;
|
|
28
|
+
readonly summary: string;
|
|
29
|
+
readonly diagnostics: readonly StatusDiagnostic[];
|
|
30
|
+
}
|
|
31
|
+
declare function buildMigrationEntries(chain: readonly MigrationChainEntry[], packages: readonly MigrationPackage[], markerHash: string | undefined): MigrationStatusEntry[];
|
|
32
|
+
declare function createMigrationStatusCommand(): Command;
|
|
33
|
+
//#endregion
|
|
34
|
+
export { MigrationStatusEntry, MigrationStatusResult, StatusDiagnostic, buildMigrationEntries, createMigrationStatusCommand };
|
|
35
|
+
//# sourceMappingURL=migration-status.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-status.d.mts","names":[],"sources":["../../src/commands/migration-status.ts"],"sourcesContent":[],"mappings":";;;;UA6CiB,oBAAA;;EAAA,SAAA,IAAA,EAAA,MAAA;EAWA,SAAA,EAAA,EAAA,MAAgB;EAOhB,SAAA,WAAA,EAAA,MAAqB,GAAA,IAGN;EAqChB,SAAA,cAAA,EAAqB,MAAA;EACnB,SAAA,gBAAA,EAAA,MAAA;EACG,SAAA,cAAA,EAAA,OAAA;EAElB,SAAA,MAAA,EAAA,SAAA,GAAA,SAAA,GAAA,SAAA;;AA8Ra,UAjVC,gBAAA,CAiVD;;;;;;UA1UC,qBAAA;;;gCAGe;;;;;iCAKC;;iBAgCjB,qBAAA,iBACE,0CACG,qDAElB;iBA8Ra,4BAAA,CAAA,GAAgC"}
|