@prisma-next/cli 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/README.md +13 -9
- 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 +19 -19
- package/src/commands/db-init.ts +48 -2
- package/src/commands/db-sign.ts +9 -5
- package/src/commands/db-update.ts +54 -8
- package/src/commands/migrate.ts +123 -42
- package/src/commands/migration-check.ts +43 -83
- package/src/commands/migration-graph.ts +15 -41
- package/src/commands/migration-list.ts +231 -74
- package/src/commands/migration-log.ts +8 -14
- package/src/commands/migration-new.ts +44 -48
- package/src/commands/migration-plan.ts +411 -196
- package/src/commands/migration-show.ts +65 -284
- package/src/commands/migration-status.ts +116 -110
- package/src/commands/ref.ts +53 -8
- package/src/control-api/client.ts +0 -1
- package/src/control-api/contract-enrichment.ts +6 -42
- package/src/control-api/operations/contract-emit.ts +7 -2
- package/src/control-api/operations/db-verify.ts +9 -5
- package/src/control-api/operations/migration-apply.ts +36 -23
- package/src/control-api/types.ts +3 -10
- package/src/migration-cli.ts +4 -4
- package/src/utils/cli-errors.ts +234 -0
- package/src/utils/command-helpers.ts +1 -20
- package/src/utils/contract-at-errors.ts +96 -0
- package/src/utils/contract-space-aggregate-loader.ts +336 -117
- package/src/utils/formatters/migration-list-data-column.ts +115 -0
- package/src/utils/formatters/migration-list-graph-layout.ts +268 -0
- package/src/utils/formatters/migration-list-graph-render.ts +311 -0
- package/src/utils/formatters/migration-list-graph-topology.ts +158 -0
- package/src/utils/formatters/migration-list-render.ts +191 -0
- package/src/utils/formatters/migration-list-styler.ts +61 -0
- package/src/utils/formatters/migration-list-types.ts +21 -0
- package/src/utils/formatters/migrations.ts +29 -38
- package/src/utils/glyph-mode.ts +22 -0
- package/src/utils/integrity-violation-to-check-failure.ts +130 -0
- package/src/utils/plan-resolution.ts +258 -0
- package/src/utils/ref-advancement.ts +68 -0
- package/src/utils/terminal-ui.ts +42 -1
- 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,158 @@
|
|
|
1
|
+
import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';
|
|
2
|
+
import type { MigrationListEntry } from './migration-list-types';
|
|
3
|
+
|
|
4
|
+
export type MigrationEdgeKind = 'forward' | 'rollback' | 'self';
|
|
5
|
+
|
|
6
|
+
export interface MigrationListGraphTopology {
|
|
7
|
+
readonly kindByMigrationHash: ReadonlyMap<string, MigrationEdgeKind>;
|
|
8
|
+
readonly forwardInDegree: ReadonlyMap<string, number>;
|
|
9
|
+
readonly forwardOutDegree: ReadonlyMap<string, number>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface NonSelfEdge {
|
|
13
|
+
readonly entry: MigrationListEntry;
|
|
14
|
+
readonly from: string;
|
|
15
|
+
readonly to: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function canonicalFrom(from: string | null): string {
|
|
19
|
+
return from ?? EMPTY_CONTRACT_HASH;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function compareDirNameDesc(a: MigrationListEntry, b: MigrationListEntry): number {
|
|
23
|
+
return b.dirName.localeCompare(a.dirName);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function bumpDegree(map: Map<string, number>, key: string): void {
|
|
27
|
+
map.set(key, (map.get(key) ?? 0) + 1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function classifyMigrationListGraphTopology(
|
|
31
|
+
entries: readonly MigrationListEntry[],
|
|
32
|
+
): MigrationListGraphTopology {
|
|
33
|
+
const nodes = new Set<string>();
|
|
34
|
+
const kindByMigrationHash = new Map<string, MigrationEdgeKind>();
|
|
35
|
+
const outgoingByFrom = new Map<string, NonSelfEdge[]>();
|
|
36
|
+
|
|
37
|
+
for (const entry of entries) {
|
|
38
|
+
const from = canonicalFrom(entry.from);
|
|
39
|
+
const to = entry.to;
|
|
40
|
+
nodes.add(from);
|
|
41
|
+
nodes.add(to);
|
|
42
|
+
|
|
43
|
+
if (from === to) {
|
|
44
|
+
kindByMigrationHash.set(entry.migrationHash, 'self');
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const bucket = outgoingByFrom.get(from);
|
|
49
|
+
const edge: NonSelfEdge = { entry, from, to };
|
|
50
|
+
if (bucket) bucket.push(edge);
|
|
51
|
+
else outgoingByFrom.set(from, [edge]);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
for (const bucket of outgoingByFrom.values()) {
|
|
55
|
+
bucket.sort((a, b) => compareDirNameDesc(a.entry, b.entry));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const nonSelfInDegree = new Map<string, number>();
|
|
59
|
+
for (const node of nodes) {
|
|
60
|
+
nonSelfInDegree.set(node, 0);
|
|
61
|
+
}
|
|
62
|
+
for (const bucket of outgoingByFrom.values()) {
|
|
63
|
+
for (const edge of bucket) {
|
|
64
|
+
bumpDegree(nonSelfInDegree, edge.to);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const dfsRoots: string[] = [];
|
|
69
|
+
for (const node of nodes) {
|
|
70
|
+
if ((nonSelfInDegree.get(node) ?? 0) === 0) {
|
|
71
|
+
dfsRoots.push(node);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
dfsRoots.sort((a, b) => {
|
|
75
|
+
if (a === EMPTY_CONTRACT_HASH) return -1;
|
|
76
|
+
if (b === EMPTY_CONTRACT_HASH) return 1;
|
|
77
|
+
return a.localeCompare(b);
|
|
78
|
+
});
|
|
79
|
+
if (dfsRoots.length === 0) {
|
|
80
|
+
dfsRoots.push(...[...nodes].sort((a, b) => a.localeCompare(b)));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const WHITE = 0;
|
|
84
|
+
const GRAY = 1;
|
|
85
|
+
const BLACK = 2;
|
|
86
|
+
const color = new Map<string, number>();
|
|
87
|
+
for (const node of nodes) {
|
|
88
|
+
color.set(node, WHITE);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
interface Frame {
|
|
92
|
+
node: string;
|
|
93
|
+
outgoing: readonly NonSelfEdge[];
|
|
94
|
+
index: number;
|
|
95
|
+
}
|
|
96
|
+
const stack: Frame[] = [];
|
|
97
|
+
|
|
98
|
+
function pushFrame(node: string): void {
|
|
99
|
+
color.set(node, GRAY);
|
|
100
|
+
stack.push({ node, outgoing: outgoingByFrom.get(node) ?? [], index: 0 });
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function runDfsFrom(root: string): void {
|
|
104
|
+
if (color.get(root) !== WHITE) return;
|
|
105
|
+
pushFrame(root);
|
|
106
|
+
|
|
107
|
+
while (stack.length > 0) {
|
|
108
|
+
const frame = stack[stack.length - 1];
|
|
109
|
+
if (frame === undefined) break;
|
|
110
|
+
if (frame.index >= frame.outgoing.length) {
|
|
111
|
+
color.set(frame.node, BLACK);
|
|
112
|
+
stack.pop();
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const edge = frame.outgoing[frame.index];
|
|
117
|
+
frame.index += 1;
|
|
118
|
+
if (edge === undefined) continue;
|
|
119
|
+
|
|
120
|
+
const v = edge.to;
|
|
121
|
+
const vColor = color.get(v);
|
|
122
|
+
if (vColor === GRAY) {
|
|
123
|
+
kindByMigrationHash.set(edge.entry.migrationHash, 'rollback');
|
|
124
|
+
} else {
|
|
125
|
+
kindByMigrationHash.set(edge.entry.migrationHash, 'forward');
|
|
126
|
+
if (vColor === WHITE) {
|
|
127
|
+
pushFrame(v);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
for (const root of dfsRoots) {
|
|
134
|
+
runDfsFrom(root);
|
|
135
|
+
}
|
|
136
|
+
const remainingWhite = [...nodes].filter((node) => color.get(node) === WHITE);
|
|
137
|
+
remainingWhite.sort((a, b) => a.localeCompare(b));
|
|
138
|
+
for (const root of remainingWhite) {
|
|
139
|
+
runDfsFrom(root);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const forwardInDegree = new Map<string, number>();
|
|
143
|
+
const forwardOutDegree = new Map<string, number>();
|
|
144
|
+
|
|
145
|
+
for (const entry of entries) {
|
|
146
|
+
if (kindByMigrationHash.get(entry.migrationHash) !== 'forward') continue;
|
|
147
|
+
const from = canonicalFrom(entry.from);
|
|
148
|
+
const to = entry.to;
|
|
149
|
+
bumpDegree(forwardOutDegree, from);
|
|
150
|
+
bumpDegree(forwardInDegree, to);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
kindByMigrationHash,
|
|
155
|
+
forwardInDegree,
|
|
156
|
+
forwardOutDegree,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import type { GlyphMode } from '../glyph-mode';
|
|
2
|
+
import {
|
|
3
|
+
computeMigrationDirNameWidth,
|
|
4
|
+
formatMigrationDataColumn,
|
|
5
|
+
migrationListEmptySource,
|
|
6
|
+
migrationListForwardArrow,
|
|
7
|
+
migrationListKindGlyph,
|
|
8
|
+
} from './migration-list-data-column';
|
|
9
|
+
import {
|
|
10
|
+
classifyMigrationListGraphTopology,
|
|
11
|
+
type MigrationEdgeKind,
|
|
12
|
+
type MigrationListGraphTopology,
|
|
13
|
+
} from './migration-list-graph-topology';
|
|
14
|
+
import type { MigrationListEntry, MigrationListResult } from './migration-list-types';
|
|
15
|
+
|
|
16
|
+
export type { GlyphMode } from '../glyph-mode';
|
|
17
|
+
export type { MigrationEdgeKind } from './migration-list-graph-topology';
|
|
18
|
+
export type {
|
|
19
|
+
MigrationListEntry,
|
|
20
|
+
MigrationListResult,
|
|
21
|
+
MigrationSpaceListEntry,
|
|
22
|
+
} from './migration-list-types';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Semantic styler for `migration list` output tokens. Token-typed so
|
|
26
|
+
* the renderer composes presentation-neutral fragments and the styler
|
|
27
|
+
* decides how each token kind is decorated (ANSI codes, plain text,
|
|
28
|
+
* etc.). The renderer pads with raw spaces *outside* styled tokens so
|
|
29
|
+
* visible column widths stay stable regardless of what the styler
|
|
30
|
+
* emits — adding ANSI escape sequences never disturbs alignment.
|
|
31
|
+
*
|
|
32
|
+
* `invariants` and `refs` receive the underlying string arrays rather
|
|
33
|
+
* than a pre-joined string so per-element styling (e.g. distinguishing
|
|
34
|
+
* the live-DB `db` marker from user-named refs) is possible without
|
|
35
|
+
* having to re-parse a joined block.
|
|
36
|
+
*/
|
|
37
|
+
export interface MigrationListStyler {
|
|
38
|
+
kind(text: string): string;
|
|
39
|
+
dirName(text: string): string;
|
|
40
|
+
sourceHash(text: string): string;
|
|
41
|
+
destHash(text: string): string;
|
|
42
|
+
glyph(text: string): string;
|
|
43
|
+
lane(text: string): string;
|
|
44
|
+
invariants(ids: readonly string[]): string;
|
|
45
|
+
refs(names: readonly string[]): string;
|
|
46
|
+
spaceHeading(text: string): string;
|
|
47
|
+
summary(text: string): string;
|
|
48
|
+
emptyState(text: string): string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const IDENTITY_MIGRATION_LIST_STYLER: MigrationListStyler = {
|
|
52
|
+
kind: (text) => text,
|
|
53
|
+
dirName: (text) => text,
|
|
54
|
+
sourceHash: (text) => text,
|
|
55
|
+
destHash: (text) => text,
|
|
56
|
+
glyph: (text) => text,
|
|
57
|
+
lane: (text) => text,
|
|
58
|
+
invariants: (ids) => `{${ids.join(', ')}}`,
|
|
59
|
+
refs: (names) => `(${names.join(', ')})`,
|
|
60
|
+
spaceHeading: (text) => text,
|
|
61
|
+
summary: (text) => text,
|
|
62
|
+
emptyState: (text) => text,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
function resolveEdgeKind(
|
|
66
|
+
migrationHash: string,
|
|
67
|
+
kindByMigrationHash: ReadonlyMap<string, MigrationEdgeKind>,
|
|
68
|
+
): MigrationEdgeKind {
|
|
69
|
+
return kindByMigrationHash.get(migrationHash) ?? 'forward';
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function formatMigrationRow(
|
|
73
|
+
migration: MigrationListEntry,
|
|
74
|
+
dirNameWidth: number,
|
|
75
|
+
edgeKind: MigrationEdgeKind,
|
|
76
|
+
glyphMode: GlyphMode,
|
|
77
|
+
style: MigrationListStyler,
|
|
78
|
+
): string {
|
|
79
|
+
const kindColumn = `${style.kind(migrationListKindGlyph(glyphMode, edgeKind))} `;
|
|
80
|
+
const data = formatMigrationDataColumn(migration, {
|
|
81
|
+
dirNameWidth,
|
|
82
|
+
edgeKind,
|
|
83
|
+
style,
|
|
84
|
+
forwardArrow: migrationListForwardArrow(glyphMode),
|
|
85
|
+
emptySource: migrationListEmptySource(glyphMode),
|
|
86
|
+
});
|
|
87
|
+
return `${kindColumn}${data}`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function formatEmptyStateLine(spaceId: string, style: MigrationListStyler): string {
|
|
91
|
+
return style.emptyState(`There are no migrations in migrations/${spaceId}/ yet`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function renderSpaceBlock(
|
|
95
|
+
spaceId: string,
|
|
96
|
+
migrations: readonly MigrationListEntry[],
|
|
97
|
+
multiSpace: boolean,
|
|
98
|
+
glyphMode: GlyphMode,
|
|
99
|
+
kindByMigrationHash: ReadonlyMap<string, MigrationEdgeKind>,
|
|
100
|
+
style: MigrationListStyler,
|
|
101
|
+
): readonly string[] {
|
|
102
|
+
if (migrations.length === 0) {
|
|
103
|
+
const emptyLine = formatEmptyStateLine(spaceId, style);
|
|
104
|
+
if (!multiSpace) {
|
|
105
|
+
return [emptyLine];
|
|
106
|
+
}
|
|
107
|
+
return [style.spaceHeading(`${spaceId}:`), ` ${emptyLine}`];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const dirNameWidth = computeMigrationDirNameWidth(migrations);
|
|
111
|
+
const rows = migrations.map((entry) =>
|
|
112
|
+
formatMigrationRow(
|
|
113
|
+
entry,
|
|
114
|
+
dirNameWidth,
|
|
115
|
+
resolveEdgeKind(entry.migrationHash, kindByMigrationHash),
|
|
116
|
+
glyphMode,
|
|
117
|
+
style,
|
|
118
|
+
),
|
|
119
|
+
);
|
|
120
|
+
if (!multiSpace) {
|
|
121
|
+
return rows;
|
|
122
|
+
}
|
|
123
|
+
return [style.spaceHeading(`${spaceId}:`), ...rows.map((row) => ` ${row}`)];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function buildMigrationListTopologyBySpace(
|
|
127
|
+
result: MigrationListResult,
|
|
128
|
+
): ReadonlyMap<string, MigrationListGraphTopology> {
|
|
129
|
+
const topologyBySpaceId = new Map<string, MigrationListGraphTopology>();
|
|
130
|
+
for (const space of result.spaces) {
|
|
131
|
+
topologyBySpaceId.set(space.spaceId, classifyMigrationListGraphTopology(space.migrations));
|
|
132
|
+
}
|
|
133
|
+
return topologyBySpaceId;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Compose the styled `migration list` output. The renderer is
|
|
138
|
+
* presentation-neutral — every token passes through `style` before
|
|
139
|
+
* landing in the output, so the same composition serves the pure-text
|
|
140
|
+
* path ({@link renderMigrationList} via
|
|
141
|
+
* {@link IDENTITY_MIGRATION_LIST_STYLER}) and the ANSI-styled CLI path
|
|
142
|
+
* (via the ANSI styler the CLI shell wires up).
|
|
143
|
+
*/
|
|
144
|
+
export function renderMigrationListWithStyle(
|
|
145
|
+
result: MigrationListResult,
|
|
146
|
+
style: MigrationListStyler,
|
|
147
|
+
glyphMode: GlyphMode = 'unicode',
|
|
148
|
+
topologyBySpaceId: ReadonlyMap<
|
|
149
|
+
string,
|
|
150
|
+
MigrationListGraphTopology
|
|
151
|
+
> = buildMigrationListTopologyBySpace(result),
|
|
152
|
+
): string {
|
|
153
|
+
const multiSpace = result.spaces.length > 1;
|
|
154
|
+
const lines: string[] = [];
|
|
155
|
+
|
|
156
|
+
for (let index = 0; index < result.spaces.length; index++) {
|
|
157
|
+
const space = result.spaces[index]!;
|
|
158
|
+
if (index > 0) {
|
|
159
|
+
lines.push('');
|
|
160
|
+
}
|
|
161
|
+
const topology = topologyBySpaceId.get(space.spaceId);
|
|
162
|
+
const kindByMigrationHash =
|
|
163
|
+
topology?.kindByMigrationHash ??
|
|
164
|
+
classifyMigrationListGraphTopology(space.migrations).kindByMigrationHash;
|
|
165
|
+
lines.push(
|
|
166
|
+
...renderSpaceBlock(
|
|
167
|
+
space.spaceId,
|
|
168
|
+
space.migrations,
|
|
169
|
+
multiSpace,
|
|
170
|
+
glyphMode,
|
|
171
|
+
kindByMigrationHash,
|
|
172
|
+
style,
|
|
173
|
+
),
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const totalMigrations = result.spaces.reduce(
|
|
178
|
+
(count, space) => count + space.migrations.length,
|
|
179
|
+
0,
|
|
180
|
+
);
|
|
181
|
+
if (totalMigrations > 0) {
|
|
182
|
+
lines.push('');
|
|
183
|
+
lines.push(style.summary(result.summary));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return lines.join('\n');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export function renderMigrationList(result: MigrationListResult): string {
|
|
190
|
+
return renderMigrationListWithStyle(result, IDENTITY_MIGRATION_LIST_STYLER);
|
|
191
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { bold, cyan, cyanBright, dim, green, greenBright, yellow } from 'colorette';
|
|
2
|
+
import { IDENTITY_MIGRATION_LIST_STYLER, type MigrationListStyler } from './migration-list-render';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The reserved ref name for the live-database marker. Treated as a
|
|
6
|
+
* structurally distinct token from user-named refs so the styler can
|
|
7
|
+
* make it visually pop in `(refs)` decorations.
|
|
8
|
+
*/
|
|
9
|
+
const DB_REF_NAME = 'db';
|
|
10
|
+
|
|
11
|
+
function styleRefName(name: string): string {
|
|
12
|
+
return name === DB_REF_NAME ? bold(greenBright(name)) : green(name);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Build a {@link MigrationListStyler} that decorates `migration list`
|
|
17
|
+
* tokens with ANSI SGR codes. When `useColor` is `false` (non-TTY,
|
|
18
|
+
* `--no-color`, `NO_COLOR=1`, piped output) the function returns the
|
|
19
|
+
* shared identity styler so callers get plain text with zero ANSI
|
|
20
|
+
* bytes — pipe-friendly by construction.
|
|
21
|
+
*
|
|
22
|
+
* Palette:
|
|
23
|
+
*
|
|
24
|
+
* - `dirName`: bold
|
|
25
|
+
* - `sourceHash`: dim cyan
|
|
26
|
+
* - `destHash`: bright cyan
|
|
27
|
+
* - `kind` (`*` / `↩` / `⟲`): bright — the signal; lanes and arrows dim
|
|
28
|
+
* - `glyph` (`→` / `⟲` / `∅`): dim
|
|
29
|
+
* - `lane` (graph gutter lines `│` and fan/join connectors `├─┐` / `├─┘`): dim
|
|
30
|
+
* - `invariants` (`{...}`): yellow
|
|
31
|
+
* - `refs` (`(...)`): green; the live-DB `db` marker inside is green-bold
|
|
32
|
+
* - `spaceHeading` (`<spaceId>:`): bold
|
|
33
|
+
* - `summary`: dim
|
|
34
|
+
* - `emptyState`: dim
|
|
35
|
+
*/
|
|
36
|
+
export function createAnsiMigrationListStyler(opts: {
|
|
37
|
+
readonly useColor: boolean;
|
|
38
|
+
}): MigrationListStyler {
|
|
39
|
+
if (!opts.useColor) {
|
|
40
|
+
return IDENTITY_MIGRATION_LIST_STYLER;
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
// Kind glyphs stay bright in both flat and graph views; lanes carry the dim gutter.
|
|
44
|
+
kind: (text) => text,
|
|
45
|
+
dirName: (text) => bold(text),
|
|
46
|
+
sourceHash: (text) => dim(cyan(text)),
|
|
47
|
+
destHash: (text) => cyanBright(text),
|
|
48
|
+
glyph: (text) => dim(text),
|
|
49
|
+
lane: (text) => dim(text),
|
|
50
|
+
invariants: (ids) => yellow(`{${ids.join(', ')}}`),
|
|
51
|
+
refs: (names) => {
|
|
52
|
+
const open = green('(');
|
|
53
|
+
const close = green(')');
|
|
54
|
+
const separator = green(', ');
|
|
55
|
+
return open + names.map(styleRefName).join(separator) + close;
|
|
56
|
+
},
|
|
57
|
+
spaceHeading: (text) => bold(text),
|
|
58
|
+
summary: (text) => dim(text),
|
|
59
|
+
emptyState: (text) => dim(text),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface MigrationListEntry {
|
|
2
|
+
readonly dirName: string;
|
|
3
|
+
readonly from: string | null;
|
|
4
|
+
readonly to: string;
|
|
5
|
+
readonly migrationHash: string;
|
|
6
|
+
readonly operationCount: number;
|
|
7
|
+
readonly createdAt: string;
|
|
8
|
+
readonly refs: readonly string[];
|
|
9
|
+
readonly providedInvariants: readonly string[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface MigrationSpaceListEntry {
|
|
13
|
+
readonly spaceId: string;
|
|
14
|
+
readonly migrations: readonly MigrationListEntry[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface MigrationListResult {
|
|
18
|
+
readonly ok: true;
|
|
19
|
+
readonly spaces: readonly MigrationSpaceListEntry[];
|
|
20
|
+
readonly summary: string;
|
|
21
|
+
}
|
|
@@ -82,6 +82,8 @@ export interface MigrationCommandResult {
|
|
|
82
82
|
* into a single ambiguous list. See {@link AggregatePerSpaceExecutionEntry}.
|
|
83
83
|
*/
|
|
84
84
|
readonly perSpace?: ReadonlyArray<AggregatePerSpaceExecutionEntry>;
|
|
85
|
+
readonly advancedRef?: { readonly name: string; readonly hash: string } | null;
|
|
86
|
+
readonly plannedAdvanceRef?: { readonly name: string; readonly hash: string } | null;
|
|
85
87
|
readonly summary: string;
|
|
86
88
|
readonly timings: {
|
|
87
89
|
readonly total: number;
|
|
@@ -208,6 +210,15 @@ export function formatMigrationPlanOutput(
|
|
|
208
210
|
lines.push(`${formatDimText(`Destination hash: ${result.plan.destination.storageHash}`)}`);
|
|
209
211
|
}
|
|
210
212
|
|
|
213
|
+
if (result.plannedAdvanceRef) {
|
|
214
|
+
lines.push('');
|
|
215
|
+
lines.push(
|
|
216
|
+
formatDimText(
|
|
217
|
+
`Would advance ref "${result.plannedAdvanceRef.name}" → ${result.plannedAdvanceRef.hash}`,
|
|
218
|
+
),
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
211
222
|
// Statement preview (any family that implements OperationPreviewCapable)
|
|
212
223
|
const preview = result.plan?.preview;
|
|
213
224
|
if (preview) {
|
|
@@ -260,6 +271,7 @@ export interface MigrationApplyCommandOutputResult {
|
|
|
260
271
|
readonly timings?: {
|
|
261
272
|
readonly total: number;
|
|
262
273
|
};
|
|
274
|
+
readonly advancedRef?: { readonly name: string; readonly hash: string } | null;
|
|
263
275
|
}
|
|
264
276
|
|
|
265
277
|
export function formatMigrationApplyCommandOutput(
|
|
@@ -284,6 +296,13 @@ export function formatMigrationApplyCommandOutput(
|
|
|
284
296
|
}
|
|
285
297
|
}
|
|
286
298
|
|
|
299
|
+
if (result.advancedRef) {
|
|
300
|
+
lines.push('');
|
|
301
|
+
lines.push(
|
|
302
|
+
formatDimText(`Advanced ref "${result.advancedRef.name}" → ${result.advancedRef.hash}`),
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
|
|
287
306
|
lines.push('');
|
|
288
307
|
lines.push(formatDimText('Next: prisma-next migration status'));
|
|
289
308
|
|
|
@@ -295,9 +314,7 @@ export function formatMigrationApplyCommandOutput(
|
|
|
295
314
|
return lines.join('\n');
|
|
296
315
|
}
|
|
297
316
|
|
|
298
|
-
interface
|
|
299
|
-
readonly kind: 'present';
|
|
300
|
-
readonly spaceId: string;
|
|
317
|
+
interface MigrationShowPresent {
|
|
301
318
|
readonly dirName: string;
|
|
302
319
|
readonly dirPath: string;
|
|
303
320
|
readonly from: string | null;
|
|
@@ -313,22 +330,11 @@ interface MigrationShowSpacePresent {
|
|
|
313
330
|
readonly summary: string;
|
|
314
331
|
}
|
|
315
332
|
|
|
316
|
-
interface MigrationShowSpaceMissing {
|
|
317
|
-
readonly kind: 'missing';
|
|
318
|
-
readonly spaceId: string;
|
|
319
|
-
readonly summary: string;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
type MigrationShowSpaceResult = MigrationShowSpacePresent | MigrationShowSpaceMissing;
|
|
323
|
-
|
|
324
333
|
interface MigrationShowResult {
|
|
325
|
-
readonly
|
|
334
|
+
readonly migration: MigrationShowPresent;
|
|
326
335
|
}
|
|
327
336
|
|
|
328
|
-
function formatSpaceShowBlock(
|
|
329
|
-
space: MigrationShowSpacePresent,
|
|
330
|
-
useColor: boolean,
|
|
331
|
-
): readonly string[] {
|
|
337
|
+
function formatSpaceShowBlock(space: MigrationShowPresent, useColor: boolean): readonly string[] {
|
|
332
338
|
const formatGreen = createColorFormatter(useColor, green);
|
|
333
339
|
const formatYellow = createColorFormatter(useColor, yellow);
|
|
334
340
|
const formatDimText = (text: string) => formatDim(useColor, text);
|
|
@@ -384,28 +390,7 @@ export function formatMigrationShowOutput(result: MigrationShowResult, flags: Gl
|
|
|
384
390
|
}
|
|
385
391
|
|
|
386
392
|
const useColor = flags.color !== false;
|
|
387
|
-
|
|
388
|
-
const multipleSpaces = result.spaces.length > 1;
|
|
389
|
-
const lines: string[] = [];
|
|
390
|
-
|
|
391
|
-
for (let i = 0; i < result.spaces.length; i++) {
|
|
392
|
-
const space = result.spaces[i]!;
|
|
393
|
-
if (multipleSpaces) {
|
|
394
|
-
lines.push(formatDimText(`── ${space.spaceId} ──`));
|
|
395
|
-
}
|
|
396
|
-
if (space.kind === 'missing') {
|
|
397
|
-
lines.push(formatDimText(` ${space.summary}`));
|
|
398
|
-
} else {
|
|
399
|
-
for (const line of formatSpaceShowBlock(space, useColor)) {
|
|
400
|
-
lines.push(line);
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
if (i < result.spaces.length - 1) {
|
|
404
|
-
lines.push('');
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
return lines.join('\n');
|
|
393
|
+
return formatSpaceShowBlock(result.migration, useColor).join('\n');
|
|
409
394
|
}
|
|
410
395
|
|
|
411
396
|
/**
|
|
@@ -470,6 +455,12 @@ export function formatMigrationApplyOutput(
|
|
|
470
455
|
if (isVerbose(flags, 1)) {
|
|
471
456
|
lines.push(`${formatDimText(` Total time: ${result.timings.total}ms`)}`);
|
|
472
457
|
}
|
|
458
|
+
|
|
459
|
+
if (result.advancedRef) {
|
|
460
|
+
lines.push(
|
|
461
|
+
formatDimText(`Advanced ref "${result.advancedRef.name}" → ${result.advancedRef.hash}`),
|
|
462
|
+
);
|
|
463
|
+
}
|
|
473
464
|
}
|
|
474
465
|
|
|
475
466
|
return lines.join('\n');
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type GlyphMode = 'unicode' | 'ascii';
|
|
2
|
+
|
|
3
|
+
export interface GlyphModeInput {
|
|
4
|
+
readonly isTTY: boolean;
|
|
5
|
+
readonly env: Readonly<Record<string, string | undefined>>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function localeString(env: Readonly<Record<string, string | undefined>>): string {
|
|
9
|
+
return env['LC_ALL'] ?? env['LC_CTYPE'] ?? env['LANG'] ?? '';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function isUtf8Locale(env: Readonly<Record<string, string | undefined>>): boolean {
|
|
13
|
+
const locale = localeString(env);
|
|
14
|
+
if (locale.length === 0) return false;
|
|
15
|
+
return /UTF-8|utf8/i.test(locale);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function detectGlyphMode(input: GlyphModeInput): GlyphMode {
|
|
19
|
+
if (!input.isTTY) return 'ascii';
|
|
20
|
+
if (!isUtf8Locale(input.env)) return 'ascii';
|
|
21
|
+
return 'unicode';
|
|
22
|
+
}
|