@prisma-next/cli 0.12.0-dev.27 → 0.12.0-dev.29
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/dist/cli.mjs +12 -12
- package/dist/{client-V7BkIQrQ.mjs → client-xeWpMlq1.mjs} +22 -11
- package/dist/client-xeWpMlq1.mjs.map +1 -0
- package/dist/{command-helpers-DlrUCI7s.mjs → command-helpers-DK_5ItoJ.mjs} +253 -2
- package/dist/command-helpers-DK_5ItoJ.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 +4 -5
- package/dist/commands/db-init.mjs.map +1 -1
- package/dist/commands/db-schema.mjs +3 -3
- package/dist/commands/db-sign.mjs +4 -4
- package/dist/commands/db-update.d.mts.map +1 -1
- package/dist/commands/db-update.mjs +10 -7
- package/dist/commands/db-update.mjs.map +1 -1
- package/dist/commands/db-verify.mjs +1 -1
- package/dist/commands/migrate.d.mts +1 -1
- package/dist/commands/migrate.mjs +5 -6
- package/dist/commands/migrate.mjs.map +1 -1
- package/dist/commands/migration-check.mjs +1 -1
- package/dist/commands/migration-graph.d.mts +12 -15
- package/dist/commands/migration-graph.d.mts.map +1 -1
- package/dist/commands/migration-graph.mjs +84 -51
- package/dist/commands/migration-graph.mjs.map +1 -1
- package/dist/commands/migration-list.d.mts +14 -5
- package/dist/commands/migration-list.d.mts.map +1 -1
- package/dist/commands/migration-list.mjs +1 -187
- package/dist/commands/migration-log.d.mts +2 -2
- package/dist/commands/migration-log.mjs +1 -1
- package/dist/commands/migration-new.mjs +3 -3
- package/dist/commands/migration-plan.mjs +1 -1
- package/dist/commands/migration-show.d.mts +1 -1
- package/dist/commands/migration-show.mjs +3 -4
- package/dist/commands/migration-show.mjs.map +1 -1
- package/dist/commands/migration-status.d.mts +21 -3
- package/dist/commands/migration-status.d.mts.map +1 -1
- package/dist/commands/migration-status.mjs +2 -3
- package/dist/commands/ref.d.mts +1 -1
- package/dist/commands/ref.mjs +3 -3
- package/dist/commands/telemetry/index.mjs +1 -1
- package/dist/{contract-at-errors-DlZHXSkI.mjs → contract-at-errors-DG3kjgoz.mjs} +2 -2
- package/dist/{contract-at-errors-DlZHXSkI.mjs.map → contract-at-errors-DG3kjgoz.mjs.map} +1 -1
- package/dist/{contract-emit-S53EyBRV.mjs → contract-emit-BO0l6fnT.mjs} +3 -3
- package/dist/{contract-emit-S53EyBRV.mjs.map → contract-emit-BO0l6fnT.mjs.map} +1 -1
- package/dist/{contract-emit-CaKp92-Q.mjs → contract-emit-C0Bs0VRj.mjs} +3 -3
- package/dist/{contract-emit-CaKp92-Q.mjs.map → contract-emit-C0Bs0VRj.mjs.map} +1 -1
- package/dist/{contract-infer-Cebb-_Qx.mjs → contract-infer-2wtPflGH.mjs} +3 -3
- package/dist/{contract-infer-Cebb-_Qx.mjs.map → contract-infer-2wtPflGH.mjs.map} +1 -1
- package/dist/{contract-space-aggregate-loader-Dvl1SJ4C.mjs → contract-space-aggregate-loader-Dbr3-jHF.mjs} +3 -3
- package/dist/{contract-space-aggregate-loader-Dvl1SJ4C.mjs.map → contract-space-aggregate-loader-Dbr3-jHF.mjs.map} +1 -1
- package/dist/{db-verify-B1OoWEWn.mjs → db-verify-CxHiSiTG.mjs} +4 -4
- package/dist/{db-verify-B1OoWEWn.mjs.map → db-verify-CxHiSiTG.mjs.map} +1 -1
- 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/{framework-components-DCAT1uUC.mjs → framework-components-CxOVKAAh.mjs} +2 -2
- package/dist/{framework-components-DCAT1uUC.mjs.map → framework-components-CxOVKAAh.mjs.map} +1 -1
- package/dist/{init-Kf3T4A4W.mjs → init-R272pxux.mjs} +3 -3
- package/dist/{init-Kf3T4A4W.mjs.map → init-R272pxux.mjs.map} +1 -1
- package/dist/{inspect-live-schema-DTqflZ8X.mjs → inspect-live-schema-RekOwfi5.mjs} +3 -3
- package/dist/{inspect-live-schema-DTqflZ8X.mjs.map → inspect-live-schema-RekOwfi5.mjs.map} +1 -1
- package/dist/{migration-check-Ccyd0QKb.mjs → migration-check-Dc0cOhKH.mjs} +2 -2
- package/dist/{migration-check-Ccyd0QKb.mjs.map → migration-check-Dc0cOhKH.mjs.map} +1 -1
- package/dist/{migration-command-scaffold-DI7_SFL0.mjs → migration-command-scaffold-ApB3NxWY.mjs} +3 -3
- package/dist/{migration-command-scaffold-DI7_SFL0.mjs.map → migration-command-scaffold-ApB3NxWY.mjs.map} +1 -1
- package/dist/{migration-graph-tree-render-DyDBuJEX.mjs → migration-graph-space-render-dmLLWift.mjs} +389 -210
- package/dist/migration-graph-space-render-dmLLWift.mjs.map +1 -0
- package/dist/migration-list-C5sXrl0U.mjs +228 -0
- package/dist/migration-list-C5sXrl0U.mjs.map +1 -0
- package/dist/{migration-list-types-DV9PBc7Z.d.mts → migration-list-types-DS63IdFd.d.mts} +1 -1
- package/dist/{migration-list-types-DV9PBc7Z.d.mts.map → migration-list-types-DS63IdFd.d.mts.map} +1 -1
- package/dist/{migration-log-Des4seHP.mjs → migration-log-DD_vCbYW.mjs} +4 -4
- package/dist/{migration-log-Des4seHP.mjs.map → migration-log-DD_vCbYW.mjs.map} +1 -1
- package/dist/{migration-plan-DxDTBzGS.mjs → migration-plan-CeTjQOIG.mjs} +5 -5
- package/dist/{migration-plan-DxDTBzGS.mjs.map → migration-plan-CeTjQOIG.mjs.map} +1 -1
- package/dist/{migration-status-CCwqA-vi.mjs → migration-status-qV8ctwPy.mjs} +61 -45
- package/dist/migration-status-qV8ctwPy.mjs.map +1 -0
- package/dist/{ref-advancement-DUZqsue6.mjs → ref-advancement-V1o-9LVK.mjs} +1 -1
- package/dist/{ref-advancement-DUZqsue6.mjs.map → ref-advancement-V1o-9LVK.mjs.map} +1 -1
- package/dist/{telemetry-LFFQmqHd.mjs → telemetry-S-NGi9U6.mjs} +2 -2
- package/dist/{telemetry-LFFQmqHd.mjs.map → telemetry-S-NGi9U6.mjs.map} +1 -1
- package/dist/{terminal-ui-C3xGyxW-.d.mts → terminal-ui-5Y6mrg93.d.mts} +1 -1
- package/dist/{terminal-ui-C3xGyxW-.d.mts.map → terminal-ui-5Y6mrg93.d.mts.map} +1 -1
- package/dist/{types-BdS8PoKM.d.mts → types-Mh7mdPHM.d.mts} +5 -1
- package/dist/types-Mh7mdPHM.d.mts.map +1 -0
- package/dist/{verify-C0TARc6h.mjs → verify-BdI-BgYi.mjs} +2 -2
- package/dist/{verify-C0TARc6h.mjs.map → verify-BdI-BgYi.mjs.map} +1 -1
- package/package.json +18 -18
- package/src/commands/db-update.ts +7 -1
- package/src/commands/migration-graph.ts +125 -58
- package/src/commands/migration-list.ts +43 -9
- package/src/commands/migration-status.ts +106 -74
- package/src/control-api/operations/db-apply.ts +24 -1
- package/src/control-api/types.ts +4 -0
- package/src/utils/cli-errors.ts +17 -0
- package/src/utils/formatters/errors.ts +11 -0
- package/src/utils/formatters/migration-graph-lane-colors.ts +164 -1
- package/src/utils/formatters/migration-graph-rows.ts +128 -15
- package/src/utils/formatters/migration-graph-space-render.ts +138 -0
- package/src/utils/formatters/migration-graph-tree-render.ts +149 -239
- package/src/utils/formatters/migration-list-data-column.ts +6 -0
- package/src/utils/formatters/migration-list-render.ts +43 -23
- package/src/utils/formatters/migration-list-styler.ts +48 -5
- package/src/utils/formatters/migrations.ts +25 -1
- package/src/utils/legend.ts +38 -0
- package/dist/client-V7BkIQrQ.mjs.map +0 -1
- package/dist/command-helpers-DlrUCI7s.mjs.map +0 -1
- package/dist/commands/migration-list.mjs.map +0 -1
- package/dist/migration-graph-tree-render-DyDBuJEX.mjs.map +0 -1
- package/dist/migration-status-CCwqA-vi.mjs.map +0 -1
- package/dist/migration-types-CAQ-0TEE.d.mts +0 -15
- package/dist/migration-types-CAQ-0TEE.d.mts.map +0 -1
- package/dist/migrations-B3H6RTXb.mjs +0 -228
- package/dist/migrations-B3H6RTXb.mjs.map +0 -1
- package/dist/types-BdS8PoKM.d.mts.map +0 -1
|
@@ -3,18 +3,13 @@ import type {
|
|
|
3
3
|
ContractMarkerRecordLike,
|
|
4
4
|
ContractSpaceMember,
|
|
5
5
|
} from '@prisma-next/migration-tools/aggregate';
|
|
6
|
-
import { requireHeadRef } from '@prisma-next/migration-tools/aggregate';
|
|
7
6
|
import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';
|
|
8
7
|
import {
|
|
9
8
|
errorNoInvariantPath,
|
|
10
9
|
errorUnknownInvariant,
|
|
11
10
|
MigrationToolsError,
|
|
12
11
|
} from '@prisma-next/migration-tools/errors';
|
|
13
|
-
import {
|
|
14
|
-
findPath,
|
|
15
|
-
findPathWithDecision,
|
|
16
|
-
findReachableLeaves,
|
|
17
|
-
} from '@prisma-next/migration-tools/migration-graph';
|
|
12
|
+
import { findPath, findPathWithDecision } from '@prisma-next/migration-tools/migration-graph';
|
|
18
13
|
import { parseContractRef } from '@prisma-next/migration-tools/ref-resolution';
|
|
19
14
|
import type { RefEntry, Refs } from '@prisma-next/migration-tools/refs';
|
|
20
15
|
import { readRefs } from '@prisma-next/migration-tools/refs';
|
|
@@ -47,16 +42,19 @@ import {
|
|
|
47
42
|
loadContractRawSafely,
|
|
48
43
|
refusePackageCorruptionOnAggregate,
|
|
49
44
|
} from '../utils/contract-space-aggregate-loader';
|
|
50
|
-
import { buildMigrationGraphLayout } from '../utils/formatters/migration-graph-layout';
|
|
51
|
-
import { buildMigrationGraphRows } from '../utils/formatters/migration-graph-rows';
|
|
52
45
|
import {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
46
|
+
computeGlobalMaxDirNameWidth,
|
|
47
|
+
computeGlobalMaxEdgeTreePrefixWidth,
|
|
48
|
+
indentMigrationGraphTreeBlock,
|
|
49
|
+
renderMigrationGraphSpaceTree,
|
|
50
|
+
} from '../utils/formatters/migration-graph-space-render';
|
|
51
|
+
import type { MigrationEdgeAnnotation } from '../utils/formatters/migration-graph-tree-render';
|
|
52
|
+
import { renderMigrationGraphLegend } from '../utils/formatters/migration-graph-tree-render';
|
|
56
53
|
import type { MigrationListEntry } from '../utils/formatters/migration-list-types';
|
|
57
54
|
import { formatStyledHeader } from '../utils/formatters/styled';
|
|
58
55
|
import type { CommonCommandOptions } from '../utils/global-flags';
|
|
59
56
|
import { type GlobalFlags, parseGlobalFlagsOrExit } from '../utils/global-flags';
|
|
57
|
+
import { shouldShowLegend, validateLegendOptions } from '../utils/legend';
|
|
60
58
|
import type { StatusDiagnostic } from '../utils/migration-types';
|
|
61
59
|
import { handleResult } from '../utils/result-handler';
|
|
62
60
|
import { createTerminalUI, type TerminalUI } from '../utils/terminal-ui';
|
|
@@ -77,6 +75,7 @@ interface MigrationStatusOptions extends CommonCommandOptions {
|
|
|
77
75
|
readonly to?: string;
|
|
78
76
|
readonly from?: string;
|
|
79
77
|
readonly space?: string;
|
|
78
|
+
readonly legend?: boolean;
|
|
80
79
|
}
|
|
81
80
|
|
|
82
81
|
export interface MigrationStatusMigrationEntry extends MigrationListEntry {
|
|
@@ -112,26 +111,8 @@ function shortDisplayHash(hash: string): string {
|
|
|
112
111
|
return stripped.slice(0, 12);
|
|
113
112
|
}
|
|
114
113
|
|
|
115
|
-
function
|
|
116
|
-
|
|
117
|
-
contractHash: string,
|
|
118
|
-
activeRefHash: string | undefined,
|
|
119
|
-
): string | undefined {
|
|
120
|
-
const graph = member.graph();
|
|
121
|
-
if (activeRefHash !== undefined && graph.nodes.has(activeRefHash)) {
|
|
122
|
-
return activeRefHash;
|
|
123
|
-
}
|
|
124
|
-
if (graph.nodes.has(contractHash)) {
|
|
125
|
-
return contractHash;
|
|
126
|
-
}
|
|
127
|
-
if (graph.nodes.size === 0) {
|
|
128
|
-
return requireHeadRef(member).hash;
|
|
129
|
-
}
|
|
130
|
-
const leaves = findReachableLeaves(graph, EMPTY_CONTRACT_HASH);
|
|
131
|
-
if (leaves.length === 1) {
|
|
132
|
-
return leaves[0];
|
|
133
|
-
}
|
|
134
|
-
return undefined;
|
|
114
|
+
function resolveTarget(contractHash: string, activeRefHash: string | undefined): string {
|
|
115
|
+
return activeRefHash ?? contractHash;
|
|
135
116
|
}
|
|
136
117
|
|
|
137
118
|
function buildStatusMigrations(
|
|
@@ -146,28 +127,35 @@ function buildStatusMigrations(
|
|
|
146
127
|
|
|
147
128
|
function renderSpaceTree(args: {
|
|
148
129
|
readonly member: ContractSpaceMember;
|
|
149
|
-
readonly
|
|
130
|
+
readonly liveContractHash: string;
|
|
131
|
+
readonly migrations: readonly MigrationListEntry[];
|
|
150
132
|
readonly markerHash: string | undefined;
|
|
151
133
|
readonly showDbMarker: boolean;
|
|
152
|
-
readonly
|
|
153
|
-
readonly edgeAnnotations: ReadonlyMap<string, MigrationEdgeAnnotation>;
|
|
134
|
+
readonly statusOverlay: ReadonlyMap<string, MigrationEdgeAnnotation>;
|
|
154
135
|
readonly colorize: boolean;
|
|
155
136
|
readonly glyphMode: 'unicode' | 'ascii';
|
|
137
|
+
readonly globalMaxEdgeTreePrefixWidth?: number;
|
|
138
|
+
readonly globalMaxDirNameWidth?: number;
|
|
156
139
|
}): string {
|
|
157
140
|
const graph = args.member.graph();
|
|
158
141
|
if (graph.nodes.size === 0) {
|
|
159
142
|
return '';
|
|
160
143
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
refsByHash,
|
|
166
|
-
|
|
167
|
-
contractHash: args.contractHash,
|
|
168
|
-
edgeAnnotationsByHash: args.edgeAnnotations,
|
|
144
|
+
return renderMigrationGraphSpaceTree({
|
|
145
|
+
graph,
|
|
146
|
+
migrations: args.migrations,
|
|
147
|
+
liveContractHash: args.liveContractHash,
|
|
148
|
+
refsByHash: listRefsByContractHash(args.member),
|
|
149
|
+
statusOverlayByHash: args.statusOverlay,
|
|
169
150
|
colorize: args.colorize,
|
|
170
151
|
glyphMode: args.glyphMode,
|
|
152
|
+
...(args.showDbMarker && args.markerHash !== undefined ? { dbHash: args.markerHash } : {}),
|
|
153
|
+
...(args.globalMaxEdgeTreePrefixWidth !== undefined
|
|
154
|
+
? { globalMaxEdgeTreePrefixWidth: args.globalMaxEdgeTreePrefixWidth }
|
|
155
|
+
: {}),
|
|
156
|
+
...(args.globalMaxDirNameWidth !== undefined
|
|
157
|
+
? { globalMaxDirNameWidth: args.globalMaxDirNameWidth }
|
|
158
|
+
: {}),
|
|
171
159
|
});
|
|
172
160
|
}
|
|
173
161
|
|
|
@@ -175,6 +163,27 @@ function countPending(migrations: readonly MigrationStatusMigrationEntry[]): num
|
|
|
175
163
|
return migrations.filter((m) => m.status === 'pending').length;
|
|
176
164
|
}
|
|
177
165
|
|
|
166
|
+
export function buildNoPathSummary(args: {
|
|
167
|
+
readonly markerHash: string | undefined;
|
|
168
|
+
readonly targetHash: string;
|
|
169
|
+
readonly explicitTarget: boolean;
|
|
170
|
+
readonly refName: string | undefined;
|
|
171
|
+
}): string {
|
|
172
|
+
const markerPart =
|
|
173
|
+
args.markerHash !== undefined
|
|
174
|
+
? `the database state (${shortDisplayHash(args.markerHash)})`
|
|
175
|
+
: 'the database state';
|
|
176
|
+
const targetShort = shortDisplayHash(args.targetHash);
|
|
177
|
+
if (!args.explicitTarget) {
|
|
178
|
+
return `No migration path from ${markerPart} to the application's contract (${targetShort}). Run \`prisma-next migration plan --name <name>\` to author one.`;
|
|
179
|
+
}
|
|
180
|
+
const targetLabel =
|
|
181
|
+
args.refName !== undefined
|
|
182
|
+
? `the target (${targetShort} via \`${args.refName}\`)`
|
|
183
|
+
: `the target (${targetShort})`;
|
|
184
|
+
return `No migration path from ${markerPart} to ${targetLabel}. Run \`prisma-next migration plan --name <name>\` to author one, or pass \`--to <contract>\` to pick a reachable target.`;
|
|
185
|
+
}
|
|
186
|
+
|
|
178
187
|
export function buildStatusHeadline(args: {
|
|
179
188
|
readonly pendingCount: number;
|
|
180
189
|
readonly targetHash: string;
|
|
@@ -185,7 +194,7 @@ export function buildStatusHeadline(args: {
|
|
|
185
194
|
return `Database marker ${shortDisplayHash(args.markerHash)} is not in the on-disk migration graph`;
|
|
186
195
|
}
|
|
187
196
|
if (args.pendingCount === 0) {
|
|
188
|
-
return '
|
|
197
|
+
return 'Up to date';
|
|
189
198
|
}
|
|
190
199
|
return `${args.pendingCount} pending — run \`prisma-next migrate --to ${shortDisplayHash(args.targetHash)}\``;
|
|
191
200
|
}
|
|
@@ -361,6 +370,15 @@ async function executeMigrationStatusCommand(
|
|
|
361
370
|
flags,
|
|
362
371
|
});
|
|
363
372
|
ui.stderr(header);
|
|
373
|
+
if (shouldShowLegend(options, flags)) {
|
|
374
|
+
ui.stderr(
|
|
375
|
+
renderMigrationGraphLegend({
|
|
376
|
+
colorize: flags.color !== false,
|
|
377
|
+
glyphMode: ui.resolveGlyphMode(false),
|
|
378
|
+
}),
|
|
379
|
+
);
|
|
380
|
+
ui.stderr('');
|
|
381
|
+
}
|
|
364
382
|
}
|
|
365
383
|
|
|
366
384
|
const listSpaces = await migrationSpaceListEntriesFromAggregate(aggregate, migrationsDir);
|
|
@@ -440,7 +458,21 @@ async function executeMigrationStatusCommand(
|
|
|
440
458
|
let markerCannotReachTarget = false;
|
|
441
459
|
let headlineTargetHash = activeRefHash ?? contractHash;
|
|
442
460
|
let totalPending = 0;
|
|
443
|
-
|
|
461
|
+
|
|
462
|
+
const globalLayoutInputs = showSpaceHeadings
|
|
463
|
+
? scopedSpaces
|
|
464
|
+
.filter((spaceEntry) => spaceEntry.migrations.length > 0)
|
|
465
|
+
.map((spaceEntry) => ({
|
|
466
|
+
graph: aggregate.space(spaceEntry.spaceId)!.graph(),
|
|
467
|
+
liveContractHash: contractHash,
|
|
468
|
+
}))
|
|
469
|
+
: [];
|
|
470
|
+
const globalMaxEdgeTreePrefixWidth =
|
|
471
|
+
globalLayoutInputs.length > 0
|
|
472
|
+
? computeGlobalMaxEdgeTreePrefixWidth(globalLayoutInputs)
|
|
473
|
+
: undefined;
|
|
474
|
+
const globalMaxDirNameWidth =
|
|
475
|
+
globalLayoutInputs.length > 0 ? computeGlobalMaxDirNameWidth(globalLayoutInputs) : undefined;
|
|
444
476
|
|
|
445
477
|
for (const spaceEntry of scopedSpaces) {
|
|
446
478
|
const member = aggregate.space(spaceEntry.spaceId);
|
|
@@ -449,20 +481,7 @@ async function executeMigrationStatusCommand(
|
|
|
449
481
|
}
|
|
450
482
|
const graph = member.graph();
|
|
451
483
|
const spaceContractHash = member.contract().storage.storageHash;
|
|
452
|
-
const targetHash =
|
|
453
|
-
if (targetHash === undefined) {
|
|
454
|
-
hasAmbiguousTarget = true;
|
|
455
|
-
diagnostics.push({
|
|
456
|
-
code: 'MIGRATION.DIVERGED',
|
|
457
|
-
severity: 'warn',
|
|
458
|
-
message: 'There are multiple valid migration paths — you must select a target',
|
|
459
|
-
hints: [
|
|
460
|
-
"Use '--to <contract>' to select a target",
|
|
461
|
-
"Or 'prisma-next ref set <name> <hash>' to create one",
|
|
462
|
-
],
|
|
463
|
-
});
|
|
464
|
-
continue;
|
|
465
|
-
}
|
|
484
|
+
const targetHash = resolveTarget(spaceContractHash, activeRefHash);
|
|
466
485
|
if (spaceEntry.spaceId === aggregate.app.spaceId) {
|
|
467
486
|
headlineTargetHash = targetHash;
|
|
468
487
|
}
|
|
@@ -477,9 +496,8 @@ async function executeMigrationStatusCommand(
|
|
|
477
496
|
if (
|
|
478
497
|
connected &&
|
|
479
498
|
!usingFromOverride &&
|
|
480
|
-
markerHash !== undefined &&
|
|
481
499
|
markerInGraph &&
|
|
482
|
-
|
|
500
|
+
originHash !== targetHash &&
|
|
483
501
|
findPath(graph, originHash, targetHash) === null
|
|
484
502
|
) {
|
|
485
503
|
markerCannotReachTarget = true;
|
|
@@ -511,13 +529,15 @@ async function executeMigrationStatusCommand(
|
|
|
511
529
|
});
|
|
512
530
|
const tree = renderSpaceTree({
|
|
513
531
|
member,
|
|
514
|
-
|
|
532
|
+
liveContractHash: contractHash,
|
|
533
|
+
migrations: spaceEntry.migrations,
|
|
515
534
|
markerHash,
|
|
516
535
|
showDbMarker,
|
|
517
|
-
|
|
518
|
-
edgeAnnotations: annotations,
|
|
536
|
+
statusOverlay: annotations,
|
|
519
537
|
colorize,
|
|
520
538
|
glyphMode,
|
|
539
|
+
...(globalMaxEdgeTreePrefixWidth !== undefined ? { globalMaxEdgeTreePrefixWidth } : {}),
|
|
540
|
+
...(globalMaxDirNameWidth !== undefined ? { globalMaxDirNameWidth } : {}),
|
|
521
541
|
});
|
|
522
542
|
const migrations = buildStatusMigrations(spaceEntry.migrations, annotations);
|
|
523
543
|
const pending = countPending(migrations);
|
|
@@ -529,9 +549,11 @@ async function executeMigrationStatusCommand(
|
|
|
529
549
|
targetHash,
|
|
530
550
|
migrations,
|
|
531
551
|
});
|
|
552
|
+
const displayTree =
|
|
553
|
+
showSpaceHeadings && tree.length > 0 ? indentMigrationGraphTreeBlock(tree, ' ') : tree;
|
|
532
554
|
treeSections.push({
|
|
533
555
|
spaceId: spaceEntry.spaceId,
|
|
534
|
-
tree,
|
|
556
|
+
tree: displayTree,
|
|
535
557
|
showHeading: showSpaceHeadings,
|
|
536
558
|
});
|
|
537
559
|
}
|
|
@@ -567,16 +589,19 @@ async function executeMigrationStatusCommand(
|
|
|
567
589
|
}
|
|
568
590
|
|
|
569
591
|
const appMarkerHash = markersBySpace.get(aggregate.app.spaceId)?.storageHash;
|
|
570
|
-
const summary =
|
|
571
|
-
?
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
592
|
+
const summary = markerCannotReachTarget
|
|
593
|
+
? buildNoPathSummary({
|
|
594
|
+
markerHash: appMarkerHash,
|
|
595
|
+
targetHash: headlineTargetHash,
|
|
596
|
+
explicitTarget: options.to !== undefined,
|
|
597
|
+
refName: activeRefName,
|
|
598
|
+
})
|
|
599
|
+
: buildStatusHeadline({
|
|
600
|
+
pendingCount: totalPending,
|
|
601
|
+
targetHash: headlineTargetHash,
|
|
602
|
+
markerDiverged,
|
|
603
|
+
markerHash: appMarkerHash,
|
|
604
|
+
});
|
|
580
605
|
|
|
581
606
|
if (scopedSpaces.every((s) => s.migrations.length === 0)) {
|
|
582
607
|
return ok({
|
|
@@ -613,6 +638,7 @@ export function createMigrationStatusCommand(): Command {
|
|
|
613
638
|
'prisma-next migration status --db $DATABASE_URL',
|
|
614
639
|
'prisma-next migration status --to production --db $DATABASE_URL',
|
|
615
640
|
'prisma-next migration status --from sha256:abc --to production',
|
|
641
|
+
'prisma-next migration status --legend --from sha256:abc --to production',
|
|
616
642
|
]);
|
|
617
643
|
setCommandSeeAlso(command, [
|
|
618
644
|
{ verb: 'migration log', oneLiner: 'Show executed migration history' },
|
|
@@ -632,10 +658,16 @@ export function createMigrationStatusCommand(): Command {
|
|
|
632
658
|
'--from <contract>',
|
|
633
659
|
'Origin contract reference; same grammar as --to. Supplying --from switches to offline path computation.',
|
|
634
660
|
)
|
|
661
|
+
.option('--legend', 'Print a key for the tree glyphs and lane colors')
|
|
635
662
|
.action(async (options: MigrationStatusOptions) => {
|
|
636
663
|
const flags = parseGlobalFlagsOrExit(options);
|
|
637
664
|
const ui = createTerminalUI(flags);
|
|
638
665
|
|
|
666
|
+
const legendValidation = validateLegendOptions(options, flags);
|
|
667
|
+
if (!legendValidation.ok) {
|
|
668
|
+
process.exit(handleResult(legendValidation, flags, ui));
|
|
669
|
+
}
|
|
670
|
+
|
|
639
671
|
const result = await executeMigrationStatusCommand(options, flags, ui);
|
|
640
672
|
|
|
641
673
|
const exitCode = handleResult(result, flags, ui, (statusResult) => {
|
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
ControlExtensionDescriptor,
|
|
6
6
|
ControlFamilyInstance,
|
|
7
7
|
MigrationOperationPolicy,
|
|
8
|
+
MigrationPlannerConflict,
|
|
8
9
|
MigrationPlanOperation,
|
|
9
10
|
OperationPreview,
|
|
10
11
|
TargetMigrationsCapability,
|
|
@@ -32,7 +33,12 @@ import type {
|
|
|
32
33
|
OnControlProgress,
|
|
33
34
|
PerSpaceExecutionEntry,
|
|
34
35
|
} from '../types';
|
|
35
|
-
import {
|
|
36
|
+
import {
|
|
37
|
+
applyMigration,
|
|
38
|
+
buildPerSpaceBreakdown,
|
|
39
|
+
collectOrdered,
|
|
40
|
+
type OrderedResolution,
|
|
41
|
+
} from './apply';
|
|
36
42
|
import { stripOperations } from './migration-helpers';
|
|
37
43
|
|
|
38
44
|
/**
|
|
@@ -175,6 +181,7 @@ export async function executeApply<TFamilyId extends string, TTargetId extends s
|
|
|
175
181
|
onProgress?.({ action, kind: 'spanEnd', spanId: SPAN_IDS.plan, outcome: 'ok' });
|
|
176
182
|
|
|
177
183
|
const orderedResolutions = collectOrdered(planResult.value.applyOrder, planResult.value.perSpace);
|
|
184
|
+
const plannerWarnings = aggregatePlannerWarnings(orderedResolutions);
|
|
178
185
|
|
|
179
186
|
// The destination's structural shape comes from the app's plan — its
|
|
180
187
|
// `destination` is the storage hash users see in CLI output.
|
|
@@ -202,6 +209,7 @@ export async function executeApply<TFamilyId extends string, TTargetId extends s
|
|
|
202
209
|
preview,
|
|
203
210
|
perSpace,
|
|
204
211
|
summary,
|
|
212
|
+
...ifDefined('warnings', plannerWarnings),
|
|
205
213
|
});
|
|
206
214
|
}
|
|
207
215
|
|
|
@@ -228,6 +236,7 @@ export async function executeApply<TFamilyId extends string, TTargetId extends s
|
|
|
228
236
|
summary: applied.failure.summary,
|
|
229
237
|
...ifDefined('why', applied.failure.why),
|
|
230
238
|
meta: applied.failure.meta,
|
|
239
|
+
...ifDefined('warnings', plannerWarnings),
|
|
231
240
|
});
|
|
232
241
|
}
|
|
233
242
|
|
|
@@ -246,9 +255,17 @@ export async function executeApply<TFamilyId extends string, TTargetId extends s
|
|
|
246
255
|
operationsExecuted: applied.value.totalOpsExecuted,
|
|
247
256
|
perSpace: applied.value.perSpace,
|
|
248
257
|
summary,
|
|
258
|
+
...ifDefined('warnings', plannerWarnings),
|
|
249
259
|
});
|
|
250
260
|
}
|
|
251
261
|
|
|
262
|
+
function aggregatePlannerWarnings(
|
|
263
|
+
orderedResolutions: readonly OrderedResolution[],
|
|
264
|
+
): readonly MigrationPlannerConflict[] | undefined {
|
|
265
|
+
const warnings = orderedResolutions.flatMap((r) => r.entry.warnings ?? []);
|
|
266
|
+
return warnings.length > 0 ? warnings : undefined;
|
|
267
|
+
}
|
|
268
|
+
|
|
252
269
|
/**
|
|
253
270
|
* Compare the live `_prisma_marker` rows against the aggregate's
|
|
254
271
|
* declared members. Any marker row whose `space` is not a member of
|
|
@@ -339,6 +356,7 @@ function wrapPlanResult(args: {
|
|
|
339
356
|
readonly preview: OperationPreview | undefined;
|
|
340
357
|
readonly perSpace: readonly PerSpaceExecutionEntry[];
|
|
341
358
|
readonly summary: string;
|
|
359
|
+
readonly warnings?: readonly MigrationPlannerConflict[];
|
|
342
360
|
}): DbInitResult | DbUpdateResult {
|
|
343
361
|
const success: DbInitSuccess | DbUpdateSuccess = {
|
|
344
362
|
mode: 'plan',
|
|
@@ -352,6 +370,7 @@ function wrapPlanResult(args: {
|
|
|
352
370
|
},
|
|
353
371
|
perSpace: args.perSpace,
|
|
354
372
|
summary: args.summary,
|
|
373
|
+
...ifDefined('warnings', args.warnings),
|
|
355
374
|
};
|
|
356
375
|
return ok(success);
|
|
357
376
|
}
|
|
@@ -363,6 +382,7 @@ function wrapApplyResult(args: {
|
|
|
363
382
|
readonly operationsExecuted: number;
|
|
364
383
|
readonly perSpace: readonly PerSpaceExecutionEntry[];
|
|
365
384
|
readonly summary: string;
|
|
385
|
+
readonly warnings?: readonly MigrationPlannerConflict[];
|
|
366
386
|
}): DbInitResult | DbUpdateResult {
|
|
367
387
|
const success: DbInitSuccess | DbUpdateSuccess = {
|
|
368
388
|
mode: 'apply',
|
|
@@ -380,6 +400,7 @@ function wrapApplyResult(args: {
|
|
|
380
400
|
: { storageHash: args.destination.storageHash },
|
|
381
401
|
perSpace: args.perSpace,
|
|
382
402
|
summary: args.summary,
|
|
403
|
+
...ifDefined('warnings', args.warnings),
|
|
383
404
|
};
|
|
384
405
|
return ok(success);
|
|
385
406
|
}
|
|
@@ -388,6 +409,7 @@ function buildRunnerFailure(args: {
|
|
|
388
409
|
readonly summary: string;
|
|
389
410
|
readonly why?: string;
|
|
390
411
|
readonly meta: Record<string, unknown>;
|
|
412
|
+
readonly warnings?: readonly MigrationPlannerConflict[];
|
|
391
413
|
}): DbInitResult | DbUpdateResult {
|
|
392
414
|
const failure: DbInitFailure | DbUpdateFailure = {
|
|
393
415
|
code: 'RUNNER_FAILED',
|
|
@@ -395,6 +417,7 @@ function buildRunnerFailure(args: {
|
|
|
395
417
|
why: args.why,
|
|
396
418
|
meta: args.meta,
|
|
397
419
|
conflicts: undefined,
|
|
420
|
+
...ifDefined('warnings', args.warnings),
|
|
398
421
|
};
|
|
399
422
|
return notOk(failure) as DbInitResult | DbUpdateResult;
|
|
400
423
|
}
|
package/src/control-api/types.ts
CHANGED
|
@@ -395,6 +395,7 @@ export interface DbInitSuccess {
|
|
|
395
395
|
*/
|
|
396
396
|
readonly perSpace?: ReadonlyArray<PerSpaceExecutionEntry>;
|
|
397
397
|
readonly summary: string;
|
|
398
|
+
readonly warnings?: ReadonlyArray<MigrationPlannerConflict>;
|
|
398
399
|
}
|
|
399
400
|
|
|
400
401
|
/**
|
|
@@ -410,6 +411,7 @@ export interface DbInitFailure {
|
|
|
410
411
|
readonly summary: string;
|
|
411
412
|
readonly why: string | undefined;
|
|
412
413
|
readonly conflicts: ReadonlyArray<MigrationPlannerConflict> | undefined;
|
|
414
|
+
readonly warnings?: ReadonlyArray<MigrationPlannerConflict>;
|
|
413
415
|
readonly meta: Record<string, unknown> | undefined;
|
|
414
416
|
readonly marker?: {
|
|
415
417
|
readonly storageHash?: string;
|
|
@@ -465,6 +467,7 @@ export interface DbUpdateSuccess {
|
|
|
465
467
|
*/
|
|
466
468
|
readonly perSpace?: ReadonlyArray<PerSpaceExecutionEntry>;
|
|
467
469
|
readonly summary: string;
|
|
470
|
+
readonly warnings?: ReadonlyArray<MigrationPlannerConflict>;
|
|
468
471
|
}
|
|
469
472
|
|
|
470
473
|
/**
|
|
@@ -480,6 +483,7 @@ export interface DbUpdateFailure {
|
|
|
480
483
|
readonly summary: string;
|
|
481
484
|
readonly why: string | undefined;
|
|
482
485
|
readonly conflicts: ReadonlyArray<MigrationPlannerConflict> | undefined;
|
|
486
|
+
readonly warnings?: ReadonlyArray<MigrationPlannerConflict>;
|
|
483
487
|
readonly meta: Record<string, unknown> | undefined;
|
|
484
488
|
}
|
|
485
489
|
|
package/src/utils/cli-errors.ts
CHANGED
|
@@ -108,6 +108,23 @@ export function errorRefSetEmptySentinel(hash: string): CliStructuredError {
|
|
|
108
108
|
});
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
/**
|
|
112
|
+
* `--legend` was combined with a machine-readable or silent output flag.
|
|
113
|
+
* The legend is human-only decoration on stderr.
|
|
114
|
+
*/
|
|
115
|
+
export function errorLegendHumanOnly(
|
|
116
|
+
conflictingFlag: '--json' | '--dot' | '--quiet',
|
|
117
|
+
): CliStructuredError {
|
|
118
|
+
return errorRuntime('`--legend` is only available for human-readable output', {
|
|
119
|
+
why: `\`--legend\` prints a glyph key to stderr and cannot be combined with ${conflictingFlag}.`,
|
|
120
|
+
fix: `Omit ${conflictingFlag} to print the legend alongside the tree, or omit --legend when using ${conflictingFlag}.`,
|
|
121
|
+
meta: {
|
|
122
|
+
code: 'MIGRATION.LEGEND_HUMAN_ONLY',
|
|
123
|
+
conflictingFlag,
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
111
128
|
/**
|
|
112
129
|
* `--space <id>` was given a value that doesn't satisfy the contract-space
|
|
113
130
|
* naming rule (`[a-z][a-z0-9_-]{0,63}` per `isValidSpaceId`). Fires before
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import type { MigrationPlannerConflict } from '@prisma-next/framework-components/control';
|
|
2
|
+
import { blindCast } from '@prisma-next/utils/casts';
|
|
1
3
|
import { red } from 'colorette';
|
|
2
4
|
|
|
3
5
|
import type { CliErrorConflict, CliErrorEnvelope } from '../cli-errors';
|
|
4
6
|
import type { GlobalFlags } from '../global-flags';
|
|
5
7
|
import { createColorFormatter, formatDim, isVerbose } from './helpers';
|
|
8
|
+
import { formatPlannerWarningsBlock } from './migrations';
|
|
6
9
|
|
|
7
10
|
/**
|
|
8
11
|
* Formats error output for human-readable display.
|
|
@@ -67,6 +70,14 @@ export function formatErrorOutput(error: CliErrorEnvelope, flags: GlobalFlags):
|
|
|
67
70
|
if (error.docsUrl && isVerbose(flags, 1)) {
|
|
68
71
|
lines.push(formatDimText(error.docsUrl));
|
|
69
72
|
}
|
|
73
|
+
const plannerWarnings = error.meta?.['plannerWarnings'];
|
|
74
|
+
if (Array.isArray(plannerWarnings) && plannerWarnings.length > 0) {
|
|
75
|
+
const typedWarnings = blindCast<
|
|
76
|
+
readonly MigrationPlannerConflict[],
|
|
77
|
+
'mapDbUpdateFailure (db-update.ts) writes meta.plannerWarnings as MigrationPlannerConflict[]; meta is typed Record<string, unknown> so the channel erases the element type'
|
|
78
|
+
>(plannerWarnings);
|
|
79
|
+
lines.push(...formatPlannerWarningsBlock(typedWarnings, useColor));
|
|
80
|
+
}
|
|
70
81
|
if (isVerbose(flags, 2) && error.meta) {
|
|
71
82
|
lines.push(`${formatDimText(` Meta: ${JSON.stringify(error.meta, null, 2)}`)}`);
|
|
72
83
|
}
|