@prisma-next/cli 0.12.0-dev.5 → 0.12.0-dev.50
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-DC-UlBLy.mjs} +83 -58
- package/dist/client-DC-UlBLy.mjs.map +1 -0
- package/dist/{command-helpers-Bbw1GbwL.mjs → command-helpers-esJGBD4W.mjs} +317 -23
- package/dist/command-helpers-esJGBD4W.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 +2 -2
- package/dist/commands/migrate.d.mts.map +1 -1
- package/dist/commands/migrate.mjs +6 -8
- 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 +183 -2
- package/dist/commands/migration-graph.mjs.map +1 -0
- package/dist/commands/migration-list.d.mts +25 -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.mjs +3 -3
- package/dist/commands/migration-plan.d.mts +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 +23 -35
- 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.mjs +3 -3
- 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/{contract-at-errors-BxP-TOMl.mjs → contract-at-errors-COZAemUl.mjs} +2 -2
- package/dist/{contract-at-errors-BxP-TOMl.mjs.map → contract-at-errors-COZAemUl.mjs.map} +1 -1
- package/dist/{contract-emit-DxcGl4Uq.mjs → contract-emit-Bv46RAIO.mjs} +3 -3
- package/dist/{contract-emit-DxcGl4Uq.mjs.map → contract-emit-Bv46RAIO.mjs.map} +1 -1
- package/dist/{contract-emit-D-4jrNve.mjs → contract-emit-DIWImLqS.mjs} +5 -5
- package/dist/{contract-emit-D-4jrNve.mjs.map → contract-emit-DIWImLqS.mjs.map} +1 -1
- package/dist/{contract-infer-D8uEbJuu.mjs → contract-infer-DpGN9SAj.mjs} +3 -3
- package/dist/{contract-infer-D8uEbJuu.mjs.map → contract-infer-DpGN9SAj.mjs.map} +1 -1
- package/dist/{contract-space-aggregate-loader-DvZwdkrr.mjs → contract-space-aggregate-loader-CpNVrBqW.mjs} +63 -5
- package/dist/{contract-space-aggregate-loader-DvZwdkrr.mjs.map → contract-space-aggregate-loader-CpNVrBqW.mjs.map} +1 -1
- package/dist/{db-verify-v_vUKXTU.mjs → db-verify-Cq16Obsw.mjs} +4 -4
- package/dist/{db-verify-v_vUKXTU.mjs.map → db-verify-Cq16Obsw.mjs.map} +1 -1
- package/dist/exports/control-api.d.mts +2 -2
- package/dist/exports/control-api.d.mts.map +1 -1
- package/dist/exports/control-api.mjs +2 -2
- package/dist/exports/index.mjs +1 -1
- package/dist/exports/init-output.mjs +1 -1
- package/dist/{framework-components-fYXjz_in.mjs → framework-components-BO9VO43s.mjs} +2 -2
- package/dist/{framework-components-fYXjz_in.mjs.map → framework-components-BO9VO43s.mjs.map} +1 -1
- package/dist/{global-flags-DEHjV8_s.d.mts → global-flags-CV5LhrFg.d.mts} +1 -1
- package/dist/{global-flags-DEHjV8_s.d.mts.map → global-flags-CV5LhrFg.d.mts.map} +1 -1
- package/dist/{init-Cv9UzWL5.mjs → init-C0rjiQ9I.mjs} +5 -58
- package/dist/init-C0rjiQ9I.mjs.map +1 -0
- package/dist/{inspect-live-schema-C6ohV_oQ.mjs → inspect-live-schema-CRDKTNcf.mjs} +3 -3
- package/dist/{inspect-live-schema-C6ohV_oQ.mjs.map → inspect-live-schema-CRDKTNcf.mjs.map} +1 -1
- package/dist/migration-check-BxWlQBOs.mjs +573 -0
- package/dist/migration-check-BxWlQBOs.mjs.map +1 -0
- package/dist/{migration-command-scaffold-CjvwO6at.mjs → migration-command-scaffold-BDd9abqW.mjs} +3 -3
- package/dist/{migration-command-scaffold-CjvwO6at.mjs.map → migration-command-scaffold-BDd9abqW.mjs.map} +1 -1
- package/dist/migration-graph-space-render-CeNXh_Wy.mjs +1966 -0
- package/dist/migration-graph-space-render-CeNXh_Wy.mjs.map +1 -0
- package/dist/migration-list-vJWFuXca.mjs +228 -0
- package/dist/migration-list-vJWFuXca.mjs.map +1 -0
- package/dist/migration-log-6rcHQSI4.mjs +222 -0
- package/dist/migration-log-6rcHQSI4.mjs.map +1 -0
- package/dist/migration-path-target-UkxkgXnv.mjs +38 -0
- package/dist/migration-path-target-UkxkgXnv.mjs.map +1 -0
- package/dist/{migration-plan-9DJ7q7_z.mjs → migration-plan-CHu_erQ5.mjs} +5 -6
- package/dist/{migration-plan-9DJ7q7_z.mjs.map → migration-plan-CHu_erQ5.mjs.map} +1 -1
- package/dist/migration-status-Bjv91dE7.mjs +444 -0
- package/dist/migration-status-Bjv91dE7.mjs.map +1 -0
- package/dist/{output-B60Gw5fu.mjs → output-BD61elic.mjs} +1 -1
- package/dist/{output-B60Gw5fu.mjs.map → output-BD61elic.mjs.map} +1 -1
- package/dist/{ref-advancement-DUZqsue6.mjs → ref-advancement-CJY9zOv7.mjs} +1 -1
- package/dist/{ref-advancement-DUZqsue6.mjs.map → ref-advancement-CJY9zOv7.mjs.map} +1 -1
- package/dist/schemas-BL33A3i-.d.mts +193 -0
- package/dist/schemas-BL33A3i-.d.mts.map +1 -0
- package/dist/schemas-DJY2O09F.mjs +112 -0
- package/dist/schemas-DJY2O09F.mjs.map +1 -0
- package/dist/telemetry-CZkgkR_O.mjs +122 -0
- package/dist/telemetry-CZkgkR_O.mjs.map +1 -0
- package/dist/{terminal-ui-5Y6mrg93.d.mts → terminal-ui-BgLiAOYi.d.mts} +1 -1
- package/dist/{terminal-ui-5Y6mrg93.d.mts.map → terminal-ui-BgLiAOYi.d.mts.map} +1 -1
- package/dist/{types-Dt_SfqFm.d.mts → types-qV41eEXH.d.mts} +44 -31
- package/dist/types-qV41eEXH.d.mts.map +1 -0
- package/dist/{verify-DCA9Sldu.mjs → verify-IilvIk_E.mjs} +2 -2
- package/dist/{verify-DCA9Sldu.mjs.map → verify-IilvIk_E.mjs.map} +1 -1
- package/package.json +22 -19
- package/src/cli.ts +5 -0
- 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/json/schemas.ts +195 -0
- package/src/commands/migrate.ts +6 -6
- package/src/commands/migration-check.ts +469 -134
- package/src/commands/migration-graph.ts +162 -91
- package/src/commands/migration-list.ts +69 -39
- package/src/commands/migration-log.ts +52 -102
- package/src/commands/migration-show.ts +31 -66
- package/src/commands/migration-status-overlay.ts +61 -0
- package/src/commands/migration-status.ts +453 -1067
- package/src/commands/telemetry/index.ts +107 -0
- package/src/commands/telemetry/status.ts +67 -0
- package/src/control-api/client.ts +20 -9
- package/src/control-api/operations/contract-emit.ts +2 -2
- package/src/control-api/operations/db-init.ts +3 -3
- package/src/control-api/operations/{db-apply.ts → db-run.ts} +37 -10
- package/src/control-api/operations/db-update.ts +4 -4
- package/src/control-api/operations/{migration-apply.ts → migrate.ts} +32 -24
- package/src/control-api/operations/{apply.ts → run-migration.ts} +33 -27
- package/src/control-api/types.ts +46 -29
- package/src/utils/cli-errors.ts +70 -2
- package/src/utils/formatters/errors.ts +11 -0
- package/src/utils/formatters/migration-graph-lane-colors.ts +194 -0
- package/src/utils/formatters/migration-graph-layout.ts +51 -7
- 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 +405 -77
- 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 +123 -71
- package/src/utils/formatters/migration-list-styler.ts +48 -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/extension-pack-inputs-IDvjRCi3.mjs +0 -62
- package/dist/extension-pack-inputs-IDvjRCi3.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/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-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/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
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';
|
|
2
|
-
import { bold } from 'colorette';
|
|
2
|
+
import { bold, createColors, green, yellow } from 'colorette';
|
|
3
3
|
import stringWidth from 'string-width';
|
|
4
4
|
import type { GlyphMode } from '../glyph-mode';
|
|
5
|
+
import {
|
|
6
|
+
laneColorForColumn,
|
|
7
|
+
NEUTRAL_LANE_COLUMN,
|
|
8
|
+
type RowArcLaneColors,
|
|
9
|
+
resolveConnectorLaneColors,
|
|
10
|
+
resolveRowArcLaneColors,
|
|
11
|
+
stylerForLaneColumn,
|
|
12
|
+
} from './migration-graph-lane-colors';
|
|
13
|
+
|
|
14
|
+
export { resolveConnectorLaneColors } from './migration-graph-lane-colors';
|
|
15
|
+
|
|
5
16
|
import type {
|
|
6
17
|
MigrationGraphGridModel,
|
|
7
18
|
MigrationGraphGridRow,
|
|
@@ -12,10 +23,15 @@ import {
|
|
|
12
23
|
MIGRATION_LIST_HASH_WIDTH,
|
|
13
24
|
migrationListEmptySource,
|
|
14
25
|
migrationListForwardArrow,
|
|
26
|
+
padFromHashColumn,
|
|
15
27
|
} from './migration-list-data-column';
|
|
16
28
|
import type { MigrationEdgeKind } from './migration-list-graph-topology';
|
|
17
29
|
import type { MigrationListStyler } from './migration-list-render';
|
|
18
|
-
import {
|
|
30
|
+
import {
|
|
31
|
+
CONTRACT_MARKER_NAME,
|
|
32
|
+
createAnsiMigrationListStyler,
|
|
33
|
+
formatContractNodeOverlays,
|
|
34
|
+
} from './migration-list-styler';
|
|
19
35
|
|
|
20
36
|
const LABEL_GAP = 2;
|
|
21
37
|
|
|
@@ -26,14 +42,24 @@ const LABEL_GAP = 2;
|
|
|
26
42
|
*/
|
|
27
43
|
const DB_MARKER_NAME = 'db';
|
|
28
44
|
|
|
45
|
+
export interface MigrationEdgeAnnotation {
|
|
46
|
+
readonly status?: 'applied' | 'pending';
|
|
47
|
+
readonly operationCount?: number;
|
|
48
|
+
readonly invariants?: readonly string[];
|
|
49
|
+
}
|
|
50
|
+
|
|
29
51
|
export interface RenderMigrationGraphTreeOptions {
|
|
30
52
|
readonly refsByHash?: ReadonlyMap<string, readonly string[]>;
|
|
53
|
+
readonly edgeAnnotationsByHash?: ReadonlyMap<string, MigrationEdgeAnnotation>;
|
|
31
54
|
readonly dbHash?: string;
|
|
32
55
|
readonly contractHash?: string;
|
|
33
56
|
readonly activeRefName?: string;
|
|
34
57
|
readonly hashLength?: number;
|
|
58
|
+
readonly globalMaxEdgeTreePrefixWidth?: number;
|
|
59
|
+
readonly globalMaxDirNameWidth?: number;
|
|
35
60
|
readonly colorize: boolean;
|
|
36
61
|
readonly glyphMode?: GlyphMode;
|
|
62
|
+
readonly styler?: MigrationListStyler;
|
|
37
63
|
}
|
|
38
64
|
|
|
39
65
|
interface MigrationGraphTreeGlyphPalette {
|
|
@@ -48,6 +74,7 @@ interface MigrationGraphTreeGlyphPalette {
|
|
|
48
74
|
readonly arcBranchCorner: string;
|
|
49
75
|
readonly arcBranchTee: string;
|
|
50
76
|
readonly arcLandCorner: string;
|
|
77
|
+
readonly arcLandTee: string;
|
|
51
78
|
readonly arcCrossing: string;
|
|
52
79
|
readonly arcLandBridge: string;
|
|
53
80
|
readonly horizontalPass: string;
|
|
@@ -71,6 +98,7 @@ const UNICODE_PALETTE: MigrationGraphTreeGlyphPalette = {
|
|
|
71
98
|
arcBranchCorner: '╮ ',
|
|
72
99
|
arcBranchTee: '┬─',
|
|
73
100
|
arcLandCorner: '╯ ',
|
|
101
|
+
arcLandTee: '┴─',
|
|
74
102
|
arcCrossing: '┼─',
|
|
75
103
|
arcLandBridge: '──',
|
|
76
104
|
horizontalPass: '──',
|
|
@@ -94,6 +122,7 @@ const ASCII_PALETTE: MigrationGraphTreeGlyphPalette = {
|
|
|
94
122
|
arcBranchCorner: '\\ ',
|
|
95
123
|
arcBranchTee: '+-',
|
|
96
124
|
arcLandCorner: '/ ',
|
|
125
|
+
arcLandTee: '+-',
|
|
97
126
|
arcCrossing: '+-',
|
|
98
127
|
arcLandBridge: '--',
|
|
99
128
|
horizontalPass: '--',
|
|
@@ -109,6 +138,13 @@ function paletteFor(mode: GlyphMode): MigrationGraphTreeGlyphPalette {
|
|
|
109
138
|
return mode === 'ascii' ? ASCII_PALETTE : UNICODE_PALETTE;
|
|
110
139
|
}
|
|
111
140
|
|
|
141
|
+
function overlayStatusGlyphs(mode: GlyphMode): {
|
|
142
|
+
readonly applied: string;
|
|
143
|
+
readonly pending: string;
|
|
144
|
+
} {
|
|
145
|
+
return mode === 'ascii' ? { applied: '+', pending: '>' } : { applied: '✓', pending: '⧗' };
|
|
146
|
+
}
|
|
147
|
+
|
|
112
148
|
function arrowForEdgeKind(
|
|
113
149
|
kind: MigrationEdgeKind,
|
|
114
150
|
palette: MigrationGraphTreeGlyphPalette,
|
|
@@ -116,56 +152,153 @@ function arrowForEdgeKind(
|
|
|
116
152
|
return palette.edgeArrow[kind];
|
|
117
153
|
}
|
|
118
154
|
|
|
155
|
+
/**
|
|
156
|
+
* Forced bold for branch-coloured names. A branched name pairs its lane hue
|
|
157
|
+
* (also forced, via {@link laneColorForColumn}) with bold; both must emit even
|
|
158
|
+
* when colorette's ambient TTY detection is off, so the colorized branch name
|
|
159
|
+
* is deterministically bold + hue rather than hue-only.
|
|
160
|
+
*/
|
|
161
|
+
const { bold: forcedBold } = createColors({ useColor: true });
|
|
162
|
+
|
|
163
|
+
function laneStylerForColumn(
|
|
164
|
+
colorColumn: number,
|
|
165
|
+
colorize: boolean,
|
|
166
|
+
style: MigrationListStyler,
|
|
167
|
+
): (text: string) => string {
|
|
168
|
+
return stylerForLaneColumn(colorColumn, colorize, style.lane);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Tint a branch-owned token (direction arrow, migration name) by its edge's
|
|
173
|
+
* lane so the whole branch row reads in one colour. Column 0 has nothing to be
|
|
174
|
+
* told apart from in the common linear chain, so it keeps the token's existing
|
|
175
|
+
* default styling (`fallback`) rather than a palette hue; only lanes ≥ 1 take a
|
|
176
|
+
* colour. With colour off, the fallback (also colourless) is used unchanged.
|
|
177
|
+
*/
|
|
178
|
+
function branchStylerOrDefault(
|
|
179
|
+
column: number,
|
|
180
|
+
colorize: boolean,
|
|
181
|
+
fallback: (text: string) => string,
|
|
182
|
+
): (text: string) => string {
|
|
183
|
+
if (!colorize || column <= NEUTRAL_LANE_COLUMN) {
|
|
184
|
+
return fallback;
|
|
185
|
+
}
|
|
186
|
+
return laneColorForColumn(column);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Render a crossing tee (`┼─`): the junction stays dim/neutral so neither arc
|
|
191
|
+
* steals the cell; the trailing dash takes the served lane hue.
|
|
192
|
+
*/
|
|
193
|
+
function renderArcCrossing(
|
|
194
|
+
pair: string,
|
|
195
|
+
dashColumn: number,
|
|
196
|
+
colorize: boolean,
|
|
197
|
+
style: MigrationListStyler,
|
|
198
|
+
): string {
|
|
199
|
+
const junction = colorize ? style.lane : (text: string) => text;
|
|
200
|
+
const dash = laneStylerForColumn(dashColumn, colorize, style);
|
|
201
|
+
return junction(pair.slice(0, 1)) + dash(pair.slice(1));
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Render a connector tee (`├─` / `┬─` / `┴─`) with its junction glyph and its
|
|
206
|
+
* trailing dash coloured independently: the junction anchors its own lane while
|
|
207
|
+
* the dash leads into the branch on its right.
|
|
208
|
+
*/
|
|
209
|
+
function renderConnectorTee(
|
|
210
|
+
pair: string,
|
|
211
|
+
glyphColumn: number,
|
|
212
|
+
dashColumn: number,
|
|
213
|
+
colorize: boolean,
|
|
214
|
+
style: MigrationListStyler,
|
|
215
|
+
): string {
|
|
216
|
+
const glyph = laneStylerForColumn(glyphColumn, colorize, style);
|
|
217
|
+
if (glyphColumn === dashColumn) {
|
|
218
|
+
return glyph(pair);
|
|
219
|
+
}
|
|
220
|
+
return glyph(pair.slice(0, 1)) + laneStylerForColumn(dashColumn, colorize, style)(pair.slice(1));
|
|
221
|
+
}
|
|
222
|
+
|
|
119
223
|
/**
|
|
120
224
|
* A node-marker glyph pair (`○◂`, `○─`, `*<`, `*-`) is the contract node
|
|
121
|
-
* marker (`○` / `*`) followed by an arc connector (`◂` / `─` / `<` / `-`).
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
225
|
+
* marker (`○` / `*`) followed by an arc connector (`◂` / `─` / `<` / `-`). The
|
|
226
|
+
* marker takes its own lane's hue (so each node visibly belongs to its branch);
|
|
227
|
+
* the connector follows the arc it belongs to (its owning back-lane hue).
|
|
228
|
+
* Direction arrows are handled elsewhere — they take their edge's lane hue too.
|
|
125
229
|
*/
|
|
126
|
-
function renderNodeMarkerPair(
|
|
127
|
-
|
|
230
|
+
function renderNodeMarkerPair(
|
|
231
|
+
pair: string,
|
|
232
|
+
nodeColumn: number,
|
|
233
|
+
arcColumn: number,
|
|
234
|
+
colorize: boolean,
|
|
235
|
+
style: MigrationListStyler,
|
|
236
|
+
): string {
|
|
237
|
+
const marker = laneStylerForColumn(nodeColumn, colorize, style);
|
|
238
|
+
const connector = laneStylerForColumn(arcColumn, colorize, style);
|
|
239
|
+
return marker(pair.slice(0, 1)) + connector(pair.slice(1));
|
|
128
240
|
}
|
|
129
241
|
|
|
130
242
|
function renderCellPair(
|
|
131
243
|
cell: StructuralCell,
|
|
244
|
+
column: number,
|
|
245
|
+
colors: RowArcLaneColors,
|
|
246
|
+
colorize: boolean,
|
|
132
247
|
style: MigrationListStyler,
|
|
133
248
|
palette: MigrationGraphTreeGlyphPalette,
|
|
134
249
|
): string {
|
|
250
|
+
const laneColumn = colors.lane[column] ?? column;
|
|
251
|
+
const lane = laneStylerForColumn(laneColumn, colorize, style);
|
|
135
252
|
switch (cell.kind) {
|
|
136
|
-
case 'node':
|
|
137
|
-
|
|
138
|
-
if (cell.
|
|
139
|
-
|
|
253
|
+
case 'node': {
|
|
254
|
+
const arcColumn = colors.connector[column] ?? NEUTRAL_LANE_COLUMN;
|
|
255
|
+
if (cell.arcLand === true) {
|
|
256
|
+
return renderNodeMarkerPair(palette.arcLand, column, arcColumn, colorize, style);
|
|
257
|
+
}
|
|
258
|
+
if (cell.arcTee === true) {
|
|
259
|
+
return renderNodeMarkerPair(palette.arcTee, column, arcColumn, colorize, style);
|
|
260
|
+
}
|
|
261
|
+
return lane(palette.node);
|
|
262
|
+
}
|
|
140
263
|
case 'vertical-pass':
|
|
141
|
-
return
|
|
264
|
+
return lane(palette.verticalPass);
|
|
142
265
|
case 'edge-lane':
|
|
143
|
-
// The lane stays dim; the direction arrow (↑ / ↓ / ⟲) is the signal and
|
|
144
|
-
// stays bright, like the contract-node marker.
|
|
145
266
|
return cell.ownsLabel
|
|
146
|
-
?
|
|
147
|
-
|
|
148
|
-
|
|
267
|
+
? lane(palette.verticalPass.trimEnd()) +
|
|
268
|
+
branchStylerOrDefault(
|
|
269
|
+
column,
|
|
270
|
+
colorize,
|
|
271
|
+
style.kind,
|
|
272
|
+
)(arrowForEdgeKind(cell.edgeKind, palette))
|
|
273
|
+
: lane(palette.verticalPass);
|
|
149
274
|
case 'branch-tee':
|
|
150
|
-
return
|
|
275
|
+
return lane(palette.branchTee);
|
|
151
276
|
case 'merge-tee':
|
|
152
|
-
return
|
|
277
|
+
return lane(palette.mergeTee);
|
|
153
278
|
case 'branch-corner':
|
|
154
|
-
return
|
|
279
|
+
return lane(palette.branchCorner);
|
|
155
280
|
case 'merge-corner':
|
|
156
|
-
return
|
|
281
|
+
return lane(palette.mergeCorner);
|
|
157
282
|
case 'arc-branch-corner':
|
|
158
|
-
return
|
|
283
|
+
return lane(palette.arcBranchCorner);
|
|
159
284
|
case 'arc-branch-tee':
|
|
160
|
-
return
|
|
285
|
+
return lane(palette.arcBranchTee);
|
|
161
286
|
case 'arc-land-corner':
|
|
162
|
-
return
|
|
287
|
+
return lane(palette.arcLandCorner);
|
|
288
|
+
case 'arc-land-tee':
|
|
289
|
+
return renderConnectorTee(
|
|
290
|
+
palette.arcLandTee,
|
|
291
|
+
laneColumn,
|
|
292
|
+
colors.dash[column] ?? laneColumn,
|
|
293
|
+
colorize,
|
|
294
|
+
style,
|
|
295
|
+
);
|
|
163
296
|
case 'arc-crossing':
|
|
164
|
-
return
|
|
297
|
+
return lane(palette.arcLandBridge);
|
|
165
298
|
case 'arc-land-bridge':
|
|
166
|
-
return
|
|
299
|
+
return lane(palette.arcLandBridge);
|
|
167
300
|
case 'horizontal-pass':
|
|
168
|
-
return
|
|
301
|
+
return lane(palette.horizontalPass);
|
|
169
302
|
case 'empty':
|
|
170
303
|
return ' ';
|
|
171
304
|
}
|
|
@@ -174,34 +307,56 @@ function renderCellPair(
|
|
|
174
307
|
function renderConnectorRow(
|
|
175
308
|
row: MigrationGraphGridRow,
|
|
176
309
|
gridWidth: number,
|
|
310
|
+
colorize: boolean,
|
|
177
311
|
style: MigrationListStyler,
|
|
178
312
|
palette: MigrationGraphTreeGlyphPalette,
|
|
179
313
|
): string {
|
|
180
314
|
const isMerge = row.kind === 'merge-connector';
|
|
181
315
|
if (row.cells.length > 0) {
|
|
316
|
+
const colors = resolveConnectorLaneColors(row.cells, row.startLane ?? 0);
|
|
182
317
|
let seenTee = false;
|
|
183
318
|
let out = '';
|
|
184
|
-
for (
|
|
319
|
+
for (let column = 0; column < row.cells.length; column++) {
|
|
320
|
+
const cell = row.cells[column];
|
|
321
|
+
if (cell === undefined) continue;
|
|
322
|
+
const glyphColumn = colors.glyph[column] ?? column;
|
|
323
|
+
const dashColumn = colors.dash[column] ?? glyphColumn;
|
|
324
|
+
const lane = laneStylerForColumn(glyphColumn, colorize, style);
|
|
185
325
|
switch (cell.kind) {
|
|
186
326
|
case 'branch-tee':
|
|
187
|
-
out +=
|
|
327
|
+
out += renderConnectorTee(
|
|
328
|
+
seenTee ? palette.connectorBranchTeeCo : palette.connectorBranchTee,
|
|
329
|
+
glyphColumn,
|
|
330
|
+
dashColumn,
|
|
331
|
+
colorize,
|
|
332
|
+
style,
|
|
333
|
+
);
|
|
188
334
|
seenTee = true;
|
|
189
335
|
break;
|
|
190
336
|
case 'merge-tee':
|
|
191
|
-
out +=
|
|
337
|
+
out += renderConnectorTee(
|
|
338
|
+
seenTee ? palette.connectorMergeTeeCo : palette.connectorBranchTee,
|
|
339
|
+
glyphColumn,
|
|
340
|
+
dashColumn,
|
|
341
|
+
colorize,
|
|
342
|
+
style,
|
|
343
|
+
);
|
|
192
344
|
seenTee = true;
|
|
193
345
|
break;
|
|
194
346
|
case 'branch-corner':
|
|
195
|
-
out +=
|
|
347
|
+
out += lane(palette.branchCorner);
|
|
196
348
|
break;
|
|
197
349
|
case 'merge-corner':
|
|
198
|
-
out +=
|
|
350
|
+
out += lane(palette.mergeCorner);
|
|
199
351
|
break;
|
|
200
352
|
case 'vertical-pass':
|
|
201
|
-
out +=
|
|
353
|
+
out += lane(palette.verticalPass);
|
|
202
354
|
break;
|
|
203
355
|
case 'horizontal-pass':
|
|
204
|
-
out +=
|
|
356
|
+
out += lane(palette.horizontalPass);
|
|
357
|
+
break;
|
|
358
|
+
case 'arc-crossing':
|
|
359
|
+
out += renderArcCrossing(palette.arcCrossing, dashColumn, colorize, style);
|
|
205
360
|
break;
|
|
206
361
|
default:
|
|
207
362
|
out += ' ';
|
|
@@ -218,13 +373,15 @@ function renderConnectorRow(
|
|
|
218
373
|
|
|
219
374
|
const start = row.startLane ?? 0;
|
|
220
375
|
const end = row.endLane ?? start;
|
|
376
|
+
// The whole fork/merge run reads as one line in the served lane's hue (the
|
|
377
|
+
// corner it reaches); pass-through columns outside the run keep their own.
|
|
378
|
+
const runLane = laneStylerForColumn(end, colorize, style);
|
|
221
379
|
let out = '';
|
|
222
380
|
for (let column = 0; column < gridWidth; column++) {
|
|
223
381
|
if (column < start || column > end) out += ' ';
|
|
224
|
-
else if (column === start) out +=
|
|
225
|
-
else if (column === end)
|
|
226
|
-
|
|
227
|
-
else out += style.lane(isMerge ? palette.connectorMergeTeeCo : palette.connectorBranchTeeCo);
|
|
382
|
+
else if (column === start) out += runLane(palette.connectorBranchTee);
|
|
383
|
+
else if (column === end) out += runLane(isMerge ? palette.mergeCorner : palette.branchCorner);
|
|
384
|
+
else out += runLane(isMerge ? palette.connectorMergeTeeCo : palette.connectorBranchTeeCo);
|
|
228
385
|
}
|
|
229
386
|
return out;
|
|
230
387
|
}
|
|
@@ -239,26 +396,41 @@ function abbreviateHash(hash: string, hashLength: number, emptySource: string):
|
|
|
239
396
|
|
|
240
397
|
const MIN_HASH_DATA_COLUMN = 25;
|
|
241
398
|
|
|
399
|
+
interface ContractOverlayNames {
|
|
400
|
+
readonly markers: readonly string[];
|
|
401
|
+
readonly refs: readonly string[];
|
|
402
|
+
}
|
|
403
|
+
|
|
242
404
|
function overlayNamesForContract(
|
|
243
405
|
contractHash: string,
|
|
244
406
|
opts: RenderMigrationGraphTreeOptions,
|
|
245
|
-
):
|
|
246
|
-
const
|
|
407
|
+
): ContractOverlayNames {
|
|
408
|
+
const markers: string[] = [];
|
|
409
|
+
const refs: string[] = [];
|
|
247
410
|
const userRefs = opts.refsByHash?.get(contractHash);
|
|
248
411
|
if (userRefs) {
|
|
249
|
-
|
|
250
|
-
}
|
|
251
|
-
if (opts.dbHash === contractHash) {
|
|
252
|
-
names.push(DB_MARKER_NAME);
|
|
412
|
+
refs.push(...[...userRefs].sort((a, b) => a.localeCompare(b)));
|
|
253
413
|
}
|
|
254
414
|
if (opts.contractHash === contractHash && contractHash !== EMPTY_CONTRACT_HASH) {
|
|
255
|
-
|
|
415
|
+
markers.push(CONTRACT_MARKER_NAME);
|
|
416
|
+
}
|
|
417
|
+
if (opts.dbHash === contractHash) {
|
|
418
|
+
markers.push(DB_MARKER_NAME);
|
|
256
419
|
}
|
|
257
|
-
|
|
420
|
+
markers.sort((a, b) => {
|
|
421
|
+
if (a === CONTRACT_MARKER_NAME) {
|
|
422
|
+
return -1;
|
|
423
|
+
}
|
|
424
|
+
if (b === CONTRACT_MARKER_NAME) {
|
|
425
|
+
return 1;
|
|
426
|
+
}
|
|
427
|
+
return a.localeCompare(b);
|
|
428
|
+
});
|
|
429
|
+
return { markers, refs };
|
|
258
430
|
}
|
|
259
431
|
|
|
260
432
|
function createTreeStyler(opts: RenderMigrationGraphTreeOptions): MigrationListStyler {
|
|
261
|
-
const base = createAnsiMigrationListStyler({ useColor: opts.colorize });
|
|
433
|
+
const base = opts.styler ?? createAnsiMigrationListStyler({ useColor: opts.colorize });
|
|
262
434
|
const activeRefName = opts.activeRefName;
|
|
263
435
|
if (!opts.colorize || activeRefName === undefined) {
|
|
264
436
|
return base;
|
|
@@ -272,6 +444,40 @@ function createTreeStyler(opts: RenderMigrationGraphTreeOptions): MigrationListS
|
|
|
272
444
|
};
|
|
273
445
|
}
|
|
274
446
|
|
|
447
|
+
function formatEdgeAnnotationSuffix(
|
|
448
|
+
migrationHash: string,
|
|
449
|
+
opts: RenderMigrationGraphTreeOptions,
|
|
450
|
+
style: MigrationListStyler,
|
|
451
|
+
): string {
|
|
452
|
+
const annotation = opts.edgeAnnotationsByHash?.get(migrationHash);
|
|
453
|
+
if (annotation === undefined) {
|
|
454
|
+
return '';
|
|
455
|
+
}
|
|
456
|
+
const segments: string[] = [];
|
|
457
|
+
if (annotation.operationCount !== undefined) {
|
|
458
|
+
segments.push(`${annotation.operationCount} ops`);
|
|
459
|
+
}
|
|
460
|
+
if (annotation.invariants !== undefined && annotation.invariants.length > 0) {
|
|
461
|
+
segments.push(style.invariants(annotation.invariants));
|
|
462
|
+
}
|
|
463
|
+
const status = annotation.status;
|
|
464
|
+
if (status !== undefined) {
|
|
465
|
+
const glyphs = overlayStatusGlyphs(opts.glyphMode ?? 'unicode');
|
|
466
|
+
const glyph = status === 'applied' ? glyphs.applied : glyphs.pending;
|
|
467
|
+
const label = status === 'applied' ? 'applied' : 'pending';
|
|
468
|
+
if (!opts.colorize) {
|
|
469
|
+
segments.push(`${glyph} ${label}`);
|
|
470
|
+
} else {
|
|
471
|
+
const styler = status === 'applied' ? green : yellow;
|
|
472
|
+
segments.push(styler(`${glyph} ${label}`));
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
if (segments.length === 0) {
|
|
476
|
+
return '';
|
|
477
|
+
}
|
|
478
|
+
return ` ${segments.join(' ')}`;
|
|
479
|
+
}
|
|
480
|
+
|
|
275
481
|
function formatEdgeHashColumn(
|
|
276
482
|
edge: ClassifiedEdge,
|
|
277
483
|
style: MigrationListStyler,
|
|
@@ -280,13 +486,16 @@ function formatEdgeHashColumn(
|
|
|
280
486
|
): string {
|
|
281
487
|
if (edge.kind === 'self') {
|
|
282
488
|
const hash = abbreviateHash(edge.from, hashLength, palette.emptySource);
|
|
283
|
-
|
|
489
|
+
const source = padFromHashColumn(style.sourceHash(hash), hashLength);
|
|
490
|
+
return `${source} ${style.glyph(palette.forwardArrow)} ${style.destHash(hash)}`;
|
|
284
491
|
}
|
|
285
492
|
const source =
|
|
286
493
|
edge.from === EMPTY_CONTRACT_HASH
|
|
287
|
-
? style.glyph(palette.emptySource)
|
|
288
|
-
|
|
289
|
-
|
|
494
|
+
? padFromHashColumn(style.glyph(palette.emptySource), hashLength)
|
|
495
|
+
: padFromHashColumn(
|
|
496
|
+
style.sourceHash(abbreviateHash(edge.from, hashLength, palette.emptySource)),
|
|
497
|
+
hashLength,
|
|
498
|
+
);
|
|
290
499
|
const arrow = style.glyph(palette.forwardArrow);
|
|
291
500
|
const dest = style.destHash(abbreviateHash(edge.to, hashLength, palette.emptySource));
|
|
292
501
|
return `${source} ${arrow} ${dest}`;
|
|
@@ -297,6 +506,13 @@ function padVisible(text: string, targetWidth: number): string {
|
|
|
297
506
|
return text + ' '.repeat(padding);
|
|
298
507
|
}
|
|
299
508
|
|
|
509
|
+
const ANSI_ESCAPE = '\x1b';
|
|
510
|
+
|
|
511
|
+
function trimTrailingWhitespace(line: string): string {
|
|
512
|
+
const trailingSpaceBeforeReset = new RegExp(`[\\t ]+((?:${ANSI_ESCAPE}\\[[0-9;]*m)+)$`);
|
|
513
|
+
return line.replace(trailingSpaceBeforeReset, '$1').replace(/\s+$/, '');
|
|
514
|
+
}
|
|
515
|
+
|
|
300
516
|
function gridWidthForModel(rows: readonly MigrationGraphGridRow[]): number {
|
|
301
517
|
return rows.reduce(
|
|
302
518
|
(max, row) =>
|
|
@@ -336,6 +552,35 @@ function edgeLabelColumn(row: MigrationGraphGridRow, wideLabelColumn: number | u
|
|
|
336
552
|
return usesFullRowGutter ? row.cells.length * 2 + LABEL_GAP : (laneIndex + 1) * 2 + LABEL_GAP;
|
|
337
553
|
}
|
|
338
554
|
|
|
555
|
+
function maxEdgeTreePrefixWidth(
|
|
556
|
+
rows: readonly MigrationGraphGridRow[],
|
|
557
|
+
wideLabelColumn: number | undefined,
|
|
558
|
+
): number {
|
|
559
|
+
let max = 0;
|
|
560
|
+
for (const row of rows) {
|
|
561
|
+
if (row.kind !== 'edge' || row.edge === undefined) continue;
|
|
562
|
+
max = Math.max(max, edgeLabelColumn(row, wideLabelColumn));
|
|
563
|
+
}
|
|
564
|
+
return max;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
export function computeMaxEdgeTreePrefixWidthForLayout(model: MigrationGraphGridModel): number {
|
|
568
|
+
const wideLabelColumn = gridUsesSkipRollbackArcs(model.rows)
|
|
569
|
+
? gridWidthForModel(model.rows) * 2 + 4
|
|
570
|
+
: undefined;
|
|
571
|
+
return maxEdgeTreePrefixWidth(model.rows, wideLabelColumn);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
export function computeMaxDirNameLengthForLayout(model: MigrationGraphGridModel): number {
|
|
575
|
+
const allEdges = model.rows
|
|
576
|
+
.filter(
|
|
577
|
+
(row): row is MigrationGraphGridRow & { edge: ClassifiedEdge } =>
|
|
578
|
+
row.kind === 'edge' && row.edge !== undefined,
|
|
579
|
+
)
|
|
580
|
+
.map((row) => row.edge);
|
|
581
|
+
return maxDirNameLength(allEdges);
|
|
582
|
+
}
|
|
583
|
+
|
|
339
584
|
function nodeHasArcDecoration(row: MigrationGraphGridRow): boolean {
|
|
340
585
|
return row.cells.some(
|
|
341
586
|
(cell) => cell.kind === 'node' && (cell.arcTee === true || cell.arcLand === true),
|
|
@@ -360,6 +605,10 @@ export function renderMigrationGraphTree(
|
|
|
360
605
|
)
|
|
361
606
|
.map((row) => row.edge);
|
|
362
607
|
const maxDirNameLen = maxDirNameLength(allEdges);
|
|
608
|
+
const effectiveMaxDirNameLen = opts.globalMaxDirNameWidth ?? maxDirNameLen;
|
|
609
|
+
const maxEdgePrefixWidth =
|
|
610
|
+
opts.globalMaxEdgeTreePrefixWidth ?? maxEdgeTreePrefixWidth(model.rows, wideLabelColumn);
|
|
611
|
+
const edgeDirNameWidth = rowDirNameWidth(maxEdgePrefixWidth, effectiveMaxDirNameLen, dirNameGap);
|
|
363
612
|
|
|
364
613
|
const lines: string[] = [];
|
|
365
614
|
|
|
@@ -373,24 +622,37 @@ export function renderMigrationGraphTree(
|
|
|
373
622
|
}
|
|
374
623
|
|
|
375
624
|
if (row.kind === 'branch-connector' || row.kind === 'merge-connector') {
|
|
376
|
-
lines.push(
|
|
625
|
+
lines.push(
|
|
626
|
+
trimTrailingWhitespace(renderConnectorRow(row, gridWidth, opts.colorize, style, palette)),
|
|
627
|
+
);
|
|
377
628
|
continue;
|
|
378
629
|
}
|
|
379
630
|
|
|
380
|
-
|
|
381
|
-
|
|
631
|
+
const cellColors = resolveRowArcLaneColors(row.cells);
|
|
632
|
+
let gutter = row.cells
|
|
633
|
+
.map((cell, column) =>
|
|
634
|
+
renderCellPair(cell, column, cellColors, opts.colorize, style, palette),
|
|
635
|
+
)
|
|
636
|
+
.join('');
|
|
382
637
|
let laneSpan = row.cells.length;
|
|
383
638
|
if (row.kind === 'node') {
|
|
384
639
|
const contractHash = row.contractHash ?? EMPTY_CONTRACT_HASH;
|
|
385
|
-
if (
|
|
640
|
+
if (contractHash === EMPTY_CONTRACT_HASH) {
|
|
386
641
|
laneSpan = 1;
|
|
387
642
|
} else {
|
|
388
|
-
|
|
643
|
+
let lastActiveColumn = -1;
|
|
644
|
+
for (let column = row.cells.length - 1; column >= 0; column--) {
|
|
645
|
+
if (row.cells[column]?.kind !== 'empty') {
|
|
646
|
+
lastActiveColumn = column;
|
|
647
|
+
break;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
laneSpan = lastActiveColumn >= 0 ? lastActiveColumn + 1 : 1;
|
|
389
651
|
}
|
|
390
652
|
}
|
|
391
653
|
const labelColumn =
|
|
392
654
|
row.kind === 'edge'
|
|
393
|
-
?
|
|
655
|
+
? maxEdgePrefixWidth
|
|
394
656
|
: wideLabelColumn !== undefined &&
|
|
395
657
|
(nodeHasArcDecoration(row) || row.contractHash !== undefined)
|
|
396
658
|
? wideLabelColumn
|
|
@@ -402,18 +664,24 @@ export function renderMigrationGraphTree(
|
|
|
402
664
|
) {
|
|
403
665
|
gutter = row.cells
|
|
404
666
|
.slice(0, 1)
|
|
405
|
-
.map((cell) =>
|
|
667
|
+
.map((cell, column) =>
|
|
668
|
+
renderCellPair(cell, column, cellColors, opts.colorize, style, palette),
|
|
669
|
+
)
|
|
406
670
|
.join('');
|
|
407
671
|
} else if (row.kind === 'node' && laneSpan < row.cells.length && !nodeHasArcDecoration(row)) {
|
|
408
672
|
gutter = row.cells
|
|
409
673
|
.slice(0, laneSpan)
|
|
410
|
-
.map((cell) =>
|
|
674
|
+
.map((cell, column) =>
|
|
675
|
+
renderCellPair(cell, column, cellColors, opts.colorize, style, palette),
|
|
676
|
+
)
|
|
411
677
|
.join('');
|
|
412
678
|
} else if (gutter.length < laneSpan * 2) {
|
|
413
679
|
gutter = gutter.padEnd(laneSpan * 2, ' ');
|
|
414
680
|
}
|
|
415
|
-
const dirNameWidth =
|
|
416
|
-
|
|
681
|
+
const dirNameWidth =
|
|
682
|
+
row.kind === 'edge'
|
|
683
|
+
? edgeDirNameWidth
|
|
684
|
+
: rowDirNameWidth(labelColumn, maxDirNameLen, dirNameGap);
|
|
417
685
|
const gutterPad = padVisible(gutter, labelColumn);
|
|
418
686
|
|
|
419
687
|
if (row.kind === 'node') {
|
|
@@ -421,28 +689,30 @@ export function renderMigrationGraphTree(
|
|
|
421
689
|
if (contractHash === EMPTY_CONTRACT_HASH) {
|
|
422
690
|
const trailingLanes = row.cells
|
|
423
691
|
.slice(1)
|
|
424
|
-
.map((cell) =>
|
|
692
|
+
.map((cell, offset) =>
|
|
693
|
+
renderCellPair(cell, offset + 1, cellColors, opts.colorize, style, palette),
|
|
694
|
+
)
|
|
425
695
|
.join('');
|
|
426
696
|
const emptyGutter = palette.emptySource.padEnd(2, ' ') + trailingLanes;
|
|
427
|
-
const
|
|
428
|
-
if (
|
|
429
|
-
lines.push(emptyGutter
|
|
697
|
+
const overlays = overlayNamesForContract(contractHash, opts);
|
|
698
|
+
if (overlays.markers.length === 0 && overlays.refs.length === 0) {
|
|
699
|
+
lines.push(trimTrailingWhitespace(emptyGutter));
|
|
430
700
|
continue;
|
|
431
701
|
}
|
|
432
|
-
const overlay = style.refs
|
|
433
|
-
lines.push(`${
|
|
702
|
+
const overlay = formatContractNodeOverlays(style, overlays.markers, overlays.refs);
|
|
703
|
+
lines.push(trimTrailingWhitespace(`${emptyGutter}${' '.repeat(LABEL_GAP)}${overlay}`));
|
|
434
704
|
continue;
|
|
435
705
|
}
|
|
436
706
|
const hashText = style.sourceHash(
|
|
437
707
|
abbreviateHash(contractHash, hashLength, palette.emptySource),
|
|
438
708
|
);
|
|
439
|
-
const
|
|
440
|
-
const
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
lines.push(`${gutterPad}${hashText}${overlayPad}${overlay}
|
|
709
|
+
const overlays = overlayNamesForContract(contractHash, opts);
|
|
710
|
+
const hasOverlays = overlays.markers.length > 0 || overlays.refs.length > 0;
|
|
711
|
+
const overlayPad = hasOverlays ? ' '.repeat(LABEL_GAP) : '';
|
|
712
|
+
const overlay = hasOverlays
|
|
713
|
+
? formatContractNodeOverlays(style, overlays.markers, overlays.refs)
|
|
714
|
+
: '';
|
|
715
|
+
lines.push(trimTrailingWhitespace(`${gutterPad}${hashText}${overlayPad}${overlay}`));
|
|
446
716
|
continue;
|
|
447
717
|
}
|
|
448
718
|
|
|
@@ -450,10 +720,68 @@ export function renderMigrationGraphTree(
|
|
|
450
720
|
if (edge === undefined) continue;
|
|
451
721
|
|
|
452
722
|
const dirNamePadding = ' '.repeat(Math.max(0, dirNameWidth - edge.dirName.length));
|
|
453
|
-
const
|
|
723
|
+
const laneIndex = row.laneIndex ?? 0;
|
|
724
|
+
// A branched name keeps its bold (via `style.dirName`) and adds the lane
|
|
725
|
+
// hue, so it reads as one with its lane/arrow; column-0 names stay bold-only.
|
|
726
|
+
const dirNameStyler =
|
|
727
|
+
opts.colorize && laneIndex > NEUTRAL_LANE_COLUMN
|
|
728
|
+
? (text: string) => forcedBold(laneColorForColumn(laneIndex)(text))
|
|
729
|
+
: style.dirName;
|
|
730
|
+
const dirName = `${dirNameStyler(edge.dirName)}${dirNamePadding}`;
|
|
454
731
|
const hashColumn = formatEdgeHashColumn(edge, style, hashLength, palette);
|
|
455
|
-
|
|
732
|
+
const annotationSuffix = formatEdgeAnnotationSuffix(edge.migrationHash, opts, style);
|
|
733
|
+
lines.push(trimTrailingWhitespace(`${gutterPad}${dirName}${hashColumn}${annotationSuffix}`));
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
return lines.join('\n');
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
export interface RenderMigrationGraphLegendOptions {
|
|
740
|
+
readonly colorize: boolean;
|
|
741
|
+
readonly glyphMode?: GlyphMode;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
function formatLegendExampleMarkers(colorize: boolean): string {
|
|
745
|
+
if (!colorize) {
|
|
746
|
+
return '<contract, db>';
|
|
456
747
|
}
|
|
748
|
+
const open = green('<');
|
|
749
|
+
const close = green('>');
|
|
750
|
+
const separator = green(', ');
|
|
751
|
+
return open + green('contract') + separator + green('db') + close;
|
|
752
|
+
}
|
|
457
753
|
|
|
754
|
+
/**
|
|
755
|
+
* A compact key for the tree visual language: the contract node glyph, the
|
|
756
|
+
* in-lane direction arrows, the empty baseline, the system-marker `<…>` and
|
|
757
|
+
* user-ref `(…)` bracket conventions (two illustrative example lines), and a
|
|
758
|
+
* worked sample of the data-column `from → to` migration hash arrow.
|
|
759
|
+
*
|
|
760
|
+
* Honors the same glyph palette (unicode vs ASCII) and `colorize` gate as the
|
|
761
|
+
* tree renderer, so the key matches whatever the graph itself drew and stays
|
|
762
|
+
* pipe-safe (zero ANSI when color is off). The caller adds the trailing blank
|
|
763
|
+
* line that separates this stderr key from the tree on stdout.
|
|
764
|
+
*/
|
|
765
|
+
export function renderMigrationGraphLegend(opts: RenderMigrationGraphLegendOptions): string {
|
|
766
|
+
const palette = paletteFor(opts.glyphMode ?? 'unicode');
|
|
767
|
+
const style = createAnsiMigrationListStyler({ useColor: opts.colorize });
|
|
768
|
+
const node = palette.node.trimEnd();
|
|
769
|
+
const sampleArrow = `${style.sourceHash('aaaaaa')} ${style.glyph(palette.forwardArrow)} ${style.destHash('bbbbbb')}`;
|
|
770
|
+
const statusGlyphs = overlayStatusGlyphs(opts.glyphMode ?? 'unicode');
|
|
771
|
+
const appliedPending = opts.colorize
|
|
772
|
+
? ` ${green(statusGlyphs.applied)} ${style.summary('applied')} ${yellow(statusGlyphs.pending)} ${style.summary('pending')}`
|
|
773
|
+
: ` ${statusGlyphs.applied} ${style.summary('applied')} ${statusGlyphs.pending} ${style.summary('pending')}`;
|
|
774
|
+
const exampleMarkers = formatLegendExampleMarkers(opts.colorize);
|
|
775
|
+
const exampleRefs = opts.colorize ? style.refs(['prod', 'staging']) : '(prod, staging)';
|
|
776
|
+
const lines = [
|
|
777
|
+
'Legend:',
|
|
778
|
+
` ${style.kind(node)} ${style.summary('contract')} ${style.kind(palette.edgeArrow.forward)} ${style.summary('forward')} ${style.kind(palette.edgeArrow.rollback)} ${style.summary('rollback')}`,
|
|
779
|
+
` ${style.kind(palette.edgeArrow.self)} ${style.summary('migration without schema change')}`,
|
|
780
|
+
appliedPending,
|
|
781
|
+
` ${style.kind(palette.emptySource)} ${style.summary('empty database (baseline)')}`,
|
|
782
|
+
` ${exampleMarkers} ${style.summary('live markers (contract on disk, database state)')}`,
|
|
783
|
+
` ${exampleRefs} ${style.summary('user-defined refs')}`,
|
|
784
|
+
` ${sampleArrow} ${style.summary('migration from contract aaaaaa to bbbbbb')}`,
|
|
785
|
+
];
|
|
458
786
|
return lines.join('\n');
|
|
459
787
|
}
|