@prisma-next/cli 0.8.0-dev.1 → 0.8.0-dev.10
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 +7 -8
- 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-4d26awB-.mjs → client-XkUw4xD0.mjs} +10 -9
- package/dist/client-XkUw4xD0.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.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} +54 -36
- 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 +21 -20
- 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 +85 -47
- package/dist/commands/migration-show.mjs.map +1 -1
- package/dist/commands/migration-status.d.mts +3 -15
- 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-DLc5GYbr.mjs → contract-emit-CgoFk9AU.mjs} +3 -3
- package/dist/{contract-emit-DLc5GYbr.mjs.map → contract-emit-CgoFk9AU.mjs.map} +1 -1
- package/dist/{contract-emit-BhKR-D9Y.mjs → contract-emit-GpxW5RLe.mjs} +6 -6
- package/dist/{contract-emit-BhKR-D9Y.mjs.map → contract-emit-GpxW5RLe.mjs.map} +1 -1
- package/dist/{contract-infer-Bnla2kuK.mjs → contract-infer-D8edZOCi.mjs} +5 -5
- package/dist/{contract-infer-Bnla2kuK.mjs.map → contract-infer-D8edZOCi.mjs.map} +1 -1
- package/dist/{contract-space-aggregate-loader-BrwKK6Q6.mjs → contract-space-aggregate-loader-D68YpuPR.mjs} +3 -3
- package/dist/{contract-space-aggregate-loader-BrwKK6Q6.mjs.map → contract-space-aggregate-loader-D68YpuPR.mjs.map} +1 -1
- package/dist/{db-verify-DitNxDiE.mjs → db-verify-DtRB9iHJ.mjs} +7 -7
- package/dist/{db-verify-DitNxDiE.mjs.map → db-verify-DtRB9iHJ.mjs.map} +1 -1
- 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.mjs +2 -2
- package/dist/exports/index.mjs +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-Do4Ei0i_.mjs → graph-render-eJDcLWny.mjs} +3 -692
- package/dist/graph-render-eJDcLWny.mjs.map +1 -0
- package/dist/{init-DW94FRsD.mjs → init-Dm0QZPUA.mjs} +133 -61
- package/dist/init-Dm0QZPUA.mjs.map +1 -0
- package/dist/{inspect-live-schema-CyzAzPzF.mjs → inspect-live-schema-CPPqCips.mjs} +4 -4
- package/dist/{inspect-live-schema-CyzAzPzF.mjs.map → inspect-live-schema-CPPqCips.mjs.map} +1 -1
- package/dist/migration-cli.mjs +1 -1
- package/dist/migration-cli.mjs.map +1 -1
- package/dist/{migration-command-scaffold-Jp1rosw8.mjs → migration-command-scaffold-B_ezTTwX.mjs} +4 -4
- package/dist/{migration-command-scaffold-Jp1rosw8.mjs.map → migration-command-scaffold-B_ezTTwX.mjs.map} +1 -1
- package/dist/{migration-plan-q1pPoOCf.mjs → migration-plan-DWB-NTxH.mjs} +54 -28
- package/dist/migration-plan-DWB-NTxH.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-BS_wpjAY.d.mts} +2 -2
- package/dist/{types-LItU7E4l.d.mts.map → types-BS_wpjAY.d.mts.map} +1 -1
- 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/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} +54 -70
- 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 +17 -9
- package/src/commands/migration-plan.ts +65 -26
- package/src/commands/migration-show.ts +132 -60
- package/src/commands/migration-status.ts +77 -64
- package/src/commands/{migration-ref.ts → ref.ts} +32 -86
- package/src/control-api/operations/apply-aggregate.ts +4 -4
- package/src/control-api/operations/db-apply-aggregate.ts +3 -2
- package/src/control-api/operations/migration-apply.ts +4 -3
- package/src/control-api/types.ts +1 -2
- 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 +2 -2
- package/src/utils/contract-space-seed-phase.ts +1 -1
- 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-4d26awB-.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/init-DW94FRsD.mjs.map +0 -1
- package/dist/migration-plan-q1pPoOCf.mjs.map +0 -1
- package/dist/migration-status-Do4Ei0i_.mjs.map +0 -1
- package/dist/migrations-CTsyBXCA.mjs.map +0 -1
- package/dist/terminal-ui-C_hFNbAn.mjs.map +0 -1
- /package/dist/{cli-errors-B9OBbled.d.mts → cli-errors-DdcjVLJV.d.mts} +0 -0
|
@@ -18,6 +18,8 @@ import {
|
|
|
18
18
|
import type { MigrationMetadata } from '@prisma-next/migration-tools/metadata';
|
|
19
19
|
import { findLatestMigration } from '@prisma-next/migration-tools/migration-graph';
|
|
20
20
|
import { writeMigrationTs } from '@prisma-next/migration-tools/migration-ts';
|
|
21
|
+
import { parseContractRef } from '@prisma-next/migration-tools/ref-resolution';
|
|
22
|
+
import { readRefs } from '@prisma-next/migration-tools/refs';
|
|
21
23
|
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
22
24
|
import { Command } from 'commander';
|
|
23
25
|
import { join, relative } from 'pathe';
|
|
@@ -28,10 +30,10 @@ import {
|
|
|
28
30
|
errorContractValidationFailed,
|
|
29
31
|
errorFileNotFound,
|
|
30
32
|
errorMigrationPlanningFailed,
|
|
31
|
-
errorRuntime,
|
|
32
33
|
errorTargetMigrationNotSupported,
|
|
33
34
|
errorUnexpected,
|
|
34
35
|
mapMigrationToolsError,
|
|
36
|
+
mapRefResolutionError,
|
|
35
37
|
} from '../utils/cli-errors';
|
|
36
38
|
import {
|
|
37
39
|
addGlobalOptions,
|
|
@@ -58,6 +60,34 @@ interface MigrationPlanOptions extends CommonCommandOptions {
|
|
|
58
60
|
readonly from?: string;
|
|
59
61
|
}
|
|
60
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Load a predecessor migration's destination contract from its sibling
|
|
65
|
+
* `end-contract.json` on disk. The manifest no longer inlines the
|
|
66
|
+
* contract; the planner reads it from the canonical on-disk artefact
|
|
67
|
+
* authored by a previous `migration plan` run.
|
|
68
|
+
*
|
|
69
|
+
* Throws `CliStructuredError` with `errorFileNotFound` when the
|
|
70
|
+
* sibling file is missing — the user has likely deleted or never
|
|
71
|
+
* authored the snapshot, and the message names the file and points
|
|
72
|
+
* them at re-emitting from the source.
|
|
73
|
+
*/
|
|
74
|
+
async function readPredecessorEndContract(migrationDir: string): Promise<Contract> {
|
|
75
|
+
const path = join(migrationDir, 'end-contract.json');
|
|
76
|
+
let raw: string;
|
|
77
|
+
try {
|
|
78
|
+
raw = await readFile(path, 'utf-8');
|
|
79
|
+
} catch (error) {
|
|
80
|
+
if (error instanceof Error && (error as { code?: string }).code === 'ENOENT') {
|
|
81
|
+
throw errorFileNotFound(path, {
|
|
82
|
+
why: `Predecessor migration is missing its destination contract snapshot at ${path}`,
|
|
83
|
+
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.',
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
return JSON.parse(raw) as Contract;
|
|
89
|
+
}
|
|
90
|
+
|
|
61
91
|
export interface MigrationPlanResult {
|
|
62
92
|
readonly ok: boolean;
|
|
63
93
|
readonly noOp: boolean;
|
|
@@ -74,7 +104,7 @@ export interface MigrationPlanResult {
|
|
|
74
104
|
* Surfacing these in the result (rather than only via `ui.step` log
|
|
75
105
|
* lines) makes the cross-space side effect explicit to JSON consumers
|
|
76
106
|
* and the success-summary renderer — the same multi-space side effect
|
|
77
|
-
* that `
|
|
107
|
+
* that `migrate` will replay.
|
|
78
108
|
*/
|
|
79
109
|
readonly emittedExtensionDirs: readonly { readonly spaceId: string; readonly dirName: string }[];
|
|
80
110
|
readonly operations: readonly {
|
|
@@ -186,24 +216,26 @@ async function executeMigrationPlanCommand(
|
|
|
186
216
|
const { bundles, graph } = await loadMigrationPackages(appMigrationsDir);
|
|
187
217
|
|
|
188
218
|
if (options.from) {
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
|
|
219
|
+
const refs = await readRefs(resolveMigrationPaths(options.config, config).refsDir);
|
|
220
|
+
const refResult = parseContractRef(options.from, { graph, refs });
|
|
221
|
+
if (!refResult.ok) {
|
|
222
|
+
return notOk(mapRefResolutionError(refResult.failure));
|
|
223
|
+
}
|
|
224
|
+
fromHash = refResult.value.hash;
|
|
225
|
+
const matchingBundle = bundles.find((p) => p.metadata.to === fromHash);
|
|
226
|
+
if (!matchingBundle) {
|
|
192
227
|
return notOk(
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
fix: 'Check that the --from hash matches a known migration target hash, or omit --from to use the latest migration target.',
|
|
201
|
-
}),
|
|
228
|
+
errorUnexpected(
|
|
229
|
+
`No migration bundle found for --from "${options.from}" (resolved hash: ${fromHash})`,
|
|
230
|
+
{
|
|
231
|
+
why: `The ref resolved successfully but no on-disk migration package has an end-contract hash matching ${fromHash}.`,
|
|
232
|
+
fix: 'Provide a ref or hash that corresponds to an existing migration package, or run `migration list` to see available migrations.',
|
|
233
|
+
},
|
|
234
|
+
),
|
|
202
235
|
);
|
|
203
236
|
}
|
|
204
|
-
|
|
205
|
-
fromContract =
|
|
206
|
-
fromContractSourceDir = resolved.value.dirPath;
|
|
237
|
+
fromContractSourceDir = matchingBundle.dirPath;
|
|
238
|
+
fromContract = await readPredecessorEndContract(fromContractSourceDir);
|
|
207
239
|
} else {
|
|
208
240
|
const latestMigration = findLatestMigration(graph);
|
|
209
241
|
if (latestMigration) {
|
|
@@ -212,8 +244,8 @@ async function executeMigrationPlanCommand(
|
|
|
212
244
|
(p) => p.metadata.migrationHash === latestMigration.migrationHash,
|
|
213
245
|
);
|
|
214
246
|
if (leafPkg) {
|
|
215
|
-
fromContract = leafPkg.metadata.toContract;
|
|
216
247
|
fromContractSourceDir = leafPkg.dirPath;
|
|
248
|
+
fromContract = await readPredecessorEndContract(fromContractSourceDir);
|
|
217
249
|
}
|
|
218
250
|
}
|
|
219
251
|
}
|
|
@@ -221,6 +253,12 @@ async function executeMigrationPlanCommand(
|
|
|
221
253
|
if (MigrationToolsError.is(error)) {
|
|
222
254
|
return notOk(mapMigrationToolsError(error));
|
|
223
255
|
}
|
|
256
|
+
// `readPredecessorEndContract` raises a `CliStructuredError` directly
|
|
257
|
+
// for the missing-snapshot case so the operator gets a precise
|
|
258
|
+
// why/fix; pass it through unchanged rather than re-wrapping.
|
|
259
|
+
if (CliStructuredError.is(error)) {
|
|
260
|
+
return notOk(error);
|
|
261
|
+
}
|
|
224
262
|
// Wrap unexpected (non-MigrationToolsError) failures from the migration
|
|
225
263
|
// load phase in a structured CLI envelope. Letting them throw would
|
|
226
264
|
// bypass `handleResult()` and crash the command — see CLI structured-
|
|
@@ -327,8 +365,6 @@ async function executeMigrationPlanCommand(
|
|
|
327
365
|
const baseMetadata: Omit<MigrationMetadata, 'migrationHash' | 'providedInvariants'> = {
|
|
328
366
|
from: fromHash,
|
|
329
367
|
to: toStorageHash,
|
|
330
|
-
fromContract,
|
|
331
|
-
toContract: toContractJson,
|
|
332
368
|
hints: {
|
|
333
369
|
used: [],
|
|
334
370
|
applied: [],
|
|
@@ -491,7 +527,10 @@ export function createMigrationPlanCommand(): Command {
|
|
|
491
527
|
addGlobalOptions(command)
|
|
492
528
|
.option('--config <path>', 'Path to prisma-next.config.ts')
|
|
493
529
|
.option('--name <slug>', 'Name slug for the migration directory', 'migration')
|
|
494
|
-
.option(
|
|
530
|
+
.option(
|
|
531
|
+
'--from <contract>',
|
|
532
|
+
'Starting contract reference (hash, prefix, ref name, migration dir name, <dir>^, or ./path)',
|
|
533
|
+
)
|
|
495
534
|
.action(async (options: MigrationPlanOptions) => {
|
|
496
535
|
const flags = parseGlobalFlags(options);
|
|
497
536
|
const startTime = Date.now();
|
|
@@ -557,7 +596,7 @@ export function formatMigrationPlanOutput(result: MigrationPlanResult, flags: Gl
|
|
|
557
596
|
}
|
|
558
597
|
lines.push('');
|
|
559
598
|
lines.push(
|
|
560
|
-
`Next: review the extension migrations above, then run ${green_('prisma-next
|
|
599
|
+
`Next: review the extension migrations above, then run ${green_('prisma-next migrate')}.`,
|
|
561
600
|
);
|
|
562
601
|
}
|
|
563
602
|
|
|
@@ -628,11 +667,11 @@ export function formatMigrationPlanOutput(result: MigrationPlanResult, flags: Gl
|
|
|
628
667
|
|
|
629
668
|
lines.push('');
|
|
630
669
|
// The "Next:" hint always points at the canonical apply path
|
|
631
|
-
// (`prisma-next
|
|
632
|
-
//
|
|
633
|
-
//
|
|
670
|
+
// (`prisma-next migrate`) regardless of how many spaces were
|
|
671
|
+
// materialised — `db update` is a dev-time convenience, not the
|
|
672
|
+
// canonical replay step.
|
|
634
673
|
lines.push(
|
|
635
|
-
`Next: review ${green_(result.dir ?? '<dir>')} if needed, then run ${green_('prisma-next
|
|
674
|
+
`Next: review ${green_(result.dir ?? '<dir>')} if needed, then run ${green_('prisma-next migrate')}.`,
|
|
636
675
|
);
|
|
637
676
|
|
|
638
677
|
if (result.preview && result.preview.statements.length > 0) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import type { Contract } from '@prisma-next/contract/types';
|
|
3
3
|
import {
|
|
4
|
+
APP_SPACE_ID,
|
|
4
5
|
createControlStack,
|
|
5
6
|
type MigrationPlanOperation,
|
|
6
7
|
type OperationPreview,
|
|
@@ -12,6 +13,8 @@ import {
|
|
|
12
13
|
reconstructGraph,
|
|
13
14
|
} from '@prisma-next/migration-tools/migration-graph';
|
|
14
15
|
import type { OnDiskMigrationPackage } from '@prisma-next/migration-tools/package';
|
|
16
|
+
import { parseMigrationRef } from '@prisma-next/migration-tools/ref-resolution';
|
|
17
|
+
import { readRefs } from '@prisma-next/migration-tools/refs';
|
|
15
18
|
import { spaceMigrationDirectory } from '@prisma-next/migration-tools/spaces';
|
|
16
19
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
17
20
|
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
@@ -26,6 +29,7 @@ import {
|
|
|
26
29
|
errorRuntime,
|
|
27
30
|
errorUnexpected,
|
|
28
31
|
mapMigrationToolsError,
|
|
32
|
+
mapRefResolutionError,
|
|
29
33
|
} from '../utils/cli-errors';
|
|
30
34
|
import {
|
|
31
35
|
addGlobalOptions,
|
|
@@ -33,6 +37,7 @@ import {
|
|
|
33
37
|
resolveMigrationPaths,
|
|
34
38
|
setCommandDescriptions,
|
|
35
39
|
setCommandExamples,
|
|
40
|
+
setCommandSeeAlso,
|
|
36
41
|
} from '../utils/command-helpers';
|
|
37
42
|
import { buildContractSpaceAggregate } from '../utils/contract-space-aggregate-loader';
|
|
38
43
|
import { formatMigrationShowOutput } from '../utils/formatters/migrations';
|
|
@@ -237,7 +242,7 @@ async function executeMigrationShowCommand(
|
|
|
237
242
|
ui: TerminalUI,
|
|
238
243
|
): Promise<Result<MigrationShowResult, CliStructuredError>> {
|
|
239
244
|
const config = await loadConfig(options.config);
|
|
240
|
-
const { configPath, migrationsDir, appMigrationsDir, appMigrationsRelative } =
|
|
245
|
+
const { configPath, migrationsDir, appMigrationsDir, appMigrationsRelative, refsDir } =
|
|
241
246
|
resolveMigrationPaths(options.config, config);
|
|
242
247
|
|
|
243
248
|
const contractPathAbsolute = resolveContractPath(config);
|
|
@@ -261,7 +266,91 @@ async function executeMigrationShowCommand(
|
|
|
261
266
|
ui.stderr(header);
|
|
262
267
|
}
|
|
263
268
|
|
|
264
|
-
//
|
|
269
|
+
// `migration show` is an offline command; the control client is constructed
|
|
270
|
+
// purely to dispatch the family-specific `toOperationPreview` capability and
|
|
271
|
+
// is not connected to a database.
|
|
272
|
+
const client = createControlClient({
|
|
273
|
+
family: config.family,
|
|
274
|
+
target: config.target,
|
|
275
|
+
adapter: config.adapter,
|
|
276
|
+
...ifDefined('driver', config.driver),
|
|
277
|
+
extensionPacks: config.extensionPacks ?? [],
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
// Explicit-target path. Read the app-space migrations directory directly
|
|
281
|
+
// and resolve `target` against the app graph. We deliberately skip
|
|
282
|
+
// `buildContractSpaceAggregate` here for two reasons:
|
|
283
|
+
//
|
|
284
|
+
// 1. Functional: the user asked about ONE specific migration. They don't
|
|
285
|
+
// need extension-space enumeration; resolving + rendering the named
|
|
286
|
+
// package is enough.
|
|
287
|
+
// 2. UX: the aggregate's layout-integrity check (PN-MIG-5001) fires when
|
|
288
|
+
// an extension is declared but its migrations directory hasn't been
|
|
289
|
+
// materialised. Gating an offline read-only inspect command on that
|
|
290
|
+
// check forces users to run `migrate` against a database before they
|
|
291
|
+
// can see what a migration contains — which contradicts what an
|
|
292
|
+
// offline read-only verb should require.
|
|
293
|
+
//
|
|
294
|
+
// Same pattern as `migration list`, `migration graph`, `migration check`:
|
|
295
|
+
// those verbs read `appMigrationsDir` directly without ever consulting
|
|
296
|
+
// the aggregate.
|
|
297
|
+
if (target) {
|
|
298
|
+
try {
|
|
299
|
+
let appPkg: OnDiskMigrationPackage;
|
|
300
|
+
if (looksLikePath(target)) {
|
|
301
|
+
const resolved = resolveAppTargetPath(target, appMigrationsDir, appMigrationsRelative);
|
|
302
|
+
if (!resolved.ok) return resolved;
|
|
303
|
+
appPkg = await readMigrationPackage(resolved.value);
|
|
304
|
+
} else {
|
|
305
|
+
const allPackages = await readMigrationsDir(appMigrationsDir);
|
|
306
|
+
if (allPackages.length === 0) {
|
|
307
|
+
return notOk(
|
|
308
|
+
errorRuntime('No migrations found', {
|
|
309
|
+
why: `No migration packages found in ${appMigrationsRelative}`,
|
|
310
|
+
fix: 'Run `prisma-next migration plan` to create a migration first.',
|
|
311
|
+
}),
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
const graph = reconstructGraph(allPackages);
|
|
315
|
+
const refs = await readRefs(refsDir);
|
|
316
|
+
const migResult = parseMigrationRef(target, { graph, refs });
|
|
317
|
+
if (!migResult.ok) {
|
|
318
|
+
return notOk(mapRefResolutionError(migResult.failure));
|
|
319
|
+
}
|
|
320
|
+
const matchedPkg = allPackages.find(
|
|
321
|
+
(p) => p.metadata.migrationHash === migResult.value.migrationHash,
|
|
322
|
+
);
|
|
323
|
+
if (!matchedPkg) {
|
|
324
|
+
return notOk(
|
|
325
|
+
errorRuntime('Migration package not found', {
|
|
326
|
+
why: `Resolved migration "${migResult.value.dirName}" but the package was not loaded`,
|
|
327
|
+
fix: 'The migrations directory may be corrupted. Inspect the migration.json files.',
|
|
328
|
+
}),
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
appPkg = matchedPkg;
|
|
332
|
+
}
|
|
333
|
+
return ok({
|
|
334
|
+
ok: true,
|
|
335
|
+
spaces: [pkgToSpaceResult(APP_SPACE_ID, appPkg, client)],
|
|
336
|
+
});
|
|
337
|
+
} catch (error) {
|
|
338
|
+
if (MigrationToolsError.is(error)) {
|
|
339
|
+
return notOk(mapMigrationToolsError(error));
|
|
340
|
+
}
|
|
341
|
+
return notOk(
|
|
342
|
+
errorUnexpected(error instanceof Error ? error.message : String(error), {
|
|
343
|
+
why: `Failed to read app-space migration: ${error instanceof Error ? error.message : String(error)}`,
|
|
344
|
+
}),
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// No-target path. Enumerate the latest migration per space (app +
|
|
350
|
+
// extensions). The aggregate-loader is needed here because we need to
|
|
351
|
+
// know which extension spaces are declared; its layout-integrity check
|
|
352
|
+
// is appropriate at this entry point because the user is asking the
|
|
353
|
+
// system to report on every loaded space.
|
|
265
354
|
let contractJsonContent: string;
|
|
266
355
|
try {
|
|
267
356
|
contractJsonContent = await readFile(contractPathAbsolute, 'utf-8');
|
|
@@ -293,7 +382,6 @@ async function executeMigrationShowCommand(
|
|
|
293
382
|
);
|
|
294
383
|
}
|
|
295
384
|
|
|
296
|
-
// Build the aggregate against current disk state to enumerate all spaces.
|
|
297
385
|
const stack = createControlStack(config);
|
|
298
386
|
const familyInstance = config.family.create(stack);
|
|
299
387
|
const aggregateResult = await buildContractSpaceAggregate({
|
|
@@ -308,66 +396,41 @@ async function executeMigrationShowCommand(
|
|
|
308
396
|
}
|
|
309
397
|
const aggregate = aggregateResult.value;
|
|
310
398
|
|
|
311
|
-
// `migration show` is an offline command; the control client is constructed
|
|
312
|
-
// purely to dispatch the family-specific `toOperationPreview` capability and
|
|
313
|
-
// is not connected to a database.
|
|
314
|
-
const client = createControlClient({
|
|
315
|
-
family: config.family,
|
|
316
|
-
target: config.target,
|
|
317
|
-
adapter: config.adapter,
|
|
318
|
-
...ifDefined('driver', config.driver),
|
|
319
|
-
extensionPacks: config.extensionPacks ?? [],
|
|
320
|
-
});
|
|
321
|
-
|
|
322
399
|
const spaces: MigrationShowSpaceResult[] = [];
|
|
323
400
|
|
|
324
|
-
// App space:
|
|
401
|
+
// App space: latest leaf.
|
|
325
402
|
try {
|
|
326
|
-
|
|
327
|
-
if (
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
appPkg = resolved.value;
|
|
345
|
-
} else {
|
|
346
|
-
const graph = reconstructGraph(allPackages);
|
|
347
|
-
const latestMigration = findLatestMigration(graph);
|
|
348
|
-
if (!latestMigration) {
|
|
349
|
-
return notOk(
|
|
350
|
-
errorRuntime('Could not resolve latest migration', {
|
|
351
|
-
why: 'No latest migration found in the migration history',
|
|
352
|
-
fix: 'The migrations directory may be corrupted. Inspect the migration.json files.',
|
|
353
|
-
}),
|
|
354
|
-
);
|
|
355
|
-
}
|
|
356
|
-
const leafPkg = allPackages.find(
|
|
357
|
-
(p) => p.metadata.migrationHash === latestMigration.migrationHash,
|
|
358
|
-
);
|
|
359
|
-
if (!leafPkg) {
|
|
360
|
-
return notOk(
|
|
361
|
-
errorRuntime('Could not resolve latest migration', {
|
|
362
|
-
why: `Latest migration ${latestMigration.dirName} does not match any package`,
|
|
363
|
-
fix: 'The migrations directory may be corrupted. Inspect the migration.json files.',
|
|
364
|
-
}),
|
|
365
|
-
);
|
|
366
|
-
}
|
|
367
|
-
appPkg = leafPkg;
|
|
368
|
-
}
|
|
403
|
+
const allPackages = await readMigrationsDir(appMigrationsDir);
|
|
404
|
+
if (allPackages.length === 0) {
|
|
405
|
+
return notOk(
|
|
406
|
+
errorRuntime('No migrations found', {
|
|
407
|
+
why: `No migration packages found in ${appMigrationsRelative}`,
|
|
408
|
+
fix: 'Run `prisma-next migration plan` to create a migration first.',
|
|
409
|
+
}),
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
const graph = reconstructGraph(allPackages);
|
|
413
|
+
const latestMigration = findLatestMigration(graph);
|
|
414
|
+
if (!latestMigration) {
|
|
415
|
+
return notOk(
|
|
416
|
+
errorRuntime('Could not resolve latest migration', {
|
|
417
|
+
why: 'No latest migration found in the migration history',
|
|
418
|
+
fix: 'The migrations directory may be corrupted. Inspect the migration.json files.',
|
|
419
|
+
}),
|
|
420
|
+
);
|
|
369
421
|
}
|
|
370
|
-
|
|
422
|
+
const leafPkg = allPackages.find(
|
|
423
|
+
(p) => p.metadata.migrationHash === latestMigration.migrationHash,
|
|
424
|
+
);
|
|
425
|
+
if (!leafPkg) {
|
|
426
|
+
return notOk(
|
|
427
|
+
errorRuntime('Could not resolve latest migration', {
|
|
428
|
+
why: `Latest migration ${latestMigration.dirName} does not match any package`,
|
|
429
|
+
fix: 'The migrations directory may be corrupted. Inspect the migration.json files.',
|
|
430
|
+
}),
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
spaces.push(pkgToSpaceResult(aggregate.app.spaceId, leafPkg, client));
|
|
371
434
|
} catch (error) {
|
|
372
435
|
if (MigrationToolsError.is(error)) {
|
|
373
436
|
return notOk(mapMigrationToolsError(error));
|
|
@@ -415,8 +478,17 @@ export function createMigrationShowCommand(): Command {
|
|
|
415
478
|
'prisma-next migration show',
|
|
416
479
|
'prisma-next migration show sha256:a1b2c3',
|
|
417
480
|
]);
|
|
481
|
+
setCommandSeeAlso(command, [
|
|
482
|
+
{ verb: 'migration status', oneLiner: 'Show migration path and pending status' },
|
|
483
|
+
{ verb: 'migration log', oneLiner: 'Show executed migration history' },
|
|
484
|
+
{ verb: 'migration list', oneLiner: 'List on-disk migrations' },
|
|
485
|
+
{ verb: 'migration graph', oneLiner: 'Show the migration graph topology' },
|
|
486
|
+
]);
|
|
418
487
|
addGlobalOptions(command)
|
|
419
|
-
.argument(
|
|
488
|
+
.argument(
|
|
489
|
+
'[target]',
|
|
490
|
+
'Migration reference: directory name, hash/prefix, or path (defaults to latest)',
|
|
491
|
+
)
|
|
420
492
|
.option('--config <path>', 'Path to prisma-next.config.ts')
|
|
421
493
|
.action(async (target: string | undefined, options: MigrationShowOptions) => {
|
|
422
494
|
const flags = parseGlobalFlags(options);
|