prisma-next 0.12.0-dev.22 → 0.12.0-dev.24

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.
Files changed (43) hide show
  1. package/dist/cli.mjs +6 -6
  2. package/dist/commands/migrate.d.mts +1 -1
  3. package/dist/commands/migration-graph.d.mts +4 -4
  4. package/dist/commands/migration-graph.mjs +164 -1
  5. package/dist/commands/migration-graph.mjs.map +1 -0
  6. package/dist/commands/migration-list.d.mts +3 -3
  7. package/dist/commands/migration-list.d.mts.map +1 -1
  8. package/dist/commands/migration-list.mjs +4 -6
  9. package/dist/commands/migration-list.mjs.map +1 -1
  10. package/dist/commands/migration-log.d.mts +3 -3
  11. package/dist/commands/migration-log.mjs +1 -1
  12. package/dist/commands/migration-plan.d.mts +1 -1
  13. package/dist/commands/migration-plan.mjs +1 -1
  14. package/dist/commands/migration-show.d.mts +1 -1
  15. package/dist/commands/migration-status.d.mts +1 -1
  16. package/dist/commands/ref.d.mts +1 -1
  17. package/dist/commands/telemetry/index.mjs +1 -1
  18. package/dist/exports/control-api.d.mts +1 -1
  19. package/dist/exports/init-output.mjs +1 -1
  20. package/dist/{global-flags-DSkV6iYT.d.mts → global-flags-DG4uY5tV.d.mts} +1 -1
  21. package/dist/{global-flags-DSkV6iYT.d.mts.map → global-flags-DG4uY5tV.d.mts.map} +1 -1
  22. package/dist/{init-S2vxszo_.mjs → init-B6kKrmf7.mjs} +2 -2
  23. package/dist/{init-S2vxszo_.mjs.map → init-B6kKrmf7.mjs.map} +1 -1
  24. package/dist/{migration-graph-CeBB07Cc.mjs → migration-graph-tree-render-BQdhKBO8.mjs} +405 -165
  25. package/dist/migration-graph-tree-render-BQdhKBO8.mjs.map +1 -0
  26. package/dist/{migration-log-Cj-T-r0o.mjs → migration-log-BzPmks3c.mjs} +2 -2
  27. package/dist/{migration-log-Cj-T-r0o.mjs.map → migration-log-BzPmks3c.mjs.map} +1 -1
  28. package/dist/{migration-plan-BQAbZkj_.mjs → migration-plan-CaeKCKp4.mjs} +1 -1
  29. package/dist/{migration-plan-BQAbZkj_.mjs.map → migration-plan-CaeKCKp4.mjs.map} +1 -1
  30. package/dist/{migration-types-Bhmj0RSa.d.mts → migration-types-CAQ-0TEE.d.mts} +1 -1
  31. package/dist/{migration-types-Bhmj0RSa.d.mts.map → migration-types-CAQ-0TEE.d.mts.map} +1 -1
  32. package/dist/{output-BD61elic.mjs → output-CF_hqzI-.mjs} +1 -1
  33. package/dist/{output-BD61elic.mjs.map → output-CF_hqzI-.mjs.map} +1 -1
  34. package/dist/{telemetry-Bu85x2Gy.mjs → telemetry-Q88WHwlv.mjs} +1 -1
  35. package/dist/{telemetry-Bu85x2Gy.mjs.map → telemetry-Q88WHwlv.mjs.map} +1 -1
  36. package/dist/{terminal-ui-BgLiAOYi.d.mts → terminal-ui-C3xGyxW-.d.mts} +1 -1
  37. package/dist/{terminal-ui-BgLiAOYi.d.mts.map → terminal-ui-C3xGyxW-.d.mts.map} +1 -1
  38. package/dist/{types-C8OcDFBe.d.mts → types-DiC683UW.d.mts} +1 -1
  39. package/dist/{types-C8OcDFBe.d.mts.map → types-DiC683UW.d.mts.map} +1 -1
  40. package/package.json +11 -11
  41. package/dist/migration-graph-CeBB07Cc.mjs.map +0 -1
  42. package/dist/migration-list-styler-CsMECsY4.mjs +0 -414
  43. package/dist/migration-list-styler-CsMECsY4.mjs.map +0 -1
