@prisma-next/cli 0.12.0 → 0.13.0-dev.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/cli.mjs +180 -163
- package/dist/cli.mjs.map +1 -1
- package/dist/{client-KgJorIvG.mjs → client-CJzuo5wX.mjs} +222 -107
- package/dist/client-CJzuo5wX.mjs.map +1 -0
- package/dist/{command-helpers-Bbw1GbwL.mjs → command-helpers-DGMvGBeX.mjs} +318 -25
- package/dist/command-helpers-DGMvGBeX.mjs.map +1 -0
- package/dist/commands/contract-emit.d.mts.map +1 -1
- package/dist/commands/contract-emit.mjs +1 -1
- package/dist/commands/contract-infer.d.mts.map +1 -1
- package/dist/commands/contract-infer.mjs +1 -1
- package/dist/commands/db-init.d.mts.map +1 -1
- package/dist/commands/db-init.mjs +4 -5
- package/dist/commands/db-init.mjs.map +1 -1
- package/dist/commands/db-schema.d.mts.map +1 -1
- package/dist/commands/db-schema.mjs +3 -3
- package/dist/commands/db-schema.mjs.map +1 -1
- package/dist/commands/db-sign.d.mts.map +1 -1
- package/dist/commands/db-sign.mjs +6 -6
- 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 +10 -7
- package/dist/commands/db-update.mjs.map +1 -1
- package/dist/commands/db-verify.d.mts.map +1 -1
- package/dist/commands/db-verify.mjs +1 -1
- package/dist/commands/migrate.d.mts +37 -3
- package/dist/commands/migrate.d.mts.map +1 -1
- package/dist/commands/migrate.mjs +298 -12
- package/dist/commands/migrate.mjs.map +1 -1
- package/dist/commands/migration-check.d.mts +55 -13
- package/dist/commands/migration-check.d.mts.map +1 -1
- package/dist/commands/migration-check.mjs +3 -2
- package/dist/commands/migration-graph.d.mts +17 -8
- package/dist/commands/migration-graph.d.mts.map +1 -1
- package/dist/commands/migration-graph.mjs +185 -2
- package/dist/commands/migration-graph.mjs.map +1 -0
- package/dist/commands/migration-list.d.mts +26 -27
- package/dist/commands/migration-list.d.mts.map +1 -1
- package/dist/commands/migration-list.mjs +2 -190
- package/dist/commands/migration-log.d.mts +9 -19
- package/dist/commands/migration-log.d.mts.map +1 -1
- package/dist/commands/migration-log.mjs +1 -137
- package/dist/commands/migration-new.d.mts.map +1 -1
- package/dist/commands/migration-new.mjs +6 -5
- package/dist/commands/migration-new.mjs.map +1 -1
- package/dist/commands/migration-plan.d.mts +1 -1
- 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 +17 -21
- package/dist/commands/migration-show.d.mts.map +1 -1
- package/dist/commands/migration-show.mjs +24 -36
- package/dist/commands/migration-show.mjs.map +1 -1
- package/dist/commands/migration-status.d.mts +42 -144
- package/dist/commands/migration-status.d.mts.map +1 -1
- package/dist/commands/migration-status.mjs +3 -759
- package/dist/commands/ref.d.mts +1 -1
- package/dist/commands/ref.d.mts.map +1 -1
- package/dist/commands/ref.mjs +4 -4
- package/dist/commands/ref.mjs.map +1 -1
- package/dist/commands/telemetry/index.d.mts +7 -0
- package/dist/commands/telemetry/index.d.mts.map +1 -0
- package/dist/commands/telemetry/index.mjs +2 -0
- package/dist/{config-loader-B6sJjXTv.mjs → config-loader-p9JMrekQ.mjs} +1 -1
- package/dist/{config-loader-B6sJjXTv.mjs.map → config-loader-p9JMrekQ.mjs.map} +1 -1
- package/dist/config-loader.mjs +1 -1
- package/dist/{contract-at-errors-BxP-TOMl.mjs → contract-at-errors-CFXsstzm.mjs} +2 -2
- package/dist/{contract-at-errors-BxP-TOMl.mjs.map → contract-at-errors-CFXsstzm.mjs.map} +1 -1
- package/dist/{contract-emit-DxcGl4Uq.mjs → contract-emit-B_qriF8B.mjs} +5 -5
- package/dist/{contract-emit-DxcGl4Uq.mjs.map → contract-emit-B_qriF8B.mjs.map} +1 -1
- package/dist/{contract-emit-D-4jrNve.mjs → contract-emit-C8HmtboH.mjs} +12 -7
- package/dist/contract-emit-C8HmtboH.mjs.map +1 -0
- package/dist/{contract-enrichment-a0V5Y_mL.mjs → contract-enrichment-gn9sWbPw.mjs} +1 -1
- package/dist/{contract-enrichment-a0V5Y_mL.mjs.map → contract-enrichment-gn9sWbPw.mjs.map} +1 -1
- package/dist/{contract-infer-D8uEbJuu.mjs → contract-infer-BYT_ra_U.mjs} +5 -5
- package/dist/contract-infer-BYT_ra_U.mjs.map +1 -0
- package/dist/{contract-space-aggregate-loader-DvZwdkrr.mjs → contract-space-aggregate-loader-ClI1KN6d.mjs} +5 -5
- package/dist/{contract-space-aggregate-loader-DvZwdkrr.mjs.map → contract-space-aggregate-loader-ClI1KN6d.mjs.map} +1 -1
- package/dist/{db-verify-v_vUKXTU.mjs → db-verify-C24FKhb7.mjs} +6 -6
- package/dist/{db-verify-v_vUKXTU.mjs.map → db-verify-C24FKhb7.mjs.map} +1 -1
- package/dist/exports/control-api.d.mts +5 -3
- package/dist/exports/control-api.d.mts.map +1 -1
- package/dist/exports/control-api.mjs +3 -3
- package/dist/exports/index.mjs +1 -1
- package/dist/exports/index.mjs.map +1 -1
- package/dist/exports/init-output.d.mts +1 -3
- package/dist/exports/init-output.d.mts.map +1 -1
- package/dist/exports/init-output.mjs +1 -1
- package/dist/{extension-pack-inputs-IDvjRCi3.mjs → extension-pack-inputs-1ySHqxKG.mjs} +1 -1
- package/dist/{extension-pack-inputs-IDvjRCi3.mjs.map → extension-pack-inputs-1ySHqxKG.mjs.map} +1 -1
- package/dist/{framework-components-fYXjz_in.mjs → framework-components-YVQHhPH7.mjs} +2 -2
- package/dist/{framework-components-fYXjz_in.mjs.map → framework-components-YVQHhPH7.mjs.map} +1 -1
- package/dist/{global-flags-DEHjV8_s.d.mts → global-flags-BpoOYtNZ.d.mts} +1 -1
- package/dist/{global-flags-DEHjV8_s.d.mts.map → global-flags-BpoOYtNZ.d.mts.map} +1 -1
- package/dist/{init-Cv9UzWL5.mjs → init-0HwB-Vh8.mjs} +5 -58
- package/dist/init-0HwB-Vh8.mjs.map +1 -0
- package/dist/{inspect-live-schema-C6ohV_oQ.mjs → inspect-live-schema-DF6IwcDl.mjs} +7 -5
- package/dist/inspect-live-schema-DF6IwcDl.mjs.map +1 -0
- package/dist/migration-check-VwM8xCZV.mjs +574 -0
- package/dist/migration-check-VwM8xCZV.mjs.map +1 -0
- package/dist/migration-cli.mjs +1 -1
- package/dist/migration-cli.mjs.map +1 -1
- package/dist/{migration-command-scaffold-CjvwO6at.mjs → migration-command-scaffold-DA-Lhx6o.mjs} +5 -5
- package/dist/{migration-command-scaffold-CjvwO6at.mjs.map → migration-command-scaffold-DA-Lhx6o.mjs.map} +1 -1
- package/dist/migration-graph-command-render-CEez7YUK.mjs +1960 -0
- package/dist/migration-graph-command-render-CEez7YUK.mjs.map +1 -0
- package/dist/migration-list-DlJJ_38Z.mjs +230 -0
- package/dist/migration-list-DlJJ_38Z.mjs.map +1 -0
- package/dist/migration-log-CG0qQAFm.mjs +222 -0
- package/dist/migration-log-CG0qQAFm.mjs.map +1 -0
- package/dist/migration-path-target-Ce6OZImp.mjs +38 -0
- package/dist/migration-path-target-Ce6OZImp.mjs.map +1 -0
- package/dist/{migration-plan-9DJ7q7_z.mjs → migration-plan-z5Ing-TD.mjs} +9 -8
- package/dist/migration-plan-z5Ing-TD.mjs.map +1 -0
- package/dist/migration-status-CD-LC2Ip.mjs +447 -0
- package/dist/migration-status-CD-LC2Ip.mjs.map +1 -0
- package/dist/{output-B60Gw5fu.mjs → output-mEQ74_nd.mjs} +1 -1
- package/dist/{output-B60Gw5fu.mjs.map → output-mEQ74_nd.mjs.map} +1 -1
- package/dist/{progress-adapter-C644QK8l.mjs → progress-adapter-CjAeTxY_.mjs} +1 -1
- package/dist/{progress-adapter-C644QK8l.mjs.map → progress-adapter-CjAeTxY_.mjs.map} +1 -1
- package/dist/{ref-advancement-DUZqsue6.mjs → ref-advancement-BkXlikCA.mjs} +1 -1
- package/dist/{ref-advancement-DUZqsue6.mjs.map → ref-advancement-BkXlikCA.mjs.map} +1 -1
- package/dist/schemas-CeGMYFYX.d.mts +191 -0
- package/dist/schemas-CeGMYFYX.d.mts.map +1 -0
- package/dist/schemas-KhXMzNA_.mjs +112 -0
- package/dist/schemas-KhXMzNA_.mjs.map +1 -0
- package/dist/telemetry-BIM4beEO.mjs +122 -0
- package/dist/telemetry-BIM4beEO.mjs.map +1 -0
- package/dist/{terminal-ui-5Y6mrg93.d.mts → terminal-ui-DGRNFWna.d.mts} +1 -1
- package/dist/terminal-ui-DGRNFWna.d.mts.map +1 -0
- package/dist/{types-Dt_SfqFm.d.mts → types-C_tYiJYx.d.mts} +53 -31
- package/dist/types-C_tYiJYx.d.mts.map +1 -0
- package/dist/{verify-DCA9Sldu.mjs → verify-DcOYZ1tH.mjs} +2 -2
- package/dist/{verify-DCA9Sldu.mjs.map → verify-DcOYZ1tH.mjs.map} +1 -1
- package/package.json +26 -22
- package/src/cli.ts +5 -0
- package/src/commands/contract-infer.ts +2 -2
- package/src/commands/db-update.ts +7 -1
- package/src/commands/init/index.ts +6 -35
- package/src/commands/init/init.ts +1 -14
- package/src/commands/init/inputs.ts +0 -75
- package/src/commands/inspect-live-schema.ts +10 -0
- package/src/commands/json/schemas.ts +195 -0
- package/src/commands/migrate.ts +527 -8
- package/src/commands/migration-check.ts +469 -134
- package/src/commands/migration-graph.ts +164 -91
- package/src/commands/migration-list.ts +72 -39
- package/src/commands/migration-log.ts +52 -102
- package/src/commands/migration-new.ts +2 -1
- package/src/commands/migration-plan.ts +2 -1
- package/src/commands/migration-show.ts +31 -66
- package/src/commands/migration-status-overlay.ts +61 -0
- package/src/commands/migration-status.ts +458 -1066
- package/src/commands/telemetry/index.ts +107 -0
- package/src/commands/telemetry/status.ts +67 -0
- package/src/control-api/client.ts +70 -9
- package/src/control-api/operations/contract-emit.ts +22 -2
- package/src/control-api/operations/db-init.ts +6 -3
- package/src/control-api/operations/{db-apply.ts → db-run.ts} +55 -14
- package/src/control-api/operations/db-update.ts +7 -4
- package/src/control-api/operations/db-verify.ts +15 -5
- package/src/control-api/operations/{migration-apply.ts → migrate.ts} +181 -80
- package/src/control-api/operations/{apply.ts → run-migration.ts} +33 -27
- package/src/control-api/types.ts +56 -29
- package/src/utils/cli-errors.ts +70 -2
- package/src/utils/formatters/errors.ts +11 -0
- package/src/utils/formatters/migration-graph-command-render.ts +239 -0
- package/src/utils/formatters/migration-graph-grid-layout.ts +1134 -0
- package/src/utils/formatters/migration-graph-labels.ts +408 -0
- package/src/utils/formatters/migration-graph-model.ts +103 -0
- package/src/utils/formatters/migration-graph-occlusion-render.ts +258 -0
- package/src/utils/formatters/migration-graph-rows.ts +128 -15
- package/src/utils/formatters/migration-graph-space-render.ts +188 -0
- package/src/utils/formatters/migration-list-data-column.ts +4 -91
- package/src/utils/formatters/migration-list-graph-topology.ts +72 -94
- package/src/utils/formatters/migration-list-render.ts +135 -71
- package/src/utils/formatters/migration-list-styler.ts +46 -5
- package/src/utils/formatters/migration-list-types.ts +5 -21
- package/src/utils/formatters/migration-log-table.ts +205 -0
- package/src/utils/formatters/migrations.ts +33 -11
- package/src/utils/global-flags.ts +35 -0
- package/src/utils/integrity-violation-to-check-failure.ts +28 -19
- package/src/utils/legend.ts +38 -0
- package/src/utils/migration-path-target.ts +60 -0
- package/src/utils/telemetry.ts +68 -32
- package/dist/client-KgJorIvG.mjs.map +0 -1
- package/dist/command-helpers-Bbw1GbwL.mjs.map +0 -1
- package/dist/commands/migration-list.mjs.map +0 -1
- package/dist/commands/migration-log.mjs.map +0 -1
- package/dist/commands/migration-status.mjs.map +0 -1
- package/dist/contract-emit-D-4jrNve.mjs.map +0 -1
- package/dist/contract-infer-D8uEbJuu.mjs.map +0 -1
- package/dist/graph-render-rFAqZujX.mjs +0 -1081
- package/dist/graph-render-rFAqZujX.mjs.map +0 -1
- package/dist/init-Cv9UzWL5.mjs.map +0 -1
- package/dist/inspect-live-schema-C6ohV_oQ.mjs.map +0 -1
- package/dist/migration-check-BiBJoYYW.mjs +0 -341
- package/dist/migration-check-BiBJoYYW.mjs.map +0 -1
- package/dist/migration-graph-D7DVUElV.mjs +0 -1232
- package/dist/migration-graph-D7DVUElV.mjs.map +0 -1
- package/dist/migration-list-styler-BRwF4-gy.mjs +0 -399
- package/dist/migration-list-styler-BRwF4-gy.mjs.map +0 -1
- package/dist/migration-plan-9DJ7q7_z.mjs.map +0 -1
- package/dist/migration-types-D2FW63pr.d.mts +0 -15
- package/dist/migration-types-D2FW63pr.d.mts.map +0 -1
- package/dist/migrations-Cv2jxNNK.mjs +0 -228
- package/dist/migrations-Cv2jxNNK.mjs.map +0 -1
- package/dist/terminal-ui-5Y6mrg93.d.mts.map +0 -1
- package/dist/types-Dt_SfqFm.d.mts.map +0 -1
- package/src/utils/formatters/graph-migration-mapper.ts +0 -235
- package/src/utils/formatters/graph-render.ts +0 -1323
- package/src/utils/formatters/graph-types.ts +0 -120
- package/src/utils/formatters/migration-graph-layout.ts +0 -1119
- package/src/utils/formatters/migration-graph-tree-render.ts +0 -459
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Occlusion renderer for the line/plane/occlusion migration-graph.
|
|
3
|
+
*
|
|
4
|
+
* Per cell: pick the topmost-plane line (lowest plane number = drawn on top),
|
|
5
|
+
* look up its glyph, apply colour from the line's lane or role. Lower-plane
|
|
6
|
+
* lines are occluded (not drawn).
|
|
7
|
+
*
|
|
8
|
+
* Colour is forced via createColors({ useColor: true }) regardless of NO_COLOR.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { createColors } from 'colorette';
|
|
12
|
+
import {
|
|
13
|
+
type Cell,
|
|
14
|
+
type CellLine,
|
|
15
|
+
DEFAULT_COLS_PER_LANE,
|
|
16
|
+
type Direction,
|
|
17
|
+
type Grid,
|
|
18
|
+
type PathRole,
|
|
19
|
+
} from './migration-graph-model';
|
|
20
|
+
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// Force-colour seam — always emits ANSI regardless of NO_COLOR.
|
|
23
|
+
// Same technique as gallery-cells.ts.
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
const palette = createColors({ useColor: true });
|
|
26
|
+
|
|
27
|
+
// Lane colour palette: lane N → colour N+1 (lane0=white, lane1=cyan, …).
|
|
28
|
+
// No red (reads as an error). The on-path highlight uses greenBright (SGR 92),
|
|
29
|
+
// distinct from flat-lane green (SGR 32).
|
|
30
|
+
type Colorizer = (text: string) => string;
|
|
31
|
+
|
|
32
|
+
const LANE_COLORIZERS: Colorizer[] = [
|
|
33
|
+
palette.white,
|
|
34
|
+
palette.cyan,
|
|
35
|
+
palette.yellow,
|
|
36
|
+
palette.blueBright,
|
|
37
|
+
palette.magenta,
|
|
38
|
+
palette.green,
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
function laneColor(lane: number): Colorizer {
|
|
42
|
+
return LANE_COLORIZERS[lane % LANE_COLORIZERS.length] ?? ((t) => t);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The colourizer for a lane's hue (lane0 = white, lane1 = cyan, …). Exported
|
|
47
|
+
* so the per-row LABEL renderer can tint a migration name in its lane's colour,
|
|
48
|
+
* matching the node `○`, the edges, and the arrows drawn in the gutter — one
|
|
49
|
+
* colour per lane across glyph and text.
|
|
50
|
+
*/
|
|
51
|
+
export function laneColorizer(lane: number): (text: string) => string {
|
|
52
|
+
return laneColor(lane);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
// Focus colour: on-path → green, off-path → dim. Read straight off the line's
|
|
57
|
+
// role; a defined role always overrides the lane rotation.
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
function roleColor(role: PathRole): Colorizer {
|
|
60
|
+
return role === 'on-path' ? palette.greenBright : palette.dim;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
// Glyph alphabet — unicode and ASCII variants.
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
export type GraphGlyphMode = 'unicode' | 'ascii';
|
|
67
|
+
|
|
68
|
+
interface GraphGlyphAlphabet {
|
|
69
|
+
readonly vertical: string;
|
|
70
|
+
readonly horizontal: string;
|
|
71
|
+
readonly cornerUpRight: string;
|
|
72
|
+
readonly cornerDownRight: string;
|
|
73
|
+
readonly cornerUpLeft: string;
|
|
74
|
+
readonly cornerDownLeft: string;
|
|
75
|
+
readonly arrowUp: string;
|
|
76
|
+
readonly arrowDown: string;
|
|
77
|
+
readonly node: string;
|
|
78
|
+
readonly selfLoop: string;
|
|
79
|
+
readonly landingArrow: string;
|
|
80
|
+
readonly fallback: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const UNICODE_ALPHABET: GraphGlyphAlphabet = {
|
|
84
|
+
vertical: '│',
|
|
85
|
+
horizontal: '─',
|
|
86
|
+
cornerUpRight: '╰',
|
|
87
|
+
cornerDownRight: '╭',
|
|
88
|
+
cornerUpLeft: '╯',
|
|
89
|
+
cornerDownLeft: '╮',
|
|
90
|
+
arrowUp: '↑',
|
|
91
|
+
arrowDown: '↓',
|
|
92
|
+
node: '○',
|
|
93
|
+
selfLoop: '⟲',
|
|
94
|
+
landingArrow: '◂',
|
|
95
|
+
fallback: '?',
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const ASCII_ALPHABET: GraphGlyphAlphabet = {
|
|
99
|
+
vertical: '|',
|
|
100
|
+
horizontal: '-',
|
|
101
|
+
cornerUpRight: '\\',
|
|
102
|
+
cornerDownRight: '/',
|
|
103
|
+
cornerUpLeft: '/',
|
|
104
|
+
cornerDownLeft: '\\',
|
|
105
|
+
arrowUp: '^',
|
|
106
|
+
arrowDown: 'v',
|
|
107
|
+
node: '*',
|
|
108
|
+
selfLoop: '@',
|
|
109
|
+
landingArrow: '<',
|
|
110
|
+
fallback: '?',
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
function alphabetFor(mode: GraphGlyphMode): GraphGlyphAlphabet {
|
|
114
|
+
return mode === 'ascii' ? ASCII_ALPHABET : UNICODE_ALPHABET;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function glyphFor(dirs: ReadonlySet<Direction>, alphabet: GraphGlyphAlphabet): string {
|
|
118
|
+
const has = (d: Direction) => dirs.has(d);
|
|
119
|
+
|
|
120
|
+
if (has('up') && has('down') && !has('left') && !has('right')) return alphabet.vertical;
|
|
121
|
+
if (has('left') && has('right') && !has('up') && !has('down')) return alphabet.horizontal;
|
|
122
|
+
if (has('up') && has('right') && !has('down') && !has('left')) return alphabet.cornerUpRight;
|
|
123
|
+
if (has('down') && has('right') && !has('up') && !has('left')) return alphabet.cornerDownRight;
|
|
124
|
+
if (has('up') && has('left') && !has('down') && !has('right')) return alphabet.cornerUpLeft;
|
|
125
|
+
if (has('down') && has('left') && !has('up') && !has('right')) return alphabet.cornerDownLeft;
|
|
126
|
+
if (has('up') && !has('down') && !has('left') && !has('right')) return alphabet.arrowUp;
|
|
127
|
+
if (has('down') && !has('up') && !has('left') && !has('right')) return alphabet.arrowDown;
|
|
128
|
+
|
|
129
|
+
// Fallback: shouldn't happen in well-formed grids
|
|
130
|
+
return alphabet.fallback;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ---------------------------------------------------------------------------
|
|
134
|
+
// renderCell — project one cell to a coloured string fragment.
|
|
135
|
+
// ---------------------------------------------------------------------------
|
|
136
|
+
|
|
137
|
+
const NO_COLOR: Colorizer = (t) => t;
|
|
138
|
+
|
|
139
|
+
function renderCell(cell: Cell, colorEnabled: boolean, alphabet: GraphGlyphAlphabet): string {
|
|
140
|
+
// Node marker overrides everything
|
|
141
|
+
if (cell.node !== undefined) {
|
|
142
|
+
// Every node uses ○ — the ∅ identifier is only used as the label, not as a
|
|
143
|
+
// glyph, per the golden colour model. Colour by role (focus) or lane (flat).
|
|
144
|
+
const colorize = !colorEnabled
|
|
145
|
+
? NO_COLOR
|
|
146
|
+
: cell.node.role !== undefined
|
|
147
|
+
? roleColor(cell.node.role)
|
|
148
|
+
: laneColor(cell.node.lane);
|
|
149
|
+
return colorize(alphabet.node);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (cell.lines.length === 0) {
|
|
153
|
+
return ' ';
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Pick the drawn line by occlusion: lowest plane number wins (drawn on top).
|
|
157
|
+
// At an equal plane, on-path beats off-path explicitly — never rely on array
|
|
158
|
+
// order to break the tie (the single-owner invariant should already prevent
|
|
159
|
+
// a same-plane on/off-path collision, but the priority is made explicit here).
|
|
160
|
+
const topLine = cell.lines.reduce<CellLine>((best, current) => {
|
|
161
|
+
if (current.plane < best.plane) return current;
|
|
162
|
+
if (current.plane > best.plane) return best;
|
|
163
|
+
if (current.line.role === 'on-path' && best.line.role !== 'on-path') return current;
|
|
164
|
+
return best;
|
|
165
|
+
}, cell.lines[0]!);
|
|
166
|
+
|
|
167
|
+
const glyph =
|
|
168
|
+
topLine.selfLoop === true
|
|
169
|
+
? alphabet.selfLoop
|
|
170
|
+
: topLine.landingArrow === true
|
|
171
|
+
? alphabet.landingArrow
|
|
172
|
+
: glyphFor(topLine.directions, alphabet);
|
|
173
|
+
const colorize = !colorEnabled
|
|
174
|
+
? NO_COLOR
|
|
175
|
+
: topLine.line.role !== undefined
|
|
176
|
+
? roleColor(topLine.line.role)
|
|
177
|
+
: laneColor(topLine.line.lane);
|
|
178
|
+
return colorize(glyph);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// ---------------------------------------------------------------------------
|
|
182
|
+
// RenderGridOptions
|
|
183
|
+
// ---------------------------------------------------------------------------
|
|
184
|
+
|
|
185
|
+
export interface RenderGridOptions {
|
|
186
|
+
readonly colorize?: boolean;
|
|
187
|
+
readonly colsPerLane?: number;
|
|
188
|
+
readonly glyphMode?: GraphGlyphMode;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// ---------------------------------------------------------------------------
|
|
192
|
+
// renderGrid — the main render function.
|
|
193
|
+
//
|
|
194
|
+
// Produces the final string: one line per grid row, each cell rendered to
|
|
195
|
+
// a coloured character. Trailing empty cells are trimmed, but we always
|
|
196
|
+
// include up to the last non-empty cell's connector column
|
|
197
|
+
// (the full 2-col-per-lane width for the active lane count).
|
|
198
|
+
// ---------------------------------------------------------------------------
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Render a single grid row to a coloured string. A completely empty row returns
|
|
202
|
+
* the empty string (the row is NOT dropped) so callers that pair grid rows with
|
|
203
|
+
* an external per-row label list keep a 1:1 index correspondence. `renderGrid`
|
|
204
|
+
* itself drops empty rows for its standalone output (but preserves separator rows).
|
|
205
|
+
*/
|
|
206
|
+
export function renderGridRow(
|
|
207
|
+
row: readonly (Cell | undefined)[],
|
|
208
|
+
opts: RenderGridOptions = {},
|
|
209
|
+
): string {
|
|
210
|
+
// Inter-component separator row — always renders as an empty line.
|
|
211
|
+
if (row[0]?.separator === true) {
|
|
212
|
+
return '';
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Find the last non-empty cell index
|
|
216
|
+
let lastNonEmpty = -1;
|
|
217
|
+
for (let i = row.length - 1; i >= 0; i--) {
|
|
218
|
+
const cell = row[i];
|
|
219
|
+
if (cell !== undefined && (cell.lines.length > 0 || cell.node !== undefined)) {
|
|
220
|
+
lastNonEmpty = i;
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (lastNonEmpty < 0) {
|
|
226
|
+
return '';
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Extend to the next even column boundary (connector col of the current lane)
|
|
230
|
+
// so that connector columns are always present for active lane ranges.
|
|
231
|
+
const colsPerLane = opts.colsPerLane ?? DEFAULT_COLS_PER_LANE;
|
|
232
|
+
const colorEnabled = opts.colorize ?? true;
|
|
233
|
+
const alphabet = alphabetFor(opts.glyphMode ?? 'unicode');
|
|
234
|
+
const lastLane = Math.floor(lastNonEmpty / colsPerLane);
|
|
235
|
+
const lastConnectorCol = lastLane * colsPerLane + (colsPerLane - 1);
|
|
236
|
+
const renderThrough = Math.max(lastNonEmpty, lastConnectorCol);
|
|
237
|
+
|
|
238
|
+
let line = '';
|
|
239
|
+
for (let col = 0; col <= Math.min(renderThrough, row.length - 1); col++) {
|
|
240
|
+
const cell = row[col];
|
|
241
|
+
line += cell === undefined ? ' ' : renderCell(cell, colorEnabled, alphabet);
|
|
242
|
+
}
|
|
243
|
+
return line;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export function renderGrid(grid: Grid, opts: RenderGridOptions = {}): string {
|
|
247
|
+
const lines: string[] = [];
|
|
248
|
+
for (const row of grid) {
|
|
249
|
+
const isSeparator = row[0]?.separator === true;
|
|
250
|
+
const rendered = renderGridRow(row, opts);
|
|
251
|
+
if (rendered === '' && !isSeparator) {
|
|
252
|
+
// Completely empty non-separator row — skip in standalone output.
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
lines.push(rendered);
|
|
256
|
+
}
|
|
257
|
+
return lines.join('\n');
|
|
258
|
+
}
|
|
@@ -173,10 +173,19 @@ function compareNodesTipsFirst(a: string, b: string, rank: ReadonlyMap<string, n
|
|
|
173
173
|
* at the same rank — stable across edge-insertion order and correct under
|
|
174
174
|
* diamonds, cross-links, and rollbacks.
|
|
175
175
|
*/
|
|
176
|
+
function maxRank(rank: ReadonlyMap<string, number>): number {
|
|
177
|
+
let max = 0;
|
|
178
|
+
for (const value of rank.values()) {
|
|
179
|
+
if (value > max) max = value;
|
|
180
|
+
}
|
|
181
|
+
return max;
|
|
182
|
+
}
|
|
183
|
+
|
|
176
184
|
function layerNodesByLongestForwardPath(
|
|
177
185
|
componentNodes: ReadonlySet<string>,
|
|
178
186
|
topology: MigrationListGraphTopology,
|
|
179
187
|
graph: MigrationGraph,
|
|
188
|
+
contractHash: string | undefined,
|
|
180
189
|
): readonly string[] {
|
|
181
190
|
const forwardOut = new Map<string, string[]>();
|
|
182
191
|
|
|
@@ -224,6 +233,15 @@ function layerNodesByLongestForwardPath(
|
|
|
224
233
|
}
|
|
225
234
|
}
|
|
226
235
|
|
|
236
|
+
if (
|
|
237
|
+
contractHash !== undefined &&
|
|
238
|
+
contractHash !== EMPTY_CONTRACT_HASH &&
|
|
239
|
+
componentNodes.has(contractHash) &&
|
|
240
|
+
(forwardOut.get(contractHash) ?? []).length === 0
|
|
241
|
+
) {
|
|
242
|
+
rank.set(contractHash, maxRank(rank) + 1);
|
|
243
|
+
}
|
|
244
|
+
|
|
227
245
|
return [...componentNodes].sort((a, b) => compareNodesTipsFirst(a, b, rank));
|
|
228
246
|
}
|
|
229
247
|
|
|
@@ -262,6 +280,99 @@ function detachedContractHash(
|
|
|
262
280
|
: undefined;
|
|
263
281
|
}
|
|
264
282
|
|
|
283
|
+
function isForwardLeaf(node: string, edges: readonly ClassifiedEdge[]): boolean {
|
|
284
|
+
return !edges.some((e) => e.kind === 'forward' && e.from === node && e.from !== e.to);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function forwardReachableFrom(
|
|
288
|
+
start: string,
|
|
289
|
+
forwardTo: ReadonlyMap<string, readonly string[]>,
|
|
290
|
+
): ReadonlySet<string> {
|
|
291
|
+
const reachable = new Set<string>([start]);
|
|
292
|
+
const queue = [start];
|
|
293
|
+
while (queue.length > 0) {
|
|
294
|
+
const node = queue.shift();
|
|
295
|
+
if (node === undefined) continue;
|
|
296
|
+
for (const next of forwardTo.get(node) ?? []) {
|
|
297
|
+
if (!reachable.has(next)) {
|
|
298
|
+
reachable.add(next);
|
|
299
|
+
queue.push(next);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return reachable;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function buildForwardToMap(edges: readonly ClassifiedEdge[]): Map<string, string[]> {
|
|
307
|
+
const forwardTo = new Map<string, string[]>();
|
|
308
|
+
for (const edge of edges) {
|
|
309
|
+
if (edge.kind !== 'forward' || edge.from === edge.to) continue;
|
|
310
|
+
const bucket = forwardTo.get(edge.from);
|
|
311
|
+
if (bucket) bucket.push(edge.to);
|
|
312
|
+
else forwardTo.set(edge.from, [edge.to]);
|
|
313
|
+
}
|
|
314
|
+
return forwardTo;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function sortEdgesForContractHashTrunk(
|
|
318
|
+
edges: ClassifiedEdge[],
|
|
319
|
+
contractHash: string | undefined,
|
|
320
|
+
): ClassifiedEdge[] {
|
|
321
|
+
if (
|
|
322
|
+
contractHash === undefined ||
|
|
323
|
+
contractHash === EMPTY_CONTRACT_HASH ||
|
|
324
|
+
!isForwardLeaf(contractHash, edges)
|
|
325
|
+
) {
|
|
326
|
+
return edges;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const preferredLeaf = contractHash;
|
|
330
|
+
const forwardTo = buildForwardToMap(edges);
|
|
331
|
+
const reachability = new Map<string, ReadonlySet<string>>();
|
|
332
|
+
function canReachContractHash(from: string): boolean {
|
|
333
|
+
let cached = reachability.get(from);
|
|
334
|
+
if (cached === undefined) {
|
|
335
|
+
cached = forwardReachableFrom(from, forwardTo);
|
|
336
|
+
reachability.set(from, cached);
|
|
337
|
+
}
|
|
338
|
+
return cached.has(preferredLeaf);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function trunkBias(edge: ClassifiedEdge): number {
|
|
342
|
+
if (edge.kind !== 'forward' || edge.from === edge.to) return 0;
|
|
343
|
+
if (edge.to === preferredLeaf) return 2;
|
|
344
|
+
if (canReachContractHash(edge.to)) return 1;
|
|
345
|
+
return 0;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return edges
|
|
349
|
+
.map((edge, index) => ({ edge, index, bias: trunkBias(edge) }))
|
|
350
|
+
.sort((a, b) => {
|
|
351
|
+
if (a.edge.from !== b.edge.from) return a.index - b.index;
|
|
352
|
+
if (a.bias !== b.bias) return b.bias - a.bias;
|
|
353
|
+
return a.index - b.index;
|
|
354
|
+
})
|
|
355
|
+
.map(({ edge }) => edge);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function rebuildEdgeLookupMaps(edges: readonly ClassifiedEdge[]): {
|
|
359
|
+
edgesByFrom: Map<string, ClassifiedEdge[]>;
|
|
360
|
+
edgesByTo: Map<string, ClassifiedEdge[]>;
|
|
361
|
+
} {
|
|
362
|
+
const edgesByFrom = new Map<string, ClassifiedEdge[]>();
|
|
363
|
+
const edgesByTo = new Map<string, ClassifiedEdge[]>();
|
|
364
|
+
for (const classified of edges) {
|
|
365
|
+
const fromBucket = edgesByFrom.get(classified.from);
|
|
366
|
+
if (fromBucket) fromBucket.push(classified);
|
|
367
|
+
else edgesByFrom.set(classified.from, [classified]);
|
|
368
|
+
|
|
369
|
+
const toBucket = edgesByTo.get(classified.to);
|
|
370
|
+
if (toBucket) toBucket.push(classified);
|
|
371
|
+
else edgesByTo.set(classified.to, [classified]);
|
|
372
|
+
}
|
|
373
|
+
return { edgesByFrom, edgesByTo };
|
|
374
|
+
}
|
|
375
|
+
|
|
265
376
|
export function buildMigrationGraphRows(
|
|
266
377
|
graph: MigrationGraph,
|
|
267
378
|
options: BuildMigrationGraphRowsOptions = {},
|
|
@@ -284,31 +395,23 @@ export function buildMigrationGraphRows(
|
|
|
284
395
|
|
|
285
396
|
// 2. Build classified edge list
|
|
286
397
|
const edges: ClassifiedEdge[] = [];
|
|
287
|
-
const edgesByFrom = new Map<string, ClassifiedEdge[]>();
|
|
288
|
-
const edgesByTo = new Map<string, ClassifiedEdge[]>();
|
|
289
398
|
|
|
290
399
|
for (const edgeList of graph.forwardChain.values()) {
|
|
291
400
|
for (const edge of edgeList) {
|
|
292
401
|
const kind = topology.kindByMigrationHash.get(edge.migrationHash) ?? 'forward';
|
|
293
|
-
|
|
402
|
+
edges.push({
|
|
294
403
|
migrationHash: edge.migrationHash,
|
|
295
404
|
from: edge.from,
|
|
296
405
|
to: edge.to,
|
|
297
406
|
dirName: edge.dirName,
|
|
298
407
|
kind,
|
|
299
|
-
};
|
|
300
|
-
edges.push(classified);
|
|
301
|
-
|
|
302
|
-
const fromBucket = edgesByFrom.get(edge.from);
|
|
303
|
-
if (fromBucket) fromBucket.push(classified);
|
|
304
|
-
else edgesByFrom.set(edge.from, [classified]);
|
|
305
|
-
|
|
306
|
-
const toBucket = edgesByTo.get(edge.to);
|
|
307
|
-
if (toBucket) toBucket.push(classified);
|
|
308
|
-
else edgesByTo.set(edge.to, [classified]);
|
|
408
|
+
});
|
|
309
409
|
}
|
|
310
410
|
}
|
|
311
411
|
|
|
412
|
+
const sortedEdges = sortEdgesForContractHashTrunk(edges, options.contractHash);
|
|
413
|
+
const { edgesByFrom, edgesByTo } = rebuildEdgeLookupMaps(sortedEdges);
|
|
414
|
+
|
|
312
415
|
// 3. Find weakly-connected components (ordered: EMPTY first, then lex)
|
|
313
416
|
const components = weaklyConnectedComponents(graph);
|
|
314
417
|
|
|
@@ -318,7 +421,12 @@ export function buildMigrationGraphRows(
|
|
|
318
421
|
if (i > 0) nodes.push(null);
|
|
319
422
|
const component = components[i];
|
|
320
423
|
if (component === undefined) continue;
|
|
321
|
-
const ordered = layerNodesByLongestForwardPath(
|
|
424
|
+
const ordered = layerNodesByLongestForwardPath(
|
|
425
|
+
component,
|
|
426
|
+
topology,
|
|
427
|
+
graph,
|
|
428
|
+
options.contractHash,
|
|
429
|
+
);
|
|
322
430
|
for (const node of ordered) {
|
|
323
431
|
nodes.push(node);
|
|
324
432
|
}
|
|
@@ -332,5 +440,10 @@ export function buildMigrationGraphRows(
|
|
|
332
440
|
nodes.unshift(detached);
|
|
333
441
|
}
|
|
334
442
|
|
|
335
|
-
return {
|
|
443
|
+
return {
|
|
444
|
+
nodes,
|
|
445
|
+
edges: sortedEdges,
|
|
446
|
+
edgesByFrom,
|
|
447
|
+
edgesByTo,
|
|
448
|
+
};
|
|
336
449
|
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import type { MigrationGraph } from '@prisma-next/migration-tools/graph';
|
|
2
|
+
import type { GlyphMode } from '../glyph-mode';
|
|
3
|
+
import {
|
|
4
|
+
computeLabelColumn,
|
|
5
|
+
computeMaxDirNameWidth,
|
|
6
|
+
renderMigrationGraphCommand,
|
|
7
|
+
} from './migration-graph-command-render';
|
|
8
|
+
import { buildGrid } from './migration-graph-grid-layout';
|
|
9
|
+
import type { MigrationEdgeAnnotation } from './migration-graph-labels';
|
|
10
|
+
import type { Highlight } from './migration-graph-model';
|
|
11
|
+
import { buildMigrationGraphRows } from './migration-graph-rows';
|
|
12
|
+
import {
|
|
13
|
+
buildEdgeAnnotationsByHashFromListEntries,
|
|
14
|
+
buildRefsByHashFromListEntries,
|
|
15
|
+
type MigrationListStyler,
|
|
16
|
+
} from './migration-list-render';
|
|
17
|
+
import type { MigrationListEntry } from './migration-list-types';
|
|
18
|
+
|
|
19
|
+
export { buildEdgeAnnotationsByHashFromListEntries } from './migration-list-render';
|
|
20
|
+
|
|
21
|
+
export function mergeMigrationEdgeAnnotations(
|
|
22
|
+
listOverlay: ReadonlyMap<string, MigrationEdgeAnnotation>,
|
|
23
|
+
statusOverlay: ReadonlyMap<string, MigrationEdgeAnnotation>,
|
|
24
|
+
): ReadonlyMap<string, MigrationEdgeAnnotation> {
|
|
25
|
+
const merged = new Map<string, MigrationEdgeAnnotation>();
|
|
26
|
+
for (const [migrationHash, listAnnotation] of listOverlay) {
|
|
27
|
+
const statusAnnotation = statusOverlay.get(migrationHash);
|
|
28
|
+
merged.set(migrationHash, {
|
|
29
|
+
...listAnnotation,
|
|
30
|
+
...(statusAnnotation?.status !== undefined ? { status: statusAnnotation.status } : {}),
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
return merged;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Translate `migrate --show` per-edge path-highlight annotations into a
|
|
38
|
+
* {@link Highlight}. With any `pathHighlight` present the result is focus mode
|
|
39
|
+
* (on-path lifted green, off-path dim); otherwise flat (lane-rotation colour).
|
|
40
|
+
*/
|
|
41
|
+
export function highlightFromEdgeAnnotations(
|
|
42
|
+
edgeAnnotationsByHash: ReadonlyMap<string, MigrationEdgeAnnotation>,
|
|
43
|
+
): Highlight {
|
|
44
|
+
const onPath = new Set<string>();
|
|
45
|
+
let anyPathHighlight = false;
|
|
46
|
+
for (const [migrationHash, annotation] of edgeAnnotationsByHash) {
|
|
47
|
+
if (annotation.pathHighlight === undefined) continue;
|
|
48
|
+
anyPathHighlight = true;
|
|
49
|
+
if (annotation.pathHighlight === 'on-path') onPath.add(migrationHash);
|
|
50
|
+
}
|
|
51
|
+
return anyPathHighlight ? { mode: 'focus', onPath } : { mode: 'flat', onPath: new Set() };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface RenderMigrationGraphSpaceTreeInput {
|
|
55
|
+
readonly graph: MigrationGraph;
|
|
56
|
+
readonly migrations: readonly MigrationListEntry[];
|
|
57
|
+
readonly liveContractHash: string;
|
|
58
|
+
readonly glyphMode: GlyphMode;
|
|
59
|
+
readonly colorize: boolean;
|
|
60
|
+
readonly refsByHash?: ReadonlyMap<string, readonly string[]>;
|
|
61
|
+
readonly statusOverlayByHash?: ReadonlyMap<string, MigrationEdgeAnnotation>;
|
|
62
|
+
readonly dbHash?: string;
|
|
63
|
+
readonly styler?: MigrationListStyler;
|
|
64
|
+
/**
|
|
65
|
+
* Cross-space override for the gutter→label column (the widest gutter across
|
|
66
|
+
* sibling space sections, plus the label gap). Named for historical
|
|
67
|
+
* continuity with the previous renderer's prefix-width input.
|
|
68
|
+
*/
|
|
69
|
+
readonly globalMaxEdgeTreePrefixWidth?: number;
|
|
70
|
+
readonly globalMaxDirNameWidth?: number;
|
|
71
|
+
/**
|
|
72
|
+
* Whether this render is for the app space. When false, `contractHash` is not
|
|
73
|
+
* forwarded to `buildMigrationGraphRows` (suppressing the floating working-
|
|
74
|
+
* contract node) and the `@contract` marker is suppressed. Defaults to `true`.
|
|
75
|
+
*/
|
|
76
|
+
readonly isAppSpace?: boolean;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface ComputeGlobalMaxEdgeTreePrefixWidthInput {
|
|
80
|
+
readonly graph: MigrationGraph;
|
|
81
|
+
readonly liveContractHash: string;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function buildGridForInput(input: ComputeGlobalMaxEdgeTreePrefixWidthInput): {
|
|
85
|
+
readonly grid: ReturnType<typeof buildGrid>;
|
|
86
|
+
readonly rowModel: ReturnType<typeof buildMigrationGraphRows>;
|
|
87
|
+
} {
|
|
88
|
+
const rowModel = buildMigrationGraphRows(input.graph, { contractHash: input.liveContractHash });
|
|
89
|
+
const grid = buildGrid(rowModel, {}, { mode: 'flat', onPath: new Set() });
|
|
90
|
+
return { grid, rowModel };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The widest gutter→label column across the given space layouts. Cross-space
|
|
95
|
+
* callers pass this back in so every section's labels share one column.
|
|
96
|
+
*/
|
|
97
|
+
export function computeGlobalMaxEdgeTreePrefixWidth(
|
|
98
|
+
inputs: readonly ComputeGlobalMaxEdgeTreePrefixWidthInput[],
|
|
99
|
+
glyphMode: GlyphMode = 'unicode',
|
|
100
|
+
): number {
|
|
101
|
+
let globalMax = 0;
|
|
102
|
+
for (const input of inputs) {
|
|
103
|
+
const { grid } = buildGridForInput(input);
|
|
104
|
+
globalMax = Math.max(globalMax, computeLabelColumn(grid, glyphMode));
|
|
105
|
+
}
|
|
106
|
+
return globalMax;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function computeGlobalMaxDirNameWidth(
|
|
110
|
+
inputs: readonly ComputeGlobalMaxEdgeTreePrefixWidthInput[],
|
|
111
|
+
): number {
|
|
112
|
+
let globalMax = 0;
|
|
113
|
+
for (const input of inputs) {
|
|
114
|
+
const { rowModel } = buildGridForInput(input);
|
|
115
|
+
globalMax = Math.max(globalMax, computeMaxDirNameWidth(rowModel));
|
|
116
|
+
}
|
|
117
|
+
return globalMax;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function renderMigrationGraphSpaceTreeInternal(input: RenderMigrationGraphSpaceTreeInput): string {
|
|
121
|
+
const appSpace = input.isAppSpace !== false;
|
|
122
|
+
const rowModel = buildMigrationGraphRows(input.graph, {
|
|
123
|
+
...(appSpace ? { contractHash: input.liveContractHash } : {}),
|
|
124
|
+
});
|
|
125
|
+
const listOverlay = buildEdgeAnnotationsByHashFromListEntries(input.migrations);
|
|
126
|
+
const edgeAnnotationsByHash =
|
|
127
|
+
input.statusOverlayByHash === undefined
|
|
128
|
+
? listOverlay
|
|
129
|
+
: mergeMigrationEdgeAnnotations(listOverlay, input.statusOverlayByHash);
|
|
130
|
+
const highlight = highlightFromEdgeAnnotations(edgeAnnotationsByHash);
|
|
131
|
+
const grid = buildGrid(rowModel, {}, highlight);
|
|
132
|
+
|
|
133
|
+
return renderMigrationGraphCommand({
|
|
134
|
+
grid,
|
|
135
|
+
rowModel,
|
|
136
|
+
colorize: input.colorize,
|
|
137
|
+
glyphMode: input.glyphMode,
|
|
138
|
+
contractHash: input.liveContractHash,
|
|
139
|
+
isAppSpace: appSpace,
|
|
140
|
+
edgeAnnotationsByHash,
|
|
141
|
+
refsByHash: input.refsByHash ?? buildRefsByHashFromListEntries(input.migrations),
|
|
142
|
+
...(input.dbHash !== undefined ? { dbHash: input.dbHash } : {}),
|
|
143
|
+
...(input.styler !== undefined ? { styler: input.styler } : {}),
|
|
144
|
+
...(input.globalMaxEdgeTreePrefixWidth !== undefined
|
|
145
|
+
? { globalLabelColumn: input.globalMaxEdgeTreePrefixWidth }
|
|
146
|
+
: {}),
|
|
147
|
+
...(input.globalMaxDirNameWidth !== undefined
|
|
148
|
+
? { globalMaxDirNameWidth: input.globalMaxDirNameWidth }
|
|
149
|
+
: {}),
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function renderMigrationGraphSpaceTree(input: RenderMigrationGraphSpaceTreeInput): string {
|
|
154
|
+
return renderMigrationGraphSpaceTreeInternal(input);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function renderMigrationGraphSpaceTrees(
|
|
158
|
+
inputs: readonly RenderMigrationGraphSpaceTreeInput[],
|
|
159
|
+
): readonly string[] {
|
|
160
|
+
const globalInputs: ComputeGlobalMaxEdgeTreePrefixWidthInput[] = inputs.map((input) => ({
|
|
161
|
+
graph: input.graph,
|
|
162
|
+
liveContractHash: input.liveContractHash,
|
|
163
|
+
}));
|
|
164
|
+
const glyphMode = inputs[0]?.glyphMode ?? 'unicode';
|
|
165
|
+
const globalLabelColumn =
|
|
166
|
+
inputs.length > 1 ? computeGlobalMaxEdgeTreePrefixWidth(globalInputs, glyphMode) : undefined;
|
|
167
|
+
const globalMaxDirName =
|
|
168
|
+
inputs.length > 1 ? computeGlobalMaxDirNameWidth(globalInputs) : undefined;
|
|
169
|
+
return inputs.map((input) =>
|
|
170
|
+
renderMigrationGraphSpaceTreeInternal({
|
|
171
|
+
...input,
|
|
172
|
+
...(globalLabelColumn !== undefined
|
|
173
|
+
? { globalMaxEdgeTreePrefixWidth: globalLabelColumn }
|
|
174
|
+
: {}),
|
|
175
|
+
...(globalMaxDirName !== undefined ? { globalMaxDirNameWidth: globalMaxDirName } : {}),
|
|
176
|
+
}),
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function indentMigrationGraphTreeBlock(treeOutput: string, indent: string): string {
|
|
181
|
+
if (treeOutput.length === 0) {
|
|
182
|
+
return treeOutput;
|
|
183
|
+
}
|
|
184
|
+
return treeOutput
|
|
185
|
+
.split('\n')
|
|
186
|
+
.map((line) => (line.length === 0 ? line : `${indent}${line}`))
|
|
187
|
+
.join('\n');
|
|
188
|
+
}
|