prisma-next 0.11.0-dev.6 → 0.11.0-dev.61
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 +9 -10
- package/dist/cli.mjs.map +1 -1
- package/dist/{client-oXO2WCPD.mjs → client-CD3om3R0.mjs} +44 -24
- package/dist/client-CD3om3R0.mjs.map +1 -0
- package/dist/{command-helpers-DtavI0wJ.mjs → command-helpers-CoceqqMl.mjs} +642 -45
- package/dist/command-helpers-CoceqqMl.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 +32 -7
- 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 -4
- 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 +12 -10
- 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 +41 -11
- 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 +5 -1
- package/dist/commands/migrate.d.mts.map +1 -1
- package/dist/commands/migrate.mjs +75 -40
- package/dist/commands/migrate.mjs.map +1 -1
- package/dist/commands/migration-check.d.mts +4 -3
- package/dist/commands/migration-check.d.mts.map +1 -1
- package/dist/commands/migration-check.mjs +1 -280
- package/dist/commands/migration-graph.d.mts +11 -2
- package/dist/commands/migration-graph.d.mts.map +1 -1
- package/dist/commands/migration-graph.mjs +15 -30
- package/dist/commands/migration-graph.mjs.map +1 -1
- package/dist/commands/migration-list.d.mts +66 -4
- package/dist/commands/migration-list.d.mts.map +1 -1
- package/dist/commands/migration-list.mjs +2 -103
- package/dist/commands/migration-log.d.mts +10 -1
- package/dist/commands/migration-log.d.mts.map +1 -1
- package/dist/commands/migration-log.mjs +10 -15
- package/dist/commands/migration-log.mjs.map +1 -1
- package/dist/commands/migration-new.d.mts.map +1 -1
- package/dist/commands/migration-new.mjs +32 -38
- package/dist/commands/migration-new.mjs.map +1 -1
- package/dist/commands/migration-plan.d.mts +2 -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 +4 -55
- package/dist/commands/migration-show.d.mts.map +1 -1
- package/dist/commands/migration-show.mjs +61 -153
- package/dist/commands/migration-show.mjs.map +1 -1
- package/dist/commands/migration-status.d.mts +5 -40
- package/dist/commands/migration-status.d.mts.map +1 -1
- package/dist/commands/migration-status.mjs +81 -76
- package/dist/commands/migration-status.mjs.map +1 -1
- package/dist/commands/ref.d.mts +1 -1
- package/dist/commands/ref.d.mts.map +1 -1
- package/dist/commands/ref.mjs +38 -10
- package/dist/commands/ref.mjs.map +1 -1
- package/dist/config-loader-B6sJjXTv.mjs.map +1 -1
- package/dist/config-loader.d.mts.map +1 -1
- package/dist/contract-at-errors-Bhf2jnkp.mjs +42 -0
- package/dist/contract-at-errors-Bhf2jnkp.mjs.map +1 -0
- package/dist/{contract-emit-CmsklifJ.mjs → contract-emit-C47r1loe.mjs} +4 -6
- package/dist/{contract-emit-CmsklifJ.mjs.map → contract-emit-C47r1loe.mjs.map} +1 -1
- package/dist/{contract-emit-o-8VmdQX.mjs → contract-emit-DxEfEc-M.mjs} +21 -7
- package/dist/{contract-emit-o-8VmdQX.mjs.map → contract-emit-DxEfEc-M.mjs.map} +1 -1
- package/dist/{contract-enrichment-Dani0mMW.mjs → contract-enrichment-a0V5Y_mL.mjs} +4 -25
- package/dist/contract-enrichment-a0V5Y_mL.mjs.map +1 -0
- package/dist/{contract-infer-pKkiCt7C.mjs → contract-infer-B5EhTqdR.mjs} +3 -4
- package/dist/{contract-infer-pKkiCt7C.mjs.map → contract-infer-B5EhTqdR.mjs.map} +1 -1
- package/dist/contract-space-aggregate-loader-lafgkTwG.mjs +247 -0
- package/dist/contract-space-aggregate-loader-lafgkTwG.mjs.map +1 -0
- package/dist/{db-verify-AoIUriL4.mjs → db-verify-B7fbkGnp.mjs} +5 -7
- package/dist/{db-verify-AoIUriL4.mjs.map → db-verify-B7fbkGnp.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 +3 -3
- package/dist/exports/index.d.mts.map +1 -1
- package/dist/exports/index.mjs +1 -1
- package/dist/exports/index.mjs.map +1 -1
- 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 +62 -0
- package/dist/extension-pack-inputs-IDvjRCi3.mjs.map +1 -0
- package/dist/{framework-components-65gOHkHB.mjs → framework-components-R_O3y5IW.mjs} +2 -2
- package/dist/{framework-components-65gOHkHB.mjs.map → framework-components-R_O3y5IW.mjs.map} +1 -1
- package/dist/global-flags-2SPgqWma.d.mts +34 -0
- package/dist/global-flags-2SPgqWma.d.mts.map +1 -0
- package/dist/{graph-render-DJVv0_uf.mjs → graph-render-rFAqZujX.mjs} +2 -2
- package/dist/{graph-render-DJVv0_uf.mjs.map → graph-render-rFAqZujX.mjs.map} +1 -1
- package/dist/{init-Db5Itt5r.mjs → init-BQNpPozW.mjs} +4 -5
- package/dist/{init-Db5Itt5r.mjs.map → init-BQNpPozW.mjs.map} +1 -1
- package/dist/{inspect-live-schema-LeWvkZVz.mjs → inspect-live-schema-CQJnuPgD.mjs} +4 -5
- package/dist/{inspect-live-schema-LeWvkZVz.mjs.map → inspect-live-schema-CQJnuPgD.mjs.map} +1 -1
- package/dist/migration-check-CKfQlAWR.mjs +341 -0
- package/dist/migration-check-CKfQlAWR.mjs.map +1 -0
- package/dist/migration-cli.d.mts.map +1 -1
- package/dist/migration-cli.mjs +4 -4
- package/dist/migration-cli.mjs.map +1 -1
- package/dist/{migration-command-scaffold-BtkunvFQ.mjs → migration-command-scaffold-CE931d1-.mjs} +4 -5
- package/dist/{migration-command-scaffold-BtkunvFQ.mjs.map → migration-command-scaffold-CE931d1-.mjs.map} +1 -1
- package/dist/migration-list-A3bJ4j5e.mjs +780 -0
- package/dist/migration-list-A3bJ4j5e.mjs.map +1 -0
- package/dist/{migration-plan-C2jeH1J5.mjs → migration-plan-ZZm8C0s-.mjs} +372 -133
- package/dist/migration-plan-ZZm8C0s-.mjs.map +1 -0
- package/dist/{migration-types-BXWvz12q.d.mts → migration-types-q64xAI_J.d.mts} +1 -1
- package/dist/{migration-types-BXWvz12q.d.mts.map → migration-types-q64xAI_J.d.mts.map} +1 -1
- package/dist/{migrations-CwZMa1Ck.mjs → migrations-CjO1DsYe.mjs} +12 -13
- package/dist/migrations-CjO1DsYe.mjs.map +1 -0
- package/dist/{output-BlsrGMEF.mjs → output-DEg3SSnJ.mjs} +1 -1
- package/dist/{output-BlsrGMEF.mjs.map → output-DEg3SSnJ.mjs.map} +1 -1
- package/dist/{progress-adapter-DFfvZcYL.mjs → progress-adapter-C644QK8l.mjs} +1 -1
- package/dist/{progress-adapter-DFfvZcYL.mjs.map → progress-adapter-C644QK8l.mjs.map} +1 -1
- package/dist/ref-advancement-DUZqsue6.mjs +50 -0
- package/dist/ref-advancement-DUZqsue6.mjs.map +1 -0
- package/dist/terminal-ui-sLZt2cxc.d.mts +133 -0
- package/dist/terminal-ui-sLZt2cxc.d.mts.map +1 -0
- package/dist/{types-C9FfXb1l.d.mts → types-DK-ge7eR.d.mts} +5 -11
- package/dist/types-DK-ge7eR.d.mts.map +1 -0
- package/dist/{verify-Bom75OYI.mjs → verify-vl983Ed-.mjs} +2 -2
- package/dist/{verify-Bom75OYI.mjs.map → verify-vl983Ed-.mjs.map} +1 -1
- package/package.json +11 -11
- package/dist/cli-errors-Czmx92Zy.d.mts +0 -3
- package/dist/cli-errors-Djtz98Vm.mjs +0 -71
- package/dist/cli-errors-Djtz98Vm.mjs.map +0 -1
- package/dist/client-oXO2WCPD.mjs.map +0 -1
- package/dist/command-helpers-DtavI0wJ.mjs.map +0 -1
- package/dist/commands/migration-check.mjs.map +0 -1
- package/dist/commands/migration-list.mjs.map +0 -1
- package/dist/contract-enrichment-Dani0mMW.mjs.map +0 -1
- package/dist/contract-space-aggregate-loader-BmNQwlws.mjs +0 -160
- package/dist/contract-space-aggregate-loader-BmNQwlws.mjs.map +0 -1
- package/dist/global-flags-CdE7M0d9.d.mts +0 -15
- package/dist/global-flags-CdE7M0d9.d.mts.map +0 -1
- package/dist/migration-plan-C2jeH1J5.mjs.map +0 -1
- package/dist/migrations-CwZMa1Ck.mjs.map +0 -1
- package/dist/rolldown-runtime-twds-ZHy.mjs +0 -14
- package/dist/terminal-ui-BiB_8KNo.mjs +0 -379
- package/dist/terminal-ui-BiB_8KNo.mjs.map +0 -1
- package/dist/types-C9FfXb1l.d.mts.map +0 -1
|
@@ -0,0 +1,780 @@
|
|
|
1
|
+
import { t as loadConfig } from "./config-loader-B6sJjXTv.mjs";
|
|
2
|
+
import { B as errorInvalidSpaceId, Q as errorSpaceNotFound, T as formatStyledHeader, _ as createTerminalUI, d as setCommandSeeAlso, g as parseGlobalFlagsOrExit, l as setCommandDescriptions, s as resolveMigrationPaths, t as addGlobalOptions, u as setCommandExamples, y as handleResult } from "./command-helpers-CoceqqMl.mjs";
|
|
3
|
+
import { r as buildReadAggregate } from "./contract-space-aggregate-loader-lafgkTwG.mjs";
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
import { ifDefined } from "@prisma-next/utils/defined";
|
|
6
|
+
import { notOk, ok } from "@prisma-next/utils/result";
|
|
7
|
+
import { APP_SPACE_ID, RESERVED_SPACE_SUBDIR_NAMES, isValidSpaceId, listContractSpaceDirectories } from "@prisma-next/migration-tools/spaces";
|
|
8
|
+
import { bold, cyan, cyanBright, dim, green, greenBright, yellow } from "colorette";
|
|
9
|
+
import { EMPTY_CONTRACT_HASH } from "@prisma-next/migration-tools/constants";
|
|
10
|
+
import { HEAD_REF_NAME, refsByContractHash } from "@prisma-next/migration-tools/refs";
|
|
11
|
+
const MIGRATION_LIST_UNICODE_KIND_GLYPH = {
|
|
12
|
+
forward: "*",
|
|
13
|
+
rollback: "↩",
|
|
14
|
+
self: "⟲"
|
|
15
|
+
};
|
|
16
|
+
const MIGRATION_LIST_ASCII_KIND_GLYPH = {
|
|
17
|
+
forward: "*",
|
|
18
|
+
rollback: "<",
|
|
19
|
+
self: "~"
|
|
20
|
+
};
|
|
21
|
+
function migrationListKindGlyph(glyphMode, edgeKind) {
|
|
22
|
+
return glyphMode === "ascii" ? MIGRATION_LIST_ASCII_KIND_GLYPH[edgeKind] : MIGRATION_LIST_UNICODE_KIND_GLYPH[edgeKind];
|
|
23
|
+
}
|
|
24
|
+
function migrationListForwardArrow(glyphMode) {
|
|
25
|
+
return glyphMode === "ascii" ? "->" : "→";
|
|
26
|
+
}
|
|
27
|
+
function migrationListEmptySource(glyphMode) {
|
|
28
|
+
return glyphMode === "ascii" ? "-" : "∅";
|
|
29
|
+
}
|
|
30
|
+
function abbreviateContractHash(hash) {
|
|
31
|
+
return (hash.startsWith("sha256:") ? hash.slice(7) : hash).slice(0, 7);
|
|
32
|
+
}
|
|
33
|
+
function computeMigrationDirNameWidth(migrations) {
|
|
34
|
+
if (migrations.length === 0) return 0;
|
|
35
|
+
return Math.max(...migrations.map((entry) => entry.dirName.length)) + 2;
|
|
36
|
+
}
|
|
37
|
+
function formatSourceColumn(from, style, emptySource) {
|
|
38
|
+
if (from === null) return style.glyph(emptySource) + " ".repeat(7 - emptySource.length);
|
|
39
|
+
return style.sourceHash(abbreviateContractHash(from));
|
|
40
|
+
}
|
|
41
|
+
function formatDecorations(providedInvariants, refs, style) {
|
|
42
|
+
const blocks = [];
|
|
43
|
+
if (providedInvariants.length > 0) blocks.push(style.invariants(providedInvariants));
|
|
44
|
+
if (refs.length > 0) blocks.push(style.refs(refs));
|
|
45
|
+
if (blocks.length === 0) return "";
|
|
46
|
+
return ` ${blocks.join(" ")}`;
|
|
47
|
+
}
|
|
48
|
+
function formatMigrationDataColumn(migration, options) {
|
|
49
|
+
const { dirNameWidth, edgeKind, style, forwardArrow = "→", emptySource = "∅" } = options;
|
|
50
|
+
const dirNamePadding = " ".repeat(Math.max(0, dirNameWidth - migration.dirName.length));
|
|
51
|
+
const dirName = `${style.dirName(migration.dirName)}${dirNamePadding}`;
|
|
52
|
+
const decorations = formatDecorations(migration.providedInvariants, migration.refs, style);
|
|
53
|
+
if (edgeKind === "self") {
|
|
54
|
+
const contractHash = migration.from ?? migration.to;
|
|
55
|
+
return `${dirName}${style.sourceHash(abbreviateContractHash(contractHash))}${decorations}`;
|
|
56
|
+
}
|
|
57
|
+
return `${dirName}${formatSourceColumn(migration.from, style, emptySource)} ${style.glyph(forwardArrow)} ${style.destHash(abbreviateContractHash(migration.to))}${decorations}`;
|
|
58
|
+
}
|
|
59
|
+
function formatNodeLineDataColumn(contractHash, style) {
|
|
60
|
+
return style.sourceHash(abbreviateContractHash(contractHash));
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
//#region src/utils/formatters/migration-list-graph-topology.ts
|
|
64
|
+
function canonicalFrom$1(from) {
|
|
65
|
+
return from ?? EMPTY_CONTRACT_HASH;
|
|
66
|
+
}
|
|
67
|
+
function compareDirNameDesc(a, b) {
|
|
68
|
+
return b.dirName.localeCompare(a.dirName);
|
|
69
|
+
}
|
|
70
|
+
function bumpDegree(map, key) {
|
|
71
|
+
map.set(key, (map.get(key) ?? 0) + 1);
|
|
72
|
+
}
|
|
73
|
+
function classifyMigrationListGraphTopology(entries) {
|
|
74
|
+
const nodes = /* @__PURE__ */ new Set();
|
|
75
|
+
const kindByMigrationHash = /* @__PURE__ */ new Map();
|
|
76
|
+
const outgoingByFrom = /* @__PURE__ */ new Map();
|
|
77
|
+
for (const entry of entries) {
|
|
78
|
+
const from = canonicalFrom$1(entry.from);
|
|
79
|
+
const to = entry.to;
|
|
80
|
+
nodes.add(from);
|
|
81
|
+
nodes.add(to);
|
|
82
|
+
if (from === to) {
|
|
83
|
+
kindByMigrationHash.set(entry.migrationHash, "self");
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
const bucket = outgoingByFrom.get(from);
|
|
87
|
+
const edge = {
|
|
88
|
+
entry,
|
|
89
|
+
from,
|
|
90
|
+
to
|
|
91
|
+
};
|
|
92
|
+
if (bucket) bucket.push(edge);
|
|
93
|
+
else outgoingByFrom.set(from, [edge]);
|
|
94
|
+
}
|
|
95
|
+
for (const bucket of outgoingByFrom.values()) bucket.sort((a, b) => compareDirNameDesc(a.entry, b.entry));
|
|
96
|
+
const nonSelfInDegree = /* @__PURE__ */ new Map();
|
|
97
|
+
for (const node of nodes) nonSelfInDegree.set(node, 0);
|
|
98
|
+
for (const bucket of outgoingByFrom.values()) for (const edge of bucket) bumpDegree(nonSelfInDegree, edge.to);
|
|
99
|
+
const dfsRoots = [];
|
|
100
|
+
for (const node of nodes) if ((nonSelfInDegree.get(node) ?? 0) === 0) dfsRoots.push(node);
|
|
101
|
+
dfsRoots.sort((a, b) => {
|
|
102
|
+
if (a === EMPTY_CONTRACT_HASH) return -1;
|
|
103
|
+
if (b === EMPTY_CONTRACT_HASH) return 1;
|
|
104
|
+
return a.localeCompare(b);
|
|
105
|
+
});
|
|
106
|
+
if (dfsRoots.length === 0) dfsRoots.push(...[...nodes].sort((a, b) => a.localeCompare(b)));
|
|
107
|
+
const WHITE = 0;
|
|
108
|
+
const GRAY = 1;
|
|
109
|
+
const BLACK = 2;
|
|
110
|
+
const color = /* @__PURE__ */ new Map();
|
|
111
|
+
for (const node of nodes) color.set(node, WHITE);
|
|
112
|
+
const stack = [];
|
|
113
|
+
function pushFrame(node) {
|
|
114
|
+
color.set(node, GRAY);
|
|
115
|
+
stack.push({
|
|
116
|
+
node,
|
|
117
|
+
outgoing: outgoingByFrom.get(node) ?? [],
|
|
118
|
+
index: 0
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
function runDfsFrom(root) {
|
|
122
|
+
if (color.get(root) !== WHITE) return;
|
|
123
|
+
pushFrame(root);
|
|
124
|
+
while (stack.length > 0) {
|
|
125
|
+
const frame = stack[stack.length - 1];
|
|
126
|
+
if (frame === void 0) break;
|
|
127
|
+
if (frame.index >= frame.outgoing.length) {
|
|
128
|
+
color.set(frame.node, BLACK);
|
|
129
|
+
stack.pop();
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
const edge = frame.outgoing[frame.index];
|
|
133
|
+
frame.index += 1;
|
|
134
|
+
if (edge === void 0) continue;
|
|
135
|
+
const v = edge.to;
|
|
136
|
+
const vColor = color.get(v);
|
|
137
|
+
if (vColor === GRAY) kindByMigrationHash.set(edge.entry.migrationHash, "rollback");
|
|
138
|
+
else {
|
|
139
|
+
kindByMigrationHash.set(edge.entry.migrationHash, "forward");
|
|
140
|
+
if (vColor === WHITE) pushFrame(v);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
for (const root of dfsRoots) runDfsFrom(root);
|
|
145
|
+
const remainingWhite = [...nodes].filter((node) => color.get(node) === WHITE);
|
|
146
|
+
remainingWhite.sort((a, b) => a.localeCompare(b));
|
|
147
|
+
for (const root of remainingWhite) runDfsFrom(root);
|
|
148
|
+
const forwardInDegree = /* @__PURE__ */ new Map();
|
|
149
|
+
const forwardOutDegree = /* @__PURE__ */ new Map();
|
|
150
|
+
for (const entry of entries) {
|
|
151
|
+
if (kindByMigrationHash.get(entry.migrationHash) !== "forward") continue;
|
|
152
|
+
const from = canonicalFrom$1(entry.from);
|
|
153
|
+
const to = entry.to;
|
|
154
|
+
bumpDegree(forwardOutDegree, from);
|
|
155
|
+
bumpDegree(forwardInDegree, to);
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
kindByMigrationHash,
|
|
159
|
+
forwardInDegree,
|
|
160
|
+
forwardOutDegree
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
//#endregion
|
|
164
|
+
//#region src/utils/formatters/migration-list-graph-layout.ts
|
|
165
|
+
function canonicalFrom(from) {
|
|
166
|
+
return from ?? EMPTY_CONTRACT_HASH;
|
|
167
|
+
}
|
|
168
|
+
function forwardInDegree(topology, hash) {
|
|
169
|
+
return topology.forwardInDegree.get(hash) ?? 0;
|
|
170
|
+
}
|
|
171
|
+
function forwardOutDegree(topology, hash) {
|
|
172
|
+
return topology.forwardOutDegree.get(hash) ?? 0;
|
|
173
|
+
}
|
|
174
|
+
function buildForwardProducersByTo(entries, kindByMigrationHash) {
|
|
175
|
+
const byTo = /* @__PURE__ */ new Map();
|
|
176
|
+
for (const entry of entries) {
|
|
177
|
+
if (kindByMigrationHash.get(entry.migrationHash) !== "forward") continue;
|
|
178
|
+
const bucket = byTo.get(entry.to);
|
|
179
|
+
if (bucket) bucket.push(entry);
|
|
180
|
+
else byTo.set(entry.to, [entry]);
|
|
181
|
+
}
|
|
182
|
+
return byTo;
|
|
183
|
+
}
|
|
184
|
+
function countForwardProducersTo(forwardProducersByTo, contract) {
|
|
185
|
+
return forwardProducersByTo.get(contract)?.length ?? 0;
|
|
186
|
+
}
|
|
187
|
+
function hasLaterForwardDepartingFrom(entries, startIndex, contract, kindByMigrationHash) {
|
|
188
|
+
for (let index = startIndex + 1; index < entries.length; index++) {
|
|
189
|
+
const later = entries[index];
|
|
190
|
+
if (later === void 0) continue;
|
|
191
|
+
if (kindByMigrationHash.get(later.migrationHash) !== "forward") continue;
|
|
192
|
+
if (canonicalFrom(later.from) === contract) return true;
|
|
193
|
+
}
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
function computeMigrationListGraphLayout(entries, topology = classifyMigrationListGraphTopology(entries)) {
|
|
197
|
+
const { kindByMigrationHash } = topology;
|
|
198
|
+
const forwardProducersByTo = buildForwardProducersByTo(entries, kindByMigrationHash);
|
|
199
|
+
const convergencesEmitted = /* @__PURE__ */ new Set();
|
|
200
|
+
const producerLaneByHash = /* @__PURE__ */ new Map();
|
|
201
|
+
const lanes = [];
|
|
202
|
+
const rows = [];
|
|
203
|
+
function emitNodeLine(contractHash) {
|
|
204
|
+
rows.push({
|
|
205
|
+
kind: "nodeLine",
|
|
206
|
+
contractHash,
|
|
207
|
+
laneIndex: 0
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
function emitConnector(connectorKind, contractHash, startLane, endLane, branchCount) {
|
|
211
|
+
rows.push({
|
|
212
|
+
kind: "connector",
|
|
213
|
+
connectorKind,
|
|
214
|
+
contractHash,
|
|
215
|
+
startLane,
|
|
216
|
+
endLane,
|
|
217
|
+
branchCount
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
function activeLaneIndices() {
|
|
221
|
+
const indices = [];
|
|
222
|
+
for (let index = 0; index < lanes.length; index++) if (lanes[index]?.active) indices.push(index);
|
|
223
|
+
return indices;
|
|
224
|
+
}
|
|
225
|
+
function lanesWanting(contract) {
|
|
226
|
+
const indices = [];
|
|
227
|
+
for (let index = 0; index < lanes.length; index++) {
|
|
228
|
+
const lane = lanes[index];
|
|
229
|
+
if (lane?.active && lane.want === contract) indices.push(index);
|
|
230
|
+
}
|
|
231
|
+
return indices;
|
|
232
|
+
}
|
|
233
|
+
function ensureLane(index) {
|
|
234
|
+
while (lanes.length <= index) lanes.push({
|
|
235
|
+
want: "",
|
|
236
|
+
active: false
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
function openLaneAtRight(want) {
|
|
240
|
+
const index = lanes.length;
|
|
241
|
+
lanes.push({
|
|
242
|
+
want,
|
|
243
|
+
active: true
|
|
244
|
+
});
|
|
245
|
+
return index;
|
|
246
|
+
}
|
|
247
|
+
function closeLane(index) {
|
|
248
|
+
ensureLane(index);
|
|
249
|
+
const lane = lanes[index];
|
|
250
|
+
if (lane) lane.active = false;
|
|
251
|
+
}
|
|
252
|
+
function emitJoinAbove(contractHash, laneIndices) {
|
|
253
|
+
if (laneIndices.length < 2) return;
|
|
254
|
+
const startLane = Math.min(...laneIndices);
|
|
255
|
+
emitConnector("joinAbove", contractHash, startLane, Math.max(...laneIndices), laneIndices.length);
|
|
256
|
+
for (const index of laneIndices) if (index !== startLane) closeLane(index);
|
|
257
|
+
}
|
|
258
|
+
function emitConvergencePreamble(contract) {
|
|
259
|
+
if (convergencesEmitted.has(contract)) return;
|
|
260
|
+
if (forwardInDegree(topology, contract) < 2) return;
|
|
261
|
+
const consumersWanting = lanesWanting(contract);
|
|
262
|
+
if (forwardOutDegree(topology, contract) >= 2 && consumersWanting.length >= 2) emitJoinAbove(contract, consumersWanting);
|
|
263
|
+
emitNodeLine(contract);
|
|
264
|
+
const producers = forwardProducersByTo.get(contract) ?? [];
|
|
265
|
+
if (producers.length >= 2) emitConnector("fanBelow", contract, 0, producers.length - 1, producers.length);
|
|
266
|
+
for (const [producerIndex, producer] of producers.entries()) {
|
|
267
|
+
ensureLane(producerIndex);
|
|
268
|
+
lanes[producerIndex] = {
|
|
269
|
+
want: canonicalFrom(producer.from),
|
|
270
|
+
active: true
|
|
271
|
+
};
|
|
272
|
+
producerLaneByHash.set(producer.migrationHash, producerIndex);
|
|
273
|
+
}
|
|
274
|
+
convergencesEmitted.add(contract);
|
|
275
|
+
}
|
|
276
|
+
function placeWoven(entry, edgeKind, laneIndex) {
|
|
277
|
+
const passThroughLanes = activeLaneIndices().filter((index) => index !== laneIndex);
|
|
278
|
+
rows.push({
|
|
279
|
+
kind: "migration",
|
|
280
|
+
entry,
|
|
281
|
+
edgeKind,
|
|
282
|
+
laneIndex,
|
|
283
|
+
passThroughLanes,
|
|
284
|
+
woven: true
|
|
285
|
+
});
|
|
286
|
+
ensureLane(laneIndex);
|
|
287
|
+
lanes[laneIndex] = {
|
|
288
|
+
want: canonicalFrom(entry.from),
|
|
289
|
+
active: true
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
function placeUnwoven(entry, edgeKind) {
|
|
293
|
+
const passThroughLanes = activeLaneIndices();
|
|
294
|
+
const laneIndex = passThroughLanes.length === 0 ? 0 : Math.max(...passThroughLanes) + 1;
|
|
295
|
+
rows.push({
|
|
296
|
+
kind: "migration",
|
|
297
|
+
entry,
|
|
298
|
+
edgeKind,
|
|
299
|
+
laneIndex,
|
|
300
|
+
passThroughLanes,
|
|
301
|
+
woven: false
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
for (let entryIndex = 0; entryIndex < entries.length; entryIndex++) {
|
|
305
|
+
const entry = entries[entryIndex];
|
|
306
|
+
const edgeKind = kindByMigrationHash.get(entry.migrationHash) ?? "forward";
|
|
307
|
+
const to = entry.to;
|
|
308
|
+
if (edgeKind !== "forward") {
|
|
309
|
+
placeUnwoven(entry, edgeKind);
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
if (forwardInDegree(topology, to) >= 2 && !convergencesEmitted.has(to)) emitConvergencePreamble(to);
|
|
313
|
+
const presetLane = producerLaneByHash.get(entry.migrationHash);
|
|
314
|
+
const wantingTo = lanesWanting(to);
|
|
315
|
+
if (wantingTo.length >= 2 && countForwardProducersTo(forwardProducersByTo, to) === 1) emitJoinAbove(to, wantingTo);
|
|
316
|
+
if (presetLane !== void 0) {
|
|
317
|
+
placeWoven(entry, edgeKind, presetLane);
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
const firstWanting = wantingTo[0];
|
|
321
|
+
if (firstWanting !== void 0) {
|
|
322
|
+
placeWoven(entry, edgeKind, firstWanting);
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
if (hasLaterForwardDepartingFrom(entries, entryIndex, to, kindByMigrationHash)) {
|
|
326
|
+
placeUnwoven(entry, edgeKind);
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
placeWoven(entry, edgeKind, openLaneAtRight(canonicalFrom(entry.from)));
|
|
330
|
+
}
|
|
331
|
+
return { rows };
|
|
332
|
+
}
|
|
333
|
+
//#endregion
|
|
334
|
+
//#region src/utils/formatters/migration-list-graph-render.ts
|
|
335
|
+
const UNICODE_PALETTE = {
|
|
336
|
+
lane: "│",
|
|
337
|
+
node: "o",
|
|
338
|
+
forwardArrow: migrationListForwardArrow("unicode"),
|
|
339
|
+
emptySource: migrationListEmptySource("unicode"),
|
|
340
|
+
kind: MIGRATION_LIST_UNICODE_KIND_GLYPH,
|
|
341
|
+
fanBelow: (branchCount) => branchCount === 2 ? "├─┐" : "├─┬─┐",
|
|
342
|
+
joinAbove: (branchCount) => branchCount === 2 ? "├─┘" : "└─┴─┘"
|
|
343
|
+
};
|
|
344
|
+
const ASCII_PALETTE = {
|
|
345
|
+
lane: "|",
|
|
346
|
+
node: "o",
|
|
347
|
+
forwardArrow: migrationListForwardArrow("ascii"),
|
|
348
|
+
emptySource: migrationListEmptySource("ascii"),
|
|
349
|
+
kind: MIGRATION_LIST_ASCII_KIND_GLYPH,
|
|
350
|
+
fanBelow: (branchCount) => branchCount === 2 ? "+-\\" : "+-|-\\",
|
|
351
|
+
joinAbove: (branchCount) => branchCount === 2 ? "+-/" : "/-+-/"
|
|
352
|
+
};
|
|
353
|
+
function paletteFor(mode) {
|
|
354
|
+
return mode === "ascii" ? ASCII_PALETTE : UNICODE_PALETTE;
|
|
355
|
+
}
|
|
356
|
+
function migrationEntries(layout) {
|
|
357
|
+
const entries = [];
|
|
358
|
+
for (const row of layout.rows) if (row.kind === "migration") entries.push(row.entry);
|
|
359
|
+
return entries;
|
|
360
|
+
}
|
|
361
|
+
function layoutMaxLaneIndex(layout) {
|
|
362
|
+
let max = 0;
|
|
363
|
+
for (const row of layout.rows) if (row.kind === "migration") max = Math.max(max, row.laneIndex, ...row.passThroughLanes);
|
|
364
|
+
else if (row.kind === "connector") max = Math.max(max, row.endLane);
|
|
365
|
+
else max = Math.max(max, row.laneIndex);
|
|
366
|
+
return max;
|
|
367
|
+
}
|
|
368
|
+
function laneCell(glyph) {
|
|
369
|
+
return `${glyph} `;
|
|
370
|
+
}
|
|
371
|
+
function emptyLaneCell() {
|
|
372
|
+
return " ";
|
|
373
|
+
}
|
|
374
|
+
function renderMigrationGutter(row, maxLane, palette, style) {
|
|
375
|
+
const cells = [];
|
|
376
|
+
for (let lane = 0; lane <= maxLane; lane++) if (lane === row.laneIndex) cells.push(laneCell(style.kind(palette.kind[row.edgeKind])));
|
|
377
|
+
else if (row.passThroughLanes.includes(lane)) cells.push(laneCell(style.lane(palette.lane)));
|
|
378
|
+
else cells.push(emptyLaneCell());
|
|
379
|
+
return cells.join("");
|
|
380
|
+
}
|
|
381
|
+
function renderNodeLineGutter(row, openLanes, maxLane, palette, style) {
|
|
382
|
+
const cells = [];
|
|
383
|
+
for (let lane = 0; lane <= maxLane; lane++) if (lane === row.laneIndex) cells.push(laneCell(palette.node));
|
|
384
|
+
else if (openLanes.has(lane)) cells.push(laneCell(style.lane(palette.lane)));
|
|
385
|
+
else cells.push(emptyLaneCell());
|
|
386
|
+
return cells.join("");
|
|
387
|
+
}
|
|
388
|
+
function renderConnectorGutter(row, openLanes, maxLane, palette, style) {
|
|
389
|
+
const spanWidth = (row.endLane - row.startLane + 1) * 2;
|
|
390
|
+
let spanGlyph = (row.connectorKind === "fanBelow" ? palette.fanBelow(row.branchCount) : palette.joinAbove(row.branchCount)).padEnd(spanWidth, " ").slice(0, spanWidth);
|
|
391
|
+
if (![...openLanes].some((lane) => lane < row.startLane || lane > row.endLane) && spanGlyph.endsWith(" ")) spanGlyph = spanGlyph.slice(0, -1);
|
|
392
|
+
let gutter = "";
|
|
393
|
+
for (let lane = 0; lane < row.startLane; lane++) gutter += openLanes.has(lane) ? laneCell(style.lane(palette.lane)) : emptyLaneCell();
|
|
394
|
+
gutter += style.lane(spanGlyph);
|
|
395
|
+
for (let lane = row.endLane + 1; lane <= maxLane; lane++) gutter += openLanes.has(lane) ? laneCell(style.lane(palette.lane)) : emptyLaneCell();
|
|
396
|
+
return gutter;
|
|
397
|
+
}
|
|
398
|
+
function advanceOpenLanes(row, openLanes) {
|
|
399
|
+
if (row.kind === "migration") return new Set([row.laneIndex, ...row.passThroughLanes]);
|
|
400
|
+
if (row.kind === "connector") {
|
|
401
|
+
if (row.connectorKind === "fanBelow") {
|
|
402
|
+
const next = new Set(openLanes);
|
|
403
|
+
for (let lane = row.startLane; lane <= row.endLane; lane++) next.add(lane);
|
|
404
|
+
return next;
|
|
405
|
+
}
|
|
406
|
+
const next = new Set(openLanes);
|
|
407
|
+
for (let lane = row.startLane + 1; lane <= row.endLane; lane++) next.delete(lane);
|
|
408
|
+
return next;
|
|
409
|
+
}
|
|
410
|
+
return openLanes;
|
|
411
|
+
}
|
|
412
|
+
function renderMigrationListGraphWithStyle(layout, style, glyphMode) {
|
|
413
|
+
const palette = paletteFor(glyphMode);
|
|
414
|
+
const migrations = migrationEntries(layout);
|
|
415
|
+
const layoutMaxLane = layoutMaxLaneIndex(layout);
|
|
416
|
+
const dirNameWidth = computeMigrationDirNameWidth(migrations);
|
|
417
|
+
const gutterMaxLane = layoutMaxLane;
|
|
418
|
+
const blockDataStart = (layoutMaxLane + 1) * 2;
|
|
419
|
+
const gutterVisibleWidth = (gutterMaxLane + 1) * 2;
|
|
420
|
+
const lines = [];
|
|
421
|
+
let openLanes = /* @__PURE__ */ new Set();
|
|
422
|
+
function padToDataColumn(gutter, dataStart) {
|
|
423
|
+
return gutter + " ".repeat(Math.max(0, dataStart - gutterVisibleWidth));
|
|
424
|
+
}
|
|
425
|
+
for (const row of layout.rows) {
|
|
426
|
+
if (row.kind === "migration") {
|
|
427
|
+
const gutter = renderMigrationGutter(row, gutterMaxLane, palette, style);
|
|
428
|
+
const data = formatMigrationDataColumn(row.entry, {
|
|
429
|
+
dirNameWidth,
|
|
430
|
+
edgeKind: row.edgeKind,
|
|
431
|
+
style,
|
|
432
|
+
forwardArrow: palette.forwardArrow,
|
|
433
|
+
emptySource: palette.emptySource
|
|
434
|
+
});
|
|
435
|
+
lines.push(`${padToDataColumn(gutter, blockDataStart)}${data}`);
|
|
436
|
+
} else if (row.kind === "nodeLine") {
|
|
437
|
+
const gutter = renderNodeLineGutter(row, openLanes, gutterMaxLane, palette, style);
|
|
438
|
+
const data = formatNodeLineDataColumn(row.contractHash, style);
|
|
439
|
+
lines.push(`${padToDataColumn(gutter, blockDataStart)}${data}`);
|
|
440
|
+
} else lines.push(renderConnectorGutter(row, openLanes, gutterMaxLane, palette, style));
|
|
441
|
+
openLanes = advanceOpenLanes(row, openLanes);
|
|
442
|
+
}
|
|
443
|
+
return lines.map((line) => line.trimEnd()).join("\n");
|
|
444
|
+
}
|
|
445
|
+
function formatGraphEmptyStateLine(spaceId, style) {
|
|
446
|
+
return style.emptyState(`There are no migrations in migrations/${spaceId}/ yet`);
|
|
447
|
+
}
|
|
448
|
+
function renderGraphSpaceBlock(spaceId, migrations, multiSpace, style, glyphMode, topology) {
|
|
449
|
+
if (migrations.length === 0) {
|
|
450
|
+
const emptyLine = formatGraphEmptyStateLine(spaceId, style);
|
|
451
|
+
if (!multiSpace) return [emptyLine];
|
|
452
|
+
return [style.spaceHeading(`${spaceId}:`), ` ${emptyLine}`];
|
|
453
|
+
}
|
|
454
|
+
const rows = renderMigrationListGraphWithStyle(computeMigrationListGraphLayout(migrations, topology), style, glyphMode).split("\n");
|
|
455
|
+
if (!multiSpace) return rows;
|
|
456
|
+
return [style.spaceHeading(`${spaceId}:`), ...rows.map((row) => ` ${row}`)];
|
|
457
|
+
}
|
|
458
|
+
function renderMigrationListGraphResult(result, style, glyphMode, topologyBySpaceId) {
|
|
459
|
+
const multiSpace = result.spaces.length > 1;
|
|
460
|
+
const lines = [];
|
|
461
|
+
for (let index = 0; index < result.spaces.length; index++) {
|
|
462
|
+
const space = result.spaces[index];
|
|
463
|
+
if (index > 0) lines.push("");
|
|
464
|
+
const topology = topologyBySpaceId.get(space.spaceId);
|
|
465
|
+
if (topology === void 0) throw new Error(`missing topology for space ${space.spaceId}`);
|
|
466
|
+
lines.push(...renderGraphSpaceBlock(space.spaceId, space.migrations, multiSpace, style, glyphMode, topology));
|
|
467
|
+
}
|
|
468
|
+
if (result.spaces.reduce((count, space) => count + space.migrations.length, 0) > 0) {
|
|
469
|
+
lines.push("");
|
|
470
|
+
lines.push(style.summary(result.summary));
|
|
471
|
+
}
|
|
472
|
+
return lines.join("\n");
|
|
473
|
+
}
|
|
474
|
+
//#endregion
|
|
475
|
+
//#region src/utils/formatters/migration-list-render.ts
|
|
476
|
+
const IDENTITY_MIGRATION_LIST_STYLER = {
|
|
477
|
+
kind: (text) => text,
|
|
478
|
+
dirName: (text) => text,
|
|
479
|
+
sourceHash: (text) => text,
|
|
480
|
+
destHash: (text) => text,
|
|
481
|
+
glyph: (text) => text,
|
|
482
|
+
lane: (text) => text,
|
|
483
|
+
invariants: (ids) => `{${ids.join(", ")}}`,
|
|
484
|
+
refs: (names) => `(${names.join(", ")})`,
|
|
485
|
+
spaceHeading: (text) => text,
|
|
486
|
+
summary: (text) => text,
|
|
487
|
+
emptyState: (text) => text
|
|
488
|
+
};
|
|
489
|
+
function resolveEdgeKind(migrationHash, kindByMigrationHash) {
|
|
490
|
+
return kindByMigrationHash.get(migrationHash) ?? "forward";
|
|
491
|
+
}
|
|
492
|
+
function formatMigrationRow(migration, dirNameWidth, edgeKind, glyphMode, style) {
|
|
493
|
+
return `${`${style.kind(migrationListKindGlyph(glyphMode, edgeKind))} `}${formatMigrationDataColumn(migration, {
|
|
494
|
+
dirNameWidth,
|
|
495
|
+
edgeKind,
|
|
496
|
+
style,
|
|
497
|
+
forwardArrow: migrationListForwardArrow(glyphMode),
|
|
498
|
+
emptySource: migrationListEmptySource(glyphMode)
|
|
499
|
+
})}`;
|
|
500
|
+
}
|
|
501
|
+
function formatEmptyStateLine(spaceId, style) {
|
|
502
|
+
return style.emptyState(`There are no migrations in migrations/${spaceId}/ yet`);
|
|
503
|
+
}
|
|
504
|
+
function renderSpaceBlock(spaceId, migrations, multiSpace, glyphMode, kindByMigrationHash, style) {
|
|
505
|
+
if (migrations.length === 0) {
|
|
506
|
+
const emptyLine = formatEmptyStateLine(spaceId, style);
|
|
507
|
+
if (!multiSpace) return [emptyLine];
|
|
508
|
+
return [style.spaceHeading(`${spaceId}:`), ` ${emptyLine}`];
|
|
509
|
+
}
|
|
510
|
+
const dirNameWidth = computeMigrationDirNameWidth(migrations);
|
|
511
|
+
const rows = migrations.map((entry) => formatMigrationRow(entry, dirNameWidth, resolveEdgeKind(entry.migrationHash, kindByMigrationHash), glyphMode, style));
|
|
512
|
+
if (!multiSpace) return rows;
|
|
513
|
+
return [style.spaceHeading(`${spaceId}:`), ...rows.map((row) => ` ${row}`)];
|
|
514
|
+
}
|
|
515
|
+
function buildMigrationListTopologyBySpace(result) {
|
|
516
|
+
const topologyBySpaceId = /* @__PURE__ */ new Map();
|
|
517
|
+
for (const space of result.spaces) topologyBySpaceId.set(space.spaceId, classifyMigrationListGraphTopology(space.migrations));
|
|
518
|
+
return topologyBySpaceId;
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Compose the styled `migration list` output. The renderer is
|
|
522
|
+
* presentation-neutral — every token passes through `style` before
|
|
523
|
+
* landing in the output, so the same composition serves the pure-text
|
|
524
|
+
* path ({@link renderMigrationList} via
|
|
525
|
+
* {@link IDENTITY_MIGRATION_LIST_STYLER}) and the ANSI-styled CLI path
|
|
526
|
+
* (via the ANSI styler the CLI shell wires up).
|
|
527
|
+
*/
|
|
528
|
+
function renderMigrationListWithStyle(result, style, glyphMode = "unicode", topologyBySpaceId = buildMigrationListTopologyBySpace(result)) {
|
|
529
|
+
const multiSpace = result.spaces.length > 1;
|
|
530
|
+
const lines = [];
|
|
531
|
+
for (let index = 0; index < result.spaces.length; index++) {
|
|
532
|
+
const space = result.spaces[index];
|
|
533
|
+
if (index > 0) lines.push("");
|
|
534
|
+
const kindByMigrationHash = topologyBySpaceId.get(space.spaceId)?.kindByMigrationHash ?? classifyMigrationListGraphTopology(space.migrations).kindByMigrationHash;
|
|
535
|
+
lines.push(...renderSpaceBlock(space.spaceId, space.migrations, multiSpace, glyphMode, kindByMigrationHash, style));
|
|
536
|
+
}
|
|
537
|
+
if (result.spaces.reduce((count, space) => count + space.migrations.length, 0) > 0) {
|
|
538
|
+
lines.push("");
|
|
539
|
+
lines.push(style.summary(result.summary));
|
|
540
|
+
}
|
|
541
|
+
return lines.join("\n");
|
|
542
|
+
}
|
|
543
|
+
//#endregion
|
|
544
|
+
//#region src/utils/formatters/migration-list-styler.ts
|
|
545
|
+
/**
|
|
546
|
+
* The reserved ref name for the live-database marker. Treated as a
|
|
547
|
+
* structurally distinct token from user-named refs so the styler can
|
|
548
|
+
* make it visually pop in `(refs)` decorations.
|
|
549
|
+
*/
|
|
550
|
+
const DB_REF_NAME = "db";
|
|
551
|
+
function styleRefName(name) {
|
|
552
|
+
return name === DB_REF_NAME ? bold(greenBright(name)) : green(name);
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Build a {@link MigrationListStyler} that decorates `migration list`
|
|
556
|
+
* tokens with ANSI SGR codes. When `useColor` is `false` (non-TTY,
|
|
557
|
+
* `--no-color`, `NO_COLOR=1`, piped output) the function returns the
|
|
558
|
+
* shared identity styler so callers get plain text with zero ANSI
|
|
559
|
+
* bytes — pipe-friendly by construction.
|
|
560
|
+
*
|
|
561
|
+
* Palette:
|
|
562
|
+
*
|
|
563
|
+
* - `dirName`: bold
|
|
564
|
+
* - `sourceHash`: dim cyan
|
|
565
|
+
* - `destHash`: bright cyan
|
|
566
|
+
* - `kind` (`*` / `↩` / `⟲`): bright — the signal; lanes and arrows dim
|
|
567
|
+
* - `glyph` (`→` / `⟲` / `∅`): dim
|
|
568
|
+
* - `lane` (graph gutter lines `│` and fan/join connectors `├─┐` / `├─┘`): dim
|
|
569
|
+
* - `invariants` (`{...}`): yellow
|
|
570
|
+
* - `refs` (`(...)`): green; the live-DB `db` marker inside is green-bold
|
|
571
|
+
* - `spaceHeading` (`<spaceId>:`): bold
|
|
572
|
+
* - `summary`: dim
|
|
573
|
+
* - `emptyState`: dim
|
|
574
|
+
*/
|
|
575
|
+
function createAnsiMigrationListStyler(opts) {
|
|
576
|
+
if (!opts.useColor) return IDENTITY_MIGRATION_LIST_STYLER;
|
|
577
|
+
return {
|
|
578
|
+
kind: (text) => text,
|
|
579
|
+
dirName: (text) => bold(text),
|
|
580
|
+
sourceHash: (text) => dim(cyan(text)),
|
|
581
|
+
destHash: (text) => cyanBright(text),
|
|
582
|
+
glyph: (text) => dim(text),
|
|
583
|
+
lane: (text) => dim(text),
|
|
584
|
+
invariants: (ids) => yellow(`{${ids.join(", ")}}`),
|
|
585
|
+
refs: (names) => {
|
|
586
|
+
const open = green("(");
|
|
587
|
+
const close = green(")");
|
|
588
|
+
const separator = green(", ");
|
|
589
|
+
return open + names.map(styleRefName).join(separator) + close;
|
|
590
|
+
},
|
|
591
|
+
spaceHeading: (text) => bold(text),
|
|
592
|
+
summary: (text) => dim(text),
|
|
593
|
+
emptyState: (text) => dim(text)
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
//#endregion
|
|
597
|
+
//#region src/commands/migration-list.ts
|
|
598
|
+
function compareSpaceIds(a, b) {
|
|
599
|
+
if (a === APP_SPACE_ID) return b === APP_SPACE_ID ? 0 : -1;
|
|
600
|
+
if (b === APP_SPACE_ID) return 1;
|
|
601
|
+
if (a < b) return -1;
|
|
602
|
+
if (a > b) return 1;
|
|
603
|
+
return 0;
|
|
604
|
+
}
|
|
605
|
+
function compareDirNamesDescending(a, b) {
|
|
606
|
+
if (a.dirName < b.dirName) return 1;
|
|
607
|
+
if (a.dirName > b.dirName) return -1;
|
|
608
|
+
return 0;
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Ref names decorating a space's destination contract hashes. The
|
|
612
|
+
* tolerant `member.refs` deliberately omits the structural `head.json`;
|
|
613
|
+
* for extension spaces the old enumerator surfaced it as a `head`
|
|
614
|
+
* decoration on the tip migration, so fold `member.headRef` back in to
|
|
615
|
+
* keep that output. The app space synthesises its head, so it carries
|
|
616
|
+
* no on-disk `head` ref to restore.
|
|
617
|
+
*/
|
|
618
|
+
function listRefsByContractHash(member) {
|
|
619
|
+
const byHash = new Map(refsByContractHash(member.refs));
|
|
620
|
+
if (member.spaceId !== APP_SPACE_ID && member.headRef !== null) {
|
|
621
|
+
const hash = member.headRef.hash;
|
|
622
|
+
const bucket = byHash.get(hash) ?? [];
|
|
623
|
+
if (!bucket.includes(HEAD_REF_NAME)) byHash.set(hash, [...bucket, HEAD_REF_NAME].sort());
|
|
624
|
+
}
|
|
625
|
+
return byHash;
|
|
626
|
+
}
|
|
627
|
+
async function orderedOnDiskSpaceIds(projectMigrationsDir) {
|
|
628
|
+
return (await listContractSpaceDirectories(projectMigrationsDir)).filter((name) => !RESERVED_SPACE_SUBDIR_NAMES.has(name)).filter(isValidSpaceId).sort(compareSpaceIds);
|
|
629
|
+
}
|
|
630
|
+
/**
|
|
631
|
+
* Project the loaded {@link ContractSpaceAggregate} into the render-ready
|
|
632
|
+
* {@link MigrationSpaceListEntry} rows `migration list` displays.
|
|
633
|
+
*
|
|
634
|
+
* Space membership matches the on-disk contract-space directories (not the
|
|
635
|
+
* aggregate's always-present synthesized app member when `migrations/app/`
|
|
636
|
+
* is absent); package and ref data come from `aggregate.space(id)`.
|
|
637
|
+
*/
|
|
638
|
+
async function migrationSpaceListEntriesFromAggregate(aggregate, projectMigrationsDir) {
|
|
639
|
+
const spaceIds = await orderedOnDiskSpaceIds(projectMigrationsDir);
|
|
640
|
+
const spaces = [];
|
|
641
|
+
for (const spaceId of spaceIds) {
|
|
642
|
+
const member = aggregate.space(spaceId);
|
|
643
|
+
if (member === void 0) continue;
|
|
644
|
+
const refsByHash = listRefsByContractHash(member);
|
|
645
|
+
const migrations = member.packages.map((pkg) => ({
|
|
646
|
+
dirName: pkg.dirName,
|
|
647
|
+
from: pkg.metadata.from,
|
|
648
|
+
to: pkg.metadata.to,
|
|
649
|
+
migrationHash: pkg.metadata.migrationHash,
|
|
650
|
+
operationCount: pkg.ops.length,
|
|
651
|
+
createdAt: pkg.metadata.createdAt,
|
|
652
|
+
refs: refsByHash.get(pkg.metadata.to) ?? [],
|
|
653
|
+
providedInvariants: pkg.metadata.providedInvariants
|
|
654
|
+
})).sort(compareDirNamesDescending);
|
|
655
|
+
spaces.push({
|
|
656
|
+
spaceId,
|
|
657
|
+
migrations
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
return spaces;
|
|
661
|
+
}
|
|
662
|
+
function renderMigrationListHumanOutput(result, options) {
|
|
663
|
+
const styler = createAnsiMigrationListStyler({ useColor: options.useColor });
|
|
664
|
+
const topologyBySpaceId = buildMigrationListTopologyBySpace(result);
|
|
665
|
+
if (options.graph) return renderMigrationListGraphResult(result, styler, options.glyphMode, topologyBySpaceId);
|
|
666
|
+
return renderMigrationListWithStyle(result, styler, options.glyphMode, topologyBySpaceId);
|
|
667
|
+
}
|
|
668
|
+
function computeSummary(spaces) {
|
|
669
|
+
const totalMigrations = spaces.reduce((count, space) => count + space.migrations.length, 0);
|
|
670
|
+
if (spaces.length <= 1) return `${totalMigrations} migration(s) on disk`;
|
|
671
|
+
return `${totalMigrations} migration(s) across ${spaces.length} contract space(s)`;
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* Policy core of `migration list`: validates `--space`, narrows the
|
|
675
|
+
* pre-enumerated spaces, and assembles a {@link MigrationListResult}.
|
|
676
|
+
*
|
|
677
|
+
* - `migrations/` missing or contains no valid space directories →
|
|
678
|
+
* caller passes `spaces: []`; this synthesizes `[{ spaceId: APP_SPACE_ID, migrations: [] }]`.
|
|
679
|
+
* - `--space <id>` on an existing-but-empty space → `{ spaceId, migrations: [] }` in the input.
|
|
680
|
+
* - `--space <id>` on a non-existent (or reserved) space → `SPACE_NOT_FOUND`.
|
|
681
|
+
*/
|
|
682
|
+
function runMigrationList(inputs) {
|
|
683
|
+
const { spaces, spaceFilter } = inputs;
|
|
684
|
+
if (spaceFilter !== void 0 && !isValidSpaceId(spaceFilter)) return notOk(errorInvalidSpaceId(spaceFilter));
|
|
685
|
+
if (spaceFilter !== void 0 && !spaces.some((s) => s.spaceId === spaceFilter)) return notOk(errorSpaceNotFound(spaceFilter, spaces.map((s) => s.spaceId).sort()));
|
|
686
|
+
const scopedSpaces = spaceFilter !== void 0 ? spaces.filter((s) => s.spaceId === spaceFilter) : spaces;
|
|
687
|
+
const resultSpaces = scopedSpaces.length === 0 ? [{
|
|
688
|
+
spaceId: APP_SPACE_ID,
|
|
689
|
+
migrations: []
|
|
690
|
+
}] : scopedSpaces;
|
|
691
|
+
return ok({
|
|
692
|
+
ok: true,
|
|
693
|
+
spaces: resultSpaces,
|
|
694
|
+
summary: computeSummary(resultSpaces)
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
/**
|
|
698
|
+
* CLI shell: loads config, resolves paths, prints the styled header on
|
|
699
|
+
* stderr (interactive mode only), and delegates to {@link runMigrationList}.
|
|
700
|
+
* Kept intentionally thin so the unit-testable surface lives in the core.
|
|
701
|
+
*/
|
|
702
|
+
async function executeMigrationListCommand(options, flags, ui) {
|
|
703
|
+
const config = await loadConfig(options.config);
|
|
704
|
+
const { configPath, migrationsDir, migrationsRelative } = resolveMigrationPaths(options.config, config);
|
|
705
|
+
if (!flags.json && !flags.quiet) {
|
|
706
|
+
const header = formatStyledHeader({
|
|
707
|
+
command: "migration list",
|
|
708
|
+
description: "List on-disk migrations, latest first, per contract space",
|
|
709
|
+
details: [
|
|
710
|
+
{
|
|
711
|
+
label: "config",
|
|
712
|
+
value: configPath
|
|
713
|
+
},
|
|
714
|
+
{
|
|
715
|
+
label: "migrations",
|
|
716
|
+
value: migrationsRelative
|
|
717
|
+
},
|
|
718
|
+
...options.space !== void 0 ? [{
|
|
719
|
+
label: "space",
|
|
720
|
+
value: options.space
|
|
721
|
+
}] : []
|
|
722
|
+
],
|
|
723
|
+
flags
|
|
724
|
+
});
|
|
725
|
+
ui.stderr(header);
|
|
726
|
+
}
|
|
727
|
+
const loaded = await buildReadAggregate(config, { migrationsDir });
|
|
728
|
+
if (!loaded.ok) return notOk(loaded.failure);
|
|
729
|
+
return runMigrationList({
|
|
730
|
+
spaces: await migrationSpaceListEntriesFromAggregate(loaded.value.aggregate, migrationsDir),
|
|
731
|
+
...ifDefined("spaceFilter", options.space)
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
function createMigrationListCommand() {
|
|
735
|
+
const command = new Command("list");
|
|
736
|
+
setCommandDescriptions(command, "List on-disk migrations, latest first, per contract space", "Enumerates every on-disk migration under migrations/<space>/ for every\ncontract space found on disk, latest first. Offline — does not consult\nthe database. Each row leads with a kind glyph (* forward, ↩ rollback,\n⟲ self), then dirName, then source → destination contract hashes\n(7-char git-style). Self-edges show a single hash. Invariants render as\n{...}; refs on the destination as (production, db). Pass --space <id>\nto narrow to one contract space. --graph draws the forward spine with\nlane gutters; --ascii forces ASCII glyphs (orthogonal to --no-color).");
|
|
737
|
+
setCommandExamples(command, [
|
|
738
|
+
"prisma-next migration list",
|
|
739
|
+
"prisma-next migration list --graph",
|
|
740
|
+
"prisma-next migration list --space app",
|
|
741
|
+
"prisma-next migration list --graph --ascii",
|
|
742
|
+
"prisma-next migration list --json"
|
|
743
|
+
]);
|
|
744
|
+
setCommandSeeAlso(command, [
|
|
745
|
+
{
|
|
746
|
+
verb: "migration status",
|
|
747
|
+
oneLiner: "Show migration path and pending status"
|
|
748
|
+
},
|
|
749
|
+
{
|
|
750
|
+
verb: "migration log",
|
|
751
|
+
oneLiner: "Show executed migration history"
|
|
752
|
+
},
|
|
753
|
+
{
|
|
754
|
+
verb: "migration graph",
|
|
755
|
+
oneLiner: "Show the migration graph topology"
|
|
756
|
+
},
|
|
757
|
+
{
|
|
758
|
+
verb: "migration show",
|
|
759
|
+
oneLiner: "Display migration package contents"
|
|
760
|
+
}
|
|
761
|
+
]);
|
|
762
|
+
addGlobalOptions(command).option("--config <path>", "Path to prisma-next.config.ts").option("--space <id>", "Narrow output to a single contract space").option("--graph", "Draw migration relationships as an annotated tree").option("--ascii", "Use ASCII glyphs for --graph (pipe-friendly)").action(async (options) => {
|
|
763
|
+
const flags = parseGlobalFlagsOrExit(options);
|
|
764
|
+
const ui = createTerminalUI(flags);
|
|
765
|
+
const exitCode = handleResult(await executeMigrationListCommand(options, flags, ui), flags, ui, (listResult) => {
|
|
766
|
+
if (flags.json) ui.output(JSON.stringify(listResult, null, 2));
|
|
767
|
+
else if (!flags.quiet) ui.output(renderMigrationListHumanOutput(listResult, {
|
|
768
|
+
graph: options.graph === true,
|
|
769
|
+
glyphMode: ui.resolveGlyphMode(options.ascii === true),
|
|
770
|
+
useColor: ui.useColor
|
|
771
|
+
}));
|
|
772
|
+
});
|
|
773
|
+
process.exit(exitCode);
|
|
774
|
+
});
|
|
775
|
+
return command;
|
|
776
|
+
}
|
|
777
|
+
//#endregion
|
|
778
|
+
export { runMigrationList as a, renderMigrationListHumanOutput as i, executeMigrationListCommand as n, migrationSpaceListEntriesFromAggregate as r, createMigrationListCommand as t };
|
|
779
|
+
|
|
780
|
+
//# sourceMappingURL=migration-list-A3bJ4j5e.mjs.map
|