@@ -1,11 +1,4 @@
1
- import { t as loadConfig } from "./config-loader-B6sJjXTv.mjs";
2
- import { E as formatStyledHeader, _ as parseGlobalFlagsOrExit, b as handleResult, d as setCommandSeeAlso, l as setCommandDescriptions, s as resolveMigrationPaths, t as addGlobalOptions, u as setCommandExamples, v as createTerminalUI } from "./command-helpers-Cmdqyhz9.mjs";
3
- import { r as buildReadAggregate } from "./contract-space-aggregate-loader-CirAEsM8.mjs";
4
- import { i as migrationGraphToRenderInput, n as graphRenderer } from "./graph-render-rFAqZujX.mjs";
5
- import { c as migrationListForwardArrow, l as classifyMigrationGraphTopology, n as createAnsiMigrationListStyler, s as migrationListEmptySource, t as CONTRACT_MARKER_NAME } from "./migration-list-styler-CsMECsY4.mjs";
6
- import { Command } from "commander";
7
- import { ok } from "@prisma-next/utils/result";
8
- import { bold, createColors } from "colorette";
1
+ import { bold, createColors, cyan, cyanBright, dim, green, yellow } from "colorette";
9
2
  import stringWidth from "string-width";
10
3
  import { EMPTY_CONTRACT_HASH } from "@prisma-next/migration-tools/constants";
11
4
  //#region src/utils/formatters/migration-graph-layout.ts
@@ -655,6 +648,224 @@ function buildMigrationGraphLayout(rowModel) {
655
648
  };
656
649
  }
657
650
  //#endregion
