@prisma-next/migration-tools 0.11.0-dev.43 → 0.11.0-dev.45
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/exports/aggregate.d.mts +3 -3
- package/dist/exports/aggregate.mjs +1 -1
- package/dist/exports/enumerate-migration-spaces.d.mts +1 -1
- package/dist/exports/graph.d.mts +1 -1
- package/dist/exports/hash.d.mts +2 -2
- package/dist/exports/invariants.d.mts +1 -1
- package/dist/exports/io.d.mts +1 -1
- package/dist/exports/metadata.d.mts +1 -1
- package/dist/exports/migration-graph.d.mts +2 -2
- package/dist/exports/migration-list-graph-layout.d.mts +34 -0
- package/dist/exports/migration-list-graph-layout.d.mts.map +1 -0
- package/dist/exports/migration-list-graph-layout.mjs +170 -0
- package/dist/exports/migration-list-graph-layout.mjs.map +1 -0
- package/dist/exports/migration-list-graph-topology.d.mts +2 -0
- package/dist/exports/migration-list-graph-topology.mjs +2 -0
- package/dist/exports/migration-list-types.d.mts +1 -1
- package/dist/exports/migration.d.mts +1 -1
- package/dist/exports/package.d.mts +1 -1
- package/dist/exports/ref-resolution.d.mts +2 -2
- package/dist/exports/refs.d.mts +1 -1
- package/dist/exports/spaces.d.mts +1 -1
- package/dist/exports/spaces.mjs +1 -1
- package/dist/{graph-ZAIvH6p4.d.mts → graph-B0LIIjIu.d.mts} +1 -1
- package/dist/{graph-ZAIvH6p4.d.mts.map → graph-B0LIIjIu.d.mts.map} +1 -1
- package/dist/{migration-graph-D97XMWVH.d.mts → migration-graph-fl5ChjXE.d.mts} +3 -3
- package/dist/{migration-graph-D97XMWVH.d.mts.map → migration-graph-fl5ChjXE.d.mts.map} +1 -1
- package/dist/migration-list-graph-topology-BtFzbZ9L.mjs +103 -0
- package/dist/migration-list-graph-topology-BtFzbZ9L.mjs.map +1 -0
- package/dist/migration-list-graph-topology-CafEnhPT.d.mts +13 -0
- package/dist/migration-list-graph-topology-CafEnhPT.d.mts.map +1 -0
- package/dist/{migration-list-types-B-qimPet.d.mts → migration-list-types-wLyb3E-p.d.mts} +1 -1
- package/dist/{migration-list-types-B-qimPet.d.mts.map → migration-list-types-wLyb3E-p.d.mts.map} +1 -1
- package/dist/{package-DIttKL7X.d.mts → package-Ca-J_z_0.d.mts} +1 -1
- package/dist/{package-DIttKL7X.d.mts.map → package-Ca-J_z_0.d.mts.map} +1 -1
- package/dist/{read-contract-space-contract-_EvvV5Gl.mjs → read-contract-space-contract-C4fEdoXO.mjs} +1 -1
- package/dist/{read-contract-space-contract-_EvvV5Gl.mjs.map → read-contract-space-contract-C4fEdoXO.mjs.map} +1 -1
- package/dist/{refs-BaygQaFD.d.mts → refs-D8xBNqs7.d.mts} +1 -1
- package/dist/{refs-BaygQaFD.d.mts.map → refs-D8xBNqs7.d.mts.map} +1 -1
- package/package.json +14 -6
- package/src/exports/migration-list-graph-layout.ts +9 -0
- package/src/exports/migration-list-graph-topology.ts +2 -0
- package/src/migration-list-graph-layout.ts +256 -0
- package/src/migration-list-graph-topology.ts +156 -0
- /package/dist/{metadata-BCH1rNsk.d.mts → metadata-COhIQCiH.d.mts} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { n as OnDiskMigrationPackage } from "../package-
|
|
2
|
-
import { n as MigrationGraph } from "../graph-
|
|
3
|
-
import { t as PathDecision } from "../migration-graph-
|
|
1
|
+
import { n as OnDiskMigrationPackage } from "../package-Ca-J_z_0.mjs";
|
|
2
|
+
import { n as MigrationGraph } from "../graph-B0LIIjIu.mjs";
|
|
3
|
+
import { t as PathDecision } from "../migration-graph-fl5ChjXE.mjs";
|
|
4
4
|
import { ControlFamilyInstance, MigrationOperationPolicy, MigrationPlan, MigrationPlanOperation, MigrationPlannerConflict, TargetMigrationsCapability } from "@prisma-next/framework-components/control";
|
|
5
5
|
import { Result } from "@prisma-next/utils/result";
|
|
6
6
|
import { Contract } from "@prisma-next/contract/types";
|
|
@@ -3,7 +3,7 @@ import { s as readMigrationsDir } from "../io-Dc64lvaL.mjs";
|
|
|
3
3
|
import "../constants-DWV9_o2Z.mjs";
|
|
4
4
|
import { l as reconstructGraph, o as findPathWithDecision } from "../migration-graph-D5JeadSE.mjs";
|
|
5
5
|
import { c as spaceMigrationDirectory, r as APP_SPACE_ID, t as listContractSpaceDirectories } from "../verify-contract-spaces-DIdQLGo7.mjs";
|
|
6
|
-
import { n as readContractSpaceHeadRef, t as readContractSpaceContract } from "../read-contract-space-contract-
|
|
6
|
+
import { n as readContractSpaceHeadRef, t as readContractSpaceContract } from "../read-contract-space-contract-C4fEdoXO.mjs";
|
|
7
7
|
import { notOk, ok } from "@prisma-next/utils/result";
|
|
8
8
|
//#region src/aggregate/extract-storage-element-names.ts
|
|
9
9
|
/**
|
package/dist/exports/graph.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as MigrationGraph, t as MigrationEdge } from "../graph-
|
|
1
|
+
import { n as MigrationGraph, t as MigrationEdge } from "../graph-B0LIIjIu.mjs";
|
|
2
2
|
export { type MigrationEdge, type MigrationGraph };
|
package/dist/exports/hash.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as OnDiskMigrationPackage, t as MigrationOps } from "../package-
|
|
2
|
-
import { t as MigrationMetadata } from "../metadata-
|
|
1
|
+
import { n as OnDiskMigrationPackage, t as MigrationOps } from "../package-Ca-J_z_0.mjs";
|
|
2
|
+
import { t as MigrationMetadata } from "../metadata-COhIQCiH.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/hash.d.ts
|
|
5
5
|
interface VerifyResult {
|
package/dist/exports/io.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as OnDiskMigrationPackage, t as MigrationOps } from "../package-
|
|
1
|
+
import { n as OnDiskMigrationPackage, t as MigrationOps } from "../package-Ca-J_z_0.mjs";
|
|
2
2
|
import { MigrationMetadata, MigrationPackage } from "@prisma-next/framework-components/control";
|
|
3
3
|
|
|
4
4
|
//#region src/io.d.ts
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as MigrationMetadata } from "../metadata-
|
|
1
|
+
import { t as MigrationMetadata } from "../metadata-COhIQCiH.mjs";
|
|
2
2
|
export { type MigrationMetadata };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as MigrationGraph } from "../graph-
|
|
2
|
-
import { a as findLeaf, c as findPathWithInvariants, i as findLatestMigration, l as findReachableLeaves, n as detectCycles, o as findPath, r as detectOrphans, s as findPathWithDecision, t as PathDecision, u as reconstructGraph } from "../migration-graph-
|
|
1
|
+
import { n as MigrationGraph } from "../graph-B0LIIjIu.mjs";
|
|
2
|
+
import { a as findLeaf, c as findPathWithInvariants, i as findLatestMigration, l as findReachableLeaves, n as detectCycles, o as findPath, r as detectOrphans, s as findPathWithDecision, t as PathDecision, u as reconstructGraph } from "../migration-graph-fl5ChjXE.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/graph-membership.d.ts
|
|
5
5
|
declare function isGraphNode(hash: string, graph: MigrationGraph): boolean;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { t as MigrationListEntry } from "../migration-list-types-wLyb3E-p.mjs";
|
|
2
|
+
import { t as EdgeKind } from "../migration-list-graph-topology-CafEnhPT.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/migration-list-graph-layout.d.ts
|
|
5
|
+
type ConnectorKind = 'fanBelow' | 'joinBelow';
|
|
6
|
+
interface MigrationLayoutRow {
|
|
7
|
+
readonly kind: 'migration';
|
|
8
|
+
readonly entry: MigrationListEntry;
|
|
9
|
+
readonly edgeKind: EdgeKind;
|
|
10
|
+
readonly laneIndex: number;
|
|
11
|
+
readonly passThroughLanes: readonly number[];
|
|
12
|
+
readonly woven: boolean;
|
|
13
|
+
}
|
|
14
|
+
interface NodeLineLayoutRow {
|
|
15
|
+
readonly kind: 'nodeLine';
|
|
16
|
+
readonly contractHash: string;
|
|
17
|
+
readonly laneIndex: number;
|
|
18
|
+
}
|
|
19
|
+
interface ConnectorLayoutRow {
|
|
20
|
+
readonly kind: 'connector';
|
|
21
|
+
readonly connectorKind: ConnectorKind;
|
|
22
|
+
readonly contractHash: string;
|
|
23
|
+
readonly startLane: number;
|
|
24
|
+
readonly endLane: number;
|
|
25
|
+
readonly branchCount: number;
|
|
26
|
+
}
|
|
27
|
+
type LayoutRow = MigrationLayoutRow | NodeLineLayoutRow | ConnectorLayoutRow;
|
|
28
|
+
interface MigrationListGraphLayout {
|
|
29
|
+
readonly rows: readonly LayoutRow[];
|
|
30
|
+
}
|
|
31
|
+
declare function computeMigrationListGraphLayout(entries: readonly MigrationListEntry[]): MigrationListGraphLayout;
|
|
32
|
+
//#endregion
|
|
33
|
+
export { type ConnectorKind, type ConnectorLayoutRow, type LayoutRow, type MigrationLayoutRow, type MigrationListGraphLayout, type NodeLineLayoutRow, computeMigrationListGraphLayout };
|
|
34
|
+
//# sourceMappingURL=migration-list-graph-layout.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-list-graph-layout.d.mts","names":[],"sources":["../../src/migration-list-graph-layout.ts"],"mappings":";;;;KAIY,aAAA;AAAA,UAEK,kBAAA;EAAA,SACN,IAAA;EAAA,SACA,KAAA,EAAO,kBAAA;EAAA,SACP,QAAA,EAAU,QAAQ;EAAA,SAClB,SAAA;EAAA,SACA,gBAAA;EAAA,SACA,KAAA;AAAA;AAAA,UAGM,iBAAA;EAAA,SACN,IAAA;EAAA,SACA,YAAA;EAAA,SACA,SAAA;AAAA;AAAA,UAGM,kBAAA;EAAA,SACN,IAAA;EAAA,SACA,aAAA,EAAe,aAAa;EAAA,SAC5B,YAAA;EAAA,SACA,SAAA;EAAA,SACA,OAAA;EAAA,SACA,WAAA;AAAA;AAAA,KAGC,SAAA,GAAY,kBAAA,GAAqB,iBAAA,GAAoB,kBAAA;AAAA,UAEhD,wBAAA;EAAA,SACN,IAAA,WAAe,SAAS;AAAA;AAAA,iBAoDnB,+BAAA,CACd,OAAA,WAAkB,kBAAA,KACjB,wBAAwB"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import "../constants-DWV9_o2Z.mjs";
|
|
2
|
+
import { t as classifyMigrationListGraphTopology } from "../migration-list-graph-topology-BtFzbZ9L.mjs";
|
|
3
|
+
//#region src/migration-list-graph-layout.ts
|
|
4
|
+
function canonicalFrom(from) {
|
|
5
|
+
return from ?? "sha256:empty";
|
|
6
|
+
}
|
|
7
|
+
function canonicalTo(entry) {
|
|
8
|
+
return entry.to;
|
|
9
|
+
}
|
|
10
|
+
function forwardInDegree(topology, hash) {
|
|
11
|
+
return topology.forwardInDegree.get(hash) ?? 0;
|
|
12
|
+
}
|
|
13
|
+
function forwardOutDegree(topology, hash) {
|
|
14
|
+
return topology.forwardOutDegree.get(hash) ?? 0;
|
|
15
|
+
}
|
|
16
|
+
function buildForwardProducersByTo(entries, kindByMigrationHash) {
|
|
17
|
+
const byTo = /* @__PURE__ */ new Map();
|
|
18
|
+
for (const entry of entries) {
|
|
19
|
+
if (kindByMigrationHash.get(entry.migrationHash) !== "forward") continue;
|
|
20
|
+
const to = canonicalTo(entry);
|
|
21
|
+
const bucket = byTo.get(to);
|
|
22
|
+
if (bucket) bucket.push(entry);
|
|
23
|
+
else byTo.set(to, [entry]);
|
|
24
|
+
}
|
|
25
|
+
return byTo;
|
|
26
|
+
}
|
|
27
|
+
function countForwardProducersTo(forwardProducersByTo, contract) {
|
|
28
|
+
return forwardProducersByTo.get(contract)?.length ?? 0;
|
|
29
|
+
}
|
|
30
|
+
function computeMigrationListGraphLayout(entries) {
|
|
31
|
+
const topology = classifyMigrationListGraphTopology(entries);
|
|
32
|
+
const { kindByMigrationHash } = topology;
|
|
33
|
+
const forwardProducersByTo = buildForwardProducersByTo(entries, kindByMigrationHash);
|
|
34
|
+
const convergencesEmitted = /* @__PURE__ */ new Set();
|
|
35
|
+
const producerLaneByHash = /* @__PURE__ */ new Map();
|
|
36
|
+
const lanes = [];
|
|
37
|
+
const rows = [];
|
|
38
|
+
function emitNodeLine(contractHash) {
|
|
39
|
+
rows.push({
|
|
40
|
+
kind: "nodeLine",
|
|
41
|
+
contractHash,
|
|
42
|
+
laneIndex: 0
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
function emitConnector(connectorKind, contractHash, startLane, endLane, branchCount) {
|
|
46
|
+
rows.push({
|
|
47
|
+
kind: "connector",
|
|
48
|
+
connectorKind,
|
|
49
|
+
contractHash,
|
|
50
|
+
startLane,
|
|
51
|
+
endLane,
|
|
52
|
+
branchCount
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function activeLaneIndices() {
|
|
56
|
+
const indices = [];
|
|
57
|
+
for (let index = 0; index < lanes.length; index++) if (lanes[index]?.active) indices.push(index);
|
|
58
|
+
return indices;
|
|
59
|
+
}
|
|
60
|
+
function lanesWanting(contract) {
|
|
61
|
+
const indices = [];
|
|
62
|
+
for (let index = 0; index < lanes.length; index++) {
|
|
63
|
+
const lane = lanes[index];
|
|
64
|
+
if (lane?.active && lane.want === contract) indices.push(index);
|
|
65
|
+
}
|
|
66
|
+
return indices;
|
|
67
|
+
}
|
|
68
|
+
function ensureLane(index) {
|
|
69
|
+
while (lanes.length <= index) lanes.push({
|
|
70
|
+
want: "",
|
|
71
|
+
active: false
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
function openLaneAtRight(want) {
|
|
75
|
+
const index = lanes.length;
|
|
76
|
+
lanes.push({
|
|
77
|
+
want,
|
|
78
|
+
active: true
|
|
79
|
+
});
|
|
80
|
+
return index;
|
|
81
|
+
}
|
|
82
|
+
function closeLane(index) {
|
|
83
|
+
ensureLane(index);
|
|
84
|
+
const lane = lanes[index];
|
|
85
|
+
if (lane) lane.active = false;
|
|
86
|
+
}
|
|
87
|
+
function emitJoinBelow(contractHash, laneIndices) {
|
|
88
|
+
if (laneIndices.length < 2) return;
|
|
89
|
+
const startLane = Math.min(...laneIndices);
|
|
90
|
+
emitConnector("joinBelow", contractHash, startLane, Math.max(...laneIndices), laneIndices.length);
|
|
91
|
+
for (const index of laneIndices) if (index !== startLane) closeLane(index);
|
|
92
|
+
}
|
|
93
|
+
function emitConvergencePreamble(contract) {
|
|
94
|
+
if (convergencesEmitted.has(contract)) return;
|
|
95
|
+
if (forwardInDegree(topology, contract) < 2) return;
|
|
96
|
+
const consumersWanting = lanesWanting(contract);
|
|
97
|
+
if (forwardOutDegree(topology, contract) >= 2 && consumersWanting.length >= 2) emitJoinBelow(contract, consumersWanting);
|
|
98
|
+
emitNodeLine(contract);
|
|
99
|
+
const producers = forwardProducersByTo.get(contract) ?? [];
|
|
100
|
+
if (producers.length >= 2) emitConnector("fanBelow", contract, 0, producers.length - 1, producers.length);
|
|
101
|
+
for (const [producerIndex, producer] of producers.entries()) {
|
|
102
|
+
ensureLane(producerIndex);
|
|
103
|
+
lanes[producerIndex] = {
|
|
104
|
+
want: canonicalFrom(producer.from),
|
|
105
|
+
active: true
|
|
106
|
+
};
|
|
107
|
+
producerLaneByHash.set(producer.migrationHash, producerIndex);
|
|
108
|
+
}
|
|
109
|
+
convergencesEmitted.add(contract);
|
|
110
|
+
}
|
|
111
|
+
function assignProducerLane(entry) {
|
|
112
|
+
const preset = producerLaneByHash.get(entry.migrationHash);
|
|
113
|
+
if (preset !== void 0) return preset;
|
|
114
|
+
}
|
|
115
|
+
function placeWoven(entry, edgeKind, laneIndex) {
|
|
116
|
+
const passThroughLanes = activeLaneIndices().filter((index) => index !== laneIndex);
|
|
117
|
+
rows.push({
|
|
118
|
+
kind: "migration",
|
|
119
|
+
entry,
|
|
120
|
+
edgeKind,
|
|
121
|
+
laneIndex,
|
|
122
|
+
passThroughLanes,
|
|
123
|
+
woven: true
|
|
124
|
+
});
|
|
125
|
+
ensureLane(laneIndex);
|
|
126
|
+
lanes[laneIndex] = {
|
|
127
|
+
want: canonicalFrom(entry.from),
|
|
128
|
+
active: true
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function placeUnwoven(entry, edgeKind) {
|
|
132
|
+
const passThroughLanes = activeLaneIndices();
|
|
133
|
+
const laneIndex = passThroughLanes.length === 0 ? 0 : Math.max(...passThroughLanes) + 1;
|
|
134
|
+
rows.push({
|
|
135
|
+
kind: "migration",
|
|
136
|
+
entry,
|
|
137
|
+
edgeKind,
|
|
138
|
+
laneIndex,
|
|
139
|
+
passThroughLanes,
|
|
140
|
+
woven: false
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
for (const entry of entries) {
|
|
144
|
+
const edgeKind = kindByMigrationHash.get(entry.migrationHash) ?? "forward";
|
|
145
|
+
const to = canonicalTo(entry);
|
|
146
|
+
if (edgeKind !== "forward") {
|
|
147
|
+
placeUnwoven(entry, edgeKind);
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
if (forwardInDegree(topology, to) >= 2 && !convergencesEmitted.has(to)) emitConvergencePreamble(to);
|
|
151
|
+
const presetLane = assignProducerLane(entry);
|
|
152
|
+
const wantingTo = lanesWanting(to);
|
|
153
|
+
if (wantingTo.length >= 2 && countForwardProducersTo(forwardProducersByTo, to) === 1) emitJoinBelow(to, wantingTo);
|
|
154
|
+
if (presetLane !== void 0) {
|
|
155
|
+
placeWoven(entry, edgeKind, presetLane);
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
const firstWanting = wantingTo[0];
|
|
159
|
+
if (firstWanting !== void 0) {
|
|
160
|
+
placeWoven(entry, edgeKind, firstWanting);
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
placeWoven(entry, edgeKind, openLaneAtRight(canonicalFrom(entry.from)));
|
|
164
|
+
}
|
|
165
|
+
return { rows };
|
|
166
|
+
}
|
|
167
|
+
//#endregion
|
|
168
|
+
export { computeMigrationListGraphLayout };
|
|
169
|
+
|
|
170
|
+
//# sourceMappingURL=migration-list-graph-layout.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-list-graph-layout.mjs","names":[],"sources":["../../src/migration-list-graph-layout.ts"],"sourcesContent":["import { EMPTY_CONTRACT_HASH } from './constants';\nimport { classifyMigrationListGraphTopology, type EdgeKind } from './migration-list-graph-topology';\nimport type { MigrationListEntry } from './migration-list-types';\n\nexport type ConnectorKind = 'fanBelow' | 'joinBelow';\n\nexport interface MigrationLayoutRow {\n readonly kind: 'migration';\n readonly entry: MigrationListEntry;\n readonly edgeKind: EdgeKind;\n readonly laneIndex: number;\n readonly passThroughLanes: readonly number[];\n readonly woven: boolean;\n}\n\nexport interface NodeLineLayoutRow {\n readonly kind: 'nodeLine';\n readonly contractHash: string;\n readonly laneIndex: number;\n}\n\nexport interface ConnectorLayoutRow {\n readonly kind: 'connector';\n readonly connectorKind: ConnectorKind;\n readonly contractHash: string;\n readonly startLane: number;\n readonly endLane: number;\n readonly branchCount: number;\n}\n\nexport type LayoutRow = MigrationLayoutRow | NodeLineLayoutRow | ConnectorLayoutRow;\n\nexport interface MigrationListGraphLayout {\n readonly rows: readonly LayoutRow[];\n}\n\ninterface LaneState {\n want: string;\n active: boolean;\n}\n\nfunction canonicalFrom(from: string | null): string {\n return from ?? EMPTY_CONTRACT_HASH;\n}\n\nfunction canonicalTo(entry: MigrationListEntry): string {\n return entry.to;\n}\n\nfunction forwardInDegree(\n topology: ReturnType<typeof classifyMigrationListGraphTopology>,\n hash: string,\n): number {\n return topology.forwardInDegree.get(hash) ?? 0;\n}\n\nfunction forwardOutDegree(\n topology: ReturnType<typeof classifyMigrationListGraphTopology>,\n hash: string,\n): number {\n return topology.forwardOutDegree.get(hash) ?? 0;\n}\n\nfunction buildForwardProducersByTo(\n entries: readonly MigrationListEntry[],\n kindByMigrationHash: ReadonlyMap<string, EdgeKind>,\n): Map<string, MigrationListEntry[]> {\n const byTo = new Map<string, MigrationListEntry[]>();\n for (const entry of entries) {\n if (kindByMigrationHash.get(entry.migrationHash) !== 'forward') continue;\n const to = canonicalTo(entry);\n const bucket = byTo.get(to);\n if (bucket) bucket.push(entry);\n else byTo.set(to, [entry]);\n }\n return byTo;\n}\n\nfunction countForwardProducersTo(\n forwardProducersByTo: Map<string, MigrationListEntry[]>,\n contract: string,\n): number {\n return forwardProducersByTo.get(contract)?.length ?? 0;\n}\n\nexport function computeMigrationListGraphLayout(\n entries: readonly MigrationListEntry[],\n): MigrationListGraphLayout {\n const topology = classifyMigrationListGraphTopology(entries);\n const { kindByMigrationHash } = topology;\n const forwardProducersByTo = buildForwardProducersByTo(entries, kindByMigrationHash);\n const convergencesEmitted = new Set<string>();\n const producerLaneByHash = new Map<string, number>();\n const lanes: LaneState[] = [];\n const rows: LayoutRow[] = [];\n\n function emitNodeLine(contractHash: string): void {\n rows.push({ kind: 'nodeLine', contractHash, laneIndex: 0 });\n }\n\n function emitConnector(\n connectorKind: ConnectorKind,\n contractHash: string,\n startLane: number,\n endLane: number,\n branchCount: number,\n ): void {\n rows.push({\n kind: 'connector',\n connectorKind,\n contractHash,\n startLane,\n endLane,\n branchCount,\n });\n }\n\n function activeLaneIndices(): number[] {\n const indices: number[] = [];\n for (let index = 0; index < lanes.length; index++) {\n if (lanes[index]?.active) indices.push(index);\n }\n return indices;\n }\n\n function lanesWanting(contract: string): number[] {\n const indices: number[] = [];\n for (let index = 0; index < lanes.length; index++) {\n const lane = lanes[index];\n if (lane?.active && lane.want === contract) indices.push(index);\n }\n return indices;\n }\n\n function ensureLane(index: number): void {\n while (lanes.length <= index) {\n lanes.push({ want: '', active: false });\n }\n }\n\n function openLaneAtRight(want: string): number {\n const index = lanes.length;\n lanes.push({ want, active: true });\n return index;\n }\n\n function closeLane(index: number): void {\n ensureLane(index);\n const lane = lanes[index];\n if (lane) lane.active = false;\n }\n\n function emitJoinBelow(contractHash: string, laneIndices: readonly number[]): void {\n if (laneIndices.length < 2) return;\n const startLane = Math.min(...laneIndices);\n const endLane = Math.max(...laneIndices);\n emitConnector('joinBelow', contractHash, startLane, endLane, laneIndices.length);\n for (const index of laneIndices) {\n if (index !== startLane) closeLane(index);\n }\n }\n\n function emitConvergencePreamble(contract: string): void {\n if (convergencesEmitted.has(contract)) return;\n if (forwardInDegree(topology, contract) < 2) return;\n\n const consumersWanting = lanesWanting(contract);\n if (forwardOutDegree(topology, contract) >= 2 && consumersWanting.length >= 2) {\n emitJoinBelow(contract, consumersWanting);\n }\n\n emitNodeLine(contract);\n const producers = forwardProducersByTo.get(contract) ?? [];\n if (producers.length >= 2) {\n emitConnector('fanBelow', contract, 0, producers.length - 1, producers.length);\n }\n\n for (const [producerIndex, producer] of producers.entries()) {\n ensureLane(producerIndex);\n lanes[producerIndex] = { want: canonicalFrom(producer.from), active: true };\n producerLaneByHash.set(producer.migrationHash, producerIndex);\n }\n\n convergencesEmitted.add(contract);\n }\n\n function assignProducerLane(entry: MigrationListEntry): number | undefined {\n const preset = producerLaneByHash.get(entry.migrationHash);\n if (preset !== undefined) return preset;\n return undefined;\n }\n\n function placeWoven(entry: MigrationListEntry, edgeKind: EdgeKind, laneIndex: number): void {\n const passThroughLanes = activeLaneIndices().filter((index) => index !== laneIndex);\n rows.push({\n kind: 'migration',\n entry,\n edgeKind,\n laneIndex,\n passThroughLanes,\n woven: true,\n });\n ensureLane(laneIndex);\n lanes[laneIndex] = { want: canonicalFrom(entry.from), active: true };\n }\n\n function placeUnwoven(entry: MigrationListEntry, edgeKind: EdgeKind): void {\n const passThroughLanes = activeLaneIndices();\n const laneIndex = passThroughLanes.length === 0 ? 0 : Math.max(...passThroughLanes) + 1;\n rows.push({\n kind: 'migration',\n entry,\n edgeKind,\n laneIndex,\n passThroughLanes,\n woven: false,\n });\n }\n\n for (const entry of entries) {\n const edgeKind = kindByMigrationHash.get(entry.migrationHash) ?? 'forward';\n const to = canonicalTo(entry);\n\n if (edgeKind !== 'forward') {\n placeUnwoven(entry, edgeKind);\n continue;\n }\n\n if (forwardInDegree(topology, to) >= 2 && !convergencesEmitted.has(to)) {\n emitConvergencePreamble(to);\n }\n\n const presetLane = assignProducerLane(entry);\n const wantingTo = lanesWanting(to);\n\n if (wantingTo.length >= 2 && countForwardProducersTo(forwardProducersByTo, to) === 1) {\n emitJoinBelow(to, wantingTo);\n }\n\n if (presetLane !== undefined) {\n placeWoven(entry, edgeKind, presetLane);\n continue;\n }\n\n const firstWanting = wantingTo[0];\n if (firstWanting !== undefined) {\n placeWoven(entry, edgeKind, firstWanting);\n continue;\n }\n\n const tipLaneIndex = openLaneAtRight(canonicalFrom(entry.from));\n placeWoven(entry, edgeKind, tipLaneIndex);\n }\n\n return { rows };\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as MigrationListResult, r as MigrationSpaceListEntry, t as MigrationListEntry } from "../migration-list-types-
|
|
1
|
+
import { n as MigrationListResult, r as MigrationSpaceListEntry, t as MigrationListEntry } from "../migration-list-types-wLyb3E-p.mjs";
|
|
2
2
|
export { type MigrationListEntry, type MigrationListResult, type MigrationSpaceListEntry };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as MigrationMetadata$1 } from "../metadata-
|
|
1
|
+
import { t as MigrationMetadata$1 } from "../metadata-COhIQCiH.mjs";
|
|
2
2
|
import { ControlStack, MigrationPlan, MigrationPlanOperation } from "@prisma-next/framework-components/control";
|
|
3
3
|
|
|
4
4
|
//#region src/migration-base.d.ts
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { n as OnDiskMigrationPackage, t as MigrationOps } from "../package-
|
|
1
|
+
import { n as OnDiskMigrationPackage, t as MigrationOps } from "../package-Ca-J_z_0.mjs";
|
|
2
2
|
import { MigrationPackage } from "@prisma-next/framework-components/control";
|
|
3
3
|
export { type MigrationOps, type MigrationPackage, type OnDiskMigrationPackage };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as MigrationGraph, t as MigrationEdge } from "../graph-
|
|
2
|
-
import { n as Refs } from "../refs-
|
|
1
|
+
import { n as MigrationGraph, t as MigrationEdge } from "../graph-B0LIIjIu.mjs";
|
|
2
|
+
import { n as Refs } from "../refs-D8xBNqs7.mjs";
|
|
3
3
|
import { Result } from "@prisma-next/utils/result";
|
|
4
4
|
|
|
5
5
|
//#region src/refs/types.d.ts
|
package/dist/exports/refs.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as readRefs, c as validateRefValue, i as readRef, l as writeRef, n as Refs, o as resolveRef, r as deleteRef, s as validateRefName, t as RefEntry } from "../refs-
|
|
1
|
+
import { a as readRefs, c as validateRefValue, i as readRef, l as writeRef, n as Refs, o as resolveRef, r as deleteRef, s as validateRefName, t as RefEntry } from "../refs-D8xBNqs7.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/refs/snapshot.d.ts
|
|
4
4
|
interface ContractIR {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as MigrationOps } from "../package-
|
|
1
|
+
import { t as MigrationOps } from "../package-Ca-J_z_0.mjs";
|
|
2
2
|
import { APP_SPACE_ID, ContractSpace, ContractSpaceHeadRef, ContractSpaceHeadRef as ContractSpaceHeadRef$1 } from "@prisma-next/framework-components/control";
|
|
3
3
|
import { Contract } from "@prisma-next/contract/types";
|
|
4
4
|
|
package/dist/exports/spaces.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { s as readMigrationsDir } from "../io-Dc64lvaL.mjs";
|
|
|
3
3
|
import "../constants-DWV9_o2Z.mjs";
|
|
4
4
|
import { l as reconstructGraph, o as findPathWithDecision } from "../migration-graph-D5JeadSE.mjs";
|
|
5
5
|
import { a as SPACE_REFS_DIRNAME, c as spaceMigrationDirectory, i as RESERVED_SPACE_SUBDIR_NAMES, l as spaceRefsDirectory, n as verifyContractSpaces, o as assertValidSpaceId, r as APP_SPACE_ID, s as isValidSpaceId, t as listContractSpaceDirectories } from "../verify-contract-spaces-DIdQLGo7.mjs";
|
|
6
|
-
import { n as readContractSpaceHeadRef, t as readContractSpaceContract } from "../read-contract-space-contract-
|
|
6
|
+
import { n as readContractSpaceHeadRef, t as readContractSpaceContract } from "../read-contract-space-contract-C4fEdoXO.mjs";
|
|
7
7
|
import { join } from "pathe";
|
|
8
8
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
9
9
|
import { canonicalizeJson } from "@prisma-next/framework-components/utils";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph-
|
|
1
|
+
{"version":3,"file":"graph-B0LIIjIu.d.mts","names":[],"sources":["../src/graph.ts"],"mappings":";;AAIA;;;UAAiB,aAAA;EAAA,SACN,IAAA;EAAA,SACA,EAAA;EAAA,SACA,aAAA;EAAA,SACA,OAAA;EAAA,SACA,SAAA;EAMA;;AAAU;AAGrB;;EAHW,SAAA,UAAA;AAAA;AAAA,UAGM,cAAA;EAAA,SACN,KAAA,EAAO,WAAA;EAAA,SACP,YAAA,EAAc,WAAA,kBAA6B,aAAA;EAAA,SAC3C,YAAA,EAAc,WAAA,kBAA6B,aAAA;EAAA,SAC3C,eAAA,EAAiB,WAAA,SAAoB,aAAA;AAAA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as OnDiskMigrationPackage } from "./package-
|
|
2
|
-
import { n as MigrationGraph, t as MigrationEdge } from "./graph-
|
|
1
|
+
import { n as OnDiskMigrationPackage } from "./package-Ca-J_z_0.mjs";
|
|
2
|
+
import { n as MigrationGraph, t as MigrationEdge } from "./graph-B0LIIjIu.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/migration-graph.d.ts
|
|
5
5
|
declare function reconstructGraph(packages: readonly OnDiskMigrationPackage[]): MigrationGraph;
|
|
@@ -121,4 +121,4 @@ declare function detectCycles(graph: MigrationGraph): readonly string[][];
|
|
|
121
121
|
declare function detectOrphans(graph: MigrationGraph): readonly MigrationEdge[];
|
|
122
122
|
//#endregion
|
|
123
123
|
export { findLeaf as a, findPathWithInvariants as c, findLatestMigration as i, findReachableLeaves as l, detectCycles as n, findPath as o, detectOrphans as r, findPathWithDecision as s, PathDecision as t, reconstructGraph as u };
|
|
124
|
-
//# sourceMappingURL=migration-graph-
|
|
124
|
+
//# sourceMappingURL=migration-graph-fl5ChjXE.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migration-graph-
|
|
1
|
+
{"version":3,"file":"migration-graph-fl5ChjXE.d.mts","names":[],"sources":["../src/migration-graph.ts"],"mappings":";;;;iBAsCgB,gBAAA,CAAiB,QAAA,WAAmB,sBAAA,KAA2B,cAAc;;AAA7F;;;;;;;iBAuEgB,QAAA,CACd,KAAA,EAAO,cAAA,EACP,QAAA,UACA,MAAA,oBACU,aAAa;AA3EoE;AAuE7F;;;;;;;;;;AAIyB;AA4CzB;;;;;;;AAvH6F,iBAuH7E,sBAAA,CACd,KAAA,EAAO,cAAA,EACP,QAAA,UACA,MAAA,UACA,QAAA,EAAU,WAAA,oBACA,aAAA;AAAA,UAsFK,YAAA;EAAA,SACN,YAAA,WAAuB,aAAa;EAAA,SACpC,QAAA;EAAA,SACA,MAAA;EAAA,SACA,gBAAA;EAAA,SACA,eAAA;EAAA,SACA,OAAA;EA5Fc;EAAA,SA8Fd,kBAAA;EARM;;;;;EAAA,SAcN,mBAAA;AAAA;;;;;;;;AAAmB;AAoB9B;;;;;;;;;KAAY,eAAA;EAAA,SACG,IAAA;EAAA,SAAqB,QAAA,EAAU,YAAA;AAAA;EAAA,SAC/B,IAAA;AAAA;EAAA,SAEA,IAAA;EAAA,SACA,cAAA,WAAyB,aAAa;EAAA,SACtC,OAAA;AAAA;;;;AAYkB;AAajC;;;UAfiB,2BAAA;EAAA,SACN,OAAA;EAAA,SACA,QAAA,GAAW,WAAW;AAAA;;;;;;;;;;AAkBf;iBALF,oBAAA,CACd,KAAA,EAAO,cAAA,EACP,QAAA,UACA,MAAA,UACA,OAAA,GAAS,2BAAA,GACR,eAAA;;;;;iBAqLa,mBAAA,CAAoB,KAAA,EAAO,cAAc,EAAE,QAAA;;;AAAgB;AAkB3E;;;;AAA8C;iBAA9B,QAAA,CAAS,KAAqB,EAAd,cAAc;;;;;;iBAwC9B,mBAAA,CAAoB,KAAA,EAAO,cAAA,GAAiB,aAAa;AAAA,iBAQzD,YAAA,CAAa,KAAqB,EAAd,cAAc;AAAA,iBA8DlC,aAAA,CAAc,KAAA,EAAO,cAAA,YAA0B,aAAa"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import "./constants-DWV9_o2Z.mjs";
|
|
2
|
+
//#region src/migration-list-graph-topology.ts
|
|
3
|
+
function canonicalFrom(from) {
|
|
4
|
+
return from ?? "sha256:empty";
|
|
5
|
+
}
|
|
6
|
+
function compareDirNameDesc(a, b) {
|
|
7
|
+
return b.dirName.localeCompare(a.dirName);
|
|
8
|
+
}
|
|
9
|
+
function bumpDegree(map, key) {
|
|
10
|
+
map.set(key, (map.get(key) ?? 0) + 1);
|
|
11
|
+
}
|
|
12
|
+
function classifyMigrationListGraphTopology(entries) {
|
|
13
|
+
const nodes = /* @__PURE__ */ new Set();
|
|
14
|
+
const kindByMigrationHash = /* @__PURE__ */ new Map();
|
|
15
|
+
const outgoingByFrom = /* @__PURE__ */ new Map();
|
|
16
|
+
for (const entry of entries) {
|
|
17
|
+
const from = canonicalFrom(entry.from);
|
|
18
|
+
const to = entry.to;
|
|
19
|
+
nodes.add(from);
|
|
20
|
+
nodes.add(to);
|
|
21
|
+
if (from === to) {
|
|
22
|
+
kindByMigrationHash.set(entry.migrationHash, "self");
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
const bucket = outgoingByFrom.get(from);
|
|
26
|
+
const edge = {
|
|
27
|
+
entry,
|
|
28
|
+
from,
|
|
29
|
+
to
|
|
30
|
+
};
|
|
31
|
+
if (bucket) bucket.push(edge);
|
|
32
|
+
else outgoingByFrom.set(from, [edge]);
|
|
33
|
+
}
|
|
34
|
+
for (const bucket of outgoingByFrom.values()) bucket.sort((a, b) => compareDirNameDesc(a.entry, b.entry));
|
|
35
|
+
const nonSelfInDegree = /* @__PURE__ */ new Map();
|
|
36
|
+
for (const node of nodes) nonSelfInDegree.set(node, 0);
|
|
37
|
+
for (const bucket of outgoingByFrom.values()) for (const edge of bucket) bumpDegree(nonSelfInDegree, edge.to);
|
|
38
|
+
const dfsRoots = [];
|
|
39
|
+
for (const node of nodes) if ((nonSelfInDegree.get(node) ?? 0) === 0) dfsRoots.push(node);
|
|
40
|
+
dfsRoots.sort((a, b) => {
|
|
41
|
+
if (a === "sha256:empty") return -1;
|
|
42
|
+
if (b === "sha256:empty") return 1;
|
|
43
|
+
return a.localeCompare(b);
|
|
44
|
+
});
|
|
45
|
+
if (dfsRoots.length === 0) dfsRoots.push(...[...nodes].sort((a, b) => a.localeCompare(b)));
|
|
46
|
+
const WHITE = 0;
|
|
47
|
+
const GRAY = 1;
|
|
48
|
+
const BLACK = 2;
|
|
49
|
+
const color = /* @__PURE__ */ new Map();
|
|
50
|
+
for (const node of nodes) color.set(node, WHITE);
|
|
51
|
+
const stack = [];
|
|
52
|
+
function pushFrame(node) {
|
|
53
|
+
color.set(node, GRAY);
|
|
54
|
+
stack.push({
|
|
55
|
+
node,
|
|
56
|
+
outgoing: outgoingByFrom.get(node) ?? [],
|
|
57
|
+
index: 0
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function runDfsFrom(root) {
|
|
61
|
+
if (color.get(root) !== WHITE) return;
|
|
62
|
+
pushFrame(root);
|
|
63
|
+
while (stack.length > 0) {
|
|
64
|
+
const frame = stack[stack.length - 1];
|
|
65
|
+
if (frame === void 0) break;
|
|
66
|
+
if (frame.index >= frame.outgoing.length) {
|
|
67
|
+
color.set(frame.node, BLACK);
|
|
68
|
+
stack.pop();
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const edge = frame.outgoing[frame.index];
|
|
72
|
+
frame.index += 1;
|
|
73
|
+
if (edge === void 0) continue;
|
|
74
|
+
const v = edge.to;
|
|
75
|
+
const vColor = color.get(v);
|
|
76
|
+
if (vColor === GRAY) kindByMigrationHash.set(edge.entry.migrationHash, "rollback");
|
|
77
|
+
else {
|
|
78
|
+
kindByMigrationHash.set(edge.entry.migrationHash, "forward");
|
|
79
|
+
if (vColor === WHITE) pushFrame(v);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
for (const root of dfsRoots) runDfsFrom(root);
|
|
84
|
+
for (const root of nodes) runDfsFrom(root);
|
|
85
|
+
const forwardInDegree = /* @__PURE__ */ new Map();
|
|
86
|
+
const forwardOutDegree = /* @__PURE__ */ new Map();
|
|
87
|
+
for (const entry of entries) {
|
|
88
|
+
if (kindByMigrationHash.get(entry.migrationHash) !== "forward") continue;
|
|
89
|
+
const from = canonicalFrom(entry.from);
|
|
90
|
+
const to = entry.to;
|
|
91
|
+
bumpDegree(forwardOutDegree, from);
|
|
92
|
+
bumpDegree(forwardInDegree, to);
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
kindByMigrationHash,
|
|
96
|
+
forwardInDegree,
|
|
97
|
+
forwardOutDegree
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
//#endregion
|
|
101
|
+
export { classifyMigrationListGraphTopology as t };
|
|
102
|
+
|
|
103
|
+
//# sourceMappingURL=migration-list-graph-topology-BtFzbZ9L.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-list-graph-topology-BtFzbZ9L.mjs","names":[],"sources":["../src/migration-list-graph-topology.ts"],"sourcesContent":["import { EMPTY_CONTRACT_HASH } from './constants';\nimport type { MigrationListEntry } from './migration-list-types';\n\nexport type EdgeKind = 'forward' | 'rollback' | 'self';\n\nexport interface MigrationGraphTopology {\n readonly kindByMigrationHash: ReadonlyMap<string, EdgeKind>;\n readonly forwardInDegree: ReadonlyMap<string, number>;\n readonly forwardOutDegree: ReadonlyMap<string, number>;\n}\n\ninterface NonSelfEdge {\n readonly entry: MigrationListEntry;\n readonly from: string;\n readonly to: string;\n}\n\nfunction canonicalFrom(from: string | null): string {\n return from ?? EMPTY_CONTRACT_HASH;\n}\n\nfunction compareDirNameDesc(a: MigrationListEntry, b: MigrationListEntry): number {\n return b.dirName.localeCompare(a.dirName);\n}\n\nfunction bumpDegree(map: Map<string, number>, key: string): void {\n map.set(key, (map.get(key) ?? 0) + 1);\n}\n\nexport function classifyMigrationListGraphTopology(\n entries: readonly MigrationListEntry[],\n): MigrationGraphTopology {\n const nodes = new Set<string>();\n const kindByMigrationHash = new Map<string, EdgeKind>();\n const outgoingByFrom = new Map<string, NonSelfEdge[]>();\n\n for (const entry of entries) {\n const from = canonicalFrom(entry.from);\n const to = entry.to;\n nodes.add(from);\n nodes.add(to);\n\n if (from === to) {\n kindByMigrationHash.set(entry.migrationHash, 'self');\n continue;\n }\n\n const bucket = outgoingByFrom.get(from);\n const edge: NonSelfEdge = { entry, from, to };\n if (bucket) bucket.push(edge);\n else outgoingByFrom.set(from, [edge]);\n }\n\n for (const bucket of outgoingByFrom.values()) {\n bucket.sort((a, b) => compareDirNameDesc(a.entry, b.entry));\n }\n\n const nonSelfInDegree = new Map<string, number>();\n for (const node of nodes) {\n nonSelfInDegree.set(node, 0);\n }\n for (const bucket of outgoingByFrom.values()) {\n for (const edge of bucket) {\n bumpDegree(nonSelfInDegree, edge.to);\n }\n }\n\n const dfsRoots: string[] = [];\n for (const node of nodes) {\n if ((nonSelfInDegree.get(node) ?? 0) === 0) {\n dfsRoots.push(node);\n }\n }\n dfsRoots.sort((a, b) => {\n if (a === EMPTY_CONTRACT_HASH) return -1;\n if (b === EMPTY_CONTRACT_HASH) return 1;\n return a.localeCompare(b);\n });\n if (dfsRoots.length === 0) {\n dfsRoots.push(...[...nodes].sort((a, b) => a.localeCompare(b)));\n }\n\n const WHITE = 0;\n const GRAY = 1;\n const BLACK = 2;\n const color = new Map<string, number>();\n for (const node of nodes) {\n color.set(node, WHITE);\n }\n\n interface Frame {\n node: string;\n outgoing: readonly NonSelfEdge[];\n index: number;\n }\n const stack: Frame[] = [];\n\n function pushFrame(node: string): void {\n color.set(node, GRAY);\n stack.push({ node, outgoing: outgoingByFrom.get(node) ?? [], index: 0 });\n }\n\n function runDfsFrom(root: string): void {\n if (color.get(root) !== WHITE) return;\n pushFrame(root);\n\n while (stack.length > 0) {\n const frame = stack[stack.length - 1];\n if (frame === undefined) break;\n if (frame.index >= frame.outgoing.length) {\n color.set(frame.node, BLACK);\n stack.pop();\n continue;\n }\n\n const edge = frame.outgoing[frame.index];\n frame.index += 1;\n if (edge === undefined) continue;\n\n const v = edge.to;\n const vColor = color.get(v);\n if (vColor === GRAY) {\n kindByMigrationHash.set(edge.entry.migrationHash, 'rollback');\n } else {\n kindByMigrationHash.set(edge.entry.migrationHash, 'forward');\n if (vColor === WHITE) {\n pushFrame(v);\n }\n }\n }\n }\n\n for (const root of dfsRoots) {\n runDfsFrom(root);\n }\n for (const root of nodes) {\n runDfsFrom(root);\n }\n\n const forwardInDegree = new Map<string, number>();\n const forwardOutDegree = new Map<string, number>();\n\n for (const entry of entries) {\n if (kindByMigrationHash.get(entry.migrationHash) !== 'forward') continue;\n const from = canonicalFrom(entry.from);\n const to = entry.to;\n bumpDegree(forwardOutDegree, from);\n bumpDegree(forwardInDegree, to);\n }\n\n return {\n kindByMigrationHash,\n forwardInDegree,\n forwardOutDegree,\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { t as MigrationListEntry } from "./migration-list-types-wLyb3E-p.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/migration-list-graph-topology.d.ts
|
|
4
|
+
type EdgeKind = 'forward' | 'rollback' | 'self';
|
|
5
|
+
interface MigrationGraphTopology {
|
|
6
|
+
readonly kindByMigrationHash: ReadonlyMap<string, EdgeKind>;
|
|
7
|
+
readonly forwardInDegree: ReadonlyMap<string, number>;
|
|
8
|
+
readonly forwardOutDegree: ReadonlyMap<string, number>;
|
|
9
|
+
}
|
|
10
|
+
declare function classifyMigrationListGraphTopology(entries: readonly MigrationListEntry[]): MigrationGraphTopology;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { MigrationGraphTopology as n, classifyMigrationListGraphTopology as r, EdgeKind as t };
|
|
13
|
+
//# sourceMappingURL=migration-list-graph-topology-CafEnhPT.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-list-graph-topology-CafEnhPT.d.mts","names":[],"sources":["../src/migration-list-graph-topology.ts"],"mappings":";;;KAGY,QAAA;AAAA,UAEK,sBAAA;EAAA,SACN,mBAAA,EAAqB,WAAA,SAAoB,QAAA;EAAA,SACzC,eAAA,EAAiB,WAAA;EAAA,SACjB,gBAAA,EAAkB,WAAA;AAAA;AAAA,iBAqBb,kCAAA,CACd,OAAA,WAAkB,kBAAA,KACjB,sBAAsB"}
|
|
@@ -20,4 +20,4 @@ interface MigrationListResult {
|
|
|
20
20
|
}
|
|
21
21
|
//#endregion
|
|
22
22
|
export { MigrationListResult as n, MigrationSpaceListEntry as r, MigrationListEntry as t };
|
|
23
|
-
//# sourceMappingURL=migration-list-types-
|
|
23
|
+
//# sourceMappingURL=migration-list-types-wLyb3E-p.d.mts.map
|
package/dist/{migration-list-types-B-qimPet.d.mts.map → migration-list-types-wLyb3E-p.d.mts.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migration-list-types-
|
|
1
|
+
{"version":3,"file":"migration-list-types-wLyb3E-p.d.mts","names":[],"sources":["../src/migration-list-types.ts"],"mappings":";UAAiB,kBAAA;EAAA,SACN,OAAA;EAAA,SACA,IAAA;EAAA,SACA,EAAA;EAAA,SACA,aAAA;EAAA,SACA,cAAA;EAAA,SACA,SAAA;EAAA,SACA,IAAA;EAAA,SACA,kBAAA;AAAA;AAAA,UAGM,uBAAA;EAAA,SACN,OAAA;EAAA,SACA,UAAA,WAAqB,kBAAkB;AAAA;AAAA,UAGjC,mBAAA;EAAA,SACN,EAAA;EAAA,SACA,MAAA,WAAiB,uBAAuB;EAAA,SACxC,OAAA;AAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"package-
|
|
1
|
+
{"version":3,"file":"package-Ca-J_z_0.d.mts","names":[],"sources":["../src/package.ts"],"mappings":";;;KAKY,YAAA,YAAwB,sBAAsB;;AAA1D;;;;AAA0D;AAa1D;;;;AACkB;UADD,sBAAA,SAA+B,gBAAgB;EAAA,SACrD,OAAO;AAAA"}
|
package/dist/{read-contract-space-contract-_EvvV5Gl.mjs → read-contract-space-contract-C4fEdoXO.mjs}
RENAMED
|
@@ -79,4 +79,4 @@ async function readContractSpaceContract(projectMigrationsDir, spaceId) {
|
|
|
79
79
|
//#endregion
|
|
80
80
|
export { readContractSpaceHeadRef as n, readContractSpaceContract as t };
|
|
81
81
|
|
|
82
|
-
//# sourceMappingURL=read-contract-space-contract-
|
|
82
|
+
//# sourceMappingURL=read-contract-space-contract-C4fEdoXO.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"read-contract-space-contract-
|
|
1
|
+
{"version":3,"file":"read-contract-space-contract-C4fEdoXO.mjs","names":["hasErrnoCode"],"sources":["../src/read-contract-space-head-ref.ts","../src/read-contract-space-contract.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport type { ContractSpaceHeadRef } from '@prisma-next/framework-components/control';\nimport { join } from 'pathe';\nimport { errorInvalidJson, errorInvalidRefFile } from './errors';\nimport { assertValidSpaceId, spaceMigrationDirectory, spaceRefsDirectory } from './space-layout';\n\nexport type { ContractSpaceHeadRef };\n\nfunction hasErrnoCode(error: unknown, code: string): boolean {\n return error instanceof Error && (error as { code?: string }).code === code;\n}\n\n/**\n * Read the head ref (`hash` + `invariants`) for a contract space from\n * `<projectMigrationsDir>/<spaceId>/refs/head.json`.\n *\n * Returns `null` when the file does not exist (first emit). Surfaces\n * `MIGRATION.INVALID_JSON` / `MIGRATION.INVALID_REF_FILE` on a corrupt\n * `refs/head.json` so callers can distinguish \"no head ref on disk\"\n * (returns `null`) from \"head ref present but unreadable\" (throws).\n *\n * Validates the space id against `[a-z][a-z0-9_-]{0,63}` for the same\n * filesystem-safety reasons as the rest of the per-space helpers. The\n * helper is uniform across the app and extension spaces.\n */\nexport async function readContractSpaceHeadRef(\n projectMigrationsDir: string,\n spaceId: string,\n): Promise<ContractSpaceHeadRef | null> {\n assertValidSpaceId(spaceId);\n\n const filePath = join(\n spaceRefsDirectory(spaceMigrationDirectory(projectMigrationsDir, spaceId)),\n 'head.json',\n );\n\n let raw: string;\n try {\n raw = await readFile(filePath, 'utf-8');\n } catch (error) {\n if (hasErrnoCode(error, 'ENOENT')) {\n return null;\n }\n throw error;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (e) {\n throw errorInvalidJson(filePath, e instanceof Error ? e.message : String(e));\n }\n\n if (typeof parsed !== 'object' || parsed === null) {\n throw errorInvalidRefFile(filePath, 'expected an object');\n }\n const obj = parsed as { hash?: unknown; invariants?: unknown };\n if (typeof obj.hash !== 'string') {\n throw errorInvalidRefFile(filePath, 'expected an object with a string `hash` field');\n }\n if (!Array.isArray(obj.invariants) || obj.invariants.some((value) => typeof value !== 'string')) {\n throw errorInvalidRefFile(filePath, 'expected an object with an `invariants` array of strings');\n }\n\n return { hash: obj.hash, invariants: obj.invariants as readonly string[] };\n}\n","import { readFile } from 'node:fs/promises';\nimport { join } from 'pathe';\nimport { errorInvalidJson, errorMissingFile } from './errors';\nimport { assertValidSpaceId } from './space-layout';\n\nfunction hasErrnoCode(error: unknown, code: string): boolean {\n return error instanceof Error && (error as { code?: string }).code === code;\n}\n\n/**\n * Read the on-disk contract value for a contract space\n * (`<projectMigrationsDir>/<spaceId>/contract.json`). Returns the parsed\n * JSON value as `unknown` — callers that need a typed contract validate\n * via their family's `deserializeContract` to surface schema issues.\n *\n * Companion to {@link import('./read-contract-space-head-ref').readContractSpaceHeadRef}\n * — same ENOENT-throws / corrupt-file-error semantics. Returns the\n * canonical-JSON value the framework wrote during emit, so re-running\n * this helper across machines / runs yields a byte-identical value.\n */\nexport async function readContractSpaceContract(\n projectMigrationsDir: string,\n spaceId: string,\n): Promise<unknown> {\n assertValidSpaceId(spaceId);\n\n const filePath = join(projectMigrationsDir, spaceId, 'contract.json');\n\n let raw: string;\n try {\n raw = await readFile(filePath, 'utf-8');\n } catch (error) {\n if (hasErrnoCode(error, 'ENOENT')) {\n throw errorMissingFile('contract.json', join(projectMigrationsDir, spaceId));\n }\n throw error;\n }\n\n try {\n return JSON.parse(raw);\n } catch (e) {\n throw errorInvalidJson(filePath, e instanceof Error ? e.message : String(e));\n }\n}\n"],"mappings":";;;;;AAQA,SAASA,eAAa,OAAgB,MAAuB;CAC3D,OAAO,iBAAiB,SAAU,MAA4B,SAAS;AACzE;;;;;;;;;;;;;;AAeA,eAAsB,yBACpB,sBACA,SACsC;CACtC,mBAAmB,OAAO;CAE1B,MAAM,WAAW,KACf,mBAAmB,wBAAwB,sBAAsB,OAAO,CAAC,GACzE,WACF;CAEA,IAAI;CACJ,IAAI;EACF,MAAM,MAAM,SAAS,UAAU,OAAO;CACxC,SAAS,OAAO;EACd,IAAIA,eAAa,OAAO,QAAQ,GAC9B,OAAO;EAET,MAAM;CACR;CAEA,IAAI;CACJ,IAAI;EACF,SAAS,KAAK,MAAM,GAAG;CACzB,SAAS,GAAG;EACV,MAAM,iBAAiB,UAAU,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC;CAC7E;CAEA,IAAI,OAAO,WAAW,YAAY,WAAW,MAC3C,MAAM,oBAAoB,UAAU,oBAAoB;CAE1D,MAAM,MAAM;CACZ,IAAI,OAAO,IAAI,SAAS,UACtB,MAAM,oBAAoB,UAAU,+CAA+C;CAErF,IAAI,CAAC,MAAM,QAAQ,IAAI,UAAU,KAAK,IAAI,WAAW,MAAM,UAAU,OAAO,UAAU,QAAQ,GAC5F,MAAM,oBAAoB,UAAU,0DAA0D;CAGhG,OAAO;EAAE,MAAM,IAAI;EAAM,YAAY,IAAI;CAAgC;AAC3E;;;AC5DA,SAAS,aAAa,OAAgB,MAAuB;CAC3D,OAAO,iBAAiB,SAAU,MAA4B,SAAS;AACzE;;;;;;;;;;;;AAaA,eAAsB,0BACpB,sBACA,SACkB;CAClB,mBAAmB,OAAO;CAE1B,MAAM,WAAW,KAAK,sBAAsB,SAAS,eAAe;CAEpE,IAAI;CACJ,IAAI;EACF,MAAM,MAAM,SAAS,UAAU,OAAO;CACxC,SAAS,OAAO;EACd,IAAI,aAAa,OAAO,QAAQ,GAC9B,MAAM,iBAAiB,iBAAiB,KAAK,sBAAsB,OAAO,CAAC;EAE7E,MAAM;CACR;CAEA,IAAI;EACF,OAAO,KAAK,MAAM,GAAG;CACvB,SAAS,GAAG;EACV,MAAM,iBAAiB,UAAU,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC;CAC7E;AACF"}
|
|
@@ -13,4 +13,4 @@ declare function deleteRef(refsDir: string, name: string): Promise<void>;
|
|
|
13
13
|
declare function resolveRef(refs: Refs, name: string): RefEntry;
|
|
14
14
|
//#endregion
|
|
15
15
|
export { readRefs as a, validateRefValue as c, readRef as i, writeRef as l, Refs as n, resolveRef as o, deleteRef as r, validateRefName as s, RefEntry as t };
|
|
16
|
-
//# sourceMappingURL=refs-
|
|
16
|
+
//# sourceMappingURL=refs-D8xBNqs7.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"refs-
|
|
1
|
+
{"version":3,"file":"refs-D8xBNqs7.d.mts","names":[],"sources":["../src/refs.ts"],"mappings":";UAUiB,QAAA;EAAA,SACN,IAAA;EAAA,SACA,UAAU;AAAA;AAAA,KAGT,IAAA,GAAO,QAAA,CAAS,MAAA,SAAe,QAAA;AAAA,iBAK3B,eAAA,CAAgB,IAAY;AAAA,iBAQ5B,gBAAA,CAAiB,KAAa;AAAA,iBAsBxB,OAAA,CAAQ,OAAA,UAAiB,IAAA,WAAe,OAAO,CAAC,QAAA;AAAA,iBAmChD,QAAA,CAAS,OAAA,WAAkB,OAAO,CAAC,IAAA;AAAA,iBAqDnC,QAAA,CAAS,OAAA,UAAiB,IAAA,UAAc,KAAA,EAAO,QAAA,GAAW,OAAO;AAAA,iBAoBjE,SAAA,CAAU,OAAA,UAAiB,IAAA,WAAe,OAAO;AAAA,iBAsCvD,UAAA,CAAW,IAAA,EAAM,IAAA,EAAM,IAAA,WAAe,QAAQ"}
|
package/package.json
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma-next/migration-tools",
|
|
3
|
-
"version": "0.11.0-dev.
|
|
3
|
+
"version": "0.11.0-dev.45",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"description": "On-disk migration persistence, hash verification, and chain reconstruction for Prisma Next",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@prisma-next/contract": "0.11.0-dev.
|
|
10
|
-
"@prisma-next/framework-components": "0.11.0-dev.
|
|
11
|
-
"@prisma-next/utils": "0.11.0-dev.
|
|
9
|
+
"@prisma-next/contract": "0.11.0-dev.45",
|
|
10
|
+
"@prisma-next/framework-components": "0.11.0-dev.45",
|
|
11
|
+
"@prisma-next/utils": "0.11.0-dev.45",
|
|
12
12
|
"arktype": "^2.2.0",
|
|
13
13
|
"pathe": "^2.0.3",
|
|
14
14
|
"prettier": "^3.8.3"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
|
-
"@prisma-next/tsconfig": "0.11.0-dev.
|
|
18
|
-
"@prisma-next/tsdown": "0.11.0-dev.
|
|
17
|
+
"@prisma-next/tsconfig": "0.11.0-dev.45",
|
|
18
|
+
"@prisma-next/tsdown": "0.11.0-dev.45",
|
|
19
19
|
"tsdown": "0.22.0",
|
|
20
20
|
"typescript": "5.9.3",
|
|
21
21
|
"vitest": "4.1.6"
|
|
@@ -68,6 +68,14 @@
|
|
|
68
68
|
"types": "./dist/exports/enumerate-migration-spaces.d.mts",
|
|
69
69
|
"import": "./dist/exports/enumerate-migration-spaces.mjs"
|
|
70
70
|
},
|
|
71
|
+
"./migration-list-graph-topology": {
|
|
72
|
+
"types": "./dist/exports/migration-list-graph-topology.d.mts",
|
|
73
|
+
"import": "./dist/exports/migration-list-graph-topology.mjs"
|
|
74
|
+
},
|
|
75
|
+
"./migration-list-graph-layout": {
|
|
76
|
+
"types": "./dist/exports/migration-list-graph-layout.d.mts",
|
|
77
|
+
"import": "./dist/exports/migration-list-graph-layout.mjs"
|
|
78
|
+
},
|
|
71
79
|
"./refs": {
|
|
72
80
|
"types": "./dist/exports/refs.d.mts",
|
|
73
81
|
"import": "./dist/exports/refs.mjs"
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { EMPTY_CONTRACT_HASH } from './constants';
|
|
2
|
+
import { classifyMigrationListGraphTopology, type EdgeKind } from './migration-list-graph-topology';
|
|
3
|
+
import type { MigrationListEntry } from './migration-list-types';
|
|
4
|
+
|
|
5
|
+
export type ConnectorKind = 'fanBelow' | 'joinBelow';
|
|
6
|
+
|
|
7
|
+
export interface MigrationLayoutRow {
|
|
8
|
+
readonly kind: 'migration';
|
|
9
|
+
readonly entry: MigrationListEntry;
|
|
10
|
+
readonly edgeKind: EdgeKind;
|
|
11
|
+
readonly laneIndex: number;
|
|
12
|
+
readonly passThroughLanes: readonly number[];
|
|
13
|
+
readonly woven: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface NodeLineLayoutRow {
|
|
17
|
+
readonly kind: 'nodeLine';
|
|
18
|
+
readonly contractHash: string;
|
|
19
|
+
readonly laneIndex: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface ConnectorLayoutRow {
|
|
23
|
+
readonly kind: 'connector';
|
|
24
|
+
readonly connectorKind: ConnectorKind;
|
|
25
|
+
readonly contractHash: string;
|
|
26
|
+
readonly startLane: number;
|
|
27
|
+
readonly endLane: number;
|
|
28
|
+
readonly branchCount: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type LayoutRow = MigrationLayoutRow | NodeLineLayoutRow | ConnectorLayoutRow;
|
|
32
|
+
|
|
33
|
+
export interface MigrationListGraphLayout {
|
|
34
|
+
readonly rows: readonly LayoutRow[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface LaneState {
|
|
38
|
+
want: string;
|
|
39
|
+
active: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function canonicalFrom(from: string | null): string {
|
|
43
|
+
return from ?? EMPTY_CONTRACT_HASH;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function canonicalTo(entry: MigrationListEntry): string {
|
|
47
|
+
return entry.to;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function forwardInDegree(
|
|
51
|
+
topology: ReturnType<typeof classifyMigrationListGraphTopology>,
|
|
52
|
+
hash: string,
|
|
53
|
+
): number {
|
|
54
|
+
return topology.forwardInDegree.get(hash) ?? 0;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function forwardOutDegree(
|
|
58
|
+
topology: ReturnType<typeof classifyMigrationListGraphTopology>,
|
|
59
|
+
hash: string,
|
|
60
|
+
): number {
|
|
61
|
+
return topology.forwardOutDegree.get(hash) ?? 0;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function buildForwardProducersByTo(
|
|
65
|
+
entries: readonly MigrationListEntry[],
|
|
66
|
+
kindByMigrationHash: ReadonlyMap<string, EdgeKind>,
|
|
67
|
+
): Map<string, MigrationListEntry[]> {
|
|
68
|
+
const byTo = new Map<string, MigrationListEntry[]>();
|
|
69
|
+
for (const entry of entries) {
|
|
70
|
+
if (kindByMigrationHash.get(entry.migrationHash) !== 'forward') continue;
|
|
71
|
+
const to = canonicalTo(entry);
|
|
72
|
+
const bucket = byTo.get(to);
|
|
73
|
+
if (bucket) bucket.push(entry);
|
|
74
|
+
else byTo.set(to, [entry]);
|
|
75
|
+
}
|
|
76
|
+
return byTo;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function countForwardProducersTo(
|
|
80
|
+
forwardProducersByTo: Map<string, MigrationListEntry[]>,
|
|
81
|
+
contract: string,
|
|
82
|
+
): number {
|
|
83
|
+
return forwardProducersByTo.get(contract)?.length ?? 0;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function computeMigrationListGraphLayout(
|
|
87
|
+
entries: readonly MigrationListEntry[],
|
|
88
|
+
): MigrationListGraphLayout {
|
|
89
|
+
const topology = classifyMigrationListGraphTopology(entries);
|
|
90
|
+
const { kindByMigrationHash } = topology;
|
|
91
|
+
const forwardProducersByTo = buildForwardProducersByTo(entries, kindByMigrationHash);
|
|
92
|
+
const convergencesEmitted = new Set<string>();
|
|
93
|
+
const producerLaneByHash = new Map<string, number>();
|
|
94
|
+
const lanes: LaneState[] = [];
|
|
95
|
+
const rows: LayoutRow[] = [];
|
|
96
|
+
|
|
97
|
+
function emitNodeLine(contractHash: string): void {
|
|
98
|
+
rows.push({ kind: 'nodeLine', contractHash, laneIndex: 0 });
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function emitConnector(
|
|
102
|
+
connectorKind: ConnectorKind,
|
|
103
|
+
contractHash: string,
|
|
104
|
+
startLane: number,
|
|
105
|
+
endLane: number,
|
|
106
|
+
branchCount: number,
|
|
107
|
+
): void {
|
|
108
|
+
rows.push({
|
|
109
|
+
kind: 'connector',
|
|
110
|
+
connectorKind,
|
|
111
|
+
contractHash,
|
|
112
|
+
startLane,
|
|
113
|
+
endLane,
|
|
114
|
+
branchCount,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function activeLaneIndices(): number[] {
|
|
119
|
+
const indices: number[] = [];
|
|
120
|
+
for (let index = 0; index < lanes.length; index++) {
|
|
121
|
+
if (lanes[index]?.active) indices.push(index);
|
|
122
|
+
}
|
|
123
|
+
return indices;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function lanesWanting(contract: string): number[] {
|
|
127
|
+
const indices: number[] = [];
|
|
128
|
+
for (let index = 0; index < lanes.length; index++) {
|
|
129
|
+
const lane = lanes[index];
|
|
130
|
+
if (lane?.active && lane.want === contract) indices.push(index);
|
|
131
|
+
}
|
|
132
|
+
return indices;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function ensureLane(index: number): void {
|
|
136
|
+
while (lanes.length <= index) {
|
|
137
|
+
lanes.push({ want: '', active: false });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function openLaneAtRight(want: string): number {
|
|
142
|
+
const index = lanes.length;
|
|
143
|
+
lanes.push({ want, active: true });
|
|
144
|
+
return index;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function closeLane(index: number): void {
|
|
148
|
+
ensureLane(index);
|
|
149
|
+
const lane = lanes[index];
|
|
150
|
+
if (lane) lane.active = false;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function emitJoinBelow(contractHash: string, laneIndices: readonly number[]): void {
|
|
154
|
+
if (laneIndices.length < 2) return;
|
|
155
|
+
const startLane = Math.min(...laneIndices);
|
|
156
|
+
const endLane = Math.max(...laneIndices);
|
|
157
|
+
emitConnector('joinBelow', contractHash, startLane, endLane, laneIndices.length);
|
|
158
|
+
for (const index of laneIndices) {
|
|
159
|
+
if (index !== startLane) closeLane(index);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function emitConvergencePreamble(contract: string): void {
|
|
164
|
+
if (convergencesEmitted.has(contract)) return;
|
|
165
|
+
if (forwardInDegree(topology, contract) < 2) return;
|
|
166
|
+
|
|
167
|
+
const consumersWanting = lanesWanting(contract);
|
|
168
|
+
if (forwardOutDegree(topology, contract) >= 2 && consumersWanting.length >= 2) {
|
|
169
|
+
emitJoinBelow(contract, consumersWanting);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
emitNodeLine(contract);
|
|
173
|
+
const producers = forwardProducersByTo.get(contract) ?? [];
|
|
174
|
+
if (producers.length >= 2) {
|
|
175
|
+
emitConnector('fanBelow', contract, 0, producers.length - 1, producers.length);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
for (const [producerIndex, producer] of producers.entries()) {
|
|
179
|
+
ensureLane(producerIndex);
|
|
180
|
+
lanes[producerIndex] = { want: canonicalFrom(producer.from), active: true };
|
|
181
|
+
producerLaneByHash.set(producer.migrationHash, producerIndex);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
convergencesEmitted.add(contract);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function assignProducerLane(entry: MigrationListEntry): number | undefined {
|
|
188
|
+
const preset = producerLaneByHash.get(entry.migrationHash);
|
|
189
|
+
if (preset !== undefined) return preset;
|
|
190
|
+
return undefined;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function placeWoven(entry: MigrationListEntry, edgeKind: EdgeKind, laneIndex: number): void {
|
|
194
|
+
const passThroughLanes = activeLaneIndices().filter((index) => index !== laneIndex);
|
|
195
|
+
rows.push({
|
|
196
|
+
kind: 'migration',
|
|
197
|
+
entry,
|
|
198
|
+
edgeKind,
|
|
199
|
+
laneIndex,
|
|
200
|
+
passThroughLanes,
|
|
201
|
+
woven: true,
|
|
202
|
+
});
|
|
203
|
+
ensureLane(laneIndex);
|
|
204
|
+
lanes[laneIndex] = { want: canonicalFrom(entry.from), active: true };
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function placeUnwoven(entry: MigrationListEntry, edgeKind: EdgeKind): void {
|
|
208
|
+
const passThroughLanes = activeLaneIndices();
|
|
209
|
+
const laneIndex = passThroughLanes.length === 0 ? 0 : Math.max(...passThroughLanes) + 1;
|
|
210
|
+
rows.push({
|
|
211
|
+
kind: 'migration',
|
|
212
|
+
entry,
|
|
213
|
+
edgeKind,
|
|
214
|
+
laneIndex,
|
|
215
|
+
passThroughLanes,
|
|
216
|
+
woven: false,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
for (const entry of entries) {
|
|
221
|
+
const edgeKind = kindByMigrationHash.get(entry.migrationHash) ?? 'forward';
|
|
222
|
+
const to = canonicalTo(entry);
|
|
223
|
+
|
|
224
|
+
if (edgeKind !== 'forward') {
|
|
225
|
+
placeUnwoven(entry, edgeKind);
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (forwardInDegree(topology, to) >= 2 && !convergencesEmitted.has(to)) {
|
|
230
|
+
emitConvergencePreamble(to);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const presetLane = assignProducerLane(entry);
|
|
234
|
+
const wantingTo = lanesWanting(to);
|
|
235
|
+
|
|
236
|
+
if (wantingTo.length >= 2 && countForwardProducersTo(forwardProducersByTo, to) === 1) {
|
|
237
|
+
emitJoinBelow(to, wantingTo);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (presetLane !== undefined) {
|
|
241
|
+
placeWoven(entry, edgeKind, presetLane);
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const firstWanting = wantingTo[0];
|
|
246
|
+
if (firstWanting !== undefined) {
|
|
247
|
+
placeWoven(entry, edgeKind, firstWanting);
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const tipLaneIndex = openLaneAtRight(canonicalFrom(entry.from));
|
|
252
|
+
placeWoven(entry, edgeKind, tipLaneIndex);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return { rows };
|
|
256
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { EMPTY_CONTRACT_HASH } from './constants';
|
|
2
|
+
import type { MigrationListEntry } from './migration-list-types';
|
|
3
|
+
|
|
4
|
+
export type EdgeKind = 'forward' | 'rollback' | 'self';
|
|
5
|
+
|
|
6
|
+
export interface MigrationGraphTopology {
|
|
7
|
+
readonly kindByMigrationHash: ReadonlyMap<string, EdgeKind>;
|
|
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
|
+
): MigrationGraphTopology {
|
|
33
|
+
const nodes = new Set<string>();
|
|
34
|
+
const kindByMigrationHash = new Map<string, EdgeKind>();
|
|
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
|
+
for (const root of nodes) {
|
|
137
|
+
runDfsFrom(root);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const forwardInDegree = new Map<string, number>();
|
|
141
|
+
const forwardOutDegree = new Map<string, number>();
|
|
142
|
+
|
|
143
|
+
for (const entry of entries) {
|
|
144
|
+
if (kindByMigrationHash.get(entry.migrationHash) !== 'forward') continue;
|
|
145
|
+
const from = canonicalFrom(entry.from);
|
|
146
|
+
const to = entry.to;
|
|
147
|
+
bumpDegree(forwardOutDegree, from);
|
|
148
|
+
bumpDegree(forwardInDegree, to);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
kindByMigrationHash,
|
|
153
|
+
forwardInDegree,
|
|
154
|
+
forwardOutDegree,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
File without changes
|