@prisma-next/cli 0.12.0-dev.27 → 0.12.0-dev.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +12 -12
- package/dist/{client-V7BkIQrQ.mjs → client-xeWpMlq1.mjs} +22 -11
- package/dist/client-xeWpMlq1.mjs.map +1 -0
- package/dist/{command-helpers-DlrUCI7s.mjs → command-helpers-DK_5ItoJ.mjs} +253 -2
- package/dist/command-helpers-DK_5ItoJ.mjs.map +1 -0
- package/dist/commands/contract-emit.mjs +1 -1
- package/dist/commands/contract-infer.mjs +1 -1
- package/dist/commands/db-init.mjs +4 -5
- package/dist/commands/db-init.mjs.map +1 -1
- package/dist/commands/db-schema.mjs +3 -3
- package/dist/commands/db-sign.mjs +4 -4
- package/dist/commands/db-update.d.mts.map +1 -1
- package/dist/commands/db-update.mjs +10 -7
- package/dist/commands/db-update.mjs.map +1 -1
- package/dist/commands/db-verify.mjs +1 -1
- package/dist/commands/migrate.d.mts +1 -1
- package/dist/commands/migrate.mjs +5 -6
- package/dist/commands/migrate.mjs.map +1 -1
- package/dist/commands/migration-check.mjs +1 -1
- package/dist/commands/migration-graph.d.mts +12 -15
- package/dist/commands/migration-graph.d.mts.map +1 -1
- package/dist/commands/migration-graph.mjs +84 -51
- package/dist/commands/migration-graph.mjs.map +1 -1
- package/dist/commands/migration-list.d.mts +14 -5
- package/dist/commands/migration-list.d.mts.map +1 -1
- package/dist/commands/migration-list.mjs +1 -187
- package/dist/commands/migration-log.d.mts +2 -2
- package/dist/commands/migration-log.mjs +1 -1
- package/dist/commands/migration-new.mjs +3 -3
- package/dist/commands/migration-plan.mjs +1 -1
- package/dist/commands/migration-show.d.mts +1 -1
- package/dist/commands/migration-show.mjs +3 -4
- package/dist/commands/migration-show.mjs.map +1 -1
- package/dist/commands/migration-status.d.mts +21 -3
- package/dist/commands/migration-status.d.mts.map +1 -1
- package/dist/commands/migration-status.mjs +2 -3
- package/dist/commands/ref.d.mts +1 -1
- package/dist/commands/ref.mjs +3 -3
- package/dist/commands/telemetry/index.mjs +1 -1
- package/dist/{contract-at-errors-DlZHXSkI.mjs → contract-at-errors-DG3kjgoz.mjs} +2 -2
- package/dist/{contract-at-errors-DlZHXSkI.mjs.map → contract-at-errors-DG3kjgoz.mjs.map} +1 -1
- package/dist/{contract-emit-S53EyBRV.mjs → contract-emit-BO0l6fnT.mjs} +3 -3
- package/dist/{contract-emit-S53EyBRV.mjs.map → contract-emit-BO0l6fnT.mjs.map} +1 -1
- package/dist/{contract-emit-CaKp92-Q.mjs → contract-emit-C0Bs0VRj.mjs} +3 -3
- package/dist/{contract-emit-CaKp92-Q.mjs.map → contract-emit-C0Bs0VRj.mjs.map} +1 -1
- package/dist/{contract-infer-Cebb-_Qx.mjs → contract-infer-2wtPflGH.mjs} +3 -3
- package/dist/{contract-infer-Cebb-_Qx.mjs.map → contract-infer-2wtPflGH.mjs.map} +1 -1
- package/dist/{contract-space-aggregate-loader-Dvl1SJ4C.mjs → contract-space-aggregate-loader-Dbr3-jHF.mjs} +3 -3
- package/dist/{contract-space-aggregate-loader-Dvl1SJ4C.mjs.map → contract-space-aggregate-loader-Dbr3-jHF.mjs.map} +1 -1
- package/dist/{db-verify-B1OoWEWn.mjs → db-verify-CxHiSiTG.mjs} +4 -4
- package/dist/{db-verify-B1OoWEWn.mjs.map → db-verify-CxHiSiTG.mjs.map} +1 -1
- package/dist/exports/control-api.d.mts +1 -1
- package/dist/exports/control-api.mjs +2 -2
- package/dist/exports/index.mjs +1 -1
- package/dist/{framework-components-DCAT1uUC.mjs → framework-components-CxOVKAAh.mjs} +2 -2
- package/dist/{framework-components-DCAT1uUC.mjs.map → framework-components-CxOVKAAh.mjs.map} +1 -1
- package/dist/{init-Kf3T4A4W.mjs → init-R272pxux.mjs} +3 -3
- package/dist/{init-Kf3T4A4W.mjs.map → init-R272pxux.mjs.map} +1 -1
- package/dist/{inspect-live-schema-DTqflZ8X.mjs → inspect-live-schema-RekOwfi5.mjs} +3 -3
- package/dist/{inspect-live-schema-DTqflZ8X.mjs.map → inspect-live-schema-RekOwfi5.mjs.map} +1 -1
- package/dist/{migration-check-Ccyd0QKb.mjs → migration-check-Dc0cOhKH.mjs} +2 -2
- package/dist/{migration-check-Ccyd0QKb.mjs.map → migration-check-Dc0cOhKH.mjs.map} +1 -1
- package/dist/{migration-command-scaffold-DI7_SFL0.mjs → migration-command-scaffold-ApB3NxWY.mjs} +3 -3
- package/dist/{migration-command-scaffold-DI7_SFL0.mjs.map → migration-command-scaffold-ApB3NxWY.mjs.map} +1 -1
- package/dist/{migration-graph-tree-render-DyDBuJEX.mjs → migration-graph-space-render-dmLLWift.mjs} +389 -210
- package/dist/migration-graph-space-render-dmLLWift.mjs.map +1 -0
- package/dist/migration-list-C5sXrl0U.mjs +228 -0
- package/dist/migration-list-C5sXrl0U.mjs.map +1 -0
- package/dist/{migration-list-types-DV9PBc7Z.d.mts → migration-list-types-DS63IdFd.d.mts} +1 -1
- package/dist/{migration-list-types-DV9PBc7Z.d.mts.map → migration-list-types-DS63IdFd.d.mts.map} +1 -1
- package/dist/{migration-log-Des4seHP.mjs → migration-log-DD_vCbYW.mjs} +4 -4
- package/dist/{migration-log-Des4seHP.mjs.map → migration-log-DD_vCbYW.mjs.map} +1 -1
- package/dist/{migration-plan-DxDTBzGS.mjs → migration-plan-CeTjQOIG.mjs} +5 -5
- package/dist/{migration-plan-DxDTBzGS.mjs.map → migration-plan-CeTjQOIG.mjs.map} +1 -1
- package/dist/{migration-status-CCwqA-vi.mjs → migration-status-qV8ctwPy.mjs} +61 -45
- package/dist/migration-status-qV8ctwPy.mjs.map +1 -0
- package/dist/{ref-advancement-DUZqsue6.mjs → ref-advancement-V1o-9LVK.mjs} +1 -1
- package/dist/{ref-advancement-DUZqsue6.mjs.map → ref-advancement-V1o-9LVK.mjs.map} +1 -1
- package/dist/{telemetry-LFFQmqHd.mjs → telemetry-S-NGi9U6.mjs} +2 -2
- package/dist/{telemetry-LFFQmqHd.mjs.map → telemetry-S-NGi9U6.mjs.map} +1 -1
- package/dist/{terminal-ui-C3xGyxW-.d.mts → terminal-ui-5Y6mrg93.d.mts} +1 -1
- package/dist/{terminal-ui-C3xGyxW-.d.mts.map → terminal-ui-5Y6mrg93.d.mts.map} +1 -1
- package/dist/{types-BdS8PoKM.d.mts → types-Mh7mdPHM.d.mts} +5 -1
- package/dist/types-Mh7mdPHM.d.mts.map +1 -0
- package/dist/{verify-C0TARc6h.mjs → verify-BdI-BgYi.mjs} +2 -2
- package/dist/{verify-C0TARc6h.mjs.map → verify-BdI-BgYi.mjs.map} +1 -1
- package/package.json +18 -18
- package/src/commands/db-update.ts +7 -1
- package/src/commands/migration-graph.ts +125 -58
- package/src/commands/migration-list.ts +43 -9
- package/src/commands/migration-status.ts +106 -74
- package/src/control-api/operations/db-apply.ts +24 -1
- package/src/control-api/types.ts +4 -0
- package/src/utils/cli-errors.ts +17 -0
- package/src/utils/formatters/errors.ts +11 -0
- package/src/utils/formatters/migration-graph-lane-colors.ts +164 -1
- package/src/utils/formatters/migration-graph-rows.ts +128 -15
- package/src/utils/formatters/migration-graph-space-render.ts +138 -0
- package/src/utils/formatters/migration-graph-tree-render.ts +149 -239
- package/src/utils/formatters/migration-list-data-column.ts +6 -0
- package/src/utils/formatters/migration-list-render.ts +43 -23
- package/src/utils/formatters/migration-list-styler.ts +48 -5
- package/src/utils/formatters/migrations.ts +25 -1
- package/src/utils/legend.ts +38 -0
- package/dist/client-V7BkIQrQ.mjs.map +0 -1
- package/dist/command-helpers-DlrUCI7s.mjs.map +0 -1
- package/dist/commands/migration-list.mjs.map +0 -1
- package/dist/migration-graph-tree-render-DyDBuJEX.mjs.map +0 -1
- package/dist/migration-status-CCwqA-vi.mjs.map +0 -1
- package/dist/migration-types-CAQ-0TEE.d.mts +0 -15
- package/dist/migration-types-CAQ-0TEE.d.mts.map +0 -1
- package/dist/migrations-B3H6RTXb.mjs +0 -228
- package/dist/migrations-B3H6RTXb.mjs.map +0 -1
- package/dist/types-BdS8PoKM.d.mts.map +0 -1
|
@@ -2,7 +2,17 @@ import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';
|
|
|
2
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 {
|
|
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
|
+
|
|
6
16
|
import type {
|
|
7
17
|
MigrationGraphGridModel,
|
|
8
18
|
MigrationGraphGridRow,
|
|
@@ -13,10 +23,15 @@ import {
|
|
|
13
23
|
MIGRATION_LIST_HASH_WIDTH,
|
|
14
24
|
migrationListEmptySource,
|
|
15
25
|
migrationListForwardArrow,
|
|
26
|
+
padFromHashColumn,
|
|
16
27
|
} from './migration-list-data-column';
|
|
17
28
|
import type { MigrationEdgeKind } from './migration-list-graph-topology';
|
|
18
29
|
import type { MigrationListStyler } from './migration-list-render';
|
|
19
|
-
import {
|
|
30
|
+
import {
|
|
31
|
+
CONTRACT_MARKER_NAME,
|
|
32
|
+
createAnsiMigrationListStyler,
|
|
33
|
+
formatContractNodeOverlays,
|
|
34
|
+
} from './migration-list-styler';
|
|
20
35
|
|
|
21
36
|
const LABEL_GAP = 2;
|
|
22
37
|
|
|
@@ -40,8 +55,11 @@ export interface RenderMigrationGraphTreeOptions {
|
|
|
40
55
|
readonly contractHash?: string;
|
|
41
56
|
readonly activeRefName?: string;
|
|
42
57
|
readonly hashLength?: number;
|
|
58
|
+
readonly globalMaxEdgeTreePrefixWidth?: number;
|
|
59
|
+
readonly globalMaxDirNameWidth?: number;
|
|
43
60
|
readonly colorize: boolean;
|
|
44
61
|
readonly glyphMode?: GlyphMode;
|
|
62
|
+
readonly styler?: MigrationListStyler;
|
|
45
63
|
}
|
|
46
64
|
|
|
47
65
|
interface MigrationGraphTreeGlyphPalette {
|
|
@@ -134,13 +152,6 @@ function arrowForEdgeKind(
|
|
|
134
152
|
return palette.edgeArrow[kind];
|
|
135
153
|
}
|
|
136
154
|
|
|
137
|
-
/**
|
|
138
|
-
* The leftmost lane (column 0) renders with the neutral dim lane style rather
|
|
139
|
-
* than a palette hue — in the common single-lane case it has nothing to be told
|
|
140
|
-
* apart from. Used as the "no owning arc" sentinel during colour resolution.
|
|
141
|
-
*/
|
|
142
|
-
const NEUTRAL_LANE = 0;
|
|
143
|
-
|
|
144
155
|
/**
|
|
145
156
|
* Forced bold for branch-coloured names. A branched name pairs its lane hue
|
|
146
157
|
* (also forced, via {@link laneColorForColumn}) with bold; both must emit even
|
|
@@ -149,194 +160,12 @@ const NEUTRAL_LANE = 0;
|
|
|
149
160
|
*/
|
|
150
161
|
const { bold: forcedBold } = createColors({ useColor: true });
|
|
151
162
|
|
|
152
|
-
/**
|
|
153
|
-
* The colour-source column for each cell of a row, resolved together because a
|
|
154
|
-
* routed back-arc spans columns and must read as **one hue** rather than a
|
|
155
|
-
* per-column "rainbow". An arc's horizontal bridges, corners, and node-pair
|
|
156
|
-
* connector all take the arc's owning back-lane column (the corner that closes
|
|
157
|
-
* the arc), not the column they pass through.
|
|
158
|
-
*/
|
|
159
|
-
interface RowLaneColors {
|
|
160
|
-
/** Colour column for a cell's structural glyph (lane / spine / arc body). */
|
|
161
|
-
readonly lane: readonly number[];
|
|
162
|
-
/** Colour column for a node arc-pair's connector half (`◂` / `─`). */
|
|
163
|
-
readonly connector: readonly number[];
|
|
164
|
-
/**
|
|
165
|
-
* Colour column for the trailing `─` of a landing tee (`┴─`). The junction
|
|
166
|
-
* (`lane`) keeps its own column; the dash leads into the next converging arc.
|
|
167
|
-
*/
|
|
168
|
-
readonly dash: readonly number[];
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Resolve per-cell colour columns for a row. Scanning right-to-left lets each
|
|
173
|
-
* arc segment inherit the hue of the arc it leads into.
|
|
174
|
-
*
|
|
175
|
-
* On a converging-landing line (`○◂──────┴─┴─╯`), every horizontal dash segment
|
|
176
|
-
* takes the hue of the **nearest landing anchor** — the next `arc-land-tee` or
|
|
177
|
-
* `arc-land-corner` — to its right, i.e. the branch it leads into: the bridge
|
|
178
|
-
* run leads into the first converging arc, and each tee's trailing `─` leads
|
|
179
|
-
* into the next arc out. Tee/corner junction glyphs keep their own column hue.
|
|
180
|
-
* This mirrors the forward connector's `┬─` rule (see
|
|
181
|
-
* {@link resolveConnectorLaneColors}). A single (non-converging) landing has
|
|
182
|
-
* only the corner as an anchor, so its whole horizontal run reads as one hue.
|
|
183
|
-
*
|
|
184
|
-
* The source side (`○─`, `arc-branch-tee`, `arc-branch-corner`) and pure
|
|
185
|
-
* horizontal passes are unaffected: they track the nearest corner to the right
|
|
186
|
-
* (`arcCorner`), so a routed back-arc's source fan still reads as one hue. A
|
|
187
|
-
* crossing can only be one colour, so it takes the arc owning the horizontal
|
|
188
|
-
* run at this row; the crossed vertical lane is occluded at that one cell and
|
|
189
|
-
* reappears on the next row.
|
|
190
|
-
*/
|
|
191
|
-
function resolveRowLaneColors(cells: readonly StructuralCell[]): RowLaneColors {
|
|
192
|
-
const lane = new Array<number>(cells.length);
|
|
193
|
-
const connector = new Array<number>(cells.length);
|
|
194
|
-
const dash = new Array<number>(cells.length);
|
|
195
|
-
let arcCorner = NEUTRAL_LANE;
|
|
196
|
-
let landingAnchor = NEUTRAL_LANE;
|
|
197
|
-
for (let column = cells.length - 1; column >= 0; column--) {
|
|
198
|
-
const cell = cells[column];
|
|
199
|
-
connector[column] = landingAnchor !== NEUTRAL_LANE ? landingAnchor : arcCorner;
|
|
200
|
-
switch (cell?.kind) {
|
|
201
|
-
case 'arc-branch-corner':
|
|
202
|
-
arcCorner = column;
|
|
203
|
-
lane[column] = column;
|
|
204
|
-
dash[column] = column;
|
|
205
|
-
break;
|
|
206
|
-
case 'arc-land-corner':
|
|
207
|
-
arcCorner = column;
|
|
208
|
-
landingAnchor = column;
|
|
209
|
-
lane[column] = column;
|
|
210
|
-
dash[column] = column;
|
|
211
|
-
break;
|
|
212
|
-
// An inner co-sourced arc's own back-lane junction: its vertical run
|
|
213
|
-
// continues below in this column, so the whole `┬─` keeps its own column.
|
|
214
|
-
case 'arc-branch-tee':
|
|
215
|
-
lane[column] = column;
|
|
216
|
-
dash[column] = column;
|
|
217
|
-
break;
|
|
218
|
-
// The symmetric co-landing junction: the `┴` keeps its own column (its
|
|
219
|
-
// vertical run continues above), but the trailing `─` leads into the next
|
|
220
|
-
// converging arc — the nearest landing anchor still to its right.
|
|
221
|
-
case 'arc-land-tee':
|
|
222
|
-
lane[column] = column;
|
|
223
|
-
dash[column] = landingAnchor === NEUTRAL_LANE ? column : landingAnchor;
|
|
224
|
-
landingAnchor = column;
|
|
225
|
-
break;
|
|
226
|
-
case 'arc-crossing':
|
|
227
|
-
case 'arc-land-bridge': {
|
|
228
|
-
const served = landingAnchor !== NEUTRAL_LANE ? landingAnchor : arcCorner;
|
|
229
|
-
lane[column] = served;
|
|
230
|
-
dash[column] = served;
|
|
231
|
-
break;
|
|
232
|
-
}
|
|
233
|
-
case 'horizontal-pass':
|
|
234
|
-
lane[column] = arcCorner === NEUTRAL_LANE ? column : arcCorner;
|
|
235
|
-
dash[column] = lane[column] ?? column;
|
|
236
|
-
break;
|
|
237
|
-
case 'node':
|
|
238
|
-
lane[column] = column;
|
|
239
|
-
dash[column] = column;
|
|
240
|
-
arcCorner = NEUTRAL_LANE;
|
|
241
|
-
landingAnchor = NEUTRAL_LANE;
|
|
242
|
-
break;
|
|
243
|
-
default:
|
|
244
|
-
lane[column] = column;
|
|
245
|
-
dash[column] = column;
|
|
246
|
-
arcCorner = NEUTRAL_LANE;
|
|
247
|
-
landingAnchor = NEUTRAL_LANE;
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
return { lane, connector, dash };
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* Per-cell colour for a forward branch/merge connector row, split into the
|
|
255
|
-
* cell's junction `glyph` and its trailing `dash`. A connector's horizontal run
|
|
256
|
-
* is one logical line (a fork into new lanes, or a merge into a surviving lane)
|
|
257
|
-
* and reads best as the colour of the lane each segment serves — not dim-gray
|
|
258
|
-
* or a per-pass-through-column "rainbow".
|
|
259
|
-
*/
|
|
260
|
-
interface ConnectorLaneColors {
|
|
261
|
-
/** Colour column for a cell's junction glyph (`├` / `┬` / `┴` / `╮` / `╯`). */
|
|
262
|
-
readonly glyph: readonly number[];
|
|
263
|
-
/** Colour column for a tee's trailing `─` — the branch it leads into. */
|
|
264
|
-
readonly dash: readonly number[];
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* Resolve per-cell connector colours. Scanning right-to-left, a corner or an
|
|
269
|
-
* intermediate tee anchors its own lane (its junction glyph takes that column),
|
|
270
|
-
* but a tee's **trailing dash leads into the branch on its right** (the next
|
|
271
|
-
* branch point), so `┬─` reads as "this lane, then on toward the next" rather
|
|
272
|
-
* than tinting the dash with the left lane. The leading tee at `startLane` (the
|
|
273
|
-
* fork/merge origin) and pure horizontal segments inherit the nearest branch
|
|
274
|
-
* point to their right whole-cell, so the run into a branch — or collapsing
|
|
275
|
-
* into a merge corner — stays continuous. An `arc-crossing` keeps its junction
|
|
276
|
-
* glyph at its own column but re-anchors `owner` like an intermediate tee so
|
|
277
|
-
* dashes on both sides lead into the nearest branch on their right. Pass-through
|
|
278
|
-
* verticals outside the run keep their own column (column 0 stays neutral).
|
|
279
|
-
*/
|
|
280
|
-
export function resolveConnectorLaneColors(
|
|
281
|
-
cells: readonly StructuralCell[],
|
|
282
|
-
startLane: number,
|
|
283
|
-
): ConnectorLaneColors {
|
|
284
|
-
const glyph = new Array<number>(cells.length);
|
|
285
|
-
const dash = new Array<number>(cells.length);
|
|
286
|
-
let owner = NEUTRAL_LANE;
|
|
287
|
-
for (let column = cells.length - 1; column >= 0; column--) {
|
|
288
|
-
const cell = cells[column];
|
|
289
|
-
switch (cell?.kind) {
|
|
290
|
-
case 'branch-corner':
|
|
291
|
-
case 'merge-corner':
|
|
292
|
-
owner = column;
|
|
293
|
-
glyph[column] = column;
|
|
294
|
-
dash[column] = column;
|
|
295
|
-
break;
|
|
296
|
-
case 'branch-tee':
|
|
297
|
-
case 'merge-tee':
|
|
298
|
-
if (column === startLane) {
|
|
299
|
-
const served = owner === NEUTRAL_LANE ? column : owner;
|
|
300
|
-
glyph[column] = column;
|
|
301
|
-
dash[column] = served;
|
|
302
|
-
} else {
|
|
303
|
-
dash[column] = owner === NEUTRAL_LANE ? column : owner;
|
|
304
|
-
glyph[column] = column;
|
|
305
|
-
owner = column;
|
|
306
|
-
}
|
|
307
|
-
break;
|
|
308
|
-
case 'arc-crossing':
|
|
309
|
-
glyph[column] = column;
|
|
310
|
-
dash[column] = owner === NEUTRAL_LANE ? column : owner;
|
|
311
|
-
owner = column;
|
|
312
|
-
break;
|
|
313
|
-
case 'horizontal-pass': {
|
|
314
|
-
const served = owner === NEUTRAL_LANE ? column : owner;
|
|
315
|
-
glyph[column] = served;
|
|
316
|
-
dash[column] = served;
|
|
317
|
-
break;
|
|
318
|
-
}
|
|
319
|
-
default:
|
|
320
|
-
glyph[column] = column;
|
|
321
|
-
dash[column] = column;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
return { glyph, dash };
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* Style a structural glyph by its resolved colour column. Column 0 and the
|
|
329
|
-
* neutral sentinel render dim (`style.lane`); columns ≥ 1 take a palette hue.
|
|
330
|
-
*/
|
|
331
163
|
function laneStylerForColumn(
|
|
332
164
|
colorColumn: number,
|
|
333
165
|
colorize: boolean,
|
|
334
166
|
style: MigrationListStyler,
|
|
335
167
|
): (text: string) => string {
|
|
336
|
-
|
|
337
|
-
return (text) => style.lane(text);
|
|
338
|
-
}
|
|
339
|
-
return laneColorForColumn(colorColumn);
|
|
168
|
+
return stylerForLaneColumn(colorColumn, colorize, style.lane);
|
|
340
169
|
}
|
|
341
170
|
|
|
342
171
|
/**
|
|
@@ -351,12 +180,27 @@ function branchStylerOrDefault(
|
|
|
351
180
|
colorize: boolean,
|
|
352
181
|
fallback: (text: string) => string,
|
|
353
182
|
): (text: string) => string {
|
|
354
|
-
if (!colorize || column <=
|
|
183
|
+
if (!colorize || column <= NEUTRAL_LANE_COLUMN) {
|
|
355
184
|
return fallback;
|
|
356
185
|
}
|
|
357
186
|
return laneColorForColumn(column);
|
|
358
187
|
}
|
|
359
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
|
+
|
|
360
204
|
/**
|
|
361
205
|
* Render a connector tee (`├─` / `┬─` / `┴─`) with its junction glyph and its
|
|
362
206
|
* trailing dash coloured independently: the junction anchors its own lane while
|
|
@@ -398,7 +242,7 @@ function renderNodeMarkerPair(
|
|
|
398
242
|
function renderCellPair(
|
|
399
243
|
cell: StructuralCell,
|
|
400
244
|
column: number,
|
|
401
|
-
colors:
|
|
245
|
+
colors: RowArcLaneColors,
|
|
402
246
|
colorize: boolean,
|
|
403
247
|
style: MigrationListStyler,
|
|
404
248
|
palette: MigrationGraphTreeGlyphPalette,
|
|
@@ -407,7 +251,7 @@ function renderCellPair(
|
|
|
407
251
|
const lane = laneStylerForColumn(laneColumn, colorize, style);
|
|
408
252
|
switch (cell.kind) {
|
|
409
253
|
case 'node': {
|
|
410
|
-
const arcColumn = colors.connector[column] ??
|
|
254
|
+
const arcColumn = colors.connector[column] ?? NEUTRAL_LANE_COLUMN;
|
|
411
255
|
if (cell.arcLand === true) {
|
|
412
256
|
return renderNodeMarkerPair(palette.arcLand, column, arcColumn, colorize, style);
|
|
413
257
|
}
|
|
@@ -512,7 +356,7 @@ function renderConnectorRow(
|
|
|
512
356
|
out += lane(palette.horizontalPass);
|
|
513
357
|
break;
|
|
514
358
|
case 'arc-crossing':
|
|
515
|
-
out +=
|
|
359
|
+
out += renderArcCrossing(palette.arcCrossing, dashColumn, colorize, style);
|
|
516
360
|
break;
|
|
517
361
|
default:
|
|
518
362
|
out += ' ';
|
|
@@ -552,26 +396,41 @@ function abbreviateHash(hash: string, hashLength: number, emptySource: string):
|
|
|
552
396
|
|
|
553
397
|
const MIN_HASH_DATA_COLUMN = 25;
|
|
554
398
|
|
|
399
|
+
interface ContractOverlayNames {
|
|
400
|
+
readonly markers: readonly string[];
|
|
401
|
+
readonly refs: readonly string[];
|
|
402
|
+
}
|
|
403
|
+
|
|
555
404
|
function overlayNamesForContract(
|
|
556
405
|
contractHash: string,
|
|
557
406
|
opts: RenderMigrationGraphTreeOptions,
|
|
558
|
-
):
|
|
559
|
-
const
|
|
407
|
+
): ContractOverlayNames {
|
|
408
|
+
const markers: string[] = [];
|
|
409
|
+
const refs: string[] = [];
|
|
560
410
|
const userRefs = opts.refsByHash?.get(contractHash);
|
|
561
411
|
if (userRefs) {
|
|
562
|
-
|
|
563
|
-
}
|
|
564
|
-
if (opts.dbHash === contractHash) {
|
|
565
|
-
names.push(DB_MARKER_NAME);
|
|
412
|
+
refs.push(...[...userRefs].sort((a, b) => a.localeCompare(b)));
|
|
566
413
|
}
|
|
567
414
|
if (opts.contractHash === contractHash && contractHash !== EMPTY_CONTRACT_HASH) {
|
|
568
|
-
|
|
415
|
+
markers.push(CONTRACT_MARKER_NAME);
|
|
416
|
+
}
|
|
417
|
+
if (opts.dbHash === contractHash) {
|
|
418
|
+
markers.push(DB_MARKER_NAME);
|
|
569
419
|
}
|
|
570
|
-
|
|
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 };
|
|
571
430
|
}
|
|
572
431
|
|
|
573
432
|
function createTreeStyler(opts: RenderMigrationGraphTreeOptions): MigrationListStyler {
|
|
574
|
-
const base = createAnsiMigrationListStyler({ useColor: opts.colorize });
|
|
433
|
+
const base = opts.styler ?? createAnsiMigrationListStyler({ useColor: opts.colorize });
|
|
575
434
|
const activeRefName = opts.activeRefName;
|
|
576
435
|
if (!opts.colorize || activeRefName === undefined) {
|
|
577
436
|
return base;
|
|
@@ -595,6 +454,12 @@ function formatEdgeAnnotationSuffix(
|
|
|
595
454
|
return '';
|
|
596
455
|
}
|
|
597
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
|
+
}
|
|
598
463
|
const status = annotation.status;
|
|
599
464
|
if (status !== undefined) {
|
|
600
465
|
const glyphs = overlayStatusGlyphs(opts.glyphMode ?? 'unicode');
|
|
@@ -607,17 +472,10 @@ function formatEdgeAnnotationSuffix(
|
|
|
607
472
|
segments.push(styler(`${glyph} ${label}`));
|
|
608
473
|
}
|
|
609
474
|
}
|
|
610
|
-
if (annotation.operationCount !== undefined) {
|
|
611
|
-
segments.push(`${annotation.operationCount} ops`);
|
|
612
|
-
}
|
|
613
|
-
if (annotation.invariants !== undefined && annotation.invariants.length > 0) {
|
|
614
|
-
segments.push(style.invariants(annotation.invariants));
|
|
615
|
-
}
|
|
616
475
|
if (segments.length === 0) {
|
|
617
476
|
return '';
|
|
618
477
|
}
|
|
619
|
-
|
|
620
|
-
return `${prefix}${segments.join(' ')}`;
|
|
478
|
+
return ` ${segments.join(' ')}`;
|
|
621
479
|
}
|
|
622
480
|
|
|
623
481
|
function formatEdgeHashColumn(
|
|
@@ -628,13 +486,16 @@ function formatEdgeHashColumn(
|
|
|
628
486
|
): string {
|
|
629
487
|
if (edge.kind === 'self') {
|
|
630
488
|
const hash = abbreviateHash(edge.from, hashLength, palette.emptySource);
|
|
631
|
-
|
|
489
|
+
const source = padFromHashColumn(style.sourceHash(hash), hashLength);
|
|
490
|
+
return `${source} ${style.glyph(palette.forwardArrow)} ${style.destHash(hash)}`;
|
|
632
491
|
}
|
|
633
492
|
const source =
|
|
634
493
|
edge.from === EMPTY_CONTRACT_HASH
|
|
635
|
-
? style.glyph(palette.emptySource)
|
|
636
|
-
|
|
637
|
-
|
|
494
|
+
? padFromHashColumn(style.glyph(palette.emptySource), hashLength)
|
|
495
|
+
: padFromHashColumn(
|
|
496
|
+
style.sourceHash(abbreviateHash(edge.from, hashLength, palette.emptySource)),
|
|
497
|
+
hashLength,
|
|
498
|
+
);
|
|
638
499
|
const arrow = style.glyph(palette.forwardArrow);
|
|
639
500
|
const dest = style.destHash(abbreviateHash(edge.to, hashLength, palette.emptySource));
|
|
640
501
|
return `${source} ${arrow} ${dest}`;
|
|
@@ -691,6 +552,35 @@ function edgeLabelColumn(row: MigrationGraphGridRow, wideLabelColumn: number | u
|
|
|
691
552
|
return usesFullRowGutter ? row.cells.length * 2 + LABEL_GAP : (laneIndex + 1) * 2 + LABEL_GAP;
|
|
692
553
|
}
|
|
693
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
|
+
|
|
694
584
|
function nodeHasArcDecoration(row: MigrationGraphGridRow): boolean {
|
|
695
585
|
return row.cells.some(
|
|
696
586
|
(cell) => cell.kind === 'node' && (cell.arcTee === true || cell.arcLand === true),
|
|
@@ -715,6 +605,10 @@ export function renderMigrationGraphTree(
|
|
|
715
605
|
)
|
|
716
606
|
.map((row) => row.edge);
|
|
717
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);
|
|
718
612
|
|
|
719
613
|
const lines: string[] = [];
|
|
720
614
|
|
|
@@ -734,7 +628,7 @@ export function renderMigrationGraphTree(
|
|
|
734
628
|
continue;
|
|
735
629
|
}
|
|
736
630
|
|
|
737
|
-
const cellColors =
|
|
631
|
+
const cellColors = resolveRowArcLaneColors(row.cells);
|
|
738
632
|
let gutter = row.cells
|
|
739
633
|
.map((cell, column) =>
|
|
740
634
|
renderCellPair(cell, column, cellColors, opts.colorize, style, palette),
|
|
@@ -758,7 +652,7 @@ export function renderMigrationGraphTree(
|
|
|
758
652
|
}
|
|
759
653
|
const labelColumn =
|
|
760
654
|
row.kind === 'edge'
|
|
761
|
-
?
|
|
655
|
+
? maxEdgePrefixWidth
|
|
762
656
|
: wideLabelColumn !== undefined &&
|
|
763
657
|
(nodeHasArcDecoration(row) || row.contractHash !== undefined)
|
|
764
658
|
? wideLabelColumn
|
|
@@ -784,8 +678,10 @@ export function renderMigrationGraphTree(
|
|
|
784
678
|
} else if (gutter.length < laneSpan * 2) {
|
|
785
679
|
gutter = gutter.padEnd(laneSpan * 2, ' ');
|
|
786
680
|
}
|
|
787
|
-
const dirNameWidth =
|
|
788
|
-
|
|
681
|
+
const dirNameWidth =
|
|
682
|
+
row.kind === 'edge'
|
|
683
|
+
? edgeDirNameWidth
|
|
684
|
+
: rowDirNameWidth(labelColumn, maxDirNameLen, dirNameGap);
|
|
789
685
|
const gutterPad = padVisible(gutter, labelColumn);
|
|
790
686
|
|
|
791
687
|
if (row.kind === 'node') {
|
|
@@ -798,24 +694,24 @@ export function renderMigrationGraphTree(
|
|
|
798
694
|
)
|
|
799
695
|
.join('');
|
|
800
696
|
const emptyGutter = palette.emptySource.padEnd(2, ' ') + trailingLanes;
|
|
801
|
-
const
|
|
802
|
-
if (
|
|
697
|
+
const overlays = overlayNamesForContract(contractHash, opts);
|
|
698
|
+
if (overlays.markers.length === 0 && overlays.refs.length === 0) {
|
|
803
699
|
lines.push(trimTrailingWhitespace(emptyGutter));
|
|
804
700
|
continue;
|
|
805
701
|
}
|
|
806
|
-
const overlay = style.refs
|
|
807
|
-
lines.push(trimTrailingWhitespace(`${
|
|
702
|
+
const overlay = formatContractNodeOverlays(style, overlays.markers, overlays.refs);
|
|
703
|
+
lines.push(trimTrailingWhitespace(`${emptyGutter}${' '.repeat(LABEL_GAP)}${overlay}`));
|
|
808
704
|
continue;
|
|
809
705
|
}
|
|
810
706
|
const hashText = style.sourceHash(
|
|
811
707
|
abbreviateHash(contractHash, hashLength, palette.emptySource),
|
|
812
708
|
);
|
|
813
|
-
const
|
|
814
|
-
const
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
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
|
+
: '';
|
|
819
715
|
lines.push(trimTrailingWhitespace(`${gutterPad}${hashText}${overlayPad}${overlay}`));
|
|
820
716
|
continue;
|
|
821
717
|
}
|
|
@@ -828,7 +724,7 @@ export function renderMigrationGraphTree(
|
|
|
828
724
|
// A branched name keeps its bold (via `style.dirName`) and adds the lane
|
|
829
725
|
// hue, so it reads as one with its lane/arrow; column-0 names stay bold-only.
|
|
830
726
|
const dirNameStyler =
|
|
831
|
-
opts.colorize && laneIndex >
|
|
727
|
+
opts.colorize && laneIndex > NEUTRAL_LANE_COLUMN
|
|
832
728
|
? (text: string) => forcedBold(laneColorForColumn(laneIndex)(text))
|
|
833
729
|
: style.dirName;
|
|
834
730
|
const dirName = `${dirNameStyler(edge.dirName)}${dirNamePadding}`;
|
|
@@ -845,16 +741,26 @@ export interface RenderMigrationGraphLegendOptions {
|
|
|
845
741
|
readonly glyphMode?: GlyphMode;
|
|
846
742
|
}
|
|
847
743
|
|
|
744
|
+
function formatLegendExampleMarkers(colorize: boolean): string {
|
|
745
|
+
if (!colorize) {
|
|
746
|
+
return '<contract, db>';
|
|
747
|
+
}
|
|
748
|
+
const open = green('<');
|
|
749
|
+
const close = green('>');
|
|
750
|
+
const separator = green(', ');
|
|
751
|
+
return open + green('contract') + separator + green('db') + close;
|
|
752
|
+
}
|
|
753
|
+
|
|
848
754
|
/**
|
|
849
|
-
* A compact key for the
|
|
850
|
-
* in-lane direction arrows, the empty baseline, the
|
|
851
|
-
*
|
|
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
|
|
852
758
|
* worked sample of the data-column `from → to` migration hash arrow.
|
|
853
759
|
*
|
|
854
760
|
* Honors the same glyph palette (unicode vs ASCII) and `colorize` gate as the
|
|
855
761
|
* tree renderer, so the key matches whatever the graph itself drew and stays
|
|
856
762
|
* pipe-safe (zero ANSI when color is off). The caller adds the trailing blank
|
|
857
|
-
* line that separates this stderr key from the
|
|
763
|
+
* line that separates this stderr key from the tree on stdout.
|
|
858
764
|
*/
|
|
859
765
|
export function renderMigrationGraphLegend(opts: RenderMigrationGraphLegendOptions): string {
|
|
860
766
|
const palette = paletteFor(opts.glyphMode ?? 'unicode');
|
|
@@ -865,13 +771,17 @@ export function renderMigrationGraphLegend(opts: RenderMigrationGraphLegendOptio
|
|
|
865
771
|
const appliedPending = opts.colorize
|
|
866
772
|
? ` ${green(statusGlyphs.applied)} ${style.summary('applied')} ${yellow(statusGlyphs.pending)} ${style.summary('pending')}`
|
|
867
773
|
: ` ${statusGlyphs.applied} ${style.summary('applied')} ${statusGlyphs.pending} ${style.summary('pending')}`;
|
|
868
|
-
|
|
774
|
+
const exampleMarkers = formatLegendExampleMarkers(opts.colorize);
|
|
775
|
+
const exampleRefs = opts.colorize ? style.refs(['prod', 'staging']) : '(prod, staging)';
|
|
776
|
+
const lines = [
|
|
869
777
|
'Legend:',
|
|
870
778
|
` ${style.kind(node)} ${style.summary('contract')} ${style.kind(palette.edgeArrow.forward)} ${style.summary('forward')} ${style.kind(palette.edgeArrow.rollback)} ${style.summary('rollback')}`,
|
|
871
779
|
` ${style.kind(palette.edgeArrow.self)} ${style.summary('migration without schema change')}`,
|
|
872
780
|
appliedPending,
|
|
873
781
|
` ${style.kind(palette.emptySource)} ${style.summary('empty database (baseline)')}`,
|
|
874
|
-
` ${
|
|
782
|
+
` ${exampleMarkers} ${style.summary('live markers (contract on disk, database state)')}`,
|
|
783
|
+
` ${exampleRefs} ${style.summary('user-defined refs')}`,
|
|
875
784
|
` ${sampleArrow} ${style.summary('migration from contract aaaaaa to bbbbbb')}`,
|
|
876
|
-
]
|
|
785
|
+
];
|
|
786
|
+
return lines.join('\n');
|
|
877
787
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import stringWidth from 'string-width';
|
|
1
2
|
import type { GlyphMode } from '../glyph-mode';
|
|
2
3
|
|
|
3
4
|
export const MIGRATION_LIST_HASH_WIDTH = 7;
|
|
@@ -20,3 +21,8 @@ export function abbreviateContractHash(hash: string): string {
|
|
|
20
21
|
const stripped = hash.startsWith('sha256:') ? hash.slice(7) : hash;
|
|
21
22
|
return stripped.slice(0, MIGRATION_LIST_HASH_WIDTH);
|
|
22
23
|
}
|
|
24
|
+
|
|
25
|
+
export function padFromHashColumn(text: string, width: number): string {
|
|
26
|
+
const padding = Math.max(0, width - stringWidth(text));
|
|
27
|
+
return `${' '.repeat(padding)}${text}`;
|
|
28
|
+
}
|