651
+ //#region src/utils/formatters/migration-list-graph-topology.ts
652
+ function compareDirNameDesc(a, b) {
653
+ return b.dirName.localeCompare(a.dirName);
654
+ }
655
+ function bumpDegree(map, key) {
656
+ map.set(key, (map.get(key) ?? 0) + 1);
657
+ }
658
+ function compareNodesRootFirst(a, b) {
659
+ if (a === EMPTY_CONTRACT_HASH) return -1;
660
+ if (b === EMPTY_CONTRACT_HASH) return 1;
661
+ return a.localeCompare(b);
662
+ }
663
+ /**
664
+ * Shortest-path distance of each node from the forward roots, over the given
665
+ * candidate edges. Roots are the in-degree-0 nodes (baseline first, then lex);
666
+ * a rooted component therefore distances every node by how many forward steps
667
+ * it sits from a root. A component with no root (a pure cycle) is seeded from
668
+ * its single lexically-smallest node so the cycle still gets a stable layering.
669
+ *
670
+ * Crucially this is *shortest* path, not longest: a backward (rollback) edge
671
+ * `deep → shallow` never offers a shorter route to the already-shallower
672
+ * target, so it is inert here. Distances are thus stable whether or not the
673
+ * rollbacks are still in the candidate set — which is what lets the peel below
674
+ * tell a genuine back-edge (target strictly shallower than source) apart from a
675
+ * forward edge that merely happens to share the back-edge's cycle.
676
+ */
677
+ function forwardDistances(nodes, candidates) {
678
+ const inDegree = /* @__PURE__ */ new Map();
679
+ for (const node of nodes) inDegree.set(node, 0);
680
+ for (const edge of candidates) bumpDegree(inDegree, edge.to);
681
+ const roots = [...nodes].filter((node) => (inDegree.get(node) ?? 0) === 0);
682
+ roots.sort(compareNodesRootFirst);
683
+ const seeds = roots.length > 0 ? roots : [...nodes].sort(compareNodesRootFirst).slice(0, 1);
684
+ const dist = /* @__PURE__ */ new Map();
685
+ for (const seed of seeds) dist.set(seed, 0);
686
+ const maxPasses = nodes.size;
687
+ for (let pass = 0; pass < maxPasses; pass++) {
688
+ let changed = false;
689
+ for (const edge of candidates) {
690
+ const base = dist.get(edge.from);
691
+ if (base === void 0) continue;
692
+ const next = base + 1;
693
+ if (next < (dist.get(edge.to) ?? Number.POSITIVE_INFINITY)) {
694
+ dist.set(edge.to, next);
695
+ changed = true;
696
+ }
697
+ }
698
+ if (!changed) break;
699
+ }
700
+ for (const node of nodes) if (!dist.has(node)) dist.set(node, 0);
701
+ return dist;
702
+ }
703
+ function canReachForward(start, goal, candidates) {
704
+ if (start === goal) return true;
705
+ const outgoing = /* @__PURE__ */ new Map();
706
+ for (const edge of candidates) {
707
+ const bucket = outgoing.get(edge.from);
708
+ if (bucket) bucket.push(edge.to);
709
+ else outgoing.set(edge.from, [edge.to]);
710
+ }
711
+ const visited = new Set([start]);
712
+ const queue = [start];
713
+ while (queue.length > 0) {
714
+ const node = queue.shift();
715
+ if (node === void 0) continue;
716
+ for (const next of outgoing.get(node) ?? []) {
717
+ if (next === goal) return true;
718
+ if (!visited.has(next)) {
719
+ visited.add(next);
720
+ queue.push(next);
721
+ }
722
+ }
723
+ }
724
+ return false;
725
+ }
726
+ /**
727
+ * Demote node-skipping rollbacks left forward by the DFS. An edge `from → to`
728
+ * is a rollback exactly when both hold:
729
+ * 1. `to` is a forward-ancestor of `from` — `to` can still reach `from` over
730
+ * the other forward edges, so the edge closes a cycle; and
731
+ * 2. `to` is strictly shallower than `from` (smaller forward distance) — the
732
+ * edge points back toward the root rather than advancing history.
733
+ *
734
+ * Condition 2 is the discriminator: in a cycle created by a rollback every edge
735
+ * satisfies condition 1, but only the rollback itself runs deep → shallow. The
736
+ * forward chain edges run shallow → deep and are never peeled, however many
737
+ * rollbacks converge on the same target. Tight back-edges whose source and
738
+ * target sit at the same distance (mutual two-node cycles) are already resolved
739
+ * by the DFS immediate-parent rule, so they never reach this pass. One edge is
740
+ * peeled per iteration (dirName-descending tie-break) and distances/reachability
741
+ * are recomputed, making the outcome independent of edge input order.
742
+ */
743
+ function peelNodeSkippingRollbacks(nodes, kindByMigrationHash, nonSelf) {
744
+ let candidates = nonSelf.filter((edge) => kindByMigrationHash.get(edge.hash) === "forward");
745
+ while (candidates.length > 0) {
746
+ const dist = forwardDistances(nodes, candidates);
747
+ const backEdges = candidates.filter((edge) => {
748
+ if ((dist.get(edge.to) ?? 0) >= (dist.get(edge.from) ?? 0)) return false;
749
+ const without = candidates.filter((candidate) => candidate !== edge);
750
+ return canReachForward(edge.to, edge.from, without);
751
+ });
752
+ if (backEdges.length === 0) break;
753
+ backEdges.sort(compareDirNameDesc);
754
+ const rollback = backEdges[0];
755
+ if (rollback === void 0) break;
756
+ kindByMigrationHash.set(rollback.hash, "rollback");
757
+ candidates = candidates.filter((edge) => edge !== rollback);
758
+ }
759
+ }
760
+ /**
761
+ * DFS with dirName-descending traversal. A GRAY target is a rollback only when it
762
+ * is the immediate DFS parent of the source — cross-links to other GRAY nodes
763
+ * stay forward. A follow-up peel pass demotes node-skipping rollbacks (target is
764
+ * a forward-ancestor of the source and sits strictly shallower than it).
765
+ */
766
+ function classifyNormalizedEdges(edges) {
767
+ const nodes = /* @__PURE__ */ new Set();
768
+ const kindByMigrationHash = /* @__PURE__ */ new Map();
769
+ const outgoingByFrom = /* @__PURE__ */ new Map();
770
+ const nonSelf = [];
771
+ for (const edge of edges) {
772
+ nodes.add(edge.from);
773
+ nodes.add(edge.to);
774
+ if (edge.from === edge.to) {
775
+ kindByMigrationHash.set(edge.hash, "self");
776
+ continue;
777
+ }
778
+ nonSelf.push(edge);
779
+ const bucket = outgoingByFrom.get(edge.from);
780
+ if (bucket) bucket.push(edge);
781
+ else outgoingByFrom.set(edge.from, [edge]);
782
+ }
783
+ for (const bucket of outgoingByFrom.values()) bucket.sort(compareDirNameDesc);
784
+ const nonSelfInDegree = /* @__PURE__ */ new Map();
785
+ for (const node of nodes) nonSelfInDegree.set(node, 0);
786
+ for (const bucket of outgoingByFrom.values()) for (const edge of bucket) bumpDegree(nonSelfInDegree, edge.to);
787
+ const dfsRoots = [];
788
+ for (const node of nodes) if ((nonSelfInDegree.get(node) ?? 0) === 0) dfsRoots.push(node);
789
+ dfsRoots.sort((a, b) => {
790
+ if (a === EMPTY_CONTRACT_HASH) return -1;
791
+ if (b === EMPTY_CONTRACT_HASH) return 1;
792
+ return a.localeCompare(b);
793
+ });
794
+ if (dfsRoots.length === 0) dfsRoots.push(...[...nodes].sort((a, b) => a.localeCompare(b)));
795
+ const WHITE = 0;
796
+ const GRAY = 1;
797
+ const BLACK = 2;
798
+ const color = /* @__PURE__ */ new Map();
799
+ const dfsParent = /* @__PURE__ */ new Map();
800
+ for (const node of nodes) color.set(node, WHITE);
801
+ const stack = [];
802
+ function isImmediateDfsParent(ancestor, node) {
803
+ return dfsParent.get(node) === ancestor;
804
+ }
805
+ function pushFrame(node, parent) {
806
+ color.set(node, GRAY);
807
+ dfsParent.set(node, parent);
808
+ stack.push({
809
+ node,
810
+ outgoing: outgoingByFrom.get(node) ?? [],
811
+ index: 0
812
+ });
813
+ }
814
+ function runDfsFrom(root) {
815
+ if (color.get(root) !== WHITE) return;
816
+ pushFrame(root, void 0);
817
+ while (stack.length > 0) {
818
+ const frame = stack[stack.length - 1];
819
+ if (frame === void 0) break;
820
+ if (frame.index >= frame.outgoing.length) {
821
+ color.set(frame.node, BLACK);
822
+ stack.pop();
823
+ continue;
824
+ }
825
+ const edge = frame.outgoing[frame.index];
826
+ frame.index += 1;
827
+ if (edge === void 0) continue;
828
+ const v = edge.to;
829
+ const vColor = color.get(v);
830
+ if (vColor === GRAY && isImmediateDfsParent(v, frame.node)) kindByMigrationHash.set(edge.hash, "rollback");
831
+ else {
832
+ kindByMigrationHash.set(edge.hash, "forward");
833
+ if (vColor === WHITE) pushFrame(v, frame.node);
834
+ }
835
+ }
836
+ }
837
+ for (const root of dfsRoots) runDfsFrom(root);
838
+ const remainingWhite = [...nodes].filter((node) => color.get(node) === WHITE);
839
+ remainingWhite.sort((a, b) => a.localeCompare(b));
840
+ for (const root of remainingWhite) runDfsFrom(root);
841
+ peelNodeSkippingRollbacks(nodes, kindByMigrationHash, nonSelf);
842
+ const forwardInDegree = /* @__PURE__ */ new Map();
843
+ const forwardOutDegree = /* @__PURE__ */ new Map();
844
+ for (const edge of edges) {
845
+ if (kindByMigrationHash.get(edge.hash) !== "forward") continue;
846
+ bumpDegree(forwardOutDegree, edge.from);
847
+ bumpDegree(forwardInDegree, edge.to);
848
+ }
849
+ return {
850
+ kindByMigrationHash,
851
+ forwardInDegree,
852
+ forwardOutDegree
853
+ };
854
+ }
855
+ /**
856
+ * Classify forward/rollback/self for a `MigrationGraph` edge set (Tier-3).
857
+ */
858
+ function classifyMigrationGraphTopology(graph) {
859
+ const normalized = [];
860
+ for (const edges of graph.forwardChain.values()) for (const edge of edges) normalized.push({
861
+ hash: edge.migrationHash,
862
+ from: edge.from,
863
+ to: edge.to,
864
+ dirName: edge.dirName
865
+ });
866
+ return classifyNormalizedEdges(normalized);
867
+ }
868
+ //#endregion
658
869
  //#region src/utils/formatters/migration-graph-rows.ts
