prisma-next 0.12.0-dev.6 → 0.12.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/dist/cli.mjs +2 -2
- package/dist/commands/migration-graph.d.mts +19 -1
- package/dist/commands/migration-graph.d.mts.map +1 -1
- package/dist/commands/migration-graph.mjs +2 -2
- package/dist/{migration-graph-D7DVUElV.mjs → migration-graph-C9WC-7eO.mjs} +324 -78
- package/dist/migration-graph-C9WC-7eO.mjs.map +1 -0
- package/package.json +11 -11
- package/dist/migration-graph-D7DVUElV.mjs.map +0 -1
package/dist/cli.mjs
CHANGED
|
@@ -9,7 +9,7 @@ import { createDbUpdateCommand } from "./commands/db-update.mjs";
|
|
|
9
9
|
import { t as createDbVerifyCommand } from "./db-verify-v_vUKXTU.mjs";
|
|
10
10
|
import { createMigrateCommand } from "./commands/migrate.mjs";
|
|
11
11
|
import { t as createMigrationCheckCommand } from "./migration-check-BiBJoYYW.mjs";
|
|
12
|
-
import { t as createMigrationGraphCommand } from "./migration-graph-
|
|
12
|
+
import { t as createMigrationGraphCommand } from "./migration-graph-C9WC-7eO.mjs";
|
|
13
13
|
import { createMigrationListCommand } from "./commands/migration-list.mjs";
|
|
14
14
|
import { createMigrationLogCommand } from "./commands/migration-log.mjs";
|
|
15
15
|
import { createMigrationNewCommand } from "./commands/migration-new.mjs";
|
|
@@ -24,7 +24,7 @@ import { fileURLToPath } from "node:url";
|
|
|
24
24
|
import { readUserConfig, resolveGating, runTelemetry } from "@prisma-next/cli-telemetry";
|
|
25
25
|
import { distance } from "closest-match";
|
|
26
26
|
//#region package.json
|
|
27
|
-
var version = "0.12.0-dev.
|
|
27
|
+
var version = "0.12.0-dev.8";
|
|
28
28
|
//#endregion
|
|
29
29
|
//#region src/utils/telemetry.ts
|
|
30
30
|
/**
|
|
@@ -12,7 +12,25 @@ interface MigrationGraphOptions extends CommonCommandOptions {
|
|
|
12
12
|
readonly dot?: boolean;
|
|
13
13
|
readonly tree?: boolean;
|
|
14
14
|
readonly ascii?: boolean;
|
|
15
|
+
readonly legend?: boolean;
|
|
15
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* `--legend` describes the `--tree` visual language, so passing it auto-enables
|
|
19
|
+
* the tree path (it has nothing to say about the legacy dagre default).
|
|
20
|
+
*/
|
|
21
|
+
declare function migrationGraphUsesTree(options: {
|
|
22
|
+
readonly tree?: boolean;
|
|
23
|
+
readonly legend?: boolean;
|
|
24
|
+
}): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* The legend is decoration printed alongside the command header on stderr, so
|
|
27
|
+
* it is suppressed for the machine-readable / silent paths (`--json`, `--dot`,
|
|
28
|
+
* `--quiet`) exactly as the header is.
|
|
29
|
+
*/
|
|
30
|
+
declare function migrationGraphShowsLegend(options: {
|
|
31
|
+
readonly legend?: boolean;
|
|
32
|
+
readonly dot?: boolean;
|
|
33
|
+
}, flags: GlobalFlags): boolean;
|
|
16
34
|
interface MigrationGraphResult {
|
|
17
35
|
readonly ok: true;
|
|
18
36
|
readonly graph: MigrationGraph;
|
|
@@ -23,5 +41,5 @@ interface MigrationGraphResult {
|
|
|
23
41
|
declare function executeMigrationGraphCommand(options: MigrationGraphOptions, flags: GlobalFlags, ui: TerminalUI): Promise<Result<MigrationGraphResult, CliStructuredError>>;
|
|
24
42
|
declare function createMigrationGraphCommand(): Command;
|
|
25
43
|
//#endregion
|
|
26
|
-
export { MigrationGraphResult, createMigrationGraphCommand, executeMigrationGraphCommand };
|
|
44
|
+
export { MigrationGraphResult, createMigrationGraphCommand, executeMigrationGraphCommand, migrationGraphShowsLegend, migrationGraphUsesTree };
|
|
27
45
|
//# sourceMappingURL=migration-graph.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migration-graph.d.mts","names":[],"sources":["../../src/commands/migration-graph.ts"],"mappings":";;;;;;;;;
|
|
1
|
+
{"version":3,"file":"migration-graph.d.mts","names":[],"sources":["../../src/commands/migration-graph.ts"],"mappings":";;;;;;;;;UA6BU,qBAAA,SAA8B,oBAAoB;EAAA,SACjD,MAAA;EAAA,SACA,GAAA;EAAA,SACA,IAAA;EAAA,SACA,KAAA;EAAA,SACA,MAAA;AAAA;;;;;iBAOK,sBAAA,CAAuB,OAAA;EAAA,SAC5B,IAAA;EAAA,SACA,MAAA;AAAA;;;;;;iBAUK,yBAAA,CACd,OAAA;EAAA,SAAoB,MAAA;EAAA,SAA2B,GAAA;AAAA,GAC/C,KAAA,EAAO,WAAW;AAAA,UAOH,oBAAA;EAAA,SACN,EAAA;EAAA,SACA,KAAA,EAAO,cAAA;EAAA,SACP,YAAA;EAAA,SACA,IAAA,WAAe,SAAS;EAAA,SACxB,OAAA;AAAA;AAAA,iBAGW,4BAAA,CACpB,OAAA,EAAS,qBAAA,EACT,KAAA,EAAO,WAAA,EACP,EAAA,EAAI,UAAA,GACH,OAAA,CAAQ,MAAA,CAAO,oBAAA,EAAsB,kBAAA;AAAA,iBAoDxB,2BAAA,CAAA,GAA+B,OAAO"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as executeMigrationGraphCommand, t as createMigrationGraphCommand } from "../migration-graph-
|
|
2
|
-
export { createMigrationGraphCommand, executeMigrationGraphCommand };
|
|
1
|
+
import { i as migrationGraphUsesTree, n as executeMigrationGraphCommand, r as migrationGraphShowsLegend, t as createMigrationGraphCommand } from "../migration-graph-C9WC-7eO.mjs";
|
|
2
|
+
export { createMigrationGraphCommand, executeMigrationGraphCommand, migrationGraphShowsLegend, migrationGraphUsesTree };
|
|
@@ -5,7 +5,7 @@ import { i as migrationGraphToRenderInput, n as graphRenderer } from "./graph-re
|
|
|
5
5
|
import { a as migrationListEmptySource, n as createAnsiMigrationListStyler, o as migrationListForwardArrow, s as classifyMigrationGraphTopology, t as CONTRACT_MARKER_NAME } from "./migration-list-styler-BRwF4-gy.mjs";
|
|
6
6
|
import { Command } from "commander";
|
|
7
7
|
import { ok } from "@prisma-next/utils/result";
|
|
8
|
-
import { bold } from "colorette";
|
|
8
|
+
import { bold, createColors } from "colorette";
|
|
9
9
|
import stringWidth from "string-width";
|
|
10
10
|
import { EMPTY_CONTRACT_HASH } from "@prisma-next/migration-tools/constants";
|
|
11
11
|
//#region src/utils/formatters/migration-graph-layout.ts
|
|
@@ -171,7 +171,7 @@ function classifyEdgeAdjacency(edge, position) {
|
|
|
171
171
|
function emptyCells(width) {
|
|
172
172
|
return Array.from({ length: width }, () => ({ kind: "empty" }));
|
|
173
173
|
}
|
|
174
|
-
function buildBranchConnectorCells(startLane, endLane, activeLanes, gridWidth) {
|
|
174
|
+
function buildBranchConnectorCells(startLane, endLane, fanTargetLanes, activeLanes, gridWidth) {
|
|
175
175
|
const cells = emptyCells(gridWidth);
|
|
176
176
|
for (let lane = 0; lane < gridWidth; lane++) {
|
|
177
177
|
if (activeLanes.has(lane) && (lane < startLane || lane > endLane)) {
|
|
@@ -180,11 +180,13 @@ function buildBranchConnectorCells(startLane, endLane, activeLanes, gridWidth) {
|
|
|
180
180
|
}
|
|
181
181
|
if (lane === startLane) cells[lane] = { kind: "branch-tee" };
|
|
182
182
|
else if (lane === endLane) cells[lane] = { kind: "branch-corner" };
|
|
183
|
-
else if (lane > startLane && lane < endLane) cells[lane] = { kind: "branch-tee" };
|
|
183
|
+
else if (lane > startLane && lane < endLane) if (fanTargetLanes.has(lane)) cells[lane] = { kind: "branch-tee" };
|
|
184
|
+
else if (activeLanes.has(lane)) cells[lane] = { kind: "arc-crossing" };
|
|
185
|
+
else cells[lane] = { kind: "branch-tee" };
|
|
184
186
|
}
|
|
185
187
|
return cells;
|
|
186
188
|
}
|
|
187
|
-
function buildMergeConnectorCells(startLane, endLane, activeLanes, gridWidth) {
|
|
189
|
+
function buildMergeConnectorCells(startLane, endLane, fanTargetLanes, activeLanes, gridWidth) {
|
|
188
190
|
const cells = emptyCells(gridWidth);
|
|
189
191
|
for (let lane = 0; lane < gridWidth; lane++) {
|
|
190
192
|
if (activeLanes.has(lane) && (lane < startLane || lane > endLane)) {
|
|
@@ -193,7 +195,9 @@ function buildMergeConnectorCells(startLane, endLane, activeLanes, gridWidth) {
|
|
|
193
195
|
}
|
|
194
196
|
if (lane === startLane) cells[lane] = { kind: "merge-tee" };
|
|
195
197
|
else if (lane === endLane) cells[lane] = { kind: "merge-corner" };
|
|
196
|
-
else if (lane > startLane && lane < endLane)
|
|
198
|
+
else if (lane > startLane && lane < endLane) if (fanTargetLanes.has(lane)) cells[lane] = { kind: "merge-tee" };
|
|
199
|
+
else if (activeLanes.has(lane)) cells[lane] = { kind: "arc-crossing" };
|
|
200
|
+
else cells[lane] = { kind: "horizontal-pass" };
|
|
197
201
|
}
|
|
198
202
|
return cells;
|
|
199
203
|
}
|
|
@@ -504,18 +508,19 @@ function layoutComponent(componentNodes, allEdges) {
|
|
|
504
508
|
const endLane = Math.max(...laneIndices);
|
|
505
509
|
ensureGridWidth(endLane + 1);
|
|
506
510
|
const activeLanes = new Set(activeLaneIndices());
|
|
511
|
+
const fanTargetLanes = new Set(laneIndices);
|
|
507
512
|
rows.push({
|
|
508
513
|
kind: "merge-connector",
|
|
509
514
|
contractHash,
|
|
510
515
|
startLane,
|
|
511
516
|
endLane,
|
|
512
517
|
branchCount: laneIndices.length,
|
|
513
|
-
cells: buildMergeConnectorCells(startLane, endLane, activeLanes, gridWidth)
|
|
518
|
+
cells: buildMergeConnectorCells(startLane, endLane, fanTargetLanes, activeLanes, gridWidth)
|
|
514
519
|
});
|
|
515
520
|
for (const index of laneIndices) if (index !== startLane) setLane(index, null);
|
|
516
521
|
return startLane;
|
|
517
522
|
}
|
|
518
|
-
function emitBranchConnector(contractHash, startLane, endLane, branchCount) {
|
|
523
|
+
function emitBranchConnector(contractHash, startLane, endLane, branchCount, fanTargetLanes) {
|
|
519
524
|
ensureGridWidth(endLane + 1);
|
|
520
525
|
const activeLanes = new Set(activeLaneIndices());
|
|
521
526
|
rows.push({
|
|
@@ -524,7 +529,7 @@ function layoutComponent(componentNodes, allEdges) {
|
|
|
524
529
|
startLane,
|
|
525
530
|
endLane,
|
|
526
531
|
branchCount,
|
|
527
|
-
cells: buildBranchConnectorCells(startLane, endLane, activeLanes, gridWidth)
|
|
532
|
+
cells: buildBranchConnectorCells(startLane, endLane, new Set(fanTargetLanes), activeLanes, gridWidth)
|
|
528
533
|
});
|
|
529
534
|
}
|
|
530
535
|
function emitEdgeRow(edge, lane, convergenceProducer) {
|
|
@@ -597,7 +602,7 @@ function layoutComponent(componentNodes, allEdges) {
|
|
|
597
602
|
}
|
|
598
603
|
if (groups.length >= 2) {
|
|
599
604
|
const endLane = Math.max(...laneForGroup);
|
|
600
|
-
emitBranchConnector(node, column, endLane, groups.length);
|
|
605
|
+
emitBranchConnector(node, column, endLane, groups.length, laneForGroup);
|
|
601
606
|
}
|
|
602
607
|
for (let groupIndex = 0; groupIndex < groups.length; groupIndex++) {
|
|
603
608
|
const group = groups[groupIndex];
|
|
@@ -835,6 +840,30 @@ function buildMigrationGraphRows(graph, options = {}) {
|
|
|
835
840
|
};
|
|
836
841
|
}
|
|
837
842
|
//#endregion
|
|
843
|
+
//#region src/utils/formatters/migration-graph-lane-colors.ts
|
|
844
|
+
const { magenta: magenta$1, cyan: cyan$1, green: green$1, yellow: yellow$1, blueBright, red: red$1 } = createColors({ useColor: true });
|
|
845
|
+
const LANE_COLOR_CYCLE = [
|
|
846
|
+
magenta$1,
|
|
847
|
+
cyan$1,
|
|
848
|
+
green$1,
|
|
849
|
+
yellow$1,
|
|
850
|
+
blueBright,
|
|
851
|
+
red$1
|
|
852
|
+
];
|
|
853
|
+
/**
|
|
854
|
+
* The hue for a gutter column. The leftmost lane (column 0) is **neutral** — it
|
|
855
|
+
* has nothing to be told apart from in the common single-lane linear case, so
|
|
856
|
+
* the renderer dims it rather than tinting it; the rotating palette is reserved
|
|
857
|
+
* for columns ≥ 1 (where a second lane exists to distinguish). Callers must dim
|
|
858
|
+
* column 0 themselves; this returns identity for it as a guard. A lane freed and
|
|
859
|
+
* reused by a later branch keeps its column's hue — coloring is by position, not
|
|
860
|
+
* branch identity, exactly like `git log --graph`.
|
|
861
|
+
*/
|
|
862
|
+
function laneColorForColumn(column) {
|
|
863
|
+
if (column <= 0) return (text) => text;
|
|
864
|
+
return LANE_COLOR_CYCLE[(column - 1) % LANE_COLOR_CYCLE.length] ?? ((text) => text);
|
|
865
|
+
}
|
|
866
|
+
//#endregion
|
|
838
867
|
//#region src/utils/formatters/migration-graph-tree-render.ts
|
|
839
868
|
const LABEL_GAP = 2;
|
|
840
869
|
/**
|
|
@@ -902,74 +931,233 @@ function arrowForEdgeKind(kind, palette) {
|
|
|
902
931
|
return palette.edgeArrow[kind];
|
|
903
932
|
}
|
|
904
933
|
/**
|
|
934
|
+
* The leftmost lane (column 0) renders with the neutral dim lane style rather
|
|
935
|
+
* than a palette hue — in the common single-lane case it has nothing to be told
|
|
936
|
+
* apart from. Used as the "no owning arc" sentinel during colour resolution.
|
|
937
|
+
*/
|
|
938
|
+
const NEUTRAL_LANE = 0;
|
|
939
|
+
/**
|
|
940
|
+
* Forced bold for branch-coloured names. A branched name pairs its lane hue
|
|
941
|
+
* (also forced, via {@link laneColorForColumn}) with bold; both must emit even
|
|
942
|
+
* when colorette's ambient TTY detection is off, so the colorized branch name
|
|
943
|
+
* is deterministically bold + hue rather than hue-only.
|
|
944
|
+
*/
|
|
945
|
+
const { bold: forcedBold } = createColors({ useColor: true });
|
|
946
|
+
/**
|
|
947
|
+
* Resolve per-cell colour columns for a row. Scanning right-to-left lets each
|
|
948
|
+
* arc bridge inherit the corner column that closes it (the arc's back-lane), so
|
|
949
|
+
* the whole arc — vertical run (already its own column), horizontal bridges,
|
|
950
|
+
* corners, crossings, and the `◂`/`─` connector — reads as a single continuous
|
|
951
|
+
* hue. A crossing can only be one colour, so rather than leave it dim (wrong for
|
|
952
|
+
* both crossing lines) it takes the arc owning the horizontal run at this row
|
|
953
|
+
* (the nearest corner to its right); the crossed vertical lane is simply
|
|
954
|
+
* occluded at that one cell and reappears on the next row.
|
|
955
|
+
*/
|
|
956
|
+
function resolveRowLaneColors(cells) {
|
|
957
|
+
const lane = new Array(cells.length);
|
|
958
|
+
const connector = new Array(cells.length);
|
|
959
|
+
let arcCorner = NEUTRAL_LANE;
|
|
960
|
+
for (let column = cells.length - 1; column >= 0; column--) {
|
|
961
|
+
const cell = cells[column];
|
|
962
|
+
connector[column] = arcCorner;
|
|
963
|
+
switch (cell?.kind) {
|
|
964
|
+
case "arc-branch-corner":
|
|
965
|
+
case "arc-land-corner":
|
|
966
|
+
arcCorner = column;
|
|
967
|
+
lane[column] = column;
|
|
968
|
+
break;
|
|
969
|
+
case "arc-branch-tee":
|
|
970
|
+
lane[column] = column;
|
|
971
|
+
break;
|
|
972
|
+
case "arc-crossing":
|
|
973
|
+
case "arc-land-bridge":
|
|
974
|
+
lane[column] = arcCorner;
|
|
975
|
+
break;
|
|
976
|
+
case "horizontal-pass":
|
|
977
|
+
lane[column] = arcCorner === NEUTRAL_LANE ? column : arcCorner;
|
|
978
|
+
break;
|
|
979
|
+
case "node":
|
|
980
|
+
lane[column] = column;
|
|
981
|
+
arcCorner = NEUTRAL_LANE;
|
|
982
|
+
break;
|
|
983
|
+
default:
|
|
984
|
+
lane[column] = column;
|
|
985
|
+
arcCorner = NEUTRAL_LANE;
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
return {
|
|
989
|
+
lane,
|
|
990
|
+
connector
|
|
991
|
+
};
|
|
992
|
+
}
|
|
993
|
+
/**
|
|
994
|
+
* Resolve per-cell connector colours. Scanning right-to-left, a corner or an
|
|
995
|
+
* intermediate tee anchors its own lane (its junction glyph takes that column),
|
|
996
|
+
* but a tee's **trailing dash leads into the branch on its right** (the next
|
|
997
|
+
* branch point), so `┬─` reads as "this lane, then on toward the next" rather
|
|
998
|
+
* than tinting the dash with the left lane. The leading tee at `startLane` (the
|
|
999
|
+
* fork/merge origin) and pure horizontal segments inherit the nearest branch
|
|
1000
|
+
* point to their right whole-cell, so the run into a branch — or collapsing
|
|
1001
|
+
* into a merge corner — stays continuous. Pass-through verticals outside the
|
|
1002
|
+
* run keep their own column (column 0 stays neutral).
|
|
1003
|
+
*/
|
|
1004
|
+
function resolveConnectorLaneColors(cells, startLane) {
|
|
1005
|
+
const glyph = new Array(cells.length);
|
|
1006
|
+
const dash = new Array(cells.length);
|
|
1007
|
+
let owner = NEUTRAL_LANE;
|
|
1008
|
+
for (let column = cells.length - 1; column >= 0; column--) switch (cells[column]?.kind) {
|
|
1009
|
+
case "branch-corner":
|
|
1010
|
+
case "merge-corner":
|
|
1011
|
+
owner = column;
|
|
1012
|
+
glyph[column] = column;
|
|
1013
|
+
dash[column] = column;
|
|
1014
|
+
break;
|
|
1015
|
+
case "branch-tee":
|
|
1016
|
+
case "merge-tee":
|
|
1017
|
+
if (column === startLane) {
|
|
1018
|
+
const served = owner === NEUTRAL_LANE ? column : owner;
|
|
1019
|
+
glyph[column] = column;
|
|
1020
|
+
dash[column] = served;
|
|
1021
|
+
} else {
|
|
1022
|
+
dash[column] = owner === NEUTRAL_LANE ? column : owner;
|
|
1023
|
+
glyph[column] = column;
|
|
1024
|
+
owner = column;
|
|
1025
|
+
}
|
|
1026
|
+
break;
|
|
1027
|
+
case "arc-crossing":
|
|
1028
|
+
glyph[column] = column;
|
|
1029
|
+
dash[column] = column;
|
|
1030
|
+
break;
|
|
1031
|
+
case "horizontal-pass": {
|
|
1032
|
+
const served = owner === NEUTRAL_LANE ? column : owner;
|
|
1033
|
+
glyph[column] = served;
|
|
1034
|
+
dash[column] = served;
|
|
1035
|
+
break;
|
|
1036
|
+
}
|
|
1037
|
+
default:
|
|
1038
|
+
glyph[column] = column;
|
|
1039
|
+
dash[column] = column;
|
|
1040
|
+
}
|
|
1041
|
+
return {
|
|
1042
|
+
glyph,
|
|
1043
|
+
dash
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
1046
|
+
/**
|
|
1047
|
+
* Style a structural glyph by its resolved colour column. Column 0 and the
|
|
1048
|
+
* neutral sentinel render dim (`style.lane`); columns ≥ 1 take a palette hue.
|
|
1049
|
+
*/
|
|
1050
|
+
function laneStylerForColumn(colorColumn, colorize, style) {
|
|
1051
|
+
if (!colorize || colorColumn <= NEUTRAL_LANE) return (text) => style.lane(text);
|
|
1052
|
+
return laneColorForColumn(colorColumn);
|
|
1053
|
+
}
|
|
1054
|
+
/**
|
|
1055
|
+
* Tint a branch-owned token (direction arrow, migration name) by its edge's
|
|
1056
|
+
* lane so the whole branch row reads in one colour. Column 0 has nothing to be
|
|
1057
|
+
* told apart from in the common linear chain, so it keeps the token's existing
|
|
1058
|
+
* default styling (`fallback`) rather than a palette hue; only lanes ≥ 1 take a
|
|
1059
|
+
* colour. With colour off, the fallback (also colourless) is used unchanged.
|
|
1060
|
+
*/
|
|
1061
|
+
function branchStylerOrDefault(column, colorize, fallback) {
|
|
1062
|
+
if (!colorize || column <= NEUTRAL_LANE) return fallback;
|
|
1063
|
+
return laneColorForColumn(column);
|
|
1064
|
+
}
|
|
1065
|
+
/**
|
|
1066
|
+
* Render a connector tee (`├─` / `┬─` / `┴─`) with its junction glyph and its
|
|
1067
|
+
* trailing dash coloured independently: the junction anchors its own lane while
|
|
1068
|
+
* the dash leads into the branch on its right.
|
|
1069
|
+
*/
|
|
1070
|
+
function renderConnectorTee(pair, glyphColumn, dashColumn, colorize, style) {
|
|
1071
|
+
const glyph = laneStylerForColumn(glyphColumn, colorize, style);
|
|
1072
|
+
if (glyphColumn === dashColumn) return glyph(pair);
|
|
1073
|
+
return glyph(pair.slice(0, 1)) + laneStylerForColumn(dashColumn, colorize, style)(pair.slice(1));
|
|
1074
|
+
}
|
|
1075
|
+
/**
|
|
905
1076
|
* A node-marker glyph pair (`○◂`, `○─`, `*<`, `*-`) is the contract node
|
|
906
|
-
* marker (`○` / `*`) followed by an arc connector (`◂` / `─` / `<` / `-`).
|
|
907
|
-
*
|
|
908
|
-
*
|
|
909
|
-
*
|
|
1077
|
+
* marker (`○` / `*`) followed by an arc connector (`◂` / `─` / `<` / `-`). The
|
|
1078
|
+
* marker takes its own lane's hue (so each node visibly belongs to its branch);
|
|
1079
|
+
* the connector follows the arc it belongs to (its owning back-lane hue).
|
|
1080
|
+
* Direction arrows are handled elsewhere — they take their edge's lane hue too.
|
|
910
1081
|
*/
|
|
911
|
-
function renderNodeMarkerPair(pair, style) {
|
|
912
|
-
|
|
1082
|
+
function renderNodeMarkerPair(pair, nodeColumn, arcColumn, colorize, style) {
|
|
1083
|
+
const marker = laneStylerForColumn(nodeColumn, colorize, style);
|
|
1084
|
+
const connector = laneStylerForColumn(arcColumn, colorize, style);
|
|
1085
|
+
return marker(pair.slice(0, 1)) + connector(pair.slice(1));
|
|
913
1086
|
}
|
|
914
|
-
function renderCellPair(cell, style, palette) {
|
|
1087
|
+
function renderCellPair(cell, column, colors, colorize, style, palette) {
|
|
1088
|
+
const lane = laneStylerForColumn(colors.lane[column] ?? column, colorize, style);
|
|
915
1089
|
switch (cell.kind) {
|
|
916
|
-
case "node":
|
|
917
|
-
|
|
918
|
-
if (cell.
|
|
919
|
-
return
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
case "
|
|
923
|
-
case "
|
|
924
|
-
case "branch-
|
|
925
|
-
case "merge-
|
|
926
|
-
case "
|
|
927
|
-
case "
|
|
928
|
-
case "arc-
|
|
929
|
-
case "arc-
|
|
930
|
-
case "arc-land-
|
|
931
|
-
case "
|
|
1090
|
+
case "node": {
|
|
1091
|
+
const arcColumn = colors.connector[column] ?? NEUTRAL_LANE;
|
|
1092
|
+
if (cell.arcLand === true) return renderNodeMarkerPair(palette.arcLand, column, arcColumn, colorize, style);
|
|
1093
|
+
if (cell.arcTee === true) return renderNodeMarkerPair(palette.arcTee, column, arcColumn, colorize, style);
|
|
1094
|
+
return lane(palette.node);
|
|
1095
|
+
}
|
|
1096
|
+
case "vertical-pass": return lane(palette.verticalPass);
|
|
1097
|
+
case "edge-lane": return cell.ownsLabel ? lane(palette.verticalPass.trimEnd()) + branchStylerOrDefault(column, colorize, style.kind)(arrowForEdgeKind(cell.edgeKind, palette)) : lane(palette.verticalPass);
|
|
1098
|
+
case "branch-tee": return lane(palette.branchTee);
|
|
1099
|
+
case "merge-tee": return lane(palette.mergeTee);
|
|
1100
|
+
case "branch-corner": return lane(palette.branchCorner);
|
|
1101
|
+
case "merge-corner": return lane(palette.mergeCorner);
|
|
1102
|
+
case "arc-branch-corner": return lane(palette.arcBranchCorner);
|
|
1103
|
+
case "arc-branch-tee": return lane(palette.arcBranchTee);
|
|
1104
|
+
case "arc-land-corner": return lane(palette.arcLandCorner);
|
|
1105
|
+
case "arc-crossing": return lane(palette.arcLandBridge);
|
|
1106
|
+
case "arc-land-bridge": return lane(palette.arcLandBridge);
|
|
1107
|
+
case "horizontal-pass": return lane(palette.horizontalPass);
|
|
932
1108
|
case "empty": return " ";
|
|
933
1109
|
}
|
|
934
1110
|
}
|
|
935
|
-
function renderConnectorRow(row, gridWidth, style, palette) {
|
|
1111
|
+
function renderConnectorRow(row, gridWidth, colorize, style, palette) {
|
|
936
1112
|
const isMerge = row.kind === "merge-connector";
|
|
937
1113
|
if (row.cells.length > 0) {
|
|
1114
|
+
const colors = resolveConnectorLaneColors(row.cells, row.startLane ?? 0);
|
|
938
1115
|
let seenTee = false;
|
|
939
1116
|
let out = "";
|
|
940
|
-
for (
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
1117
|
+
for (let column = 0; column < row.cells.length; column++) {
|
|
1118
|
+
const cell = row.cells[column];
|
|
1119
|
+
if (cell === void 0) continue;
|
|
1120
|
+
const glyphColumn = colors.glyph[column] ?? column;
|
|
1121
|
+
const dashColumn = colors.dash[column] ?? glyphColumn;
|
|
1122
|
+
const lane = laneStylerForColumn(glyphColumn, colorize, style);
|
|
1123
|
+
switch (cell.kind) {
|
|
1124
|
+
case "branch-tee":
|
|
1125
|
+
out += renderConnectorTee(seenTee ? palette.connectorBranchTeeCo : palette.connectorBranchTee, glyphColumn, dashColumn, colorize, style);
|
|
1126
|
+
seenTee = true;
|
|
1127
|
+
break;
|
|
1128
|
+
case "merge-tee":
|
|
1129
|
+
out += renderConnectorTee(seenTee ? palette.connectorMergeTeeCo : palette.connectorBranchTee, glyphColumn, dashColumn, colorize, style);
|
|
1130
|
+
seenTee = true;
|
|
1131
|
+
break;
|
|
1132
|
+
case "branch-corner":
|
|
1133
|
+
out += lane(palette.branchCorner);
|
|
1134
|
+
break;
|
|
1135
|
+
case "merge-corner":
|
|
1136
|
+
out += lane(palette.mergeCorner);
|
|
1137
|
+
break;
|
|
1138
|
+
case "vertical-pass":
|
|
1139
|
+
out += lane(palette.verticalPass);
|
|
1140
|
+
break;
|
|
1141
|
+
case "horizontal-pass":
|
|
1142
|
+
out += lane(palette.horizontalPass);
|
|
1143
|
+
break;
|
|
1144
|
+
case "arc-crossing":
|
|
1145
|
+
out += renderConnectorTee(palette.arcCrossing, glyphColumn, dashColumn, colorize, style);
|
|
1146
|
+
break;
|
|
1147
|
+
default: out += " ";
|
|
1148
|
+
}
|
|
962
1149
|
}
|
|
963
1150
|
for (let column = row.cells.length; column < gridWidth; column++) out += " ";
|
|
964
1151
|
return out;
|
|
965
1152
|
}
|
|
966
1153
|
const start = row.startLane ?? 0;
|
|
967
1154
|
const end = row.endLane ?? start;
|
|
1155
|
+
const runLane = laneStylerForColumn(end, colorize, style);
|
|
968
1156
|
let out = "";
|
|
969
1157
|
for (let column = 0; column < gridWidth; column++) if (column < start || column > end) out += " ";
|
|
970
|
-
else if (column === start) out +=
|
|
971
|
-
else if (column === end) out +=
|
|
972
|
-
else out +=
|
|
1158
|
+
else if (column === start) out += runLane(palette.connectorBranchTee);
|
|
1159
|
+
else if (column === end) out += runLane(isMerge ? palette.mergeCorner : palette.branchCorner);
|
|
1160
|
+
else out += runLane(isMerge ? palette.connectorMergeTeeCo : palette.connectorBranchTeeCo);
|
|
973
1161
|
return out;
|
|
974
1162
|
}
|
|
975
1163
|
function abbreviateHash(hash, hashLength, emptySource) {
|
|
@@ -1008,6 +1196,11 @@ function padVisible(text, targetWidth) {
|
|
|
1008
1196
|
const padding = Math.max(0, targetWidth - stringWidth(text));
|
|
1009
1197
|
return text + " ".repeat(padding);
|
|
1010
1198
|
}
|
|
1199
|
+
const ANSI_ESCAPE = "\x1B";
|
|
1200
|
+
function trimTrailingWhitespace(line) {
|
|
1201
|
+
const trailingSpaceBeforeReset = new RegExp(`[\\t ]+((?:${ANSI_ESCAPE}\\[[0-9;]*m)+)$`);
|
|
1202
|
+
return line.replace(trailingSpaceBeforeReset, "$1").replace(/\s+$/, "");
|
|
1203
|
+
}
|
|
1011
1204
|
function gridWidthForModel(rows) {
|
|
1012
1205
|
return rows.reduce((max, row) => row.kind === "node" || row.kind === "edge" ? Math.max(max, row.cells.length) : max, 1);
|
|
1013
1206
|
}
|
|
@@ -1047,20 +1240,24 @@ function renderMigrationGraphTree(model, opts) {
|
|
|
1047
1240
|
continue;
|
|
1048
1241
|
}
|
|
1049
1242
|
if (row.kind === "branch-connector" || row.kind === "merge-connector") {
|
|
1050
|
-
lines.push(renderConnectorRow(row, gridWidth, style, palette)
|
|
1243
|
+
lines.push(trimTrailingWhitespace(renderConnectorRow(row, gridWidth, opts.colorize, style, palette)));
|
|
1051
1244
|
continue;
|
|
1052
1245
|
}
|
|
1053
|
-
|
|
1054
|
-
|
|
1246
|
+
const cellColors = resolveRowLaneColors(row.cells);
|
|
1247
|
+
let gutter = row.cells.map((cell, column) => renderCellPair(cell, column, cellColors, opts.colorize, style, palette)).join("");
|
|
1055
1248
|
let laneSpan = row.cells.length;
|
|
1056
|
-
if (row.kind === "node")
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1249
|
+
if (row.kind === "node") if ((row.contractHash ?? EMPTY_CONTRACT_HASH) === EMPTY_CONTRACT_HASH) laneSpan = 1;
|
|
1250
|
+
else {
|
|
1251
|
+
let lastActiveColumn = -1;
|
|
1252
|
+
for (let column = row.cells.length - 1; column >= 0; column--) if (row.cells[column]?.kind !== "empty") {
|
|
1253
|
+
lastActiveColumn = column;
|
|
1254
|
+
break;
|
|
1255
|
+
}
|
|
1256
|
+
laneSpan = lastActiveColumn >= 0 ? lastActiveColumn + 1 : 1;
|
|
1060
1257
|
}
|
|
1061
1258
|
const labelColumn = row.kind === "edge" ? edgeLabelColumn(row, wideLabelColumn) : wideLabelColumn !== void 0 && (nodeHasArcDecoration(row) || row.contractHash !== void 0) ? wideLabelColumn : laneSpan * 2 + LABEL_GAP;
|
|
1062
|
-
if (row.kind === "edge" && row.edge?.from === EMPTY_CONTRACT_HASH && (row.laneIndex ?? 0) === 0) gutter = row.cells.slice(0, 1).map((cell) => renderCellPair(cell, style, palette)).join("");
|
|
1063
|
-
else if (row.kind === "node" && laneSpan < row.cells.length && !nodeHasArcDecoration(row)) gutter = row.cells.slice(0, laneSpan).map((cell) => renderCellPair(cell, style, palette)).join("");
|
|
1259
|
+
if (row.kind === "edge" && row.edge?.from === EMPTY_CONTRACT_HASH && (row.laneIndex ?? 0) === 0) gutter = row.cells.slice(0, 1).map((cell, column) => renderCellPair(cell, column, cellColors, opts.colorize, style, palette)).join("");
|
|
1260
|
+
else if (row.kind === "node" && laneSpan < row.cells.length && !nodeHasArcDecoration(row)) gutter = row.cells.slice(0, laneSpan).map((cell, column) => renderCellPair(cell, column, cellColors, opts.colorize, style, palette)).join("");
|
|
1064
1261
|
else if (gutter.length < laneSpan * 2) gutter = gutter.padEnd(laneSpan * 2, " ");
|
|
1065
1262
|
const dirNameWidth = rowDirNameWidth(labelColumn, maxDirNameLen, dirNameGap);
|
|
1066
1263
|
const dataColumn = labelColumn + dirNameWidth;
|
|
@@ -1068,35 +1265,76 @@ function renderMigrationGraphTree(model, opts) {
|
|
|
1068
1265
|
if (row.kind === "node") {
|
|
1069
1266
|
const contractHash = row.contractHash ?? EMPTY_CONTRACT_HASH;
|
|
1070
1267
|
if (contractHash === EMPTY_CONTRACT_HASH) {
|
|
1071
|
-
const trailingLanes = row.cells.slice(1).map((cell) => renderCellPair(cell, style, palette)).join("");
|
|
1268
|
+
const trailingLanes = row.cells.slice(1).map((cell, offset) => renderCellPair(cell, offset + 1, cellColors, opts.colorize, style, palette)).join("");
|
|
1072
1269
|
const emptyGutter = palette.emptySource.padEnd(2, " ") + trailingLanes;
|
|
1073
1270
|
const overlayNames = overlayNamesForContract(contractHash, opts);
|
|
1074
1271
|
if (overlayNames.length === 0) {
|
|
1075
|
-
lines.push(emptyGutter
|
|
1272
|
+
lines.push(trimTrailingWhitespace(emptyGutter));
|
|
1076
1273
|
continue;
|
|
1077
1274
|
}
|
|
1078
1275
|
const overlay = style.refs(overlayNames);
|
|
1079
|
-
lines.push(`${padVisible(emptyGutter, dataColumn)}${overlay}
|
|
1276
|
+
lines.push(trimTrailingWhitespace(`${padVisible(emptyGutter, dataColumn)}${overlay}`));
|
|
1080
1277
|
continue;
|
|
1081
1278
|
}
|
|
1082
1279
|
const hashText = style.sourceHash(abbreviateHash(contractHash, hashLength, palette.emptySource));
|
|
1083
1280
|
const overlayNames = overlayNamesForContract(contractHash, opts);
|
|
1084
1281
|
const overlayPad = overlayNames.length > 0 ? " ".repeat(Math.max(0, dataColumn - labelColumn - stringWidth(hashText))) : "";
|
|
1085
1282
|
const overlay = overlayNames.length > 0 ? style.refs(overlayNames) : "";
|
|
1086
|
-
lines.push(`${gutterPad}${hashText}${overlayPad}${overlay}
|
|
1283
|
+
lines.push(trimTrailingWhitespace(`${gutterPad}${hashText}${overlayPad}${overlay}`));
|
|
1087
1284
|
continue;
|
|
1088
1285
|
}
|
|
1089
1286
|
const edge = row.edge;
|
|
1090
1287
|
if (edge === void 0) continue;
|
|
1091
1288
|
const dirNamePadding = " ".repeat(Math.max(0, dirNameWidth - edge.dirName.length));
|
|
1092
|
-
const
|
|
1289
|
+
const laneIndex = row.laneIndex ?? 0;
|
|
1290
|
+
const dirName = `${(opts.colorize && laneIndex > NEUTRAL_LANE ? (text) => forcedBold(laneColorForColumn(laneIndex)(text)) : style.dirName)(edge.dirName)}${dirNamePadding}`;
|
|
1093
1291
|
const hashColumn = formatEdgeHashColumn(edge, style, hashLength, palette);
|
|
1094
|
-
lines.push(`${gutterPad}${dirName}${hashColumn}
|
|
1292
|
+
lines.push(trimTrailingWhitespace(`${gutterPad}${dirName}${hashColumn}`));
|
|
1095
1293
|
}
|
|
1096
1294
|
return lines.join("\n");
|
|
1097
1295
|
}
|
|
1296
|
+
/**
|
|
1297
|
+
* A compact key for the `--tree` visual language: the contract marker, the
|
|
1298
|
+
* in-lane direction arrows, the empty baseline, the `(refs)` overlay (including
|
|
1299
|
+
* the reserved `db` live-database and `contract` working-schema markers), and a
|
|
1300
|
+
* worked sample of the data-column `from → to` migration hash arrow.
|
|
1301
|
+
*
|
|
1302
|
+
* Honors the same glyph palette (unicode vs ASCII) and `colorize` gate as the
|
|
1303
|
+
* tree renderer, so the key matches whatever the graph itself drew and stays
|
|
1304
|
+
* pipe-safe (zero ANSI when color is off). The caller adds the trailing blank
|
|
1305
|
+
* line that separates this stderr key from the graph on stdout.
|
|
1306
|
+
*/
|
|
1307
|
+
function renderMigrationGraphLegend(opts) {
|
|
1308
|
+
const palette = paletteFor(opts.glyphMode ?? "unicode");
|
|
1309
|
+
const style = createAnsiMigrationListStyler({ useColor: opts.colorize });
|
|
1310
|
+
const node = palette.node.trimEnd();
|
|
1311
|
+
const sampleArrow = `${style.sourceHash("aaaaaa")} ${style.glyph(palette.forwardArrow)} ${style.destHash("bbbbbb")}`;
|
|
1312
|
+
return [
|
|
1313
|
+
"Legend:",
|
|
1314
|
+
` ${style.kind(node)} contract ${style.kind(palette.edgeArrow.forward)} forward ${style.kind(palette.edgeArrow.rollback)} rollback`,
|
|
1315
|
+
` ${style.kind(palette.edgeArrow.self)} migration without schema change`,
|
|
1316
|
+
` ${style.glyph(palette.emptySource)} empty database (baseline)`,
|
|
1317
|
+
` ${style.refs(["refs"])} ${DB_MARKER_NAME} / ${CONTRACT_MARKER_NAME} markers`,
|
|
1318
|
+
` ${sampleArrow} migration from contract aaaaaa to bbbbbb`
|
|
1319
|
+
].join("\n");
|
|
1320
|
+
}
|
|
1098
1321
|
//#endregion
|
|
1099
1322
|
//#region src/commands/migration-graph.ts
|
|
1323
|
+
/**
|
|
1324
|
+
* `--legend` describes the `--tree` visual language, so passing it auto-enables
|
|
1325
|
+
* the tree path (it has nothing to say about the legacy dagre default).
|
|
1326
|
+
*/
|
|
1327
|
+
function migrationGraphUsesTree(options) {
|
|
1328
|
+
return options.tree === true || options.legend === true;
|
|
1329
|
+
}
|
|
1330
|
+
/**
|
|
1331
|
+
* The legend is decoration printed alongside the command header on stderr, so
|
|
1332
|
+
* it is suppressed for the machine-readable / silent paths (`--json`, `--dot`,
|
|
1333
|
+
* `--quiet`) exactly as the header is.
|
|
1334
|
+
*/
|
|
1335
|
+
function migrationGraphShowsLegend(options, flags) {
|
|
1336
|
+
return options.legend === true && options.dot !== true && flags.json !== true && flags.quiet !== true;
|
|
1337
|
+
}
|
|
1100
1338
|
async function executeMigrationGraphCommand(options, flags, ui) {
|
|
1101
1339
|
const config = await loadConfig(options.config);
|
|
1102
1340
|
const { configPath, appMigrationsRelative, migrationsDir } = resolveMigrationPaths(options.config, config);
|
|
@@ -1114,6 +1352,13 @@ async function executeMigrationGraphCommand(options, flags, ui) {
|
|
|
1114
1352
|
flags
|
|
1115
1353
|
});
|
|
1116
1354
|
ui.stderr(header);
|
|
1355
|
+
if (migrationGraphShowsLegend(options, flags)) {
|
|
1356
|
+
ui.stderr(renderMigrationGraphLegend({
|
|
1357
|
+
colorize: flags.color !== false,
|
|
1358
|
+
glyphMode: ui.resolveGlyphMode(options.ascii === true)
|
|
1359
|
+
}));
|
|
1360
|
+
ui.stderr("");
|
|
1361
|
+
}
|
|
1117
1362
|
}
|
|
1118
1363
|
const loaded = await buildReadAggregate(config, { migrationsDir });
|
|
1119
1364
|
if (!loaded.ok) return loaded;
|
|
@@ -1139,7 +1384,8 @@ function createMigrationGraphCommand() {
|
|
|
1139
1384
|
"prisma-next migration graph --json",
|
|
1140
1385
|
"prisma-next migration graph --dot",
|
|
1141
1386
|
"prisma-next migration graph --tree",
|
|
1142
|
-
"prisma-next migration graph --tree --ascii"
|
|
1387
|
+
"prisma-next migration graph --tree --ascii",
|
|
1388
|
+
"prisma-next migration graph --legend"
|
|
1143
1389
|
]);
|
|
1144
1390
|
setCommandSeeAlso(command, [
|
|
1145
1391
|
{
|
|
@@ -1159,7 +1405,7 @@ function createMigrationGraphCommand() {
|
|
|
1159
1405
|
oneLiner: "Display migration package contents"
|
|
1160
1406
|
}
|
|
1161
1407
|
]);
|
|
1162
|
-
addGlobalOptions(command).option("--config <path>", "Path to prisma-next.config.ts").option("--dot", "Output in Graphviz DOT format").option("--tree", "Experimental condensed annotated tree renderer").option("--ascii", "Use ASCII glyphs for --tree (pipe-friendly)").action(async (options) => {
|
|
1408
|
+
addGlobalOptions(command).option("--config <path>", "Path to prisma-next.config.ts").option("--dot", "Output in Graphviz DOT format").option("--tree", "Experimental condensed annotated tree renderer").option("--ascii", "Use ASCII glyphs for --tree (pipe-friendly)").option("--legend", "Print a key for the --tree glyphs and lane colors (implies --tree)").action(async (options) => {
|
|
1163
1409
|
const flags = parseGlobalFlagsOrExit(options);
|
|
1164
1410
|
const ui = createTerminalUI(flags);
|
|
1165
1411
|
const exitCode = handleResult(await executeMigrationGraphCommand(options, flags, ui), flags, ui, (graphResult) => {
|
|
@@ -1186,7 +1432,7 @@ function createMigrationGraphCommand() {
|
|
|
1186
1432
|
edges,
|
|
1187
1433
|
summary: graphResult.summary
|
|
1188
1434
|
}, null, 2));
|
|
1189
|
-
} else if (!flags.quiet) if (options
|
|
1435
|
+
} else if (!flags.quiet) if (migrationGraphUsesTree(options)) {
|
|
1190
1436
|
const refsByHash = /* @__PURE__ */ new Map();
|
|
1191
1437
|
for (const ref of graphResult.refs) {
|
|
1192
1438
|
const existing = refsByHash.get(ref.hash);
|
|
@@ -1227,6 +1473,6 @@ function createMigrationGraphCommand() {
|
|
|
1227
1473
|
return command;
|
|
1228
1474
|
}
|
|
1229
1475
|
//#endregion
|
|
1230
|
-
export { executeMigrationGraphCommand as n, createMigrationGraphCommand as t };
|
|
1476
|
+
export { migrationGraphUsesTree as i, executeMigrationGraphCommand as n, migrationGraphShowsLegend as r, createMigrationGraphCommand as t };
|
|
1231
1477
|
|
|
1232
|
-
//# sourceMappingURL=migration-graph-
|
|
1478
|
+
//# sourceMappingURL=migration-graph-C9WC-7eO.mjs.map
|