@prisma-next/cli 0.8.0 → 0.9.0-dev.2
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 -9
- package/dist/{cli-errors-D3_sMh2K.mjs → cli-errors-CF60g2cG.mjs} +40 -2
- package/dist/cli-errors-CF60g2cG.mjs.map +1 -0
- package/dist/cli.mjs +67 -19
- package/dist/cli.mjs.map +1 -1
- package/dist/{client-BCnP7cHo.mjs → client-Brv4qlfB.mjs} +28 -30
- package/dist/client-Brv4qlfB.mjs.map +1 -0
- package/dist/{command-helpers-BeZHkxV8.mjs → command-helpers-D3vL5yi8.mjs} +29 -6
- package/dist/command-helpers-D3vL5yi8.mjs.map +1 -0
- package/dist/commands/contract-emit.mjs +1 -1
- package/dist/commands/contract-infer.mjs +1 -1
- package/dist/commands/db-init.mjs +7 -7
- package/dist/commands/db-schema.mjs +5 -5
- package/dist/commands/db-sign.d.mts.map +1 -1
- package/dist/commands/db-sign.mjs +67 -25
- package/dist/commands/db-sign.mjs.map +1 -1
- package/dist/commands/db-update.d.mts.map +1 -1
- package/dist/commands/db-update.mjs +37 -9
- package/dist/commands/db-update.mjs.map +1 -1
- package/dist/commands/db-verify.d.mts.map +1 -1
- package/dist/commands/db-verify.mjs +1 -1
- package/dist/commands/migrate.d.mts +28 -0
- package/dist/commands/migrate.d.mts.map +1 -0
- package/dist/commands/{migration-apply.mjs → migrate.mjs} +65 -39
- package/dist/commands/migrate.mjs.map +1 -0
- package/dist/commands/migration-check.d.mts +18 -0
- package/dist/commands/migration-check.d.mts.map +1 -0
- package/dist/commands/migration-check.mjs +284 -0
- package/dist/commands/migration-check.mjs.map +1 -0
- package/dist/commands/migration-graph.d.mts +16 -0
- package/dist/commands/migration-graph.d.mts.map +1 -0
- package/dist/commands/migration-graph.mjs +141 -0
- package/dist/commands/migration-graph.mjs.map +1 -0
- package/dist/commands/migration-list.d.mts +20 -0
- package/dist/commands/migration-list.d.mts.map +1 -0
- package/dist/commands/migration-list.mjs +107 -0
- package/dist/commands/migration-list.mjs.map +1 -0
- package/dist/commands/migration-log.d.mts +21 -0
- package/dist/commands/migration-log.d.mts.map +1 -0
- package/dist/commands/migration-log.mjs +146 -0
- package/dist/commands/migration-log.mjs.map +1 -0
- package/dist/commands/migration-new.d.mts.map +1 -1
- package/dist/commands/migration-new.mjs +30 -29
- package/dist/commands/migration-new.mjs.map +1 -1
- package/dist/commands/migration-plan.d.mts +2 -2
- package/dist/commands/migration-plan.d.mts.map +1 -1
- package/dist/commands/migration-plan.mjs +1 -1
- package/dist/commands/migration-show.d.mts +1 -1
- package/dist/commands/migration-show.d.mts.map +1 -1
- package/dist/commands/migration-show.mjs +90 -52
- package/dist/commands/migration-show.mjs.map +1 -1
- package/dist/commands/migration-status.d.mts +5 -17
- package/dist/commands/migration-status.d.mts.map +1 -1
- package/dist/commands/migration-status.mjs +732 -1
- package/dist/commands/migration-status.mjs.map +1 -0
- package/dist/commands/ref.d.mts +34 -0
- package/dist/commands/ref.d.mts.map +1 -0
- package/dist/commands/{migration-ref.mjs → ref.mjs} +28 -57
- package/dist/commands/ref.mjs.map +1 -0
- package/dist/{contract-emit-9DBda5Ou.mjs → contract-emit-C3STUIBg.mjs} +6 -6
- package/dist/{contract-emit-9DBda5Ou.mjs.map → contract-emit-C3STUIBg.mjs.map} +1 -1
- package/dist/{contract-emit-B77TsJqf.mjs → contract-emit-iynA3BCA.mjs} +9 -5
- package/dist/contract-emit-iynA3BCA.mjs.map +1 -0
- package/dist/{contract-infer-ByxhPjpW.mjs → contract-infer-Cnj8G1E2.mjs} +5 -5
- package/dist/{contract-infer-ByxhPjpW.mjs.map → contract-infer-Cnj8G1E2.mjs.map} +1 -1
- package/dist/{contract-space-aggregate-loader-BrwKK6Q6.mjs → contract-space-aggregate-loader-pAc8CDfY.mjs} +4 -4
- package/dist/{contract-space-aggregate-loader-BrwKK6Q6.mjs.map → contract-space-aggregate-loader-pAc8CDfY.mjs.map} +1 -1
- package/dist/{db-verify-Czm5T-J4.mjs → db-verify-D7cyH_zz.mjs} +12 -9
- package/dist/db-verify-D7cyH_zz.mjs.map +1 -0
- package/dist/errors-Cw6kyTyV.mjs +56 -0
- package/dist/errors-Cw6kyTyV.mjs.map +1 -0
- package/dist/exports/control-api.d.mts +1 -1
- package/dist/exports/control-api.d.mts.map +1 -1
- package/dist/exports/control-api.mjs +2 -2
- package/dist/exports/index.mjs +1 -1
- package/dist/exports/index.mjs.map +1 -1
- package/dist/exports/init-output.mjs +1 -1
- package/dist/{framework-components-ChqVUxR-.mjs → framework-components-xFLFpZUO.mjs} +2 -2
- package/dist/{framework-components-ChqVUxR-.mjs.map → framework-components-xFLFpZUO.mjs.map} +1 -1
- package/dist/{global-flags-Icqpxk23.d.mts → global-flags-DGmw6Kqg.d.mts} +1 -1
- package/dist/{global-flags-Icqpxk23.d.mts.map → global-flags-DGmw6Kqg.d.mts.map} +1 -1
- package/dist/{migration-status-By9G5p2H.mjs → graph-render-eJDcLWny.mjs} +3 -692
- package/dist/graph-render-eJDcLWny.mjs.map +1 -0
- package/dist/{init-B-k3a1Qw.mjs → init-DEe-IimY.mjs} +133 -61
- package/dist/init-DEe-IimY.mjs.map +1 -0
- package/dist/{inspect-live-schema-DxdBd4Er.mjs → inspect-live-schema-CWLK_lgs.mjs} +4 -4
- package/dist/{inspect-live-schema-DxdBd4Er.mjs.map → inspect-live-schema-CWLK_lgs.mjs.map} +1 -1
- package/dist/migration-cli.mjs +1 -1
- package/dist/migration-cli.mjs.map +1 -1
- package/dist/{migration-command-scaffold-BdV8JYXV.mjs → migration-command-scaffold-CmXXC1UZ.mjs} +4 -4
- package/dist/{migration-command-scaffold-BdV8JYXV.mjs.map → migration-command-scaffold-CmXXC1UZ.mjs.map} +1 -1
- package/dist/{migration-plan-mRu5K81L.mjs → migration-plan-CHyUlBV0.mjs} +76 -37
- package/dist/migration-plan-CHyUlBV0.mjs.map +1 -0
- package/dist/migration-types-D2FW63pr.d.mts +15 -0
- package/dist/migration-types-D2FW63pr.d.mts.map +1 -0
- package/dist/{migrations-CTsyBXCA.mjs → migrations-DyUf5lTt.mjs} +2 -2
- package/dist/migrations-DyUf5lTt.mjs.map +1 -0
- package/dist/{output-BVj6a971.mjs → output-B60Gw5fu.mjs} +12 -11
- package/dist/{output-BVj6a971.mjs.map → output-B60Gw5fu.mjs.map} +1 -1
- package/dist/{result-handler-rmPVKIP2.mjs → result-handler-Bm_6dDYg.mjs} +2 -2
- package/dist/{result-handler-rmPVKIP2.mjs.map → result-handler-Bm_6dDYg.mjs.map} +1 -1
- package/dist/{terminal-ui-C_hFNbAn.mjs → terminal-ui-XtOQsqe9.mjs} +2 -54
- package/dist/terminal-ui-XtOQsqe9.mjs.map +1 -0
- package/dist/{types-LItU7E4l.d.mts → types-0aS865QN.d.mts} +14 -8
- package/dist/types-0aS865QN.d.mts.map +1 -0
- package/dist/{verify-CiwNWM9N.mjs → verify-D7ypCCe6.mjs} +1 -1
- package/dist/{verify-CiwNWM9N.mjs.map → verify-D7ypCCe6.mjs.map} +1 -1
- package/package.json +39 -23
- package/src/cli.ts +78 -15
- package/src/commands/db-sign.ts +102 -32
- package/src/commands/db-update.ts +56 -4
- package/src/commands/db-verify.ts +19 -3
- package/src/commands/init/agent-skill-install.ts +145 -43
- package/src/commands/init/errors.ts +2 -2
- package/src/commands/init/exit-codes.ts +2 -2
- package/src/commands/init/index.ts +1 -1
- package/src/commands/init/init.ts +15 -6
- package/src/commands/init/inputs.ts +1 -1
- package/src/commands/init/output.ts +22 -17
- package/src/commands/{migration-apply.ts → migrate.ts} +77 -73
- package/src/commands/migration-check/exit-codes.ts +3 -0
- package/src/commands/migration-check.ts +369 -0
- package/src/commands/migration-graph.ts +184 -0
- package/src/commands/migration-list.ts +155 -0
- package/src/commands/migration-log.ts +218 -0
- package/src/commands/migration-new.ts +30 -22
- package/src/commands/migration-plan.ts +104 -35
- package/src/commands/migration-show.ts +141 -65
- package/src/commands/migration-status.ts +82 -69
- package/src/commands/{migration-ref.ts → ref.ts} +32 -86
- package/src/control-api/client.ts +30 -21
- package/src/control-api/operations/apply-aggregate.ts +4 -4
- package/src/control-api/operations/contract-emit.ts +26 -3
- package/src/control-api/operations/db-apply-aggregate.ts +4 -3
- package/src/control-api/operations/db-verify.ts +2 -2
- package/src/control-api/operations/migration-apply.ts +5 -4
- package/src/control-api/types.ts +12 -7
- package/src/load-ts-contract.ts +9 -1
- package/src/migration-cli.ts +1 -1
- package/src/utils/cli-errors.ts +37 -0
- package/src/utils/command-helpers.ts +28 -3
- package/src/utils/contract-space-aggregate-loader.ts +4 -4
- package/src/utils/contract-space-seed-phase.ts +2 -2
- package/src/utils/formatters/help.ts +12 -2
- package/src/utils/formatters/migrations.ts +2 -2
- package/dist/cli-errors-D3_sMh2K.mjs.map +0 -1
- package/dist/client-BCnP7cHo.mjs.map +0 -1
- package/dist/command-helpers-BeZHkxV8.mjs.map +0 -1
- package/dist/commands/migration-apply.d.mts +0 -51
- package/dist/commands/migration-apply.d.mts.map +0 -1
- package/dist/commands/migration-apply.mjs.map +0 -1
- package/dist/commands/migration-ref.d.mts +0 -45
- package/dist/commands/migration-ref.d.mts.map +0 -1
- package/dist/commands/migration-ref.mjs.map +0 -1
- package/dist/contract-emit-B77TsJqf.mjs.map +0 -1
- package/dist/db-verify-Czm5T-J4.mjs.map +0 -1
- package/dist/init-B-k3a1Qw.mjs.map +0 -1
- package/dist/migration-plan-mRu5K81L.mjs.map +0 -1
- package/dist/migration-status-By9G5p2H.mjs.map +0 -1
- package/dist/migrations-CTsyBXCA.mjs.map +0 -1
- package/dist/terminal-ui-C_hFNbAn.mjs.map +0 -1
- package/dist/types-LItU7E4l.d.mts.map +0 -1
- /package/dist/{cli-errors-B9OBbled.d.mts → cli-errors-DdcjVLJV.d.mts} +0 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';
|
|
2
|
+
import { MigrationToolsError } from '@prisma-next/migration-tools/errors';
|
|
3
|
+
import type { MigrationGraph } from '@prisma-next/migration-tools/graph';
|
|
4
|
+
import { readRefs } from '@prisma-next/migration-tools/refs';
|
|
5
|
+
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
6
|
+
import { Command } from 'commander';
|
|
7
|
+
import { loadConfig } from '../config-loader';
|
|
8
|
+
import {
|
|
9
|
+
type CliStructuredError,
|
|
10
|
+
errorUnexpected,
|
|
11
|
+
mapMigrationToolsError,
|
|
12
|
+
} from '../utils/cli-errors';
|
|
13
|
+
import {
|
|
14
|
+
addGlobalOptions,
|
|
15
|
+
loadMigrationPackages,
|
|
16
|
+
readContractEnvelope,
|
|
17
|
+
resolveMigrationPaths,
|
|
18
|
+
setCommandDescriptions,
|
|
19
|
+
setCommandExamples,
|
|
20
|
+
setCommandSeeAlso,
|
|
21
|
+
} from '../utils/command-helpers';
|
|
22
|
+
import { migrationGraphToRenderInput } from '../utils/formatters/graph-migration-mapper';
|
|
23
|
+
import { graphRenderer } from '../utils/formatters/graph-render';
|
|
24
|
+
import { formatStyledHeader } from '../utils/formatters/styled';
|
|
25
|
+
import type { CommonCommandOptions } from '../utils/global-flags';
|
|
26
|
+
import { type GlobalFlags, parseGlobalFlags } from '../utils/global-flags';
|
|
27
|
+
import type { StatusRef } from '../utils/migration-types';
|
|
28
|
+
import { handleResult } from '../utils/result-handler';
|
|
29
|
+
import { TerminalUI } from '../utils/terminal-ui';
|
|
30
|
+
|
|
31
|
+
interface MigrationGraphOptions extends CommonCommandOptions {
|
|
32
|
+
readonly config?: string;
|
|
33
|
+
readonly dot?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface MigrationGraphResult {
|
|
37
|
+
readonly ok: true;
|
|
38
|
+
readonly graph: MigrationGraph;
|
|
39
|
+
readonly contractHash: string | null;
|
|
40
|
+
readonly refs: readonly StatusRef[];
|
|
41
|
+
readonly summary: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function executeMigrationGraphCommand(
|
|
45
|
+
options: MigrationGraphOptions,
|
|
46
|
+
flags: GlobalFlags,
|
|
47
|
+
ui: TerminalUI,
|
|
48
|
+
): Promise<Result<MigrationGraphResult, CliStructuredError>> {
|
|
49
|
+
const config = await loadConfig(options.config);
|
|
50
|
+
const { configPath, appMigrationsDir, appMigrationsRelative, refsDir } = resolveMigrationPaths(
|
|
51
|
+
options.config,
|
|
52
|
+
config,
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
if (!flags.json && !flags.quiet) {
|
|
56
|
+
const header = formatStyledHeader({
|
|
57
|
+
command: 'migration graph',
|
|
58
|
+
description: 'Show the migration graph topology',
|
|
59
|
+
details: [
|
|
60
|
+
{ label: 'config', value: configPath },
|
|
61
|
+
{ label: 'migrations', value: appMigrationsRelative },
|
|
62
|
+
],
|
|
63
|
+
flags,
|
|
64
|
+
});
|
|
65
|
+
ui.stderr(header);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let graph: MigrationGraph;
|
|
69
|
+
try {
|
|
70
|
+
({ graph } = await loadMigrationPackages(appMigrationsDir));
|
|
71
|
+
} catch (error) {
|
|
72
|
+
if (MigrationToolsError.is(error)) return notOk(mapMigrationToolsError(error));
|
|
73
|
+
return notOk(
|
|
74
|
+
errorUnexpected(error instanceof Error ? error.message : String(error), {
|
|
75
|
+
why: `Failed to read migrations: ${error instanceof Error ? error.message : String(error)}`,
|
|
76
|
+
}),
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let contractHash: string | null = null;
|
|
81
|
+
try {
|
|
82
|
+
const envelope = await readContractEnvelope(config);
|
|
83
|
+
contractHash = envelope.storageHash;
|
|
84
|
+
} catch {
|
|
85
|
+
// Contract unreadable — render graph without contract marker
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let refs: readonly StatusRef[] = [];
|
|
89
|
+
try {
|
|
90
|
+
const allRefs = await readRefs(refsDir);
|
|
91
|
+
refs = Object.entries(allRefs).map(([name, entry]) => ({
|
|
92
|
+
name,
|
|
93
|
+
hash: entry.hash,
|
|
94
|
+
active: false,
|
|
95
|
+
}));
|
|
96
|
+
} catch {
|
|
97
|
+
// Refs unreadable — render graph without ref markers
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return ok({
|
|
101
|
+
ok: true,
|
|
102
|
+
graph,
|
|
103
|
+
contractHash,
|
|
104
|
+
refs,
|
|
105
|
+
summary: `${graph.nodes.size} node(s), ${graph.migrationByHash.size} edge(s)`,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function createMigrationGraphCommand(): Command {
|
|
110
|
+
const command = new Command('graph');
|
|
111
|
+
setCommandDescriptions(
|
|
112
|
+
command,
|
|
113
|
+
'Show the migration graph topology',
|
|
114
|
+
'Renders the migration graph as an ASCII tree. Offline — does not\n' +
|
|
115
|
+
'consult the database. Use --json for machine-readable output or\n' +
|
|
116
|
+
'--dot for Graphviz DOT format.',
|
|
117
|
+
);
|
|
118
|
+
setCommandExamples(command, [
|
|
119
|
+
'prisma-next migration graph',
|
|
120
|
+
'prisma-next migration graph --json',
|
|
121
|
+
'prisma-next migration graph --dot',
|
|
122
|
+
]);
|
|
123
|
+
setCommandSeeAlso(command, [
|
|
124
|
+
{ verb: 'migration status', oneLiner: 'Show migration path and pending status' },
|
|
125
|
+
{ verb: 'migration log', oneLiner: 'Show executed migration history' },
|
|
126
|
+
{ verb: 'migration list', oneLiner: 'List on-disk migrations' },
|
|
127
|
+
{ verb: 'migration show', oneLiner: 'Display migration package contents' },
|
|
128
|
+
]);
|
|
129
|
+
addGlobalOptions(command)
|
|
130
|
+
.option('--config <path>', 'Path to prisma-next.config.ts')
|
|
131
|
+
.option('--dot', 'Output in Graphviz DOT format')
|
|
132
|
+
.action(async (options: MigrationGraphOptions) => {
|
|
133
|
+
const flags = parseGlobalFlags(options);
|
|
134
|
+
const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });
|
|
135
|
+
const result = await executeMigrationGraphCommand(options, flags, ui);
|
|
136
|
+
const exitCode = handleResult(result, flags, ui, (graphResult) => {
|
|
137
|
+
// Explicit format flags win over the auto-JSON default. `flags.json`
|
|
138
|
+
// is auto-enabled when stdout is non-TTY (per CLI Style Guide §
|
|
139
|
+
// JSON Semantics); without this ordering, `migration graph --dot |
|
|
140
|
+
// dot -Tsvg` pipes JSON into the GraphViz binary, which then
|
|
141
|
+
// errors. `--dot` is the more specific instruction; honour it.
|
|
142
|
+
if (options.dot) {
|
|
143
|
+
const lines = ['digraph migrations {'];
|
|
144
|
+
for (const edge of graphResult.graph.migrationByHash.values()) {
|
|
145
|
+
const from = edge.from.slice(0, 12);
|
|
146
|
+
const to = edge.to.slice(0, 12);
|
|
147
|
+
lines.push(` "${from}" -> "${to}" [label="${edge.dirName}"];`);
|
|
148
|
+
}
|
|
149
|
+
lines.push('}');
|
|
150
|
+
ui.output(lines.join('\n'));
|
|
151
|
+
} else if (flags.json) {
|
|
152
|
+
const nodes = [...graphResult.graph.nodes];
|
|
153
|
+
const edges = [...graphResult.graph.migrationByHash.values()].map((e) => ({
|
|
154
|
+
dirName: e.dirName,
|
|
155
|
+
from: e.from,
|
|
156
|
+
to: e.to,
|
|
157
|
+
migrationHash: e.migrationHash,
|
|
158
|
+
}));
|
|
159
|
+
ui.output(
|
|
160
|
+
JSON.stringify({ ok: true, nodes, edges, summary: graphResult.summary }, null, 2),
|
|
161
|
+
);
|
|
162
|
+
} else if (!flags.quiet) {
|
|
163
|
+
const renderInput = migrationGraphToRenderInput({
|
|
164
|
+
graph: graphResult.graph,
|
|
165
|
+
mode: 'offline',
|
|
166
|
+
markerHash: undefined,
|
|
167
|
+
contractHash: graphResult.contractHash ?? EMPTY_CONTRACT_HASH,
|
|
168
|
+
refs: graphResult.refs,
|
|
169
|
+
activeRefHash: undefined,
|
|
170
|
+
activeRefName: undefined,
|
|
171
|
+
edgeStatuses: [],
|
|
172
|
+
});
|
|
173
|
+
const graphOutput = graphRenderer.render(renderInput.graph, {
|
|
174
|
+
...renderInput.options,
|
|
175
|
+
colorize: flags.color !== false,
|
|
176
|
+
});
|
|
177
|
+
ui.log(graphOutput);
|
|
178
|
+
ui.log(`\n${graphResult.summary}`);
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
process.exit(exitCode);
|
|
182
|
+
});
|
|
183
|
+
return command;
|
|
184
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import type { MigrationPlanOperation } from '@prisma-next/framework-components/control';
|
|
2
|
+
import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';
|
|
3
|
+
import { MigrationToolsError } from '@prisma-next/migration-tools/errors';
|
|
4
|
+
import { findPath } from '@prisma-next/migration-tools/migration-graph';
|
|
5
|
+
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
6
|
+
import { Command } from 'commander';
|
|
7
|
+
import { loadConfig } from '../config-loader';
|
|
8
|
+
import {
|
|
9
|
+
type CliStructuredError,
|
|
10
|
+
errorUnexpected,
|
|
11
|
+
mapMigrationToolsError,
|
|
12
|
+
} from '../utils/cli-errors';
|
|
13
|
+
import {
|
|
14
|
+
addGlobalOptions,
|
|
15
|
+
loadMigrationPackages,
|
|
16
|
+
resolveMigrationPaths,
|
|
17
|
+
setCommandDescriptions,
|
|
18
|
+
setCommandExamples,
|
|
19
|
+
setCommandSeeAlso,
|
|
20
|
+
} from '../utils/command-helpers';
|
|
21
|
+
import { formatStyledHeader } from '../utils/formatters/styled';
|
|
22
|
+
import type { CommonCommandOptions } from '../utils/global-flags';
|
|
23
|
+
import { type GlobalFlags, parseGlobalFlags } from '../utils/global-flags';
|
|
24
|
+
import { handleResult } from '../utils/result-handler';
|
|
25
|
+
import { TerminalUI } from '../utils/terminal-ui';
|
|
26
|
+
|
|
27
|
+
interface MigrationListOptions extends CommonCommandOptions {
|
|
28
|
+
readonly config?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface MigrationListEntry {
|
|
32
|
+
readonly dirName: string;
|
|
33
|
+
readonly from: string;
|
|
34
|
+
readonly to: string;
|
|
35
|
+
readonly migrationHash: string;
|
|
36
|
+
readonly operationCount: number;
|
|
37
|
+
readonly createdAt: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface MigrationListResult {
|
|
41
|
+
readonly ok: true;
|
|
42
|
+
readonly migrations: readonly MigrationListEntry[];
|
|
43
|
+
readonly summary: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function executeMigrationListCommand(
|
|
47
|
+
options: MigrationListOptions,
|
|
48
|
+
flags: GlobalFlags,
|
|
49
|
+
ui: TerminalUI,
|
|
50
|
+
): Promise<Result<MigrationListResult, CliStructuredError>> {
|
|
51
|
+
const config = await loadConfig(options.config);
|
|
52
|
+
const { configPath, appMigrationsDir, appMigrationsRelative } = resolveMigrationPaths(
|
|
53
|
+
options.config,
|
|
54
|
+
config,
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
if (!flags.json && !flags.quiet) {
|
|
58
|
+
const header = formatStyledHeader({
|
|
59
|
+
command: 'migration list',
|
|
60
|
+
description: 'List on-disk migrations in topological order',
|
|
61
|
+
details: [
|
|
62
|
+
{ label: 'config', value: configPath },
|
|
63
|
+
{ label: 'migrations', value: appMigrationsRelative },
|
|
64
|
+
],
|
|
65
|
+
flags,
|
|
66
|
+
});
|
|
67
|
+
ui.stderr(header);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let bundles: Awaited<ReturnType<typeof loadMigrationPackages>>['bundles'];
|
|
71
|
+
let graph: Awaited<ReturnType<typeof loadMigrationPackages>>['graph'];
|
|
72
|
+
try {
|
|
73
|
+
({ bundles, graph } = await loadMigrationPackages(appMigrationsDir));
|
|
74
|
+
} catch (error) {
|
|
75
|
+
if (MigrationToolsError.is(error)) return notOk(mapMigrationToolsError(error));
|
|
76
|
+
return notOk(
|
|
77
|
+
errorUnexpected(error instanceof Error ? error.message : String(error), {
|
|
78
|
+
why: `Failed to read migrations: ${error instanceof Error ? error.message : String(error)}`,
|
|
79
|
+
}),
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (bundles.length === 0) {
|
|
84
|
+
return ok({ ok: true, migrations: [], summary: 'No migrations found' });
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const leaves = [...graph.nodes].filter(
|
|
88
|
+
(n) => !graph.forwardChain.has(n) || graph.forwardChain.get(n)!.length === 0,
|
|
89
|
+
);
|
|
90
|
+
const targetHash =
|
|
91
|
+
leaves.length === 1 ? leaves[0]! : ([...graph.nodes].values().next().value as string);
|
|
92
|
+
const chain = findPath(graph, EMPTY_CONTRACT_HASH, targetHash) ?? [];
|
|
93
|
+
|
|
94
|
+
const pkgByDirName = new Map(bundles.map((p) => [p.dirName, p]));
|
|
95
|
+
const entries: MigrationListEntry[] = chain.map((edge) => {
|
|
96
|
+
const pkg = pkgByDirName.get(edge.dirName);
|
|
97
|
+
const ops = (pkg?.ops ?? []) as readonly MigrationPlanOperation[];
|
|
98
|
+
return {
|
|
99
|
+
dirName: edge.dirName,
|
|
100
|
+
from: edge.from,
|
|
101
|
+
to: edge.to,
|
|
102
|
+
migrationHash: edge.migrationHash,
|
|
103
|
+
operationCount: ops.length,
|
|
104
|
+
createdAt: edge.createdAt,
|
|
105
|
+
};
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
return ok({
|
|
109
|
+
ok: true,
|
|
110
|
+
migrations: entries,
|
|
111
|
+
summary: `${entries.length} migration(s) on disk`,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function createMigrationListCommand(): Command {
|
|
116
|
+
const command = new Command('list');
|
|
117
|
+
setCommandDescriptions(
|
|
118
|
+
command,
|
|
119
|
+
'List on-disk migrations in topological order',
|
|
120
|
+
'Enumerates all migration packages under migrations/<space>/ in\n' +
|
|
121
|
+
'topological order. Offline — does not consult the database.',
|
|
122
|
+
);
|
|
123
|
+
setCommandExamples(command, ['prisma-next migration list']);
|
|
124
|
+
setCommandSeeAlso(command, [
|
|
125
|
+
{ verb: 'migration status', oneLiner: 'Show migration path and pending status' },
|
|
126
|
+
{ verb: 'migration log', oneLiner: 'Show executed migration history' },
|
|
127
|
+
{ verb: 'migration graph', oneLiner: 'Show the migration graph topology' },
|
|
128
|
+
{ verb: 'migration show', oneLiner: 'Display migration package contents' },
|
|
129
|
+
]);
|
|
130
|
+
addGlobalOptions(command)
|
|
131
|
+
.option('--config <path>', 'Path to prisma-next.config.ts')
|
|
132
|
+
.action(async (options: MigrationListOptions) => {
|
|
133
|
+
const flags = parseGlobalFlags(options);
|
|
134
|
+
const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });
|
|
135
|
+
const result = await executeMigrationListCommand(options, flags, ui);
|
|
136
|
+
const exitCode = handleResult(result, flags, ui, (listResult) => {
|
|
137
|
+
if (flags.json) {
|
|
138
|
+
ui.output(JSON.stringify(listResult, null, 2));
|
|
139
|
+
} else if (!flags.quiet) {
|
|
140
|
+
if (listResult.migrations.length === 0) {
|
|
141
|
+
ui.log('No migrations found');
|
|
142
|
+
} else {
|
|
143
|
+
for (const entry of listResult.migrations) {
|
|
144
|
+
ui.log(
|
|
145
|
+
`${entry.dirName} ${entry.migrationHash.slice(0, 16)}… ${entry.operationCount} op(s)`,
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
ui.log(`\n${listResult.summary}`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
process.exit(exitCode);
|
|
153
|
+
});
|
|
154
|
+
return command;
|
|
155
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import type { MigrationPlanOperation } from '@prisma-next/framework-components/control';
|
|
2
|
+
import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';
|
|
3
|
+
import { MigrationToolsError } from '@prisma-next/migration-tools/errors';
|
|
4
|
+
import { findPath } from '@prisma-next/migration-tools/migration-graph';
|
|
5
|
+
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
6
|
+
import { cyan, dim } from 'colorette';
|
|
7
|
+
import { Command } from 'commander';
|
|
8
|
+
import { loadConfig } from '../config-loader';
|
|
9
|
+
import { createControlClient } from '../control-api/client';
|
|
10
|
+
import {
|
|
11
|
+
type CliStructuredError,
|
|
12
|
+
errorDatabaseConnectionRequired,
|
|
13
|
+
errorDriverRequired,
|
|
14
|
+
errorUnexpected,
|
|
15
|
+
mapMigrationToolsError,
|
|
16
|
+
} from '../utils/cli-errors';
|
|
17
|
+
import {
|
|
18
|
+
addGlobalOptions,
|
|
19
|
+
loadMigrationPackages,
|
|
20
|
+
maskConnectionUrl,
|
|
21
|
+
resolveMigrationPaths,
|
|
22
|
+
setCommandDescriptions,
|
|
23
|
+
setCommandExamples,
|
|
24
|
+
setCommandSeeAlso,
|
|
25
|
+
targetSupportsMigrations,
|
|
26
|
+
} from '../utils/command-helpers';
|
|
27
|
+
import { formatStyledHeader } from '../utils/formatters/styled';
|
|
28
|
+
import type { CommonCommandOptions } from '../utils/global-flags';
|
|
29
|
+
import { type GlobalFlags, parseGlobalFlags } from '../utils/global-flags';
|
|
30
|
+
import { handleResult } from '../utils/result-handler';
|
|
31
|
+
import { TerminalUI } from '../utils/terminal-ui';
|
|
32
|
+
|
|
33
|
+
interface MigrationLogOptions extends CommonCommandOptions {
|
|
34
|
+
readonly db?: string;
|
|
35
|
+
readonly config?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface MigrationLogEntry {
|
|
39
|
+
readonly dirName: string;
|
|
40
|
+
readonly from: string;
|
|
41
|
+
readonly to: string;
|
|
42
|
+
readonly migrationHash: string;
|
|
43
|
+
readonly operationCount: number;
|
|
44
|
+
readonly createdAt: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface MigrationLogResult {
|
|
48
|
+
readonly ok: true;
|
|
49
|
+
readonly markerHash: string | null;
|
|
50
|
+
readonly applied: readonly MigrationLogEntry[];
|
|
51
|
+
readonly summary: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function executeMigrationLogCommand(
|
|
55
|
+
options: MigrationLogOptions,
|
|
56
|
+
flags: GlobalFlags,
|
|
57
|
+
ui: TerminalUI,
|
|
58
|
+
): Promise<Result<MigrationLogResult, CliStructuredError>> {
|
|
59
|
+
const config = await loadConfig(options.config);
|
|
60
|
+
const { configPath, appMigrationsDir, appMigrationsRelative } = resolveMigrationPaths(
|
|
61
|
+
options.config,
|
|
62
|
+
config,
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const dbConnection = options.db ?? config.db?.connection;
|
|
66
|
+
if (!dbConnection) {
|
|
67
|
+
return notOk(
|
|
68
|
+
errorDatabaseConnectionRequired({
|
|
69
|
+
why: `Database connection is required for migration log (set db.connection in ${configPath}, or pass --db <url>)`,
|
|
70
|
+
commandName: 'migration log',
|
|
71
|
+
}),
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
if (!config.driver) {
|
|
75
|
+
return notOk(errorDriverRequired({ why: 'Config.driver is required for migration log' }));
|
|
76
|
+
}
|
|
77
|
+
if (!targetSupportsMigrations(config.target)) {
|
|
78
|
+
return notOk(errorUnexpected('Target does not support migrations'));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (!flags.json && !flags.quiet) {
|
|
82
|
+
const header = formatStyledHeader({
|
|
83
|
+
command: 'migration log',
|
|
84
|
+
description: 'Show executed migration history',
|
|
85
|
+
details: [
|
|
86
|
+
{ label: 'config', value: configPath },
|
|
87
|
+
{ label: 'migrations', value: appMigrationsRelative },
|
|
88
|
+
...(typeof dbConnection === 'string'
|
|
89
|
+
? [{ label: 'database', value: maskConnectionUrl(dbConnection) }]
|
|
90
|
+
: []),
|
|
91
|
+
],
|
|
92
|
+
flags,
|
|
93
|
+
});
|
|
94
|
+
ui.stderr(header);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
let bundles: Awaited<ReturnType<typeof loadMigrationPackages>>['bundles'];
|
|
98
|
+
let graph: Awaited<ReturnType<typeof loadMigrationPackages>>['graph'];
|
|
99
|
+
try {
|
|
100
|
+
({ bundles, graph } = await loadMigrationPackages(appMigrationsDir));
|
|
101
|
+
} catch (error) {
|
|
102
|
+
if (MigrationToolsError.is(error)) return notOk(mapMigrationToolsError(error));
|
|
103
|
+
return notOk(
|
|
104
|
+
errorUnexpected(error instanceof Error ? error.message : String(error), {
|
|
105
|
+
why: `Failed to read migrations: ${error instanceof Error ? error.message : String(error)}`,
|
|
106
|
+
}),
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const client = createControlClient({
|
|
111
|
+
family: config.family,
|
|
112
|
+
target: config.target,
|
|
113
|
+
adapter: config.adapter,
|
|
114
|
+
driver: config.driver,
|
|
115
|
+
extensionPacks: config.extensionPacks ?? [],
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
await client.connect(dbConnection);
|
|
120
|
+
const marker = await client.readMarker();
|
|
121
|
+
const markerHash = marker?.storageHash ?? null;
|
|
122
|
+
|
|
123
|
+
if (!markerHash) {
|
|
124
|
+
return ok({
|
|
125
|
+
ok: true,
|
|
126
|
+
markerHash: null,
|
|
127
|
+
applied: [],
|
|
128
|
+
summary: 'No migrations applied (database has no marker)',
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const appliedPath = findPath(graph, EMPTY_CONTRACT_HASH, markerHash);
|
|
133
|
+
if (appliedPath === null) {
|
|
134
|
+
return notOk(
|
|
135
|
+
errorUnexpected('Database marker is not reachable from migration history', {
|
|
136
|
+
why: `Marker hash ${markerHash} is not reachable from the root of the on-disk migration graph.`,
|
|
137
|
+
fix: 'The database may have been migrated outside this project. Use `migration status` to inspect the current state.',
|
|
138
|
+
}),
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
const pkgByDirName = new Map(bundles.map((p) => [p.dirName, p]));
|
|
142
|
+
const entries: MigrationLogEntry[] = appliedPath.map((edge) => {
|
|
143
|
+
const pkg = pkgByDirName.get(edge.dirName);
|
|
144
|
+
const ops = (pkg?.ops ?? []) as readonly MigrationPlanOperation[];
|
|
145
|
+
return {
|
|
146
|
+
dirName: edge.dirName,
|
|
147
|
+
from: edge.from,
|
|
148
|
+
to: edge.to,
|
|
149
|
+
migrationHash: edge.migrationHash,
|
|
150
|
+
operationCount: ops.length,
|
|
151
|
+
createdAt: edge.createdAt,
|
|
152
|
+
};
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
return ok({
|
|
156
|
+
ok: true,
|
|
157
|
+
markerHash,
|
|
158
|
+
applied: entries,
|
|
159
|
+
summary: `${entries.length} migration(s) applied`,
|
|
160
|
+
});
|
|
161
|
+
} catch (error) {
|
|
162
|
+
if (MigrationToolsError.is(error)) return notOk(mapMigrationToolsError(error));
|
|
163
|
+
return notOk(
|
|
164
|
+
errorUnexpected(error instanceof Error ? error.message : String(error), {
|
|
165
|
+
why: `Failed to read migration log: ${error instanceof Error ? error.message : String(error)}`,
|
|
166
|
+
}),
|
|
167
|
+
);
|
|
168
|
+
} finally {
|
|
169
|
+
await client.close();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function createMigrationLogCommand(): Command {
|
|
174
|
+
const command = new Command('log');
|
|
175
|
+
setCommandDescriptions(
|
|
176
|
+
command,
|
|
177
|
+
'Show executed migration history',
|
|
178
|
+
'Reads the database marker and displays the applied migration chain\n' +
|
|
179
|
+
'from the initial state to the current marker position.',
|
|
180
|
+
);
|
|
181
|
+
setCommandExamples(command, [
|
|
182
|
+
'prisma-next migration log --db $DATABASE_URL',
|
|
183
|
+
'prisma-next migration log --json --db $DATABASE_URL',
|
|
184
|
+
]);
|
|
185
|
+
setCommandSeeAlso(command, [
|
|
186
|
+
{ verb: 'migration status', oneLiner: 'Show migration path and pending status' },
|
|
187
|
+
{ verb: 'migration list', oneLiner: 'List on-disk migrations' },
|
|
188
|
+
{ verb: 'migration graph', oneLiner: 'Show the migration graph topology' },
|
|
189
|
+
{ verb: 'migration show', oneLiner: 'Display migration package contents' },
|
|
190
|
+
]);
|
|
191
|
+
addGlobalOptions(command)
|
|
192
|
+
.option('--db <url>', 'Database connection string')
|
|
193
|
+
.option('--config <path>', 'Path to prisma-next.config.ts')
|
|
194
|
+
.action(async (options: MigrationLogOptions) => {
|
|
195
|
+
const flags = parseGlobalFlags(options);
|
|
196
|
+
const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });
|
|
197
|
+
const result = await executeMigrationLogCommand(options, flags, ui);
|
|
198
|
+
const exitCode = handleResult(result, flags, ui, (logResult) => {
|
|
199
|
+
if (flags.json) {
|
|
200
|
+
ui.output(JSON.stringify(logResult, null, 2));
|
|
201
|
+
} else if (!flags.quiet) {
|
|
202
|
+
const c = (fn: (s: string) => string, s: string) => (flags.color !== false ? fn(s) : s);
|
|
203
|
+
if (logResult.applied.length === 0) {
|
|
204
|
+
ui.log(logResult.summary);
|
|
205
|
+
} else {
|
|
206
|
+
for (const entry of logResult.applied) {
|
|
207
|
+
ui.log(
|
|
208
|
+
`${c(cyan, '✓')} ${entry.dirName} ${c(dim, entry.migrationHash.slice(0, 16) + '…')} ${entry.operationCount} op(s)`,
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
ui.log(`\n${logResult.summary}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
process.exit(exitCode);
|
|
216
|
+
});
|
|
217
|
+
return command;
|
|
218
|
+
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* verbatim.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import { readFile } from 'node:fs/promises';
|
|
12
12
|
import type { Contract } from '@prisma-next/contract/types';
|
|
13
13
|
import { getEmittedArtifactPaths } from '@prisma-next/emitter';
|
|
14
14
|
import { APP_SPACE_ID, createControlStack } from '@prisma-next/framework-components/control';
|
|
@@ -32,6 +32,7 @@ import { join, relative } from 'pathe';
|
|
|
32
32
|
import { loadConfig } from '../config-loader';
|
|
33
33
|
import {
|
|
34
34
|
CliStructuredError,
|
|
35
|
+
errorFileNotFound,
|
|
35
36
|
errorRuntime,
|
|
36
37
|
errorTargetMigrationNotSupported,
|
|
37
38
|
errorUnexpected,
|
|
@@ -72,11 +73,17 @@ async function executeMigrationNewCommand(
|
|
|
72
73
|
const config = await loadConfig(options.config);
|
|
73
74
|
const { appMigrationsDir, appMigrationsRelative } = resolveMigrationPaths(options.config, config);
|
|
74
75
|
|
|
76
|
+
// Construct the family instance up-front so the on-disk contract read
|
|
77
|
+
// below crosses the serializer seam (`familyInstance.deserializeContract`)
|
|
78
|
+
// at the read site, not somewhere downstream. See TML-2536.
|
|
79
|
+
const stack = createControlStack(config);
|
|
80
|
+
const familyInstance = config.family.create(stack);
|
|
81
|
+
|
|
75
82
|
const contractPathAbsolute = resolveContractPath(config);
|
|
76
83
|
|
|
77
84
|
let contractJsonContent: string;
|
|
78
85
|
try {
|
|
79
|
-
contractJsonContent =
|
|
86
|
+
contractJsonContent = await readFile(contractPathAbsolute, 'utf-8');
|
|
80
87
|
} catch (error) {
|
|
81
88
|
if (error instanceof Error && (error as { code?: string }).code === 'ENOENT') {
|
|
82
89
|
return notOk(
|
|
@@ -89,24 +96,20 @@ async function executeMigrationNewCommand(
|
|
|
89
96
|
throw error;
|
|
90
97
|
}
|
|
91
98
|
|
|
92
|
-
let
|
|
99
|
+
let toContract: Contract;
|
|
93
100
|
try {
|
|
94
|
-
|
|
101
|
+
toContract = familyInstance.deserializeContract(JSON.parse(contractJsonContent) as unknown);
|
|
95
102
|
} catch (error) {
|
|
96
103
|
return notOk(
|
|
97
104
|
errorRuntime('Contract JSON is invalid', {
|
|
98
|
-
why: `Failed to
|
|
105
|
+
why: `Failed to deserialize ${contractPathAbsolute}: ${error instanceof Error ? error.message : String(error)}`,
|
|
99
106
|
fix: 'Run `prisma-next contract emit` to regenerate the contract',
|
|
100
107
|
}),
|
|
101
108
|
);
|
|
102
109
|
}
|
|
103
110
|
|
|
104
|
-
const toStorageHash =
|
|
105
|
-
|
|
106
|
-
| Record<string, unknown>
|
|
107
|
-
| undefined
|
|
108
|
-
)?.['storageHash'] as string | undefined;
|
|
109
|
-
if (!toStorageHash) {
|
|
111
|
+
const toStorageHash = toContract.storage?.storageHash;
|
|
112
|
+
if (typeof toStorageHash !== 'string') {
|
|
110
113
|
return notOk(
|
|
111
114
|
errorRuntime('Contract is missing storageHash', {
|
|
112
115
|
why: `Contract at ${contractPathAbsolute} has no storageHash`,
|
|
@@ -115,7 +118,6 @@ async function executeMigrationNewCommand(
|
|
|
115
118
|
);
|
|
116
119
|
}
|
|
117
120
|
|
|
118
|
-
let fromContract: Contract | null = null;
|
|
119
121
|
let fromHash: string | null = null;
|
|
120
122
|
let fromContractSourceDir: string | null = null;
|
|
121
123
|
|
|
@@ -136,7 +138,6 @@ async function executeMigrationNewCommand(
|
|
|
136
138
|
);
|
|
137
139
|
}
|
|
138
140
|
fromHash = match.metadata.to;
|
|
139
|
-
fromContract = match.metadata.toContract;
|
|
140
141
|
fromContractSourceDir = match.dirPath;
|
|
141
142
|
} else {
|
|
142
143
|
const latestMigration = findLatestMigration(graph);
|
|
@@ -146,7 +147,6 @@ async function executeMigrationNewCommand(
|
|
|
146
147
|
(p) => p.metadata.migrationHash === latestMigration.migrationHash,
|
|
147
148
|
);
|
|
148
149
|
if (leafPkg) {
|
|
149
|
-
fromContract = leafPkg.metadata.toContract;
|
|
150
150
|
fromContractSourceDir = leafPkg.dirPath;
|
|
151
151
|
}
|
|
152
152
|
}
|
|
@@ -180,8 +180,6 @@ async function executeMigrationNewCommand(
|
|
|
180
180
|
const baseMetadata: Omit<MigrationMetadata, 'migrationHash'> = {
|
|
181
181
|
from: fromHash,
|
|
182
182
|
to: toStorageHash,
|
|
183
|
-
fromContract,
|
|
184
|
-
toContract: toContractJson,
|
|
185
183
|
hints: {
|
|
186
184
|
used: [],
|
|
187
185
|
applied: [],
|
|
@@ -222,14 +220,24 @@ async function executeMigrationNewCommand(
|
|
|
222
220
|
const sourceArtifacts = getEmittedArtifactPaths(
|
|
223
221
|
join(fromContractSourceDir, 'end-contract.json'),
|
|
224
222
|
);
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
223
|
+
try {
|
|
224
|
+
await copyFilesWithRename(packageDir, [
|
|
225
|
+
{ sourcePath: sourceArtifacts.jsonPath, destName: 'start-contract.json' },
|
|
226
|
+
{ sourcePath: sourceArtifacts.dtsPath, destName: 'start-contract.d.ts' },
|
|
227
|
+
]);
|
|
228
|
+
} catch (error) {
|
|
229
|
+
if (error instanceof Error && (error as { code?: string }).code === 'ENOENT') {
|
|
230
|
+
return notOk(
|
|
231
|
+
errorFileNotFound(sourceArtifacts.jsonPath, {
|
|
232
|
+
why: `Predecessor migration is missing its destination contract snapshot at ${sourceArtifacts.jsonPath}`,
|
|
233
|
+
fix: 'Re-emit the predecessor migration (`prisma-next migration plan` from its source) so its sibling `end-contract.json` is restored, then re-run this command.',
|
|
234
|
+
}),
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
throw error;
|
|
238
|
+
}
|
|
229
239
|
}
|
|
230
240
|
|
|
231
|
-
const stack = createControlStack(config);
|
|
232
|
-
const familyInstance = config.family.create(stack);
|
|
233
241
|
const planner = migrations.createPlanner(familyInstance);
|
|
234
242
|
const emptyPlan = planner.emptyMigration(
|
|
235
243
|
{
|