659
870
  /**
660
871
  * Return the weakly-connected components of `graph` as an array of node sets,
@@ -869,6 +1080,179 @@ function laneColorForColumn(column) {
869
1080
  if (column <= 0) return (text) => text;
870
1081
  return LANE_COLOR_CYCLE[(column - 1) % LANE_COLOR_CYCLE.length] ?? ((text) => text);
871
1082
  }
1083
+ function migrationListForwardArrow(glyphMode) {
1084
+ return glyphMode === "ascii" ? "->" : "→";
1085
+ }
1086
+ function migrationListEmptySource(glyphMode) {
1087
+ return glyphMode === "ascii" ? "-" : "∅";
1088
+ }
1089
+ function abbreviateContractHash(hash) {
1090
+ return (hash.startsWith("sha256:") ? hash.slice(7) : hash).slice(0, 7);
1091
+ }
1092
+ //#endregion
1093
+ //#region src/utils/formatters/migration-list-render.ts
1094
+ const IDENTITY_MIGRATION_LIST_STYLER = {
1095
+ kind: (text) => text,
1096
+ dirName: (text) => text,
1097
+ sourceHash: (text) => text,
1098
+ destHash: (text) => text,
1099
+ glyph: (text) => text,
1100
+ lane: (text) => text,
1101
+ invariants: (ids) => `{${ids.join(", ")}}`,
1102
+ refs: (names) => `(${names.join(", ")})`,
1103
+ spaceHeading: (text) => text,
1104
+ summary: (text) => text,
1105
+ emptyState: (text) => text
1106
+ };
1107
+ function canonicalFrom(from) {
1108
+ return from ?? EMPTY_CONTRACT_HASH;
1109
+ }
1110
+ function migrationGraphFromListEntries(entries) {
1111
+ const nodes = /* @__PURE__ */ new Set();
1112
+ const forwardChain = /* @__PURE__ */ new Map();
1113
+ const reverseChain = /* @__PURE__ */ new Map();
1114
+ const migrationByHash = /* @__PURE__ */ new Map();
1115
+ for (const entry of entries) {
1116
+ const from = canonicalFrom(entry.from);
1117
+ const edge = {
1118
+ from,
1119
+ to: entry.to,
1120
+ migrationHash: entry.migrationHash,
1121
+ dirName: entry.dirName,
1122
+ createdAt: entry.createdAt,
1123
+ invariants: entry.providedInvariants
1124
+ };
1125
+ nodes.add(from);
1126
+ nodes.add(entry.to);
1127
+ const forward = forwardChain.get(from);
1128
+ if (forward) forward.push(edge);
1129
+ else forwardChain.set(from, [edge]);
1130
+ const reverse = reverseChain.get(entry.to);
1131
+ if (reverse) reverse.push(edge);
1132
+ else reverseChain.set(entry.to, [edge]);
1133
+ migrationByHash.set(entry.migrationHash, edge);
1134
+ }
1135
+ return {
1136
+ nodes,
1137
+ forwardChain,
1138
+ reverseChain,
1139
+ migrationByHash
1140
+ };
1141
+ }
1142
+ function buildEdgeAnnotationsByHashFromListEntries(entries) {
1143
+ const annotations = /* @__PURE__ */ new Map();
1144
+ for (const entry of entries) annotations.set(entry.migrationHash, {
1145
+ operationCount: entry.operationCount,
1146
+ invariants: entry.providedInvariants
1147
+ });
1148
+ return annotations;
1149
+ }
1150
+ function buildRefsByHashFromListEntries(entries) {
1151
+ const refsByHash = /* @__PURE__ */ new Map();
1152
+ for (const entry of entries) if (entry.refs.length > 0) refsByHash.set(entry.to, entry.refs);
1153
+ return refsByHash;
1154
+ }
1155
+ function formatEmptyStateLine(spaceId, style) {
1156
+ return style.emptyState(`There are no migrations in migrations/${spaceId}/ yet`);
1157
+ }
1158
+ function indentTreeBlock(treeOutput, indent) {
1159
+ if (treeOutput.length === 0) return treeOutput;
1160
+ return treeOutput.split("\n").map((line) => line.length === 0 ? line : `${indent}${line}`).join("\n");
1161
+ }
1162
+ function renderSpaceTreeBlock(spaceId, migrations, multiSpace, glyphMode, style, colorize) {
1163
+ if (migrations.length === 0) {
1164
+ const emptyLine = formatEmptyStateLine(spaceId, style);
1165
+ if (!multiSpace) return [emptyLine];
1166
+ return [style.spaceHeading(`${spaceId}:`), ` ${emptyLine}`];
1167
+ }
1168
+ const treeOutput = renderMigrationGraphTree(buildMigrationGraphLayout(buildMigrationGraphRows(migrationGraphFromListEntries(migrations))), {
1169
+ refsByHash: buildRefsByHashFromListEntries(migrations),
1170
+ edgeAnnotationsByHash: buildEdgeAnnotationsByHashFromListEntries(migrations),
1171
+ colorize,
1172
+ glyphMode
1173
+ });
1174
+ if (!multiSpace) return treeOutput.length === 0 ? [] : [treeOutput];
1175
+ const indented = indentTreeBlock(treeOutput, " ");
1176
+ return [style.spaceHeading(`${spaceId}:`), indented];
1177
+ }
1178
+ /**
1179
+ * Compose the styled `migration list` human output via the shared tree
1180
+ * renderer. Each on-disk migration is one edge row with package-fact
1181
+ * annotations; refs decorate destination contract nodes.
1182
+ *
1183
+ * `options.colorize` must match whether `style` emits ANSI (e.g. both true for
1184
+ * `createAnsiMigrationListStyler({ useColor: true })`).
1185
+ */
1186
+ function renderMigrationListWithStyle(result, style, glyphMode = "unicode", options = {}) {
1187
+ const multiSpace = result.spaces.length > 1;
1188
+ const colorize = options.colorize ?? false;
1189
+ const lines = [];
1190
+ for (let index = 0; index < result.spaces.length; index++) {
1191
+ const space = result.spaces[index];
1192
+ if (index > 0) lines.push("");
1193
+ lines.push(...renderSpaceTreeBlock(space.spaceId, space.migrations, multiSpace, glyphMode, style, colorize));
1194
+ }
1195
+ if (result.spaces.reduce((count, space) => count + space.migrations.length, 0) > 0) {
1196
+ lines.push("");
1197
+ lines.push(style.summary(result.summary));
1198
+ }
1199
+ return lines.join("\n");
1200
+ }
1201
+ //#endregion
1202
+ //#region src/utils/formatters/migration-list-styler.ts
1203
+ /**
1204
+ * The current contract overlay marker. Unlike user refs, this names the user's
1205
+ * declared desired state — the implicit base/target for `plan` / `migrate` —
1206
+ * not a stored label. It is emphasized (bold) so it stands out from plain refs
1207
+ * (including the live-database `db` marker, which is just another ref).
1208
+ */
1209
+ const CONTRACT_MARKER_NAME = "contract";
1210
+ function styleRefName(name) {
1211
+ return name === "contract" ? bold(green(name)) : green(name);
1212
+ }
1213
+ /**
1214
+ * Build a {@link MigrationListStyler} that decorates `migration list`
1215
+ * tokens with ANSI SGR codes. When `useColor` is `false` (non-TTY,
1216
+ * `--no-color`, `NO_COLOR=1`, piped output) the function returns the
1217
+ * shared identity styler so callers get plain text with zero ANSI
1218
+ * bytes — pipe-friendly by construction.
1219
+ *
1220
+ * Palette:
1221
+ *
1222
+ * - `dirName`: bold
1223
+ * - `sourceHash`: dim cyan
1224
+ * - `destHash`: bright cyan
1225
+ * - `kind` (`*` / `↩` / `⟲`): bright — the signal; lanes and arrows dim
1226
+ * - `glyph` (`→` / `⟲` / `∅`): dim
1227
+ * - `lane` (graph gutter lines `│` and fan/join connectors `├─┐` / `├─┘`): dim
1228
+ * - `invariants` (`{...}`): yellow
1229
+ * - `refs` (`(...)`): green; the `contract` desired-state marker inside is
1230
+ * green-bold (the active ref is bolded separately by the tree styler)
1231
+ * - `spaceHeading` (`<spaceId>:`): bold
1232
+ * - `summary`: dim
1233
+ * - `emptyState`: dim
1234
+ */
1235
+ function createAnsiMigrationListStyler(opts) {
1236
+ if (!opts.useColor) return IDENTITY_MIGRATION_LIST_STYLER;
1237
+ return {
1238
+ kind: (text) => text,
1239
+ dirName: (text) => bold(text),
1240
+ sourceHash: (text) => dim(cyan(text)),
1241
+ destHash: (text) => cyanBright(text),
1242
+ glyph: (text) => dim(text),
1243
+ lane: (text) => dim(text),
1244
+ invariants: (ids) => yellow(`{${ids.join(", ")}}`),
1245
+ refs: (names) => {
1246
+ const open = green("(");
1247
+ const close = green(")");
1248
+ const separator = green(", ");
1249
+ return open + names.map(styleRefName).join(separator) + close;
1250
+ },
1251
+ spaceHeading: (text) => bold(text),
1252
+ summary: (text) => dim(text),
1253
+ emptyState: (text) => dim(text)
1254
+ };
1255
+ }
872
1256
  //#endregion
