@prisma-next/cli 0.12.0 → 0.13.0-dev.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/cli.mjs +180 -163
- package/dist/cli.mjs.map +1 -1
- package/dist/{client-KgJorIvG.mjs → client-CJzuo5wX.mjs} +222 -107
- package/dist/client-CJzuo5wX.mjs.map +1 -0
- package/dist/{command-helpers-Bbw1GbwL.mjs → command-helpers-DGMvGBeX.mjs} +318 -25
- package/dist/command-helpers-DGMvGBeX.mjs.map +1 -0
- package/dist/commands/contract-emit.d.mts.map +1 -1
- package/dist/commands/contract-emit.mjs +1 -1
- package/dist/commands/contract-infer.d.mts.map +1 -1
- package/dist/commands/contract-infer.mjs +1 -1
- package/dist/commands/db-init.d.mts.map +1 -1
- package/dist/commands/db-init.mjs +4 -5
- package/dist/commands/db-init.mjs.map +1 -1
- package/dist/commands/db-schema.d.mts.map +1 -1
- package/dist/commands/db-schema.mjs +3 -3
- package/dist/commands/db-schema.mjs.map +1 -1
- package/dist/commands/db-sign.d.mts.map +1 -1
- package/dist/commands/db-sign.mjs +6 -6
- package/dist/commands/db-sign.mjs.map +1 -1
- package/dist/commands/db-update.d.mts.map +1 -1
- package/dist/commands/db-update.mjs +10 -7
- package/dist/commands/db-update.mjs.map +1 -1
- package/dist/commands/db-verify.d.mts.map +1 -1
- package/dist/commands/db-verify.mjs +1 -1
- package/dist/commands/migrate.d.mts +37 -3
- package/dist/commands/migrate.d.mts.map +1 -1
- package/dist/commands/migrate.mjs +298 -12
- package/dist/commands/migrate.mjs.map +1 -1
- package/dist/commands/migration-check.d.mts +55 -13
- package/dist/commands/migration-check.d.mts.map +1 -1
- package/dist/commands/migration-check.mjs +3 -2
- package/dist/commands/migration-graph.d.mts +17 -8
- package/dist/commands/migration-graph.d.mts.map +1 -1
- package/dist/commands/migration-graph.mjs +185 -2
- package/dist/commands/migration-graph.mjs.map +1 -0
- package/dist/commands/migration-list.d.mts +26 -27
- package/dist/commands/migration-list.d.mts.map +1 -1
- package/dist/commands/migration-list.mjs +2 -190
- package/dist/commands/migration-log.d.mts +9 -19
- package/dist/commands/migration-log.d.mts.map +1 -1
- package/dist/commands/migration-log.mjs +1 -137
- package/dist/commands/migration-new.d.mts.map +1 -1
- package/dist/commands/migration-new.mjs +6 -5
- package/dist/commands/migration-new.mjs.map +1 -1
- package/dist/commands/migration-plan.d.mts +1 -1
- package/dist/commands/migration-plan.d.mts.map +1 -1
- package/dist/commands/migration-plan.mjs +1 -1
- package/dist/commands/migration-show.d.mts +17 -21
- package/dist/commands/migration-show.d.mts.map +1 -1
- package/dist/commands/migration-show.mjs +24 -36
- package/dist/commands/migration-show.mjs.map +1 -1
- package/dist/commands/migration-status.d.mts +42 -144
- package/dist/commands/migration-status.d.mts.map +1 -1
- package/dist/commands/migration-status.mjs +3 -759
- package/dist/commands/ref.d.mts +1 -1
- package/dist/commands/ref.d.mts.map +1 -1
- package/dist/commands/ref.mjs +4 -4
- package/dist/commands/ref.mjs.map +1 -1
- package/dist/commands/telemetry/index.d.mts +7 -0
- package/dist/commands/telemetry/index.d.mts.map +1 -0
- package/dist/commands/telemetry/index.mjs +2 -0
- package/dist/{config-loader-B6sJjXTv.mjs → config-loader-p9JMrekQ.mjs} +1 -1
- package/dist/{config-loader-B6sJjXTv.mjs.map → config-loader-p9JMrekQ.mjs.map} +1 -1
- package/dist/config-loader.mjs +1 -1
- package/dist/{contract-at-errors-BxP-TOMl.mjs → contract-at-errors-CFXsstzm.mjs} +2 -2
- package/dist/{contract-at-errors-BxP-TOMl.mjs.map → contract-at-errors-CFXsstzm.mjs.map} +1 -1
- package/dist/{contract-emit-DxcGl4Uq.mjs → contract-emit-B_qriF8B.mjs} +5 -5
- package/dist/{contract-emit-DxcGl4Uq.mjs.map → contract-emit-B_qriF8B.mjs.map} +1 -1
- package/dist/{contract-emit-D-4jrNve.mjs → contract-emit-C8HmtboH.mjs} +12 -7
- package/dist/contract-emit-C8HmtboH.mjs.map +1 -0
- package/dist/{contract-enrichment-a0V5Y_mL.mjs → contract-enrichment-gn9sWbPw.mjs} +1 -1
- package/dist/{contract-enrichment-a0V5Y_mL.mjs.map → contract-enrichment-gn9sWbPw.mjs.map} +1 -1
- package/dist/{contract-infer-D8uEbJuu.mjs → contract-infer-BYT_ra_U.mjs} +5 -5
- package/dist/contract-infer-BYT_ra_U.mjs.map +1 -0
- package/dist/{contract-space-aggregate-loader-DvZwdkrr.mjs → contract-space-aggregate-loader-ClI1KN6d.mjs} +5 -5
- package/dist/{contract-space-aggregate-loader-DvZwdkrr.mjs.map → contract-space-aggregate-loader-ClI1KN6d.mjs.map} +1 -1
- package/dist/{db-verify-v_vUKXTU.mjs → db-verify-C24FKhb7.mjs} +6 -6
- package/dist/{db-verify-v_vUKXTU.mjs.map → db-verify-C24FKhb7.mjs.map} +1 -1
- package/dist/exports/control-api.d.mts +5 -3
- package/dist/exports/control-api.d.mts.map +1 -1
- package/dist/exports/control-api.mjs +3 -3
- package/dist/exports/index.mjs +1 -1
- package/dist/exports/index.mjs.map +1 -1
- package/dist/exports/init-output.d.mts +1 -3
- package/dist/exports/init-output.d.mts.map +1 -1
- package/dist/exports/init-output.mjs +1 -1
- package/dist/{extension-pack-inputs-IDvjRCi3.mjs → extension-pack-inputs-1ySHqxKG.mjs} +1 -1
- package/dist/{extension-pack-inputs-IDvjRCi3.mjs.map → extension-pack-inputs-1ySHqxKG.mjs.map} +1 -1
- package/dist/{framework-components-fYXjz_in.mjs → framework-components-YVQHhPH7.mjs} +2 -2
- package/dist/{framework-components-fYXjz_in.mjs.map → framework-components-YVQHhPH7.mjs.map} +1 -1
- package/dist/{global-flags-DEHjV8_s.d.mts → global-flags-BpoOYtNZ.d.mts} +1 -1
- package/dist/{global-flags-DEHjV8_s.d.mts.map → global-flags-BpoOYtNZ.d.mts.map} +1 -1
- package/dist/{init-Cv9UzWL5.mjs → init-0HwB-Vh8.mjs} +5 -58
- package/dist/init-0HwB-Vh8.mjs.map +1 -0
- package/dist/{inspect-live-schema-C6ohV_oQ.mjs → inspect-live-schema-DF6IwcDl.mjs} +7 -5
- package/dist/inspect-live-schema-DF6IwcDl.mjs.map +1 -0
- package/dist/migration-check-soB5uZEQ.mjs +573 -0
- package/dist/migration-check-soB5uZEQ.mjs.map +1 -0
- package/dist/migration-cli.mjs +1 -1
- package/dist/migration-cli.mjs.map +1 -1
- package/dist/{migration-command-scaffold-CjvwO6at.mjs → migration-command-scaffold-DA-Lhx6o.mjs} +5 -5
- package/dist/{migration-command-scaffold-CjvwO6at.mjs.map → migration-command-scaffold-DA-Lhx6o.mjs.map} +1 -1
- package/dist/migration-graph-command-render-CEez7YUK.mjs +1960 -0
- package/dist/migration-graph-command-render-CEez7YUK.mjs.map +1 -0
- package/dist/migration-list-DlJJ_38Z.mjs +230 -0
- package/dist/migration-list-DlJJ_38Z.mjs.map +1 -0
- package/dist/migration-log-CG0qQAFm.mjs +222 -0
- package/dist/migration-log-CG0qQAFm.mjs.map +1 -0
- package/dist/migration-path-target-Ce6OZImp.mjs +38 -0
- package/dist/migration-path-target-Ce6OZImp.mjs.map +1 -0
- package/dist/{migration-plan-9DJ7q7_z.mjs → migration-plan-z5Ing-TD.mjs} +9 -8
- package/dist/migration-plan-z5Ing-TD.mjs.map +1 -0
- package/dist/migration-status-CgWSoI_g.mjs +446 -0
- package/dist/migration-status-CgWSoI_g.mjs.map +1 -0
- package/dist/{output-B60Gw5fu.mjs → output-mEQ74_nd.mjs} +1 -1
- package/dist/{output-B60Gw5fu.mjs.map → output-mEQ74_nd.mjs.map} +1 -1
- package/dist/{progress-adapter-C644QK8l.mjs → progress-adapter-CjAeTxY_.mjs} +1 -1
- package/dist/{progress-adapter-C644QK8l.mjs.map → progress-adapter-CjAeTxY_.mjs.map} +1 -1
- package/dist/{ref-advancement-DUZqsue6.mjs → ref-advancement-BkXlikCA.mjs} +1 -1
- package/dist/{ref-advancement-DUZqsue6.mjs.map → ref-advancement-BkXlikCA.mjs.map} +1 -1
- package/dist/schemas-CeGMYFYX.d.mts +191 -0
- package/dist/schemas-CeGMYFYX.d.mts.map +1 -0
- package/dist/schemas-KhXMzNA_.mjs +112 -0
- package/dist/schemas-KhXMzNA_.mjs.map +1 -0
- package/dist/telemetry-BIM4beEO.mjs +122 -0
- package/dist/telemetry-BIM4beEO.mjs.map +1 -0
- package/dist/{terminal-ui-5Y6mrg93.d.mts → terminal-ui-DGRNFWna.d.mts} +1 -1
- package/dist/terminal-ui-DGRNFWna.d.mts.map +1 -0
- package/dist/{types-Dt_SfqFm.d.mts → types-C_tYiJYx.d.mts} +53 -31
- package/dist/types-C_tYiJYx.d.mts.map +1 -0
- package/dist/{verify-DCA9Sldu.mjs → verify-DcOYZ1tH.mjs} +2 -2
- package/dist/{verify-DCA9Sldu.mjs.map → verify-DcOYZ1tH.mjs.map} +1 -1
- package/package.json +26 -22
- package/src/cli.ts +5 -0
- package/src/commands/contract-infer.ts +2 -2
- package/src/commands/db-update.ts +7 -1
- package/src/commands/init/index.ts +6 -35
- package/src/commands/init/init.ts +1 -14
- package/src/commands/init/inputs.ts +0 -75
- package/src/commands/inspect-live-schema.ts +10 -0
- package/src/commands/json/schemas.ts +195 -0
- package/src/commands/migrate.ts +527 -8
- package/src/commands/migration-check.ts +469 -134
- package/src/commands/migration-graph.ts +164 -91
- package/src/commands/migration-list.ts +72 -39
- package/src/commands/migration-log.ts +52 -102
- package/src/commands/migration-new.ts +2 -1
- package/src/commands/migration-plan.ts +2 -1
- package/src/commands/migration-show.ts +31 -66
- package/src/commands/migration-status-overlay.ts +61 -0
- package/src/commands/migration-status.ts +458 -1066
- package/src/commands/telemetry/index.ts +107 -0
- package/src/commands/telemetry/status.ts +67 -0
- package/src/control-api/client.ts +70 -9
- package/src/control-api/operations/contract-emit.ts +22 -2
- package/src/control-api/operations/db-init.ts +6 -3
- package/src/control-api/operations/{db-apply.ts → db-run.ts} +55 -14
- package/src/control-api/operations/db-update.ts +7 -4
- package/src/control-api/operations/db-verify.ts +15 -5
- package/src/control-api/operations/{migration-apply.ts → migrate.ts} +181 -80
- package/src/control-api/operations/{apply.ts → run-migration.ts} +33 -27
- package/src/control-api/types.ts +56 -29
- package/src/utils/cli-errors.ts +70 -2
- package/src/utils/formatters/errors.ts +11 -0
- package/src/utils/formatters/migration-graph-command-render.ts +239 -0
- package/src/utils/formatters/migration-graph-grid-layout.ts +1134 -0
- package/src/utils/formatters/migration-graph-labels.ts +408 -0
- package/src/utils/formatters/migration-graph-model.ts +103 -0
- package/src/utils/formatters/migration-graph-occlusion-render.ts +258 -0
- package/src/utils/formatters/migration-graph-rows.ts +128 -15
- package/src/utils/formatters/migration-graph-space-render.ts +188 -0
- package/src/utils/formatters/migration-list-data-column.ts +4 -91
- package/src/utils/formatters/migration-list-graph-topology.ts +72 -94
- package/src/utils/formatters/migration-list-render.ts +135 -71
- package/src/utils/formatters/migration-list-styler.ts +46 -5
- package/src/utils/formatters/migration-list-types.ts +5 -21
- package/src/utils/formatters/migration-log-table.ts +205 -0
- package/src/utils/formatters/migrations.ts +33 -11
- package/src/utils/global-flags.ts +35 -0
- package/src/utils/integrity-violation-to-check-failure.ts +28 -19
- package/src/utils/legend.ts +38 -0
- package/src/utils/migration-path-target.ts +60 -0
- package/src/utils/telemetry.ts +68 -32
- package/dist/client-KgJorIvG.mjs.map +0 -1
- package/dist/command-helpers-Bbw1GbwL.mjs.map +0 -1
- package/dist/commands/migration-list.mjs.map +0 -1
- package/dist/commands/migration-log.mjs.map +0 -1
- package/dist/commands/migration-status.mjs.map +0 -1
- package/dist/contract-emit-D-4jrNve.mjs.map +0 -1
- package/dist/contract-infer-D8uEbJuu.mjs.map +0 -1
- package/dist/graph-render-rFAqZujX.mjs +0 -1081
- package/dist/graph-render-rFAqZujX.mjs.map +0 -1
- package/dist/init-Cv9UzWL5.mjs.map +0 -1
- package/dist/inspect-live-schema-C6ohV_oQ.mjs.map +0 -1
- package/dist/migration-check-BiBJoYYW.mjs +0 -341
- package/dist/migration-check-BiBJoYYW.mjs.map +0 -1
- package/dist/migration-graph-D7DVUElV.mjs +0 -1232
- package/dist/migration-graph-D7DVUElV.mjs.map +0 -1
- package/dist/migration-list-styler-BRwF4-gy.mjs +0 -399
- package/dist/migration-list-styler-BRwF4-gy.mjs.map +0 -1
- package/dist/migration-plan-9DJ7q7_z.mjs.map +0 -1
- package/dist/migration-types-D2FW63pr.d.mts +0 -15
- package/dist/migration-types-D2FW63pr.d.mts.map +0 -1
- package/dist/migrations-Cv2jxNNK.mjs +0 -228
- package/dist/migrations-Cv2jxNNK.mjs.map +0 -1
- package/dist/terminal-ui-5Y6mrg93.d.mts.map +0 -1
- package/dist/types-Dt_SfqFm.d.mts.map +0 -1
- package/src/utils/formatters/graph-migration-mapper.ts +0 -235
- package/src/utils/formatters/graph-render.ts +0 -1323
- package/src/utils/formatters/graph-types.ts +0 -120
- package/src/utils/formatters/migration-graph-layout.ts +0 -1119
- package/src/utils/formatters/migration-graph-tree-render.ts +0 -459
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-row label formatting for the command graph renderer.
|
|
3
|
+
*
|
|
4
|
+
* The command graph renderer ({@link renderMigrationGraphCommand}) derives the
|
|
5
|
+
* graph structure — rows, gutter, lane colours — from the grid pipeline. The
|
|
6
|
+
* per-row LABEL (contract hash + markers + refs for node rows;
|
|
7
|
+
* migration name + `from → to` + ops/status/will-run for migration rows) is
|
|
8
|
+
* formatted here. This module owns ONLY label text + styling; it knows nothing
|
|
9
|
+
* about lanes, gutters, or grid geometry.
|
|
10
|
+
*
|
|
11
|
+
* The label format (hash abbreviation, `from → to` arrow column, `@contract`/
|
|
12
|
+
* `@db` markers, `(refs)`, ops/status/will-run suffix, the legend) is the same
|
|
13
|
+
* as the previous renderer — that part was never the bug.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';
|
|
17
|
+
import { bold, createColors, green, yellow } from 'colorette';
|
|
18
|
+
import type { GlyphMode } from '../glyph-mode';
|
|
19
|
+
import { laneColorizer } from './migration-graph-occlusion-render';
|
|
20
|
+
import type { ClassifiedEdge } from './migration-graph-rows';
|
|
21
|
+
import {
|
|
22
|
+
MIGRATION_LIST_HASH_WIDTH,
|
|
23
|
+
migrationListEmptySource,
|
|
24
|
+
migrationListForwardArrow,
|
|
25
|
+
padFromHashColumn,
|
|
26
|
+
} from './migration-list-data-column';
|
|
27
|
+
import type { MigrationEdgeKind } from './migration-list-graph-topology';
|
|
28
|
+
import type { MigrationListStyler } from './migration-list-render';
|
|
29
|
+
import {
|
|
30
|
+
CONTRACT_MARKER_NAME,
|
|
31
|
+
createAnsiMigrationListStyler,
|
|
32
|
+
formatContractNodeOverlays,
|
|
33
|
+
} from './migration-list-styler';
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The live-database overlay marker. Just another ref as far as styling goes —
|
|
37
|
+
* the only emphasized markers are the active ref and the `contract`
|
|
38
|
+
* desired-state marker (see {@link CONTRACT_MARKER_NAME}).
|
|
39
|
+
*/
|
|
40
|
+
const DB_MARKER_NAME = 'db';
|
|
41
|
+
|
|
42
|
+
export interface MigrationEdgeAnnotation {
|
|
43
|
+
readonly status?: 'applied' | 'pending';
|
|
44
|
+
readonly operationCount?: number;
|
|
45
|
+
readonly invariants?: readonly string[];
|
|
46
|
+
/**
|
|
47
|
+
* Path-highlight annotation for `migrate --show` preview.
|
|
48
|
+
* - `'on-path'`: migration is on the chosen path; rendered in bright green.
|
|
49
|
+
* - `'off-path'`: migration is off the chosen path; fully drawn but dim grey.
|
|
50
|
+
*/
|
|
51
|
+
readonly pathHighlight?: 'on-path' | 'off-path';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Inputs that drive label formatting. A subset of the command renderer's
|
|
56
|
+
* options — everything the label functions read.
|
|
57
|
+
*/
|
|
58
|
+
export interface MigrationGraphLabelOptions {
|
|
59
|
+
readonly refsByHash?: ReadonlyMap<string, readonly string[]>;
|
|
60
|
+
readonly edgeAnnotationsByHash?: ReadonlyMap<string, MigrationEdgeAnnotation>;
|
|
61
|
+
readonly dbHash?: string;
|
|
62
|
+
readonly contractHash?: string;
|
|
63
|
+
readonly isAppSpace?: boolean;
|
|
64
|
+
readonly activeRefName?: string;
|
|
65
|
+
readonly hashLength?: number;
|
|
66
|
+
readonly colorize: boolean;
|
|
67
|
+
readonly glyphMode?: GlyphMode;
|
|
68
|
+
readonly styler?: MigrationListStyler;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Forced-color functions that always emit ANSI regardless of the ambient TTY
|
|
73
|
+
* environment (NO_COLOR, piped output). Used so on-path green / off-path dim are
|
|
74
|
+
* deterministically emitted in tests that request colour while NO_COLOR is set.
|
|
75
|
+
*/
|
|
76
|
+
const { dim: forcedDim } = createColors({ useColor: true });
|
|
77
|
+
const { greenBright: forcedGreen } = createColors({ useColor: true });
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The two label styles used in `migrate --show` path-highlight mode.
|
|
81
|
+
*
|
|
82
|
+
* - `onPath`: bold name, neutral hashes (the on-path lane glyphs are coloured
|
|
83
|
+
* green by the grid renderer, not here).
|
|
84
|
+
* - `offPath`: uniform dim grey on the name and the whole hash column.
|
|
85
|
+
*
|
|
86
|
+
* To change the on-path / off-path label colour in future, edit this object.
|
|
87
|
+
*/
|
|
88
|
+
export const PATH_HIGHLIGHT_STYLES = {
|
|
89
|
+
onPath: (_style: MigrationListStyler, colorize: boolean) => ({
|
|
90
|
+
lane: colorize ? forcedGreen : (text: string) => text,
|
|
91
|
+
arrow: (text: string) => text,
|
|
92
|
+
dirName: (text: string) => bold(text),
|
|
93
|
+
hashOverride: undefined,
|
|
94
|
+
}),
|
|
95
|
+
offPath: (colorize: boolean) => ({
|
|
96
|
+
lane: colorize ? forcedDim : (text: string) => text,
|
|
97
|
+
arrow: colorize ? forcedDim : (text: string) => text,
|
|
98
|
+
dirName: colorize ? forcedDim : (text: string) => text,
|
|
99
|
+
hashOverride: colorize ? forcedDim : undefined,
|
|
100
|
+
}),
|
|
101
|
+
} as const;
|
|
102
|
+
|
|
103
|
+
function abbreviateHash(hash: string, hashLength: number, emptySource: string): string {
|
|
104
|
+
if (hash === EMPTY_CONTRACT_HASH) {
|
|
105
|
+
return emptySource;
|
|
106
|
+
}
|
|
107
|
+
const stripped = hash.startsWith('sha256:') ? hash.slice(7) : hash;
|
|
108
|
+
return stripped.slice(0, hashLength);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
interface ContractOverlayNames {
|
|
112
|
+
readonly markers: readonly string[];
|
|
113
|
+
readonly refs: readonly string[];
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function overlayNamesForContract(
|
|
117
|
+
contractHash: string,
|
|
118
|
+
opts: MigrationGraphLabelOptions,
|
|
119
|
+
): ContractOverlayNames {
|
|
120
|
+
const markers: string[] = [];
|
|
121
|
+
const refs: string[] = [];
|
|
122
|
+
const userRefs = opts.refsByHash?.get(contractHash);
|
|
123
|
+
if (userRefs) {
|
|
124
|
+
refs.push(...[...userRefs].sort((a, b) => a.localeCompare(b)));
|
|
125
|
+
}
|
|
126
|
+
if (
|
|
127
|
+
opts.isAppSpace !== false &&
|
|
128
|
+
opts.contractHash === contractHash &&
|
|
129
|
+
contractHash !== EMPTY_CONTRACT_HASH
|
|
130
|
+
) {
|
|
131
|
+
markers.push(CONTRACT_MARKER_NAME);
|
|
132
|
+
}
|
|
133
|
+
if (opts.dbHash === contractHash) {
|
|
134
|
+
markers.push(DB_MARKER_NAME);
|
|
135
|
+
}
|
|
136
|
+
markers.sort((a, b) => {
|
|
137
|
+
if (a === CONTRACT_MARKER_NAME) return -1;
|
|
138
|
+
if (b === CONTRACT_MARKER_NAME) return 1;
|
|
139
|
+
return a.localeCompare(b);
|
|
140
|
+
});
|
|
141
|
+
return { markers, refs };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function createLabelStyler(opts: MigrationGraphLabelOptions): MigrationListStyler {
|
|
145
|
+
const base = opts.styler ?? createAnsiMigrationListStyler({ useColor: opts.colorize });
|
|
146
|
+
const activeRefName = opts.activeRefName;
|
|
147
|
+
if (!opts.colorize || activeRefName === undefined) {
|
|
148
|
+
return base;
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
...base,
|
|
152
|
+
refs: (names) => {
|
|
153
|
+
const styledNames = names.map((name) => (name === activeRefName ? bold(name) : name));
|
|
154
|
+
return base.refs(styledNames);
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function overlayStatusGlyphs(mode: GlyphMode): {
|
|
160
|
+
readonly applied: string;
|
|
161
|
+
readonly pending: string;
|
|
162
|
+
} {
|
|
163
|
+
return mode === 'ascii' ? { applied: '+', pending: '>' } : { applied: '✓', pending: '⧗' };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function formatEdgeAnnotationSuffix(
|
|
167
|
+
migrationHash: string,
|
|
168
|
+
opts: MigrationGraphLabelOptions,
|
|
169
|
+
style: MigrationListStyler,
|
|
170
|
+
): string {
|
|
171
|
+
const annotation = opts.edgeAnnotationsByHash?.get(migrationHash);
|
|
172
|
+
if (annotation === undefined) {
|
|
173
|
+
return '';
|
|
174
|
+
}
|
|
175
|
+
const isOffPath = annotation.pathHighlight === 'off-path';
|
|
176
|
+
const segments: string[] = [];
|
|
177
|
+
if (annotation.operationCount !== undefined) {
|
|
178
|
+
segments.push(`${annotation.operationCount} ops`);
|
|
179
|
+
}
|
|
180
|
+
if (annotation.invariants !== undefined && annotation.invariants.length > 0) {
|
|
181
|
+
segments.push(style.invariants(annotation.invariants));
|
|
182
|
+
}
|
|
183
|
+
const status = annotation.status;
|
|
184
|
+
if (status !== undefined) {
|
|
185
|
+
const glyphs = overlayStatusGlyphs(opts.glyphMode ?? 'unicode');
|
|
186
|
+
const glyph = status === 'applied' ? glyphs.applied : glyphs.pending;
|
|
187
|
+
const label = status === 'applied' ? 'applied' : 'pending';
|
|
188
|
+
if (!opts.colorize) {
|
|
189
|
+
segments.push(`${glyph} ${label}`);
|
|
190
|
+
} else {
|
|
191
|
+
const styler = status === 'applied' ? green : yellow;
|
|
192
|
+
segments.push(styler(`${glyph} ${label}`));
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (annotation.pathHighlight === 'on-path') {
|
|
196
|
+
const glyph = opts.glyphMode === 'ascii' ? '>' : '↑';
|
|
197
|
+
segments.push(`${glyph} will run`);
|
|
198
|
+
}
|
|
199
|
+
if (segments.length === 0) {
|
|
200
|
+
return '';
|
|
201
|
+
}
|
|
202
|
+
const suffix = ` ${segments.join(' ')}`;
|
|
203
|
+
return opts.colorize && isOffPath ? forcedDim(suffix) : suffix;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Format the `from → to` hash data column for an edge row.
|
|
208
|
+
*
|
|
209
|
+
* When `hashOverride` is provided (off-path → `dim`), it replaces ALL sub-stylers
|
|
210
|
+
* so dim reaches every character without inner ANSI codes overriding it.
|
|
211
|
+
*/
|
|
212
|
+
function formatEdgeHashColumn(
|
|
213
|
+
edge: ClassifiedEdge,
|
|
214
|
+
style: MigrationListStyler,
|
|
215
|
+
hashLength: number,
|
|
216
|
+
glyphMode: GlyphMode,
|
|
217
|
+
hashOverride?: (text: string) => string,
|
|
218
|
+
): string {
|
|
219
|
+
const emptySource = migrationListEmptySource(glyphMode);
|
|
220
|
+
const forwardArrow = migrationListForwardArrow(glyphMode);
|
|
221
|
+
const src = hashOverride ?? style.sourceHash;
|
|
222
|
+
const dst = hashOverride ?? style.destHash;
|
|
223
|
+
const glyph = hashOverride ?? style.glyph;
|
|
224
|
+
if (edge.kind === 'self') {
|
|
225
|
+
const hash = abbreviateHash(edge.from, hashLength, emptySource);
|
|
226
|
+
const source = padFromHashColumn(src(hash), hashLength);
|
|
227
|
+
return `${source} ${glyph(forwardArrow)} ${dst(hash)}`;
|
|
228
|
+
}
|
|
229
|
+
const source =
|
|
230
|
+
edge.from === EMPTY_CONTRACT_HASH
|
|
231
|
+
? padFromHashColumn(glyph(emptySource), hashLength)
|
|
232
|
+
: padFromHashColumn(src(abbreviateHash(edge.from, hashLength, emptySource)), hashLength);
|
|
233
|
+
const arrow = glyph(forwardArrow);
|
|
234
|
+
const dest = dst(abbreviateHash(edge.to, hashLength, emptySource));
|
|
235
|
+
return `${source} ${arrow} ${dest}`;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// ---------------------------------------------------------------------------
|
|
239
|
+
// Public label builders used by the command renderer.
|
|
240
|
+
// ---------------------------------------------------------------------------
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* The label text for a contract node row: the abbreviated hash (or the `∅`
|
|
244
|
+
* empty-source token for the baseline) followed by its `@contract`/`@db` markers
|
|
245
|
+
* and `(refs)`, with two spaces between the hash and the overlay block.
|
|
246
|
+
*/
|
|
247
|
+
export function formatNodeLabel(
|
|
248
|
+
contractHash: string,
|
|
249
|
+
opts: MigrationGraphLabelOptions,
|
|
250
|
+
nodeHighlight?: 'on-path' | 'off-path' | undefined,
|
|
251
|
+
): string {
|
|
252
|
+
const style = createLabelStyler(opts);
|
|
253
|
+
const hashLength = opts.hashLength ?? MIGRATION_LIST_HASH_WIDTH;
|
|
254
|
+
const emptySource = migrationListEmptySource(opts.glyphMode ?? 'unicode');
|
|
255
|
+
const overlays = overlayNamesForContract(contractHash, opts);
|
|
256
|
+
const hasOverlays = overlays.markers.length > 0 || overlays.refs.length > 0;
|
|
257
|
+
const offPath = nodeHighlight === 'off-path' && opts.colorize;
|
|
258
|
+
// The baseline's label is the ∅ empty-source token (the gutter draws ○ for
|
|
259
|
+
// every node, including the baseline); a real contract's label is its hash.
|
|
260
|
+
const hashText =
|
|
261
|
+
contractHash === EMPTY_CONTRACT_HASH
|
|
262
|
+
? (offPath ? forcedDim : style.glyph)(emptySource)
|
|
263
|
+
: (offPath ? forcedDim : style.sourceHash)(
|
|
264
|
+
abbreviateHash(contractHash, hashLength, emptySource),
|
|
265
|
+
);
|
|
266
|
+
if (!hasOverlays) return hashText;
|
|
267
|
+
const overlay = formatContractNodeOverlays(style, overlays.markers, overlays.refs);
|
|
268
|
+
return `${hashText} ${overlay}`;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* The label text for a migration row: the migration name (padded to
|
|
273
|
+
* `dirNameWidth`) followed by the `from → to` hash column and the annotation
|
|
274
|
+
* suffix (ops / status / will-run).
|
|
275
|
+
*
|
|
276
|
+
* In flat mode the name is tinted with its lane's hue (`lane` ≥ 0), so the node
|
|
277
|
+
* `○`, the edges/arrows in the gutter, and the name all read in one colour. In
|
|
278
|
+
* focus mode the on-path/off-path role overrides the lane hue (bold / dim).
|
|
279
|
+
*/
|
|
280
|
+
export function formatMigrationLabel(
|
|
281
|
+
edge: ClassifiedEdge,
|
|
282
|
+
dirNameWidth: number,
|
|
283
|
+
opts: MigrationGraphLabelOptions,
|
|
284
|
+
lane?: number,
|
|
285
|
+
): string {
|
|
286
|
+
const style = createLabelStyler(opts);
|
|
287
|
+
const hashLength = opts.hashLength ?? MIGRATION_LIST_HASH_WIDTH;
|
|
288
|
+
const glyphMode = opts.glyphMode ?? 'unicode';
|
|
289
|
+
const highlight = opts.edgeAnnotationsByHash?.get(edge.migrationHash)?.pathHighlight;
|
|
290
|
+
|
|
291
|
+
let dirNameStyler: (text: string) => string;
|
|
292
|
+
let hashOverride: ((text: string) => string) | undefined;
|
|
293
|
+
if (highlight === 'on-path') {
|
|
294
|
+
// On-path: tint the name with the on-path green (matching the route's green
|
|
295
|
+
// glyphs in the gutter), not bolded.
|
|
296
|
+
dirNameStyler = opts.colorize ? forcedGreen : (text) => text;
|
|
297
|
+
hashOverride = undefined;
|
|
298
|
+
} else if (highlight === 'off-path') {
|
|
299
|
+
dirNameStyler = opts.colorize ? forcedDim : style.dirName;
|
|
300
|
+
hashOverride = opts.colorize ? forcedDim : undefined;
|
|
301
|
+
} else if (opts.colorize && lane !== undefined) {
|
|
302
|
+
// Flat mode: tint the name with the lane hue (matching the lane's
|
|
303
|
+
// node/edge/arrow colour in the gutter), not bolded.
|
|
304
|
+
dirNameStyler = (text) => laneColorizer(lane)(text);
|
|
305
|
+
hashOverride = undefined;
|
|
306
|
+
} else {
|
|
307
|
+
dirNameStyler = style.dirName;
|
|
308
|
+
hashOverride = undefined;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const dirNamePadding = ' '.repeat(Math.max(0, dirNameWidth - edge.dirName.length));
|
|
312
|
+
const dirName = `${dirNameStyler(edge.dirName)}${dirNamePadding}`;
|
|
313
|
+
const hashColumn = formatEdgeHashColumn(edge, style, hashLength, glyphMode, hashOverride);
|
|
314
|
+
const annotationSuffix = formatEdgeAnnotationSuffix(edge.migrationHash, opts, style);
|
|
315
|
+
return `${dirName}${hashColumn}${annotationSuffix}`;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Format a single on-path migration row for the `migrate --show` run-list.
|
|
320
|
+
* Shares PATH_HIGHLIGHT_STYLES.onPath with the graph tree so the run-list and
|
|
321
|
+
* the graph are byte-for-byte identical in their name/hash columns.
|
|
322
|
+
*/
|
|
323
|
+
export function formatOnPathMigrationRow(
|
|
324
|
+
dirName: string,
|
|
325
|
+
from: string,
|
|
326
|
+
to: string,
|
|
327
|
+
dirNameWidth: number,
|
|
328
|
+
colorize: boolean,
|
|
329
|
+
glyphMode: GlyphMode,
|
|
330
|
+
): string {
|
|
331
|
+
const style = createAnsiMigrationListStyler({ useColor: colorize });
|
|
332
|
+
const s = PATH_HIGHLIGHT_STYLES.onPath(style, colorize);
|
|
333
|
+
const styledDirName = `${s.dirName(dirName)}${' '.repeat(Math.max(0, dirNameWidth - dirName.length))}`;
|
|
334
|
+
const hashLength = MIGRATION_LIST_HASH_WIDTH;
|
|
335
|
+
const emptySource = migrationListEmptySource(glyphMode);
|
|
336
|
+
const forwardArrow = migrationListForwardArrow(glyphMode);
|
|
337
|
+
const fromAbbr =
|
|
338
|
+
from === EMPTY_CONTRACT_HASH
|
|
339
|
+
? padFromHashColumn(style.glyph(emptySource), hashLength)
|
|
340
|
+
: padFromHashColumn(
|
|
341
|
+
style.sourceHash(abbreviateHash(from, hashLength, emptySource)),
|
|
342
|
+
hashLength,
|
|
343
|
+
);
|
|
344
|
+
const toAbbr =
|
|
345
|
+
to === EMPTY_CONTRACT_HASH
|
|
346
|
+
? style.glyph(emptySource)
|
|
347
|
+
: style.destHash(abbreviateHash(to, hashLength, emptySource));
|
|
348
|
+
const arrow = style.glyph(forwardArrow);
|
|
349
|
+
return `${styledDirName} ${fromAbbr} ${arrow} ${toAbbr}`;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
export interface RenderMigrationGraphLegendOptions {
|
|
353
|
+
readonly colorize: boolean;
|
|
354
|
+
readonly glyphMode?: GlyphMode;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function legendGlyphs(mode: GlyphMode): {
|
|
358
|
+
readonly node: string;
|
|
359
|
+
readonly forward: string;
|
|
360
|
+
readonly rollback: string;
|
|
361
|
+
readonly self: string;
|
|
362
|
+
} {
|
|
363
|
+
return mode === 'ascii'
|
|
364
|
+
? { node: '*', forward: '^', rollback: 'v', self: '@' }
|
|
365
|
+
: { node: '○', forward: '↑', rollback: '↓', self: '⟲' };
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function formatLegendExampleMarkers(colorize: boolean): string {
|
|
369
|
+
if (!colorize) {
|
|
370
|
+
return '@contract @db';
|
|
371
|
+
}
|
|
372
|
+
const sigil = green('@');
|
|
373
|
+
return `${sigil + bold(green('contract'))} ${sigil}${green('db')}`;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* A compact key for the tree visual language: the contract node glyph, the
|
|
378
|
+
* in-lane direction arrows, the empty baseline, the system-marker `@…` and
|
|
379
|
+
* user-ref `(…)` conventions, and a worked sample of the data-column hash arrow.
|
|
380
|
+
*/
|
|
381
|
+
export function renderMigrationGraphLegend(opts: RenderMigrationGraphLegendOptions): string {
|
|
382
|
+
const glyphMode = opts.glyphMode ?? 'unicode';
|
|
383
|
+
const style = createAnsiMigrationListStyler({ useColor: opts.colorize });
|
|
384
|
+
const glyphs = legendGlyphs(glyphMode);
|
|
385
|
+
const emptySource = migrationListEmptySource(glyphMode);
|
|
386
|
+
const forwardArrow = migrationListForwardArrow(glyphMode);
|
|
387
|
+
const sampleArrow = `${style.sourceHash('aaaaaa')} ${style.glyph(forwardArrow)} ${style.destHash('bbbbbb')}`;
|
|
388
|
+
const statusGlyphs = overlayStatusGlyphs(glyphMode);
|
|
389
|
+
const appliedPending = opts.colorize
|
|
390
|
+
? ` ${green(statusGlyphs.applied)} ${style.summary('applied')} ${yellow(statusGlyphs.pending)} ${style.summary('pending')}`
|
|
391
|
+
: ` ${statusGlyphs.applied} ${style.summary('applied')} ${statusGlyphs.pending} ${style.summary('pending')}`;
|
|
392
|
+
const exampleMarkers = formatLegendExampleMarkers(opts.colorize);
|
|
393
|
+
const exampleRefs = opts.colorize ? style.refs(['prod', 'staging']) : '(prod, staging)';
|
|
394
|
+
const lines = [
|
|
395
|
+
'Legend:',
|
|
396
|
+
` ${style.kind(glyphs.node)} ${style.summary('contract')} ${style.kind(glyphs.forward)} ${style.summary('forward')} ${style.kind(glyphs.rollback)} ${style.summary('rollback')}`,
|
|
397
|
+
` ${style.kind(glyphs.self)} ${style.summary('migration without schema change')}`,
|
|
398
|
+
appliedPending,
|
|
399
|
+
` ${style.kind(emptySource)} ${style.summary('empty database (baseline)')}`,
|
|
400
|
+
` ${exampleMarkers} ${style.summary('reserved markers — also typeable as --from/--to tokens')}`,
|
|
401
|
+
` ${exampleRefs} ${style.summary('user-defined refs')}`,
|
|
402
|
+
` ${sampleArrow} ${style.summary('migration from contract aaaaaa to bbbbbb')}`,
|
|
403
|
+
];
|
|
404
|
+
return lines.join('\n');
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Re-export the edge kind type alias for downstream label callers.
|
|
408
|
+
export type { MigrationEdgeKind };
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data structures for the line/plane/occlusion migration-graph renderer.
|
|
3
|
+
*
|
|
4
|
+
* A _line_ is the primitive. Each migration edge becomes a routed line that
|
|
5
|
+
* carries its identity. Cells hold an ordered (z) set of lines; the topmost
|
|
6
|
+
* plane wins and is drawn; lower planes are occluded.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Directions — which arms a line occupies in a cell.
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
export type Direction = 'up' | 'down' | 'left' | 'right';
|
|
13
|
+
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// PathRole — whether this line is on-path or off-path in focus mode.
|
|
16
|
+
// In flat mode all lines have role = undefined.
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
export type PathRole = 'on-path' | 'off-path';
|
|
19
|
+
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// LineRef — identity carried into every cell the line touches.
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
export interface LineRef {
|
|
24
|
+
readonly migrationHash: string;
|
|
25
|
+
readonly dirName: string;
|
|
26
|
+
readonly lane: number;
|
|
27
|
+
readonly role: PathRole | undefined;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// NodeRef — identity of a contract node in the grid.
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
export interface NodeRef {
|
|
34
|
+
readonly contractHash: string;
|
|
35
|
+
readonly isEmpty: boolean;
|
|
36
|
+
/** Lane index; used to pick the node's colour in flat mode. */
|
|
37
|
+
readonly lane: number;
|
|
38
|
+
/** In focus mode: 'on-path' (green) or 'off-path' (dim). undefined in flat mode. */
|
|
39
|
+
readonly role: PathRole | undefined;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// CellLine — one line's presence in one cell.
|
|
44
|
+
//
|
|
45
|
+
// `selfLoop` marks a self-edge (a migration whose from === to). It renders as
|
|
46
|
+
// the ⟲ glyph and is modelled separately from `directions` because a self-loop
|
|
47
|
+
// is not one of the four cardinal arms — `Direction` stays honestly up|down|
|
|
48
|
+
// left|right.
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
export interface CellLine {
|
|
51
|
+
readonly line: LineRef;
|
|
52
|
+
readonly directions: ReadonlySet<Direction>;
|
|
53
|
+
readonly plane: number;
|
|
54
|
+
readonly selfLoop?: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Marks the arrowhead where a routed back-arc lands into its target node
|
|
57
|
+
* (the connector cell immediately right of the node). Renders as `◂` instead
|
|
58
|
+
* of the box-drawing glyph for its directions.
|
|
59
|
+
*/
|
|
60
|
+
readonly landingArrow?: boolean;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
// Cell — one position in the grid.
|
|
65
|
+
//
|
|
66
|
+
// `separator` marks an inter-component blank-line row. When the first cell in
|
|
67
|
+
// a row has `separator: true`, the row renders as an empty line (the renderGrid
|
|
68
|
+
// blank-separator pass-through).
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
export interface Cell {
|
|
71
|
+
readonly node?: NodeRef;
|
|
72
|
+
readonly lines: readonly CellLine[];
|
|
73
|
+
readonly separator?: boolean;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
// Grid — the full rendered layout (rows × columns, row 0 = top of display).
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
export type Grid = readonly (readonly Cell[])[];
|
|
80
|
+
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
// GridOptions — configurable geometry.
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
|
|
85
|
+
/** Default number of grid columns allocated per lane (one rail + one connector). */
|
|
86
|
+
export const DEFAULT_COLS_PER_LANE = 2;
|
|
87
|
+
|
|
88
|
+
export interface GridOptions {
|
|
89
|
+
readonly colsPerLane?: number;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
// Highlight — focus-mode input.
|
|
94
|
+
//
|
|
95
|
+
// `flat` (the default) → trunk-on-top z-order, lane-rotation colour.
|
|
96
|
+
// `focus` → on-path-on-top z-order; the migration names in `onPath` are the
|
|
97
|
+
// chosen route. Lines whose migration is in `onPath` get role 'on-path' (green,
|
|
98
|
+
// continuous, topmost plane); every other line is 'off-path' (dim, yields).
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
export interface Highlight {
|
|
101
|
+
readonly mode: 'flat' | 'focus';
|
|
102
|
+
readonly onPath: ReadonlySet<string>;
|
|
103
|
+
}
|