@prisma-next/cli 0.12.0-dev.2 → 0.12.0-dev.21
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 +177 -160
- package/dist/cli.mjs.map +1 -1
- package/dist/{client-KgJorIvG.mjs → client-Cdxcme1x.mjs} +21 -8
- package/dist/client-Cdxcme1x.mjs.map +1 -0
- package/dist/{command-helpers-Bbw1GbwL.mjs → command-helpers-Cmdqyhz9.mjs} +32 -2
- package/dist/{command-helpers-Bbw1GbwL.mjs.map → command-helpers-Cmdqyhz9.mjs.map} +1 -1
- package/dist/commands/contract-emit.mjs +1 -1
- package/dist/commands/contract-infer.mjs +1 -1
- package/dist/commands/db-init.mjs +4 -4
- package/dist/commands/db-schema.mjs +3 -3
- package/dist/commands/db-sign.mjs +4 -4
- package/dist/commands/db-update.mjs +5 -5
- package/dist/commands/db-verify.mjs +1 -1
- package/dist/commands/migrate.d.mts +1 -1
- package/dist/commands/migrate.mjs +5 -5
- package/dist/commands/migration-check.mjs +1 -1
- package/dist/commands/migration-graph.d.mts +23 -5
- package/dist/commands/migration-graph.d.mts.map +1 -1
- package/dist/commands/migration-graph.mjs +2 -2
- package/dist/commands/migration-list.d.mts +3 -3
- package/dist/commands/migration-list.mjs +3 -3
- package/dist/commands/migration-log.d.mts +3 -3
- package/dist/commands/migration-log.mjs +3 -3
- 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 +1 -1
- package/dist/commands/migration-show.mjs +3 -3
- package/dist/commands/migration-status.d.mts +1 -1
- package/dist/commands/migration-status.mjs +4 -4
- package/dist/commands/migration-status.mjs.map +1 -1
- package/dist/commands/ref.d.mts +1 -1
- package/dist/commands/ref.mjs +2 -2
- 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-Cz0z5PJi.mjs} +2 -2
- package/dist/{contract-at-errors-BxP-TOMl.mjs.map → contract-at-errors-Cz0z5PJi.mjs.map} +1 -1
- package/dist/{contract-emit-D-4jrNve.mjs → contract-emit-CC9jDOmu.mjs} +3 -3
- package/dist/{contract-emit-D-4jrNve.mjs.map → contract-emit-CC9jDOmu.mjs.map} +1 -1
- package/dist/{contract-emit-DxcGl4Uq.mjs → contract-emit-DPMij44i.mjs} +3 -3
- package/dist/{contract-emit-DxcGl4Uq.mjs.map → contract-emit-DPMij44i.mjs.map} +1 -1
- package/dist/{contract-infer-D8uEbJuu.mjs → contract-infer-DaFPNrZH.mjs} +3 -3
- package/dist/{contract-infer-D8uEbJuu.mjs.map → contract-infer-DaFPNrZH.mjs.map} +1 -1
- package/dist/{contract-space-aggregate-loader-DvZwdkrr.mjs → contract-space-aggregate-loader-CirAEsM8.mjs} +2 -2
- package/dist/{contract-space-aggregate-loader-DvZwdkrr.mjs.map → contract-space-aggregate-loader-CirAEsM8.mjs.map} +1 -1
- package/dist/{db-verify-v_vUKXTU.mjs → db-verify-BSA1a_W_.mjs} +4 -4
- package/dist/{db-verify-v_vUKXTU.mjs.map → db-verify-BSA1a_W_.mjs.map} +1 -1
- package/dist/exports/control-api.d.mts +1 -1
- 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-DynSvww4.mjs} +2 -2
- package/dist/{framework-components-fYXjz_in.mjs.map → framework-components-DynSvww4.mjs.map} +1 -1
- package/dist/{global-flags-DEHjV8_s.d.mts → global-flags-DG4uY5tV.d.mts} +1 -1
- package/dist/{global-flags-DEHjV8_s.d.mts.map → global-flags-DG4uY5tV.d.mts.map} +1 -1
- package/dist/{init-Cv9UzWL5.mjs → init-B6kKrmf7.mjs} +5 -58
- package/dist/init-B6kKrmf7.mjs.map +1 -0
- package/dist/{inspect-live-schema-C6ohV_oQ.mjs → inspect-live-schema-Dn56wDhG.mjs} +3 -3
- package/dist/{inspect-live-schema-C6ohV_oQ.mjs.map → inspect-live-schema-Dn56wDhG.mjs.map} +1 -1
- package/dist/{migration-check-BiBJoYYW.mjs → migration-check-DzH1u-O1.mjs} +2 -2
- package/dist/{migration-check-BiBJoYYW.mjs.map → migration-check-DzH1u-O1.mjs.map} +1 -1
- package/dist/{migration-command-scaffold-CjvwO6at.mjs → migration-command-scaffold-V52dV2Tv.mjs} +3 -3
- package/dist/{migration-command-scaffold-CjvwO6at.mjs.map → migration-command-scaffold-V52dV2Tv.mjs.map} +1 -1
- package/dist/{migration-graph-D7DVUElV.mjs → migration-graph-DKl_IYsF.mjs} +377 -85
- package/dist/migration-graph-DKl_IYsF.mjs.map +1 -0
- package/dist/{migration-list-styler-BRwF4-gy.mjs → migration-list-styler-COQbZmXk.mjs} +61 -46
- package/dist/migration-list-styler-COQbZmXk.mjs.map +1 -0
- package/dist/{migration-plan-9DJ7q7_z.mjs → migration-plan-CaeKCKp4.mjs} +5 -5
- package/dist/{migration-plan-9DJ7q7_z.mjs.map → migration-plan-CaeKCKp4.mjs.map} +1 -1
- package/dist/{migration-types-D2FW63pr.d.mts → migration-types-CAQ-0TEE.d.mts} +1 -1
- package/dist/{migration-types-D2FW63pr.d.mts.map → migration-types-CAQ-0TEE.d.mts.map} +1 -1
- package/dist/{migrations-Cv2jxNNK.mjs → migrations-DQ1t3XFL.mjs} +2 -2
- package/dist/{migrations-Cv2jxNNK.mjs.map → migrations-DQ1t3XFL.mjs.map} +1 -1
- package/dist/{output-B60Gw5fu.mjs → output-CF_hqzI-.mjs} +1 -1
- package/dist/{output-B60Gw5fu.mjs.map → output-CF_hqzI-.mjs.map} +1 -1
- package/dist/telemetry-Q88WHwlv.mjs +122 -0
- package/dist/telemetry-Q88WHwlv.mjs.map +1 -0
- package/dist/{terminal-ui-5Y6mrg93.d.mts → terminal-ui-C3xGyxW-.d.mts} +1 -1
- package/dist/{terminal-ui-5Y6mrg93.d.mts.map → terminal-ui-C3xGyxW-.d.mts.map} +1 -1
- package/dist/{types-Dt_SfqFm.d.mts → types-DiC683UW.d.mts} +8 -2
- package/dist/{types-Dt_SfqFm.d.mts.map → types-DiC683UW.d.mts.map} +1 -1
- package/dist/{verify-DCA9Sldu.mjs → verify-CreSJ1Mz.mjs} +2 -2
- package/dist/{verify-DCA9Sldu.mjs.map → verify-CreSJ1Mz.mjs.map} +1 -1
- package/package.json +22 -18
- package/src/cli.ts +5 -0
- 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/migration-graph.ts +43 -2
- package/src/commands/migration-status.ts +1 -1
- package/src/commands/telemetry/index.ts +107 -0
- package/src/commands/telemetry/status.ts +67 -0
- package/src/control-api/client.ts +11 -1
- package/src/control-api/operations/apply.ts +1 -0
- package/src/control-api/operations/migration-apply.ts +10 -3
- package/src/control-api/types.ts +12 -1
- package/src/utils/formatters/migration-graph-lane-colors.ts +31 -0
- package/src/utils/formatters/migration-graph-layout.ts +51 -7
- package/src/utils/formatters/migration-graph-tree-render.ts +414 -51
- package/src/utils/formatters/migration-list-graph-topology.ts +67 -83
- package/src/utils/global-flags.ts +35 -0
- package/src/utils/telemetry.ts +68 -32
- package/dist/client-KgJorIvG.mjs.map +0 -1
- package/dist/init-Cv9UzWL5.mjs.map +0 -1
- package/dist/migration-graph-D7DVUElV.mjs.map +0 -1
- package/dist/migration-list-styler-BRwF4-gy.mjs.map +0 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';
|
|
2
|
-
import { bold } from 'colorette';
|
|
2
|
+
import { bold, createColors } from 'colorette';
|
|
3
3
|
import stringWidth from 'string-width';
|
|
4
4
|
import type { GlyphMode } from '../glyph-mode';
|
|
5
|
+
import { laneColorForColumn } from './migration-graph-lane-colors';
|
|
5
6
|
import type {
|
|
6
7
|
MigrationGraphGridModel,
|
|
7
8
|
MigrationGraphGridRow,
|
|
@@ -48,6 +49,7 @@ interface MigrationGraphTreeGlyphPalette {
|
|
|
48
49
|
readonly arcBranchCorner: string;
|
|
49
50
|
readonly arcBranchTee: string;
|
|
50
51
|
readonly arcLandCorner: string;
|
|
52
|
+
readonly arcLandTee: string;
|
|
51
53
|
readonly arcCrossing: string;
|
|
52
54
|
readonly arcLandBridge: string;
|
|
53
55
|
readonly horizontalPass: string;
|
|
@@ -71,6 +73,7 @@ const UNICODE_PALETTE: MigrationGraphTreeGlyphPalette = {
|
|
|
71
73
|
arcBranchCorner: '╮ ',
|
|
72
74
|
arcBranchTee: '┬─',
|
|
73
75
|
arcLandCorner: '╯ ',
|
|
76
|
+
arcLandTee: '┴─',
|
|
74
77
|
arcCrossing: '┼─',
|
|
75
78
|
arcLandBridge: '──',
|
|
76
79
|
horizontalPass: '──',
|
|
@@ -94,6 +97,7 @@ const ASCII_PALETTE: MigrationGraphTreeGlyphPalette = {
|
|
|
94
97
|
arcBranchCorner: '\\ ',
|
|
95
98
|
arcBranchTee: '+-',
|
|
96
99
|
arcLandCorner: '/ ',
|
|
100
|
+
arcLandTee: '+-',
|
|
97
101
|
arcCrossing: '+-',
|
|
98
102
|
arcLandBridge: '--',
|
|
99
103
|
horizontalPass: '--',
|
|
@@ -116,56 +120,327 @@ function arrowForEdgeKind(
|
|
|
116
120
|
return palette.edgeArrow[kind];
|
|
117
121
|
}
|
|
118
122
|
|
|
123
|
+
/**
|
|
124
|
+
* The leftmost lane (column 0) renders with the neutral dim lane style rather
|
|
125
|
+
* than a palette hue — in the common single-lane case it has nothing to be told
|
|
126
|
+
* apart from. Used as the "no owning arc" sentinel during colour resolution.
|
|
127
|
+
*/
|
|
128
|
+
const NEUTRAL_LANE = 0;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Forced bold for branch-coloured names. A branched name pairs its lane hue
|
|
132
|
+
* (also forced, via {@link laneColorForColumn}) with bold; both must emit even
|
|
133
|
+
* when colorette's ambient TTY detection is off, so the colorized branch name
|
|
134
|
+
* is deterministically bold + hue rather than hue-only.
|
|
135
|
+
*/
|
|
136
|
+
const { bold: forcedBold } = createColors({ useColor: true });
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* The colour-source column for each cell of a row, resolved together because a
|
|
140
|
+
* routed back-arc spans columns and must read as **one hue** rather than a
|
|
141
|
+
* per-column "rainbow". An arc's horizontal bridges, corners, and node-pair
|
|
142
|
+
* connector all take the arc's owning back-lane column (the corner that closes
|
|
143
|
+
* the arc), not the column they pass through.
|
|
144
|
+
*/
|
|
145
|
+
interface RowLaneColors {
|
|
146
|
+
/** Colour column for a cell's structural glyph (lane / spine / arc body). */
|
|
147
|
+
readonly lane: readonly number[];
|
|
148
|
+
/** Colour column for a node arc-pair's connector half (`◂` / `─`). */
|
|
149
|
+
readonly connector: readonly number[];
|
|
150
|
+
/**
|
|
151
|
+
* Colour column for the trailing `─` of a landing tee (`┴─`). The junction
|
|
152
|
+
* (`lane`) keeps its own column; the dash leads into the next converging arc.
|
|
153
|
+
*/
|
|
154
|
+
readonly dash: readonly number[];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Resolve per-cell colour columns for a row. Scanning right-to-left lets each
|
|
159
|
+
* arc segment inherit the hue of the arc it leads into.
|
|
160
|
+
*
|
|
161
|
+
* On a converging-landing line (`○◂──────┴─┴─╯`), every horizontal dash segment
|
|
162
|
+
* takes the hue of the **nearest landing anchor** — the next `arc-land-tee` or
|
|
163
|
+
* `arc-land-corner` — to its right, i.e. the branch it leads into: the bridge
|
|
164
|
+
* run leads into the first converging arc, and each tee's trailing `─` leads
|
|
165
|
+
* into the next arc out. Tee/corner junction glyphs keep their own column hue.
|
|
166
|
+
* This mirrors the forward connector's `┬─` rule (see
|
|
167
|
+
* {@link resolveConnectorLaneColors}). A single (non-converging) landing has
|
|
168
|
+
* only the corner as an anchor, so its whole horizontal run reads as one hue.
|
|
169
|
+
*
|
|
170
|
+
* The source side (`○─`, `arc-branch-tee`, `arc-branch-corner`) and pure
|
|
171
|
+
* horizontal passes are unaffected: they track the nearest corner to the right
|
|
172
|
+
* (`arcCorner`), so a routed back-arc's source fan still reads as one hue. A
|
|
173
|
+
* crossing can only be one colour, so it takes the arc owning the horizontal
|
|
174
|
+
* run at this row; the crossed vertical lane is occluded at that one cell and
|
|
175
|
+
* reappears on the next row.
|
|
176
|
+
*/
|
|
177
|
+
function resolveRowLaneColors(cells: readonly StructuralCell[]): RowLaneColors {
|
|
178
|
+
const lane = new Array<number>(cells.length);
|
|
179
|
+
const connector = new Array<number>(cells.length);
|
|
180
|
+
const dash = new Array<number>(cells.length);
|
|
181
|
+
let arcCorner = NEUTRAL_LANE;
|
|
182
|
+
let landingAnchor = NEUTRAL_LANE;
|
|
183
|
+
for (let column = cells.length - 1; column >= 0; column--) {
|
|
184
|
+
const cell = cells[column];
|
|
185
|
+
connector[column] = landingAnchor !== NEUTRAL_LANE ? landingAnchor : arcCorner;
|
|
186
|
+
switch (cell?.kind) {
|
|
187
|
+
case 'arc-branch-corner':
|
|
188
|
+
arcCorner = column;
|
|
189
|
+
lane[column] = column;
|
|
190
|
+
dash[column] = column;
|
|
191
|
+
break;
|
|
192
|
+
case 'arc-land-corner':
|
|
193
|
+
arcCorner = column;
|
|
194
|
+
landingAnchor = column;
|
|
195
|
+
lane[column] = column;
|
|
196
|
+
dash[column] = column;
|
|
197
|
+
break;
|
|
198
|
+
// An inner co-sourced arc's own back-lane junction: its vertical run
|
|
199
|
+
// continues below in this column, so the whole `┬─` keeps its own column.
|
|
200
|
+
case 'arc-branch-tee':
|
|
201
|
+
lane[column] = column;
|
|
202
|
+
dash[column] = column;
|
|
203
|
+
break;
|
|
204
|
+
// The symmetric co-landing junction: the `┴` keeps its own column (its
|
|
205
|
+
// vertical run continues above), but the trailing `─` leads into the next
|
|
206
|
+
// converging arc — the nearest landing anchor still to its right.
|
|
207
|
+
case 'arc-land-tee':
|
|
208
|
+
lane[column] = column;
|
|
209
|
+
dash[column] = landingAnchor === NEUTRAL_LANE ? column : landingAnchor;
|
|
210
|
+
landingAnchor = column;
|
|
211
|
+
break;
|
|
212
|
+
case 'arc-crossing':
|
|
213
|
+
case 'arc-land-bridge': {
|
|
214
|
+
const served = landingAnchor !== NEUTRAL_LANE ? landingAnchor : arcCorner;
|
|
215
|
+
lane[column] = served;
|
|
216
|
+
dash[column] = served;
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
case 'horizontal-pass':
|
|
220
|
+
lane[column] = arcCorner === NEUTRAL_LANE ? column : arcCorner;
|
|
221
|
+
dash[column] = lane[column] ?? column;
|
|
222
|
+
break;
|
|
223
|
+
case 'node':
|
|
224
|
+
lane[column] = column;
|
|
225
|
+
dash[column] = column;
|
|
226
|
+
arcCorner = NEUTRAL_LANE;
|
|
227
|
+
landingAnchor = NEUTRAL_LANE;
|
|
228
|
+
break;
|
|
229
|
+
default:
|
|
230
|
+
lane[column] = column;
|
|
231
|
+
dash[column] = column;
|
|
232
|
+
arcCorner = NEUTRAL_LANE;
|
|
233
|
+
landingAnchor = NEUTRAL_LANE;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return { lane, connector, dash };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Per-cell colour for a forward branch/merge connector row, split into the
|
|
241
|
+
* cell's junction `glyph` and its trailing `dash`. A connector's horizontal run
|
|
242
|
+
* is one logical line (a fork into new lanes, or a merge into a surviving lane)
|
|
243
|
+
* and reads best as the colour of the lane each segment serves — not dim-gray
|
|
244
|
+
* or a per-pass-through-column "rainbow".
|
|
245
|
+
*/
|
|
246
|
+
interface ConnectorLaneColors {
|
|
247
|
+
/** Colour column for a cell's junction glyph (`├` / `┬` / `┴` / `╮` / `╯`). */
|
|
248
|
+
readonly glyph: readonly number[];
|
|
249
|
+
/** Colour column for a tee's trailing `─` — the branch it leads into. */
|
|
250
|
+
readonly dash: readonly number[];
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Resolve per-cell connector colours. Scanning right-to-left, a corner or an
|
|
255
|
+
* intermediate tee anchors its own lane (its junction glyph takes that column),
|
|
256
|
+
* but a tee's **trailing dash leads into the branch on its right** (the next
|
|
257
|
+
* branch point), so `┬─` reads as "this lane, then on toward the next" rather
|
|
258
|
+
* than tinting the dash with the left lane. The leading tee at `startLane` (the
|
|
259
|
+
* fork/merge origin) and pure horizontal segments inherit the nearest branch
|
|
260
|
+
* point to their right whole-cell, so the run into a branch — or collapsing
|
|
261
|
+
* into a merge corner — stays continuous. An `arc-crossing` keeps its junction
|
|
262
|
+
* glyph at its own column but re-anchors `owner` like an intermediate tee so
|
|
263
|
+
* dashes on both sides lead into the nearest branch on their right. Pass-through
|
|
264
|
+
* verticals outside the run keep their own column (column 0 stays neutral).
|
|
265
|
+
*/
|
|
266
|
+
export function resolveConnectorLaneColors(
|
|
267
|
+
cells: readonly StructuralCell[],
|
|
268
|
+
startLane: number,
|
|
269
|
+
): ConnectorLaneColors {
|
|
270
|
+
const glyph = new Array<number>(cells.length);
|
|
271
|
+
const dash = new Array<number>(cells.length);
|
|
272
|
+
let owner = NEUTRAL_LANE;
|
|
273
|
+
for (let column = cells.length - 1; column >= 0; column--) {
|
|
274
|
+
const cell = cells[column];
|
|
275
|
+
switch (cell?.kind) {
|
|
276
|
+
case 'branch-corner':
|
|
277
|
+
case 'merge-corner':
|
|
278
|
+
owner = column;
|
|
279
|
+
glyph[column] = column;
|
|
280
|
+
dash[column] = column;
|
|
281
|
+
break;
|
|
282
|
+
case 'branch-tee':
|
|
283
|
+
case 'merge-tee':
|
|
284
|
+
if (column === startLane) {
|
|
285
|
+
const served = owner === NEUTRAL_LANE ? column : owner;
|
|
286
|
+
glyph[column] = column;
|
|
287
|
+
dash[column] = served;
|
|
288
|
+
} else {
|
|
289
|
+
dash[column] = owner === NEUTRAL_LANE ? column : owner;
|
|
290
|
+
glyph[column] = column;
|
|
291
|
+
owner = column;
|
|
292
|
+
}
|
|
293
|
+
break;
|
|
294
|
+
case 'arc-crossing':
|
|
295
|
+
glyph[column] = column;
|
|
296
|
+
dash[column] = owner === NEUTRAL_LANE ? column : owner;
|
|
297
|
+
owner = column;
|
|
298
|
+
break;
|
|
299
|
+
case 'horizontal-pass': {
|
|
300
|
+
const served = owner === NEUTRAL_LANE ? column : owner;
|
|
301
|
+
glyph[column] = served;
|
|
302
|
+
dash[column] = served;
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
305
|
+
default:
|
|
306
|
+
glyph[column] = column;
|
|
307
|
+
dash[column] = column;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return { glyph, dash };
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Style a structural glyph by its resolved colour column. Column 0 and the
|
|
315
|
+
* neutral sentinel render dim (`style.lane`); columns ≥ 1 take a palette hue.
|
|
316
|
+
*/
|
|
317
|
+
function laneStylerForColumn(
|
|
318
|
+
colorColumn: number,
|
|
319
|
+
colorize: boolean,
|
|
320
|
+
style: MigrationListStyler,
|
|
321
|
+
): (text: string) => string {
|
|
322
|
+
if (!colorize || colorColumn <= NEUTRAL_LANE) {
|
|
323
|
+
return (text) => style.lane(text);
|
|
324
|
+
}
|
|
325
|
+
return laneColorForColumn(colorColumn);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Tint a branch-owned token (direction arrow, migration name) by its edge's
|
|
330
|
+
* lane so the whole branch row reads in one colour. Column 0 has nothing to be
|
|
331
|
+
* told apart from in the common linear chain, so it keeps the token's existing
|
|
332
|
+
* default styling (`fallback`) rather than a palette hue; only lanes ≥ 1 take a
|
|
333
|
+
* colour. With colour off, the fallback (also colourless) is used unchanged.
|
|
334
|
+
*/
|
|
335
|
+
function branchStylerOrDefault(
|
|
336
|
+
column: number,
|
|
337
|
+
colorize: boolean,
|
|
338
|
+
fallback: (text: string) => string,
|
|
339
|
+
): (text: string) => string {
|
|
340
|
+
if (!colorize || column <= NEUTRAL_LANE) {
|
|
341
|
+
return fallback;
|
|
342
|
+
}
|
|
343
|
+
return laneColorForColumn(column);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Render a connector tee (`├─` / `┬─` / `┴─`) with its junction glyph and its
|
|
348
|
+
* trailing dash coloured independently: the junction anchors its own lane while
|
|
349
|
+
* the dash leads into the branch on its right.
|
|
350
|
+
*/
|
|
351
|
+
function renderConnectorTee(
|
|
352
|
+
pair: string,
|
|
353
|
+
glyphColumn: number,
|
|
354
|
+
dashColumn: number,
|
|
355
|
+
colorize: boolean,
|
|
356
|
+
style: MigrationListStyler,
|
|
357
|
+
): string {
|
|
358
|
+
const glyph = laneStylerForColumn(glyphColumn, colorize, style);
|
|
359
|
+
if (glyphColumn === dashColumn) {
|
|
360
|
+
return glyph(pair);
|
|
361
|
+
}
|
|
362
|
+
return glyph(pair.slice(0, 1)) + laneStylerForColumn(dashColumn, colorize, style)(pair.slice(1));
|
|
363
|
+
}
|
|
364
|
+
|
|
119
365
|
/**
|
|
120
366
|
* A node-marker glyph pair (`○◂`, `○─`, `*<`, `*-`) is the contract node
|
|
121
|
-
* marker (`○` / `*`) followed by an arc connector (`◂` / `─` / `<` / `-`).
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
367
|
+
* marker (`○` / `*`) followed by an arc connector (`◂` / `─` / `<` / `-`). The
|
|
368
|
+
* marker takes its own lane's hue (so each node visibly belongs to its branch);
|
|
369
|
+
* the connector follows the arc it belongs to (its owning back-lane hue).
|
|
370
|
+
* Direction arrows are handled elsewhere — they take their edge's lane hue too.
|
|
125
371
|
*/
|
|
126
|
-
function renderNodeMarkerPair(
|
|
127
|
-
|
|
372
|
+
function renderNodeMarkerPair(
|
|
373
|
+
pair: string,
|
|
374
|
+
nodeColumn: number,
|
|
375
|
+
arcColumn: number,
|
|
376
|
+
colorize: boolean,
|
|
377
|
+
style: MigrationListStyler,
|
|
378
|
+
): string {
|
|
379
|
+
const marker = laneStylerForColumn(nodeColumn, colorize, style);
|
|
380
|
+
const connector = laneStylerForColumn(arcColumn, colorize, style);
|
|
381
|
+
return marker(pair.slice(0, 1)) + connector(pair.slice(1));
|
|
128
382
|
}
|
|
129
383
|
|
|
130
384
|
function renderCellPair(
|
|
131
385
|
cell: StructuralCell,
|
|
386
|
+
column: number,
|
|
387
|
+
colors: RowLaneColors,
|
|
388
|
+
colorize: boolean,
|
|
132
389
|
style: MigrationListStyler,
|
|
133
390
|
palette: MigrationGraphTreeGlyphPalette,
|
|
134
391
|
): string {
|
|
392
|
+
const laneColumn = colors.lane[column] ?? column;
|
|
393
|
+
const lane = laneStylerForColumn(laneColumn, colorize, style);
|
|
135
394
|
switch (cell.kind) {
|
|
136
|
-
case 'node':
|
|
137
|
-
|
|
138
|
-
if (cell.
|
|
139
|
-
|
|
395
|
+
case 'node': {
|
|
396
|
+
const arcColumn = colors.connector[column] ?? NEUTRAL_LANE;
|
|
397
|
+
if (cell.arcLand === true) {
|
|
398
|
+
return renderNodeMarkerPair(palette.arcLand, column, arcColumn, colorize, style);
|
|
399
|
+
}
|
|
400
|
+
if (cell.arcTee === true) {
|
|
401
|
+
return renderNodeMarkerPair(palette.arcTee, column, arcColumn, colorize, style);
|
|
402
|
+
}
|
|
403
|
+
return lane(palette.node);
|
|
404
|
+
}
|
|
140
405
|
case 'vertical-pass':
|
|
141
|
-
return
|
|
406
|
+
return lane(palette.verticalPass);
|
|
142
407
|
case 'edge-lane':
|
|
143
|
-
// The lane stays dim; the direction arrow (↑ / ↓ / ⟲) is the signal and
|
|
144
|
-
// stays bright, like the contract-node marker.
|
|
145
408
|
return cell.ownsLabel
|
|
146
|
-
?
|
|
147
|
-
|
|
148
|
-
|
|
409
|
+
? lane(palette.verticalPass.trimEnd()) +
|
|
410
|
+
branchStylerOrDefault(
|
|
411
|
+
column,
|
|
412
|
+
colorize,
|
|
413
|
+
style.kind,
|
|
414
|
+
)(arrowForEdgeKind(cell.edgeKind, palette))
|
|
415
|
+
: lane(palette.verticalPass);
|
|
149
416
|
case 'branch-tee':
|
|
150
|
-
return
|
|
417
|
+
return lane(palette.branchTee);
|
|
151
418
|
case 'merge-tee':
|
|
152
|
-
return
|
|
419
|
+
return lane(palette.mergeTee);
|
|
153
420
|
case 'branch-corner':
|
|
154
|
-
return
|
|
421
|
+
return lane(palette.branchCorner);
|
|
155
422
|
case 'merge-corner':
|
|
156
|
-
return
|
|
423
|
+
return lane(palette.mergeCorner);
|
|
157
424
|
case 'arc-branch-corner':
|
|
158
|
-
return
|
|
425
|
+
return lane(palette.arcBranchCorner);
|
|
159
426
|
case 'arc-branch-tee':
|
|
160
|
-
return
|
|
427
|
+
return lane(palette.arcBranchTee);
|
|
161
428
|
case 'arc-land-corner':
|
|
162
|
-
return
|
|
429
|
+
return lane(palette.arcLandCorner);
|
|
430
|
+
case 'arc-land-tee':
|
|
431
|
+
return renderConnectorTee(
|
|
432
|
+
palette.arcLandTee,
|
|
433
|
+
laneColumn,
|
|
434
|
+
colors.dash[column] ?? laneColumn,
|
|
435
|
+
colorize,
|
|
436
|
+
style,
|
|
437
|
+
);
|
|
163
438
|
case 'arc-crossing':
|
|
164
|
-
return
|
|
439
|
+
return lane(palette.arcLandBridge);
|
|
165
440
|
case 'arc-land-bridge':
|
|
166
|
-
return
|
|
441
|
+
return lane(palette.arcLandBridge);
|
|
167
442
|
case 'horizontal-pass':
|
|
168
|
-
return
|
|
443
|
+
return lane(palette.horizontalPass);
|
|
169
444
|
case 'empty':
|
|
170
445
|
return ' ';
|
|
171
446
|
}
|
|
@@ -174,34 +449,56 @@ function renderCellPair(
|
|
|
174
449
|
function renderConnectorRow(
|
|
175
450
|
row: MigrationGraphGridRow,
|
|
176
451
|
gridWidth: number,
|
|
452
|
+
colorize: boolean,
|
|
177
453
|
style: MigrationListStyler,
|
|
178
454
|
palette: MigrationGraphTreeGlyphPalette,
|
|
179
455
|
): string {
|
|
180
456
|
const isMerge = row.kind === 'merge-connector';
|
|
181
457
|
if (row.cells.length > 0) {
|
|
458
|
+
const colors = resolveConnectorLaneColors(row.cells, row.startLane ?? 0);
|
|
182
459
|
let seenTee = false;
|
|
183
460
|
let out = '';
|
|
184
|
-
for (
|
|
461
|
+
for (let column = 0; column < row.cells.length; column++) {
|
|
462
|
+
const cell = row.cells[column];
|
|
463
|
+
if (cell === undefined) continue;
|
|
464
|
+
const glyphColumn = colors.glyph[column] ?? column;
|
|
465
|
+
const dashColumn = colors.dash[column] ?? glyphColumn;
|
|
466
|
+
const lane = laneStylerForColumn(glyphColumn, colorize, style);
|
|
185
467
|
switch (cell.kind) {
|
|
186
468
|
case 'branch-tee':
|
|
187
|
-
out +=
|
|
469
|
+
out += renderConnectorTee(
|
|
470
|
+
seenTee ? palette.connectorBranchTeeCo : palette.connectorBranchTee,
|
|
471
|
+
glyphColumn,
|
|
472
|
+
dashColumn,
|
|
473
|
+
colorize,
|
|
474
|
+
style,
|
|
475
|
+
);
|
|
188
476
|
seenTee = true;
|
|
189
477
|
break;
|
|
190
478
|
case 'merge-tee':
|
|
191
|
-
out +=
|
|
479
|
+
out += renderConnectorTee(
|
|
480
|
+
seenTee ? palette.connectorMergeTeeCo : palette.connectorBranchTee,
|
|
481
|
+
glyphColumn,
|
|
482
|
+
dashColumn,
|
|
483
|
+
colorize,
|
|
484
|
+
style,
|
|
485
|
+
);
|
|
192
486
|
seenTee = true;
|
|
193
487
|
break;
|
|
194
488
|
case 'branch-corner':
|
|
195
|
-
out +=
|
|
489
|
+
out += lane(palette.branchCorner);
|
|
196
490
|
break;
|
|
197
491
|
case 'merge-corner':
|
|
198
|
-
out +=
|
|
492
|
+
out += lane(palette.mergeCorner);
|
|
199
493
|
break;
|
|
200
494
|
case 'vertical-pass':
|
|
201
|
-
out +=
|
|
495
|
+
out += lane(palette.verticalPass);
|
|
202
496
|
break;
|
|
203
497
|
case 'horizontal-pass':
|
|
204
|
-
out +=
|
|
498
|
+
out += lane(palette.horizontalPass);
|
|
499
|
+
break;
|
|
500
|
+
case 'arc-crossing':
|
|
501
|
+
out += renderConnectorTee(palette.arcCrossing, glyphColumn, dashColumn, colorize, style);
|
|
205
502
|
break;
|
|
206
503
|
default:
|
|
207
504
|
out += ' ';
|
|
@@ -218,13 +515,15 @@ function renderConnectorRow(
|
|
|
218
515
|
|
|
219
516
|
const start = row.startLane ?? 0;
|
|
220
517
|
const end = row.endLane ?? start;
|
|
518
|
+
// The whole fork/merge run reads as one line in the served lane's hue (the
|
|
519
|
+
// corner it reaches); pass-through columns outside the run keep their own.
|
|
520
|
+
const runLane = laneStylerForColumn(end, colorize, style);
|
|
221
521
|
let out = '';
|
|
222
522
|
for (let column = 0; column < gridWidth; column++) {
|
|
223
523
|
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);
|
|
524
|
+
else if (column === start) out += runLane(palette.connectorBranchTee);
|
|
525
|
+
else if (column === end) out += runLane(isMerge ? palette.mergeCorner : palette.branchCorner);
|
|
526
|
+
else out += runLane(isMerge ? palette.connectorMergeTeeCo : palette.connectorBranchTeeCo);
|
|
228
527
|
}
|
|
229
528
|
return out;
|
|
230
529
|
}
|
|
@@ -297,6 +596,13 @@ function padVisible(text: string, targetWidth: number): string {
|
|
|
297
596
|
return text + ' '.repeat(padding);
|
|
298
597
|
}
|
|
299
598
|
|
|
599
|
+
const ANSI_ESCAPE = '\x1b';
|
|
600
|
+
|
|
601
|
+
function trimTrailingWhitespace(line: string): string {
|
|
602
|
+
const trailingSpaceBeforeReset = new RegExp(`[\\t ]+((?:${ANSI_ESCAPE}\\[[0-9;]*m)+)$`);
|
|
603
|
+
return line.replace(trailingSpaceBeforeReset, '$1').replace(/\s+$/, '');
|
|
604
|
+
}
|
|
605
|
+
|
|
300
606
|
function gridWidthForModel(rows: readonly MigrationGraphGridRow[]): number {
|
|
301
607
|
return rows.reduce(
|
|
302
608
|
(max, row) =>
|
|
@@ -373,19 +679,32 @@ export function renderMigrationGraphTree(
|
|
|
373
679
|
}
|
|
374
680
|
|
|
375
681
|
if (row.kind === 'branch-connector' || row.kind === 'merge-connector') {
|
|
376
|
-
lines.push(
|
|
682
|
+
lines.push(
|
|
683
|
+
trimTrailingWhitespace(renderConnectorRow(row, gridWidth, opts.colorize, style, palette)),
|
|
684
|
+
);
|
|
377
685
|
continue;
|
|
378
686
|
}
|
|
379
687
|
|
|
380
|
-
|
|
381
|
-
|
|
688
|
+
const cellColors = resolveRowLaneColors(row.cells);
|
|
689
|
+
let gutter = row.cells
|
|
690
|
+
.map((cell, column) =>
|
|
691
|
+
renderCellPair(cell, column, cellColors, opts.colorize, style, palette),
|
|
692
|
+
)
|
|
693
|
+
.join('');
|
|
382
694
|
let laneSpan = row.cells.length;
|
|
383
695
|
if (row.kind === 'node') {
|
|
384
696
|
const contractHash = row.contractHash ?? EMPTY_CONTRACT_HASH;
|
|
385
|
-
if (
|
|
697
|
+
if (contractHash === EMPTY_CONTRACT_HASH) {
|
|
386
698
|
laneSpan = 1;
|
|
387
699
|
} else {
|
|
388
|
-
|
|
700
|
+
let lastActiveColumn = -1;
|
|
701
|
+
for (let column = row.cells.length - 1; column >= 0; column--) {
|
|
702
|
+
if (row.cells[column]?.kind !== 'empty') {
|
|
703
|
+
lastActiveColumn = column;
|
|
704
|
+
break;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
laneSpan = lastActiveColumn >= 0 ? lastActiveColumn + 1 : 1;
|
|
389
708
|
}
|
|
390
709
|
}
|
|
391
710
|
const labelColumn =
|
|
@@ -402,12 +721,16 @@ export function renderMigrationGraphTree(
|
|
|
402
721
|
) {
|
|
403
722
|
gutter = row.cells
|
|
404
723
|
.slice(0, 1)
|
|
405
|
-
.map((cell) =>
|
|
724
|
+
.map((cell, column) =>
|
|
725
|
+
renderCellPair(cell, column, cellColors, opts.colorize, style, palette),
|
|
726
|
+
)
|
|
406
727
|
.join('');
|
|
407
728
|
} else if (row.kind === 'node' && laneSpan < row.cells.length && !nodeHasArcDecoration(row)) {
|
|
408
729
|
gutter = row.cells
|
|
409
730
|
.slice(0, laneSpan)
|
|
410
|
-
.map((cell) =>
|
|
731
|
+
.map((cell, column) =>
|
|
732
|
+
renderCellPair(cell, column, cellColors, opts.colorize, style, palette),
|
|
733
|
+
)
|
|
411
734
|
.join('');
|
|
412
735
|
} else if (gutter.length < laneSpan * 2) {
|
|
413
736
|
gutter = gutter.padEnd(laneSpan * 2, ' ');
|
|
@@ -421,16 +744,18 @@ export function renderMigrationGraphTree(
|
|
|
421
744
|
if (contractHash === EMPTY_CONTRACT_HASH) {
|
|
422
745
|
const trailingLanes = row.cells
|
|
423
746
|
.slice(1)
|
|
424
|
-
.map((cell) =>
|
|
747
|
+
.map((cell, offset) =>
|
|
748
|
+
renderCellPair(cell, offset + 1, cellColors, opts.colorize, style, palette),
|
|
749
|
+
)
|
|
425
750
|
.join('');
|
|
426
751
|
const emptyGutter = palette.emptySource.padEnd(2, ' ') + trailingLanes;
|
|
427
752
|
const overlayNames = overlayNamesForContract(contractHash, opts);
|
|
428
753
|
if (overlayNames.length === 0) {
|
|
429
|
-
lines.push(emptyGutter
|
|
754
|
+
lines.push(trimTrailingWhitespace(emptyGutter));
|
|
430
755
|
continue;
|
|
431
756
|
}
|
|
432
757
|
const overlay = style.refs(overlayNames);
|
|
433
|
-
lines.push(`${padVisible(emptyGutter, dataColumn)}${overlay}
|
|
758
|
+
lines.push(trimTrailingWhitespace(`${padVisible(emptyGutter, dataColumn)}${overlay}`));
|
|
434
759
|
continue;
|
|
435
760
|
}
|
|
436
761
|
const hashText = style.sourceHash(
|
|
@@ -442,7 +767,7 @@ export function renderMigrationGraphTree(
|
|
|
442
767
|
? ' '.repeat(Math.max(0, dataColumn - labelColumn - stringWidth(hashText)))
|
|
443
768
|
: '';
|
|
444
769
|
const overlay = overlayNames.length > 0 ? style.refs(overlayNames) : '';
|
|
445
|
-
lines.push(`${gutterPad}${hashText}${overlayPad}${overlay}
|
|
770
|
+
lines.push(trimTrailingWhitespace(`${gutterPad}${hashText}${overlayPad}${overlay}`));
|
|
446
771
|
continue;
|
|
447
772
|
}
|
|
448
773
|
|
|
@@ -450,10 +775,48 @@ export function renderMigrationGraphTree(
|
|
|
450
775
|
if (edge === undefined) continue;
|
|
451
776
|
|
|
452
777
|
const dirNamePadding = ' '.repeat(Math.max(0, dirNameWidth - edge.dirName.length));
|
|
453
|
-
const
|
|
778
|
+
const laneIndex = row.laneIndex ?? 0;
|
|
779
|
+
// A branched name keeps its bold (via `style.dirName`) and adds the lane
|
|
780
|
+
// hue, so it reads as one with its lane/arrow; column-0 names stay bold-only.
|
|
781
|
+
const dirNameStyler =
|
|
782
|
+
opts.colorize && laneIndex > NEUTRAL_LANE
|
|
783
|
+
? (text: string) => forcedBold(laneColorForColumn(laneIndex)(text))
|
|
784
|
+
: style.dirName;
|
|
785
|
+
const dirName = `${dirNameStyler(edge.dirName)}${dirNamePadding}`;
|
|
454
786
|
const hashColumn = formatEdgeHashColumn(edge, style, hashLength, palette);
|
|
455
|
-
lines.push(`${gutterPad}${dirName}${hashColumn}
|
|
787
|
+
lines.push(trimTrailingWhitespace(`${gutterPad}${dirName}${hashColumn}`));
|
|
456
788
|
}
|
|
457
789
|
|
|
458
790
|
return lines.join('\n');
|
|
459
791
|
}
|
|
792
|
+
|
|
793
|
+
export interface RenderMigrationGraphLegendOptions {
|
|
794
|
+
readonly colorize: boolean;
|
|
795
|
+
readonly glyphMode?: GlyphMode;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
/**
|
|
799
|
+
* A compact key for the `--tree` visual language: the contract marker, the
|
|
800
|
+
* in-lane direction arrows, the empty baseline, the `(refs)` overlay (including
|
|
801
|
+
* the reserved `db` live-database and `contract` working-schema markers), and a
|
|
802
|
+
* worked sample of the data-column `from → to` migration hash arrow.
|
|
803
|
+
*
|
|
804
|
+
* Honors the same glyph palette (unicode vs ASCII) and `colorize` gate as the
|
|
805
|
+
* tree renderer, so the key matches whatever the graph itself drew and stays
|
|
806
|
+
* pipe-safe (zero ANSI when color is off). The caller adds the trailing blank
|
|
807
|
+
* line that separates this stderr key from the graph on stdout.
|
|
808
|
+
*/
|
|
809
|
+
export function renderMigrationGraphLegend(opts: RenderMigrationGraphLegendOptions): string {
|
|
810
|
+
const palette = paletteFor(opts.glyphMode ?? 'unicode');
|
|
811
|
+
const style = createAnsiMigrationListStyler({ useColor: opts.colorize });
|
|
812
|
+
const node = palette.node.trimEnd();
|
|
813
|
+
const sampleArrow = `${style.sourceHash('aaaaaa')} ${style.glyph(palette.forwardArrow)} ${style.destHash('bbbbbb')}`;
|
|
814
|
+
return [
|
|
815
|
+
'Legend:',
|
|
816
|
+
` ${style.kind(node)} ${style.summary('contract')} ${style.kind(palette.edgeArrow.forward)} ${style.summary('forward')} ${style.kind(palette.edgeArrow.rollback)} ${style.summary('rollback')}`,
|
|
817
|
+
` ${style.kind(palette.edgeArrow.self)} ${style.summary('migration without schema change')}`,
|
|
818
|
+
` ${style.kind(palette.emptySource)} ${style.summary('empty database (baseline)')}`,
|
|
819
|
+
` ${style.refs(['refs'])} ${style.summary(`${DB_MARKER_NAME} / ${CONTRACT_MARKER_NAME} markers`)}`,
|
|
820
|
+
` ${sampleArrow} ${style.summary('migration from contract aaaaaa to bbbbbb')}`,
|
|
821
|
+
].join('\n');
|
|
822
|
+
}
|