873
1257
  //#region src/utils/formatters/migration-graph-tree-render.ts
874
1258
  const LABEL_GAP = 2;
@@ -1231,6 +1615,15 @@ function createTreeStyler(opts) {
1231
1615
  }
1232
1616
  };
1233
1617
  }
1618
+ function formatEdgeAnnotationSuffix(migrationHash, opts, style) {
1619
+ const annotation = opts.edgeAnnotationsByHash?.get(migrationHash);
1620
+ if (annotation === void 0) return "";
1621
+ const segments = [];
1622
+ if (annotation.operationCount !== void 0) segments.push(`${annotation.operationCount} ops`);
1623
+ if (annotation.invariants !== void 0 && annotation.invariants.length > 0) segments.push(style.invariants(annotation.invariants));
1624
+ if (segments.length === 0) return "";
1625
+ return ` ${segments.join(" ")}`;
1626
+ }
1234
1627
  function formatEdgeHashColumn(edge, style, hashLength, palette) {
1235
1628
  if (edge.kind === "self") {
1236
1629
  const hash = abbreviateHash(edge.from, hashLength, palette.emptySource);
@@ -1335,7 +1728,8 @@ function renderMigrationGraphTree(model, opts) {
1335
1728
  const laneIndex = row.laneIndex ?? 0;
1336
1729
  const dirName = `${(opts.colorize && laneIndex > NEUTRAL_LANE ? (text) => forcedBold(laneColorForColumn(laneIndex)(text)) : style.dirName)(edge.dirName)}${dirNamePadding}`;
1337
1730
  const hashColumn = formatEdgeHashColumn(edge, style, hashLength, palette);
1338
- lines.push(trimTrailingWhitespace(`${gutterPad}${dirName}${hashColumn}`));
1731
+ const annotationSuffix = formatEdgeAnnotationSuffix(edge.migrationHash, opts, style);
1732
+ lines.push(trimTrailingWhitespace(`${gutterPad}${dirName}${hashColumn}${annotationSuffix}`));
1339
1733
  }
1340
1734
  return lines.join("\n");
1341
1735
  }
@@ -1365,160 +1759,6 @@ function renderMigrationGraphLegend(opts) {
1365
1759
  ].join("\n");
1366
1760
  }
