@prisma-next/cli 0.8.0-dev.7 → 0.8.0-dev.8
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 +66 -18
- 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.mjs +5 -5
- 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-CcDCJMLk.mjs → init-e5Q4s1_5.mjs} +7 -6
- package/dist/{init-CcDCJMLk.mjs.map → init-e5Q4s1_5.mjs.map} +1 -1
- 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-DTwYi61q.mjs → migration-plan-DWB-NTxH.mjs} +26 -24
- 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-DEg3SSnJ.mjs → output-B60Gw5fu.mjs} +1 -1
- package/dist/{output-DEg3SSnJ.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/{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-plan.ts +29 -22
- 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/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/migration-plan-DTwYi61q.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
|
@@ -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);
|
|
@@ -19,8 +19,9 @@ import {
|
|
|
19
19
|
findReachableLeaves,
|
|
20
20
|
} from '@prisma-next/migration-tools/migration-graph';
|
|
21
21
|
import type { OnDiskMigrationPackage } from '@prisma-next/migration-tools/package';
|
|
22
|
+
import { parseContractRef } from '@prisma-next/migration-tools/ref-resolution';
|
|
22
23
|
import type { RefEntry, Refs } from '@prisma-next/migration-tools/refs';
|
|
23
|
-
import { readRefs
|
|
24
|
+
import { readRefs } from '@prisma-next/migration-tools/refs';
|
|
24
25
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
25
26
|
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
26
27
|
import { cyan, dim, magenta, yellow } from 'colorette';
|
|
@@ -33,6 +34,7 @@ import {
|
|
|
33
34
|
errorRuntime,
|
|
34
35
|
errorUnexpected,
|
|
35
36
|
mapMigrationToolsError,
|
|
37
|
+
mapRefResolutionError,
|
|
36
38
|
} from '../utils/cli-errors';
|
|
37
39
|
import {
|
|
38
40
|
addGlobalOptions,
|
|
@@ -43,6 +45,7 @@ import {
|
|
|
43
45
|
resolveMigrationPaths,
|
|
44
46
|
setCommandDescriptions,
|
|
45
47
|
setCommandExamples,
|
|
48
|
+
setCommandSeeAlso,
|
|
46
49
|
toPathDecisionResult,
|
|
47
50
|
toStructuralEdge,
|
|
48
51
|
} from '../utils/command-helpers';
|
|
@@ -70,10 +73,8 @@ import { TerminalUI } from '../utils/terminal-ui';
|
|
|
70
73
|
interface MigrationStatusOptions extends CommonCommandOptions {
|
|
71
74
|
readonly db?: string;
|
|
72
75
|
readonly config?: string;
|
|
73
|
-
readonly
|
|
74
|
-
readonly
|
|
75
|
-
readonly limit?: string;
|
|
76
|
-
readonly all?: boolean;
|
|
76
|
+
readonly to?: string;
|
|
77
|
+
readonly from?: string;
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
export interface MigrationStatusEntry {
|
|
@@ -94,7 +95,7 @@ export interface MigrationStatusEntry {
|
|
|
94
95
|
*
|
|
95
96
|
* - `headHash`: the on-disk head ref's hash (where the space is going).
|
|
96
97
|
* - `markerHash`: the live marker hash for the space, or null if no
|
|
97
|
-
* marker has been written yet (greenfield, or pre-`
|
|
98
|
+
* marker has been written yet (greenfield, or pre-`migrate`).
|
|
98
99
|
* - `pendingCount`: number of migration edges between marker and head.
|
|
99
100
|
* Computed via {@link graphWalkStrategy}; 0 means the space is
|
|
100
101
|
* already at head.
|
|
@@ -420,23 +421,6 @@ function resolveDisplayChain(
|
|
|
420
421
|
return toTarget;
|
|
421
422
|
}
|
|
422
423
|
|
|
423
|
-
const DEFAULT_LIMIT = 10;
|
|
424
|
-
|
|
425
|
-
function determineLimit(opts: MigrationStatusOptions) {
|
|
426
|
-
if (opts.all) {
|
|
427
|
-
// No limit
|
|
428
|
-
return;
|
|
429
|
-
}
|
|
430
|
-
if (!opts.limit) {
|
|
431
|
-
return DEFAULT_LIMIT;
|
|
432
|
-
}
|
|
433
|
-
const parsed = Number.parseInt(opts.limit, 10);
|
|
434
|
-
if (Number.isNaN(parsed)) {
|
|
435
|
-
return DEFAULT_LIMIT;
|
|
436
|
-
}
|
|
437
|
-
return parsed;
|
|
438
|
-
}
|
|
439
|
-
|
|
440
424
|
/**
|
|
441
425
|
* Build the aggregate enumeration of contract spaces for the status
|
|
442
426
|
* output. Loads the aggregate from disk (lossy on failure — extension
|
|
@@ -581,11 +565,32 @@ async function executeMigrationStatusCommand(
|
|
|
581
565
|
throw error;
|
|
582
566
|
}
|
|
583
567
|
|
|
584
|
-
|
|
585
|
-
|
|
568
|
+
let fromOverrideHash: string | undefined;
|
|
569
|
+
|
|
570
|
+
if (options.to || options.from) {
|
|
586
571
|
try {
|
|
587
|
-
|
|
588
|
-
|
|
572
|
+
const { graph: earlyGraph } = await loadMigrationPackages(appMigrationsDir);
|
|
573
|
+
|
|
574
|
+
if (options.to) {
|
|
575
|
+
const refResult = parseContractRef(options.to, { graph: earlyGraph, refs: allRefs });
|
|
576
|
+
if (!refResult.ok) {
|
|
577
|
+
return notOk(mapRefResolutionError(refResult.failure));
|
|
578
|
+
}
|
|
579
|
+
activeRefHash = refResult.value.hash;
|
|
580
|
+
if (refResult.value.provenance.kind === 'ref') {
|
|
581
|
+
const resolvedRefName = refResult.value.provenance.refName;
|
|
582
|
+
activeRefName = resolvedRefName;
|
|
583
|
+
activeRefEntry = allRefs[resolvedRefName];
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
if (options.from) {
|
|
588
|
+
const fromResult = parseContractRef(options.from, { graph: earlyGraph, refs: allRefs });
|
|
589
|
+
if (!fromResult.ok) {
|
|
590
|
+
return notOk(mapRefResolutionError(fromResult.failure));
|
|
591
|
+
}
|
|
592
|
+
fromOverrideHash = fromResult.value.hash;
|
|
593
|
+
}
|
|
589
594
|
} catch (error) {
|
|
590
595
|
if (MigrationToolsError.is(error)) {
|
|
591
596
|
return notOk(mapMigrationToolsError(error));
|
|
@@ -613,6 +618,9 @@ async function executeMigrationStatusCommand(
|
|
|
613
618
|
if (activeRefName) {
|
|
614
619
|
details.push({ label: 'ref', value: activeRefName });
|
|
615
620
|
}
|
|
621
|
+
if (options.from) {
|
|
622
|
+
details.push({ label: 'from', value: options.from });
|
|
623
|
+
}
|
|
616
624
|
if (activeRefEntry && activeRefEntry.invariants.length > 0) {
|
|
617
625
|
details.push({
|
|
618
626
|
label: 'required',
|
|
@@ -696,8 +704,8 @@ async function executeMigrationStatusCommand(
|
|
|
696
704
|
severity: 'warn',
|
|
697
705
|
message: 'There are multiple valid migration paths — you must select a target',
|
|
698
706
|
hints: [
|
|
699
|
-
"Use '--
|
|
700
|
-
"Or 'prisma-next
|
|
707
|
+
"Use '--to <contract>' to select a target",
|
|
708
|
+
"Or 'prisma-next ref set <name> <hash>' to create one",
|
|
701
709
|
],
|
|
702
710
|
});
|
|
703
711
|
}
|
|
@@ -751,6 +759,12 @@ async function executeMigrationStatusCommand(
|
|
|
751
759
|
}
|
|
752
760
|
}
|
|
753
761
|
|
|
762
|
+
if (fromOverrideHash !== undefined) {
|
|
763
|
+
markerHash = fromOverrideHash;
|
|
764
|
+
mode = 'offline';
|
|
765
|
+
allMarkers = null;
|
|
766
|
+
}
|
|
767
|
+
|
|
754
768
|
// Build the aggregate enumeration of contract spaces. Lossy on
|
|
755
769
|
// failure (extensions are simply omitted) so the existing
|
|
756
770
|
// single-space app pipeline below still runs even if extensions
|
|
@@ -863,7 +877,7 @@ async function executeMigrationStatusCommand(
|
|
|
863
877
|
code: 'MIGRATION.NO_MARKER',
|
|
864
878
|
severity: 'warn',
|
|
865
879
|
message: 'Database has not been initialized — no migration marker found',
|
|
866
|
-
hints: ["Run 'prisma-next
|
|
880
|
+
hints: ["Run 'prisma-next migrate' to apply pending migrations"],
|
|
867
881
|
});
|
|
868
882
|
}
|
|
869
883
|
|
|
@@ -923,7 +937,7 @@ async function executeMigrationStatusCommand(
|
|
|
923
937
|
let missingInvariants: readonly string[] | undefined;
|
|
924
938
|
let effectiveRequired = new Set<string>();
|
|
925
939
|
if (mode === 'online') {
|
|
926
|
-
// Mirrors `
|
|
940
|
+
// Mirrors `migrate.ts`: compute `effectiveRequired = required −
|
|
927
941
|
// marker.invariants` directly, then derive the display fields from it.
|
|
928
942
|
// `appliedInvariants` is the intersection (`required ∩ marker`), which
|
|
929
943
|
// is what JSON consumers see for the active ref; the unfiltered set
|
|
@@ -945,17 +959,17 @@ async function executeMigrationStatusCommand(
|
|
|
945
959
|
if (mode === 'online') {
|
|
946
960
|
if (markerHash !== undefined && !graph.nodes.has(markerHash) && markerHash === contractHash) {
|
|
947
961
|
summary = `${bundles.length} migration(s) on disk`;
|
|
948
|
-
} else if (activeRefHash && markerHash !== undefined) {
|
|
949
|
-
const distance = summarizeRefDistance(graph, markerHash, activeRefHash, activeRefName
|
|
962
|
+
} else if (activeRefHash && activeRefName && markerHash !== undefined) {
|
|
963
|
+
const distance = summarizeRefDistance(graph, markerHash, activeRefHash, activeRefName);
|
|
950
964
|
summary = hasInvariantWork ? `${distance} — missing invariant(s): ${missingList}` : distance;
|
|
951
965
|
} else if (pendingCount === 0 && !hasInvariantWork) {
|
|
952
966
|
summary = `Database is up to date (${appliedCount} migration${appliedCount !== 1 ? 's' : ''} applied)`;
|
|
953
967
|
} else if (pendingCount === 0 && hasInvariantWork) {
|
|
954
|
-
summary = `Missing invariant(s): ${missingList} — run 'prisma-next
|
|
968
|
+
summary = `Missing invariant(s): ${missingList} — run 'prisma-next migrate --to ${activeRefName ?? '<ref>'}' to apply`;
|
|
955
969
|
} else if (markerHash === undefined) {
|
|
956
970
|
summary = `${pendingCount} pending migration(s) — database has no marker`;
|
|
957
971
|
} else {
|
|
958
|
-
summary = `${pendingCount} pending migration(s) — run 'prisma-next
|
|
972
|
+
summary = `${pendingCount} pending migration(s) — run 'prisma-next migrate' to apply`;
|
|
959
973
|
}
|
|
960
974
|
} else {
|
|
961
975
|
summary = `${entries.length} migration(s) on disk`;
|
|
@@ -997,8 +1011,7 @@ async function executeMigrationStatusCommand(
|
|
|
997
1011
|
diagnostics.push({
|
|
998
1012
|
code: 'MIGRATION.MARKER_NOT_IN_HISTORY',
|
|
999
1013
|
severity: 'warn',
|
|
1000
|
-
message:
|
|
1001
|
-
'Database matches the current contract but was updated directly (not via migration apply)',
|
|
1014
|
+
message: 'Database matches the current contract but was updated directly (not via migrate)',
|
|
1002
1015
|
hints: ["Run 'prisma-next migration plan' to plan a migration to your current contract"],
|
|
1003
1016
|
});
|
|
1004
1017
|
} else if (pendingCount > 0) {
|
|
@@ -1006,7 +1019,7 @@ async function executeMigrationStatusCommand(
|
|
|
1006
1019
|
code: 'MIGRATION.DATABASE_BEHIND',
|
|
1007
1020
|
severity: 'info',
|
|
1008
1021
|
message: `${pendingCount} migration(s) pending`,
|
|
1009
|
-
hints: ["Run 'prisma-next
|
|
1022
|
+
hints: ["Run 'prisma-next migrate' to apply pending migrations"],
|
|
1010
1023
|
});
|
|
1011
1024
|
} else if (hasInvariantWork) {
|
|
1012
1025
|
diagnostics.push({
|
|
@@ -1014,7 +1027,7 @@ async function executeMigrationStatusCommand(
|
|
|
1014
1027
|
severity: 'info',
|
|
1015
1028
|
message: `Missing required invariant(s): ${missingList}`,
|
|
1016
1029
|
hints: [
|
|
1017
|
-
`Run 'prisma-next
|
|
1030
|
+
`Run 'prisma-next migrate --to ${activeRefName ?? '<ref>'}' to apply a path that covers the required invariants`,
|
|
1018
1031
|
],
|
|
1019
1032
|
});
|
|
1020
1033
|
} else if (!routingUnreachable) {
|
|
@@ -1056,37 +1069,41 @@ export function createMigrationStatusCommand(): Command {
|
|
|
1056
1069
|
const command = new Command('status');
|
|
1057
1070
|
setCommandDescriptions(
|
|
1058
1071
|
command,
|
|
1059
|
-
'Show migration
|
|
1060
|
-
'
|
|
1061
|
-
'
|
|
1062
|
-
'
|
|
1072
|
+
'Show migration path and pending status',
|
|
1073
|
+
'Shows which migrations are pending between the database marker and\n' +
|
|
1074
|
+
'the target contract. Requires a database connection for live status.\n' +
|
|
1075
|
+
'Use `migration graph` for topology, `migration log` for history,\n' +
|
|
1076
|
+
'and `migration list` for on-disk enumeration.',
|
|
1063
1077
|
);
|
|
1064
1078
|
setCommandExamples(command, [
|
|
1065
|
-
'prisma-next migration status',
|
|
1066
1079
|
'prisma-next migration status --db $DATABASE_URL',
|
|
1080
|
+
'prisma-next migration status --to production --db $DATABASE_URL',
|
|
1081
|
+
]);
|
|
1082
|
+
setCommandSeeAlso(command, [
|
|
1083
|
+
{ verb: 'migration log', oneLiner: 'Show executed migration history' },
|
|
1084
|
+
{ verb: 'migration list', oneLiner: 'List on-disk migrations' },
|
|
1085
|
+
{ verb: 'migration graph', oneLiner: 'Show the migration graph topology' },
|
|
1086
|
+
{ verb: 'migration show', oneLiner: 'Display migration package contents' },
|
|
1067
1087
|
]);
|
|
1068
1088
|
addGlobalOptions(command)
|
|
1069
1089
|
.option('--db <url>', 'Database connection string')
|
|
1070
1090
|
.option('--config <path>', 'Path to prisma-next.config.ts')
|
|
1071
|
-
.option(
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1091
|
+
.option(
|
|
1092
|
+
'--to <contract>',
|
|
1093
|
+
'Target contract reference (hash, prefix, ref name, migration dir name, <dir>^, or ./path)',
|
|
1094
|
+
)
|
|
1095
|
+
.option(
|
|
1096
|
+
'--from <contract>',
|
|
1097
|
+
'Origin contract reference; same grammar as --to. Supplying --from switches to offline path computation.',
|
|
1098
|
+
)
|
|
1075
1099
|
.action(async (options: MigrationStatusOptions) => {
|
|
1076
1100
|
const flags = parseGlobalFlags(options);
|
|
1077
|
-
|
|
1078
1101
|
const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });
|
|
1079
1102
|
|
|
1080
1103
|
const result = await executeMigrationStatusCommand(options, flags, ui);
|
|
1081
1104
|
|
|
1082
1105
|
const exitCode = handleResult(result, flags, ui, (statusResult) => {
|
|
1083
1106
|
if (flags.json) {
|
|
1084
|
-
// Strip non-JSON-shape fields before emitting. These belong to
|
|
1085
|
-
// the in-memory result so the human renderer can avoid
|
|
1086
|
-
// recomputing them, but they would either bloat the wire format
|
|
1087
|
-
// (graph, bundles, edgeStatuses) or expose internals
|
|
1088
|
-
// (activeRefHash, activeRefName, diverged) that consumers should
|
|
1089
|
-
// read off `pathDecision` / `refs` instead.
|
|
1090
1107
|
const {
|
|
1091
1108
|
graph: _graph,
|
|
1092
1109
|
bundles: _bundles,
|
|
@@ -1101,7 +1118,6 @@ export function createMigrationStatusCommand(): Command {
|
|
|
1101
1118
|
const colorize = flags.color !== false;
|
|
1102
1119
|
|
|
1103
1120
|
if (statusResult.graph) {
|
|
1104
|
-
const limit = determineLimit(options);
|
|
1105
1121
|
const renderInput = migrationGraphToRenderInput({
|
|
1106
1122
|
graph: statusResult.graph,
|
|
1107
1123
|
mode: statusResult.mode,
|
|
@@ -1113,16 +1129,13 @@ export function createMigrationStatusCommand(): Command {
|
|
|
1113
1129
|
edgeStatuses: statusResult.edgeStatuses,
|
|
1114
1130
|
});
|
|
1115
1131
|
|
|
1116
|
-
const graphToRender =
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
const dagreOptions =
|
|
1121
|
-
!options.graph && isLinearGraph(graphToRender) ? { ranksep: 1 } : undefined;
|
|
1132
|
+
const graphToRender = statusResult.diverged
|
|
1133
|
+
? renderInput.graph
|
|
1134
|
+
: extractRelevantSubgraph(renderInput.graph, renderInput.relevantPaths);
|
|
1135
|
+
const dagreOptions = isLinearGraph(graphToRender) ? { ranksep: 1 } : undefined;
|
|
1122
1136
|
const renderOptions = {
|
|
1123
1137
|
...renderInput.options,
|
|
1124
1138
|
colorize,
|
|
1125
|
-
...ifDefined('limit', limit),
|
|
1126
1139
|
...ifDefined('dagreOptions', dagreOptions),
|
|
1127
1140
|
};
|
|
1128
1141
|
const graphOutput = graphRenderer.render(graphToRender, renderOptions);
|
|
@@ -1209,7 +1222,7 @@ export function formatStatusSummary(result: MigrationStatusResult, colorize: boo
|
|
|
1209
1222
|
if (total > 0) {
|
|
1210
1223
|
lines.push('');
|
|
1211
1224
|
lines.push(
|
|
1212
|
-
`${c(yellow, '⧗')} ${total} pending migration(s) across ${result.spaces.length} space(s) — run 'prisma-next
|
|
1225
|
+
`${c(yellow, '⧗')} ${total} pending migration(s) across ${result.spaces.length} space(s) — run 'prisma-next migrate' to apply`,
|
|
1213
1226
|
);
|
|
1214
1227
|
}
|
|
1215
1228
|
}
|