1367
1761
  //#endregion
1368
- //#region src/commands/migration-graph.ts
1369
- /**
1370
- * `--legend` describes the `--tree` visual language, so passing it auto-enables
1371
- * the tree path (it has nothing to say about the legacy dagre default).
1372
- */
1373
- function migrationGraphUsesTree(options) {
1374
- return options.tree === true || options.legend === true;
1375
- }
1376
- /**
1377
- * The legend is decoration printed alongside the command header on stderr, so
1378
- * it is suppressed for the machine-readable / silent paths (`--json`, `--dot`,
1379
- * `--quiet`) exactly as the header is.
1380
- */
1381
- function migrationGraphShowsLegend(options, flags) {
1382
- return options.legend === true && options.dot !== true && flags.json !== true && flags.quiet !== true;
1383
- }
1384
- async function executeMigrationGraphCommand(options, flags, ui) {
1385
- const config = await loadConfig(options.config);
1386
- const { configPath, appMigrationsRelative, migrationsDir } = resolveMigrationPaths(options.config, config);
1387
- if (!flags.json && !flags.quiet) {
1388
- const header = formatStyledHeader({
1389
- command: "migration graph",
1390
- description: "Show the migration graph topology",
1391
- details: [{
1392
- label: "config",
1393
- value: configPath
1394
- }, {
1395
- label: "migrations",
1396
- value: appMigrationsRelative
1397
- }],
1398
- flags
1399
- });
1400
- ui.stderr(header);
1401
- if (migrationGraphShowsLegend(options, flags)) {
1402
- ui.stderr(renderMigrationGraphLegend({
1403
- colorize: flags.color !== false,
1404
- glyphMode: ui.resolveGlyphMode(options.ascii === true)
1405
- }));
1406
- ui.stderr("");
1407
- }
1408
- }
1409
- const loaded = await buildReadAggregate(config, { migrationsDir });
1410
- if (!loaded.ok) return loaded;
1411
- const { aggregate, contractHash } = loaded.value;
1412
- const graph = aggregate.app.graph();
1413
- return ok({
1414
- ok: true,
1415
- graph,
1416
- contractHash,
1417
- refs: Object.entries(aggregate.app.refs).map(([name, entry]) => ({
1418
- name,
1419
- hash: entry.hash,
1420
- active: false
1421
- })),
1422
- summary: `${graph.nodes.size} node(s), ${graph.migrationByHash.size} edge(s)`
1423
- });
1424
- }
1425
- function createMigrationGraphCommand() {
1426
- const command = new Command("graph");
1427
- setCommandDescriptions(command, "Show the migration graph topology", "Renders the migration graph topology. Offline — does not consult\nthe database. Use --tree for the condensed annotated tree\n(--ascii swaps box-drawing for pipe-friendly ASCII glyphs),\n--json for machine-readable output, or --dot for Graphviz DOT\nformat.");
1428
- setCommandExamples(command, [
1429
- "prisma-next migration graph",
1430
- "prisma-next migration graph --json",
1431
- "prisma-next migration graph --dot",
1432
- "prisma-next migration graph --tree",
1433
- "prisma-next migration graph --tree --ascii",
1434
- "prisma-next migration graph --legend"
1435
- ]);
1436
- setCommandSeeAlso(command, [
1437
- {
1438
- verb: "migration status",
1439
- oneLiner: "Show migration path and pending status"
1440
- },
1441
- {
1442
- verb: "migration log",
1443
- oneLiner: "Show executed migration history"
1444
- },
1445
- {
1446
- verb: "migration list",
1447
- oneLiner: "List on-disk migrations"
1448
- },
1449
- {
1450
- verb: "migration show",
1451
- oneLiner: "Display migration package contents"
1452
- }
1453
- ]);
1454
- addGlobalOptions(command).option("--config <path>", "Path to prisma-next.config.ts").option("--dot", "Output in Graphviz DOT format").option("--tree", "Experimental condensed annotated tree renderer").option("--ascii", "Use ASCII glyphs for --tree (pipe-friendly)").option("--legend", "Print a key for the --tree glyphs and lane colors (implies --tree)").action(async (options) => {
1455
- const flags = parseGlobalFlagsOrExit(options);
1456
- const ui = createTerminalUI(flags);
1457
- const exitCode = handleResult(await executeMigrationGraphCommand(options, flags, ui), flags, ui, (graphResult) => {
1458
- if (options.dot) {
1459
- const lines = ["digraph migrations {"];
1460
- for (const edge of graphResult.graph.migrationByHash.values()) {
1461
- const from = edge.from.slice(0, 12);
1462
- const to = edge.to.slice(0, 12);
1463
- lines.push(` "${from}" -> "${to}" [label="${edge.dirName}"];`);
1464
- }
1465
- lines.push("}");
1466
- ui.output(lines.join("\n"));
1467
- } else if (flags.json) {
1468
- const nodes = [...graphResult.graph.nodes];
1469
- const edges = [...graphResult.graph.migrationByHash.values()].map((e) => ({
1470
- dirName: e.dirName,
1471
- from: e.from,
1472
- to: e.to,
1473
- migrationHash: e.migrationHash
1474
- }));
1475
- ui.output(JSON.stringify({
1476
- ok: true,
1477
- nodes,
1478
- edges,
1479
- summary: graphResult.summary
1480
- }, null, 2));
1481
- } else if (!flags.quiet) if (migrationGraphUsesTree(options)) {
1482
- const refsByHash = /* @__PURE__ */ new Map();
1483
- for (const ref of graphResult.refs) {
1484
- const existing = refsByHash.get(ref.hash);
1485
- refsByHash.set(ref.hash, existing ? [...existing, ref.name] : [ref.name]);
1486
- }
1487
- const layout = buildMigrationGraphLayout(buildMigrationGraphRows(graphResult.graph, { ...graphResult.contractHash !== null ? { contractHash: graphResult.contractHash } : {} }));
1488
- const activeRef = graphResult.refs.find((ref) => ref.active);
1489
- const treeOutput = renderMigrationGraphTree(layout, {
1490
- refsByHash,
1491
- ...graphResult.contractHash !== null ? { contractHash: graphResult.contractHash } : {},
1492
- ...activeRef !== void 0 ? { activeRefName: activeRef.name } : {},
1493
- colorize: flags.color !== false,
1494
- glyphMode: ui.resolveGlyphMode(options.ascii === true)
1495
- });
1496
- ui.output(treeOutput);
1497
- ui.output(`\n${graphResult.summary}`);
1498
- } else {
1499
- const renderInput = migrationGraphToRenderInput({
1500
- graph: graphResult.graph,
1501
- mode: "offline",
1502
- markerHash: void 0,
1503
- contractHash: graphResult.contractHash ?? EMPTY_CONTRACT_HASH,
1504
- refs: graphResult.refs,
1505
- activeRefHash: void 0,
1506
- activeRefName: void 0,
1507
- edgeStatuses: []
1508
- });
1509
- const graphOutput = graphRenderer.render(renderInput.graph, {
1510
- ...renderInput.options,
1511
- colorize: flags.color !== false
1512
- });
1513
- ui.log(graphOutput);
1514
- ui.log(`\n${graphResult.summary}`);
1515
- }
1516
- });
1517
- process.exit(exitCode);
1518
- });
1519
- return command;
1520
- }
1521
- //#endregion
1522
- export { migrationGraphUsesTree as i, executeMigrationGraphCommand as n, migrationGraphShowsLegend as r, createMigrationGraphCommand as t };
1762
+ export { renderMigrationListWithStyle as a, buildMigrationGraphLayout as c, IDENTITY_MIGRATION_LIST_STYLER as i, renderMigrationGraphTree as n, abbreviateContractHash as o, createAnsiMigrationListStyler as r, buildMigrationGraphRows as s, renderMigrationGraphLegend as t };
1523
1763
 
1524
- //# sourceMappingURL=migration-graph-CeBB07Cc.mjs.map
1764
+ //# sourceMappingURL=migration-graph-tree-render-BQdhKBO8.mjs.map