@prisma-next/migration-tools 0.11.0-dev.5 → 0.11.0-dev.50
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 +4 -4
- package/dist/{errors-DGYwcwXs.mjs → errors-4YabujxZ.mjs} +15 -21
- package/dist/errors-4YabujxZ.mjs.map +1 -0
- package/dist/exports/aggregate.d.mts +275 -179
- package/dist/exports/aggregate.d.mts.map +1 -1
- package/dist/exports/aggregate.mjs +363 -184
- package/dist/exports/aggregate.mjs.map +1 -1
- package/dist/exports/enumerate-migration-spaces.d.mts +53 -0
- package/dist/exports/enumerate-migration-spaces.d.mts.map +1 -0
- package/dist/exports/enumerate-migration-spaces.mjs +107 -0
- package/dist/exports/enumerate-migration-spaces.mjs.map +1 -0
- package/dist/exports/errors.d.mts +2 -2
- package/dist/exports/errors.d.mts.map +1 -1
- package/dist/exports/errors.mjs +1 -1
- package/dist/exports/graph.d.mts +1 -1
- package/dist/exports/hash.d.mts +8 -9
- package/dist/exports/hash.d.mts.map +1 -1
- package/dist/exports/hash.mjs +1 -1
- package/dist/exports/invariants.d.mts +1 -1
- package/dist/exports/invariants.d.mts.map +1 -1
- package/dist/exports/invariants.mjs +1 -1
- package/dist/exports/io.d.mts +2 -83
- package/dist/exports/io.mjs +1 -1
- package/dist/exports/metadata.d.mts +2 -2
- package/dist/exports/migration-graph.d.mts +9 -2
- package/dist/exports/migration-graph.d.mts.map +1 -0
- package/dist/exports/migration-graph.mjs +16 -2
- package/dist/exports/migration-graph.mjs.map +1 -0
- package/dist/exports/migration-list-graph-topology.d.mts +13 -0
- package/dist/exports/migration-list-graph-topology.d.mts.map +1 -0
- package/dist/exports/migration-list-graph-topology.mjs +105 -0
- package/dist/exports/migration-list-graph-topology.mjs.map +1 -0
- package/dist/exports/migration-list-types.d.mts +2 -0
- package/dist/exports/migration-list-types.mjs +1 -0
- package/dist/exports/migration-ts.d.mts.map +1 -1
- package/dist/exports/migration-ts.mjs.map +1 -1
- package/dist/exports/migration.d.mts +5 -6
- package/dist/exports/migration.d.mts.map +1 -1
- package/dist/exports/migration.mjs +14 -32
- package/dist/exports/migration.mjs.map +1 -1
- package/dist/exports/package.d.mts +1 -1
- package/dist/exports/ref-resolution.d.mts +2 -2
- package/dist/exports/ref-resolution.d.mts.map +1 -1
- package/dist/exports/ref-resolution.mjs +1 -1
- package/dist/exports/ref-resolution.mjs.map +1 -1
- package/dist/exports/refs.d.mts +15 -2
- package/dist/exports/refs.d.mts.map +1 -0
- package/dist/exports/refs.mjs +137 -2
- package/dist/exports/refs.mjs.map +1 -0
- package/dist/exports/spaces.d.mts +31 -132
- package/dist/exports/spaces.d.mts.map +1 -1
- package/dist/exports/spaces.mjs +14 -9
- package/dist/exports/spaces.mjs.map +1 -1
- package/dist/{graph-BrLXqoUc.d.mts → graph-BUZuUeBC.d.mts} +1 -2
- package/dist/graph-BUZuUeBC.d.mts.map +1 -0
- package/dist/{hash-Cr4WIr4Z.mjs → hash--Y7vCpN3.mjs} +8 -9
- package/dist/hash--Y7vCpN3.mjs.map +1 -0
- package/dist/{invariants-0daYEzyo.mjs → invariants-CCOAyg6c.mjs} +2 -2
- package/dist/{invariants-0daYEzyo.mjs.map → invariants-CCOAyg6c.mjs.map} +1 -1
- package/dist/{io-BPLfzvZe.mjs → io-BHl0amF0.mjs} +100 -13
- package/dist/io-BHl0amF0.mjs.map +1 -0
- package/dist/io-nqFXSSTN.d.mts +124 -0
- package/dist/io-nqFXSSTN.d.mts.map +1 -0
- package/dist/metadata-Bp9X04gM.d.mts +2 -0
- package/dist/{migration-graph-De0dUZoC.d.mts → migration-graph-DtNT-cqc.d.mts} +6 -6
- package/dist/migration-graph-DtNT-cqc.d.mts.map +1 -0
- package/dist/{migration-graph-nlS4TRpn.mjs → migration-graph-kGBkIZDa.mjs} +6 -26
- package/dist/migration-graph-kGBkIZDa.mjs.map +1 -0
- package/dist/migration-list-types-BRTuXR8i.d.mts +23 -0
- package/dist/migration-list-types-BRTuXR8i.d.mts.map +1 -0
- package/dist/op-schema-D5qkXfEf.mjs.map +1 -1
- package/dist/{package-DZj8YvD0.d.mts → package-DIttKL7X.d.mts} +1 -1
- package/dist/package-DIttKL7X.d.mts.map +1 -0
- package/dist/read-contract-space-contract-BS5Oxbgw.mjs +82 -0
- package/dist/read-contract-space-contract-BS5Oxbgw.mjs.map +1 -0
- package/dist/{refs-BDHo5l_g.mjs → refs-BBKNL45K.mjs} +76 -4
- package/dist/refs-BBKNL45K.mjs.map +1 -0
- package/dist/{refs-CDaNerhT.d.mts → refs-C8f2IGM8.d.mts} +12 -2
- package/dist/refs-C8f2IGM8.d.mts.map +1 -0
- package/dist/{read-contract-space-contract-DRueB4Aa.mjs → verify-contract-spaces-BJX5gqtD.mjs} +32 -80
- package/dist/verify-contract-spaces-BJX5gqtD.mjs.map +1 -0
- package/dist/verify-contract-spaces-T0aiJlBS.d.mts +132 -0
- package/dist/verify-contract-spaces-T0aiJlBS.d.mts.map +1 -0
- package/package.json +18 -6
- package/src/aggregate/aggregate.ts +90 -0
- package/src/aggregate/check-integrity.ts +243 -0
- package/src/aggregate/loader.ts +156 -334
- package/src/aggregate/planner.ts +8 -6
- package/src/aggregate/project-schema-to-space.ts +1 -1
- package/src/aggregate/strategies/graph-walk.ts +12 -7
- package/src/aggregate/strategies/synth.ts +2 -2
- package/src/aggregate/types.ts +56 -64
- package/src/aggregate/verifier.ts +6 -4
- package/src/assert-descriptor-self-consistency.ts +6 -0
- package/src/compute-extension-space-apply-path.ts +1 -1
- package/src/emit-contract-space-artefacts.ts +4 -3
- package/src/enumerate-migration-spaces.ts +127 -0
- package/src/errors.ts +17 -2
- package/src/exports/aggregate.ts +17 -12
- package/src/exports/enumerate-migration-spaces.ts +4 -0
- package/src/exports/io.ts +2 -0
- package/src/exports/metadata.ts +1 -1
- package/src/exports/migration-graph.ts +1 -0
- package/src/exports/migration-list-graph-topology.ts +5 -0
- package/src/exports/migration-list-types.ts +5 -0
- package/src/exports/refs.ts +8 -0
- package/src/exports/spaces.ts +3 -0
- package/src/graph-membership.ts +17 -0
- package/src/graph.ts +0 -1
- package/src/hash.ts +7 -8
- package/src/integrity-violation.ts +114 -0
- package/src/io.ts +139 -14
- package/src/metadata.ts +1 -1
- package/src/migration-base.ts +10 -30
- package/src/migration-graph.ts +7 -35
- package/src/migration-list-graph-topology.ts +158 -0
- package/src/migration-list-types.ts +21 -0
- package/src/read-contract-space-head-ref.ts +5 -2
- package/src/refs/snapshot.ts +197 -0
- package/src/refs.ts +97 -1
- package/src/space-layout.ts +30 -0
- package/dist/errors-DGYwcwXs.mjs.map +0 -1
- package/dist/exports/io.d.mts.map +0 -1
- package/dist/graph-BrLXqoUc.d.mts.map +0 -1
- package/dist/hash-Cr4WIr4Z.mjs.map +0 -1
- package/dist/io-BPLfzvZe.mjs.map +0 -1
- package/dist/metadata-BFX0xdz8.d.mts +0 -2
- package/dist/migration-graph-De0dUZoC.d.mts.map +0 -1
- package/dist/migration-graph-nlS4TRpn.mjs.map +0 -1
- package/dist/package-DZj8YvD0.d.mts.map +0 -1
- package/dist/read-contract-space-contract-DRueB4Aa.mjs.map +0 -1
- package/dist/refs-BDHo5l_g.mjs.map +0 -1
- package/dist/refs-CDaNerhT.d.mts.map +0 -1
|
@@ -1,9 +1,77 @@
|
|
|
1
|
-
import { t as MigrationToolsError } from "../errors-
|
|
2
|
-
import { s as readMigrationsDir } from "../io-
|
|
3
|
-
import "../constants-DWV9_o2Z.mjs";
|
|
4
|
-
import { l as reconstructGraph, o as findPathWithDecision } from "../migration-graph-
|
|
5
|
-
import { a as
|
|
1
|
+
import { t as MigrationToolsError } from "../errors-4YabujxZ.mjs";
|
|
2
|
+
import { s as readMigrationsDir } from "../io-BHl0amF0.mjs";
|
|
3
|
+
import { t as EMPTY_CONTRACT_HASH } from "../constants-DWV9_o2Z.mjs";
|
|
4
|
+
import { l as reconstructGraph, o as findPathWithDecision } from "../migration-graph-kGBkIZDa.mjs";
|
|
5
|
+
import { a as readRefsTolerant, t as HEAD_REF_NAME } from "../refs-BBKNL45K.mjs";
|
|
6
|
+
import { c as spaceMigrationDirectory, i as RESERVED_SPACE_SUBDIR_NAMES, l as spaceRefsDirectory, r as APP_SPACE_ID, s as isValidSpaceId, t as listContractSpaceDirectories } from "../verify-contract-spaces-BJX5gqtD.mjs";
|
|
7
|
+
import { n as readContractSpaceHeadRef, t as readContractSpaceContract } from "../read-contract-space-contract-BS5Oxbgw.mjs";
|
|
6
8
|
import { notOk, ok } from "@prisma-next/utils/result";
|
|
9
|
+
//#region src/aggregate/aggregate.ts
|
|
10
|
+
/**
|
|
11
|
+
* Resolve a member's head ref, asserting it is present. The apply/verify
|
|
12
|
+
* engine only runs after `checkIntegrity` has refused on `headRefMissing`,
|
|
13
|
+
* so a member reaching the planner / verifier without a head ref is a
|
|
14
|
+
* programming error (the integrity gate was skipped), not a user-facing
|
|
15
|
+
* state. The app member's head ref is always synthesised, so this only
|
|
16
|
+
* ever guards an ungated extension space.
|
|
17
|
+
*/
|
|
18
|
+
function requireHeadRef(member) {
|
|
19
|
+
if (member.headRef === null) throw new Error(`Contract space "${member.spaceId}" has no head ref; the integrity gate must refuse a missing head ref before planning or verifying.`);
|
|
20
|
+
return member.headRef;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Build a {@link ContractSpaceMember} with lazily-memoised `graph()` and
|
|
24
|
+
* `contract()` facets.
|
|
25
|
+
*
|
|
26
|
+
* `graph()` reconstructs the migration graph from `packages` on first
|
|
27
|
+
* call and caches it. `contract()` calls `resolveContract` on first call
|
|
28
|
+
* and caches the result; a throwing `resolveContract` (e.g. a missing or
|
|
29
|
+
* undeserializable on-disk contract) re-throws on each call rather than
|
|
30
|
+
* caching a value — `checkIntegrity` surfaces that as `contractUnreadable`.
|
|
31
|
+
*/
|
|
32
|
+
function createContractSpaceMember(args) {
|
|
33
|
+
const { spaceId, packages, refs, headRef, resolveContract } = args;
|
|
34
|
+
let graphMemo;
|
|
35
|
+
let contractMemo;
|
|
36
|
+
return {
|
|
37
|
+
spaceId,
|
|
38
|
+
packages,
|
|
39
|
+
refs,
|
|
40
|
+
headRef,
|
|
41
|
+
graph() {
|
|
42
|
+
graphMemo ??= reconstructGraph(packages);
|
|
43
|
+
return graphMemo;
|
|
44
|
+
},
|
|
45
|
+
contract() {
|
|
46
|
+
contractMemo ??= resolveContract();
|
|
47
|
+
return contractMemo;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Assemble a {@link ContractSpaceAggregate} value from its members and a
|
|
53
|
+
* `checkIntegrity` implementation. The query methods (`listSpaces` /
|
|
54
|
+
* `hasSpace` / `space` / `spaces`) are derived here so every aggregate —
|
|
55
|
+
* loader-built or test-built — shares one query surface: `app` first,
|
|
56
|
+
* then `extensions` in the order supplied (the loader sorts them
|
|
57
|
+
* lex-ascending by `spaceId`).
|
|
58
|
+
*/
|
|
59
|
+
function createContractSpaceAggregate(args) {
|
|
60
|
+
const { targetId, app, extensions, checkIntegrity } = args;
|
|
61
|
+
const ordered = [app, ...extensions];
|
|
62
|
+
const byId = new Map(ordered.map((m) => [m.spaceId, m]));
|
|
63
|
+
return {
|
|
64
|
+
targetId,
|
|
65
|
+
app,
|
|
66
|
+
extensions,
|
|
67
|
+
listSpaces: () => ordered.map((m) => m.spaceId),
|
|
68
|
+
hasSpace: (id) => byId.has(id),
|
|
69
|
+
space: (id) => byId.get(id),
|
|
70
|
+
spaces: () => ordered,
|
|
71
|
+
checkIntegrity
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
//#endregion
|
|
7
75
|
//#region src/aggregate/extract-storage-element-names.ts
|
|
8
76
|
/**
|
|
9
77
|
* Extract the set of top-level storage element names a contract claims.
|
|
@@ -60,189 +128,296 @@ function addRecordKeys(value, names) {
|
|
|
60
128
|
if (typeof value === "object" && value !== null && !Array.isArray(value)) for (const name of Object.keys(value)) names.add(name);
|
|
61
129
|
}
|
|
62
130
|
//#endregion
|
|
63
|
-
//#region src/aggregate/
|
|
64
|
-
function integrityDetail(error) {
|
|
65
|
-
if (MigrationToolsError.is(error)) return error.why;
|
|
66
|
-
if (error instanceof Error) return error.message;
|
|
67
|
-
return String(error);
|
|
68
|
-
}
|
|
131
|
+
//#region src/aggregate/check-integrity.ts
|
|
69
132
|
/**
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* descriptor's `contractJson` value. Each extension space's contract
|
|
76
|
-
* is read from its on-disk `migrations/<id>/contract.json` mirror; the
|
|
77
|
-
* descriptor's role is exhausted by the seed phase that wrote that
|
|
78
|
-
* mirror in the first place. The loader composes existing
|
|
79
|
-
* migration-tools primitives — layout precheck (via
|
|
80
|
-
* {@link listContractSpaceDirectories}), integrity checks (via
|
|
81
|
-
* {@link readMigrationsDir} / {@link readContractSpaceHeadRef} /
|
|
82
|
-
* {@link readContractSpaceContract} / `deserializeContract`), and
|
|
83
|
-
* disjointness — into a single typed value.
|
|
84
|
-
*
|
|
85
|
-
* Failure semantics: every failure variant in {@link LoadAggregateError}
|
|
86
|
-
* short-circuits the load.
|
|
133
|
+
* Walk the loaded model and return **every** integrity violation — never
|
|
134
|
+
* bailing at the first. Structurally-derivable violations (load-time
|
|
135
|
+
* problems, self-edges, missing / unreachable head refs) are always
|
|
136
|
+
* produced; layout-drift checks require `declaredExtensions`, and
|
|
137
|
+
* contract / target / disjointness checks require `checkContracts`.
|
|
87
138
|
*/
|
|
88
|
-
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
spaceId
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
139
|
+
function computeIntegrityViolations(input, opts) {
|
|
140
|
+
const violations = [];
|
|
141
|
+
for (const { member, problems, refProblems, headRefProblem, isApp } of input.spaces) {
|
|
142
|
+
const { spaceId } = member;
|
|
143
|
+
for (const problem of problems) violations.push(loadProblemToViolation(spaceId, problem));
|
|
144
|
+
for (const refProblem of refProblems) violations.push({
|
|
145
|
+
kind: "refUnreadable",
|
|
146
|
+
spaceId,
|
|
147
|
+
refName: refProblem.refName,
|
|
148
|
+
detail: refProblem.detail
|
|
149
|
+
});
|
|
150
|
+
if (headRefProblem !== null) violations.push({
|
|
151
|
+
kind: "refUnreadable",
|
|
152
|
+
spaceId,
|
|
153
|
+
refName: headRefProblem.refName,
|
|
154
|
+
detail: headRefProblem.detail
|
|
155
|
+
});
|
|
156
|
+
for (const pkg of member.packages) {
|
|
157
|
+
const from = pkg.metadata.from ?? "sha256:empty";
|
|
158
|
+
const isSelfEdge = from === pkg.metadata.to;
|
|
159
|
+
const hasDataOp = pkg.ops.some((op) => op.operationClass === "data");
|
|
160
|
+
if (isSelfEdge && !hasDataOp) violations.push({
|
|
161
|
+
kind: "sameSourceAndTarget",
|
|
162
|
+
spaceId,
|
|
163
|
+
dirName: pkg.dirName,
|
|
164
|
+
hash: from
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
violations.push(...duplicateMigrationHashViolations(spaceId, member.packages));
|
|
168
|
+
if (!isApp && headRefProblem === null) {
|
|
169
|
+
if (member.headRef === null) violations.push({
|
|
170
|
+
kind: "headRefMissing",
|
|
171
|
+
spaceId
|
|
172
|
+
});
|
|
173
|
+
else if (!headRefPresentInGraph(member, member.headRef.hash)) violations.push({
|
|
174
|
+
kind: "headRefNotInGraph",
|
|
175
|
+
spaceId,
|
|
176
|
+
hash: member.headRef.hash
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (opts?.declaredExtensions !== void 0) violations.push(...layoutViolations(input.spaces, opts.declaredExtensions));
|
|
181
|
+
if (opts?.checkContracts === true) violations.push(...contractViolations(input));
|
|
182
|
+
return violations;
|
|
183
|
+
}
|
|
184
|
+
function loadProblemToViolation(spaceId, problem) {
|
|
185
|
+
switch (problem.kind) {
|
|
186
|
+
case "hashMismatch": return {
|
|
187
|
+
kind: "hashMismatch",
|
|
188
|
+
spaceId,
|
|
189
|
+
dirName: problem.dirName,
|
|
190
|
+
stored: problem.stored,
|
|
191
|
+
computed: problem.computed
|
|
192
|
+
};
|
|
193
|
+
case "providedInvariantsMismatch": return {
|
|
194
|
+
kind: "providedInvariantsMismatch",
|
|
195
|
+
spaceId,
|
|
196
|
+
dirName: problem.dirName
|
|
197
|
+
};
|
|
198
|
+
case "packageUnloadable": return {
|
|
199
|
+
kind: "packageUnloadable",
|
|
200
|
+
spaceId,
|
|
201
|
+
dirName: problem.dirName,
|
|
202
|
+
detail: problem.detail
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
function duplicateMigrationHashViolations(spaceId, packages) {
|
|
207
|
+
const dirNamesByHash = /* @__PURE__ */ new Map();
|
|
208
|
+
for (const pkg of packages) {
|
|
209
|
+
const hash = pkg.metadata.migrationHash;
|
|
210
|
+
const dirNames = dirNamesByHash.get(hash);
|
|
211
|
+
if (dirNames) dirNames.push(pkg.dirName);
|
|
212
|
+
else dirNamesByHash.set(hash, [pkg.dirName]);
|
|
213
|
+
}
|
|
214
|
+
const out = [];
|
|
215
|
+
for (const [migrationHash, dirNames] of dirNamesByHash) if (dirNames.length > 1) out.push({
|
|
216
|
+
kind: "duplicateMigrationHash",
|
|
217
|
+
spaceId,
|
|
218
|
+
migrationHash,
|
|
219
|
+
dirNames: [...dirNames].sort()
|
|
101
220
|
});
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
221
|
+
return out;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Whether a space's head-ref hash is present in its reconstructed graph.
|
|
225
|
+
* An empty graph is reachable only by the empty-contract sentinel.
|
|
226
|
+
*/
|
|
227
|
+
function headRefPresentInGraph(member, headHash) {
|
|
228
|
+
const graph = member.graph();
|
|
229
|
+
if (graph.nodes.size === 0) return headHash === EMPTY_CONTRACT_HASH;
|
|
230
|
+
return graph.nodes.has(headHash);
|
|
231
|
+
}
|
|
232
|
+
function layoutViolations(spaces, declaredExtensions) {
|
|
233
|
+
const out = [];
|
|
234
|
+
const extensionSpaceIds = new Set(spaces.filter((s) => !s.isApp).map((s) => s.member.spaceId));
|
|
235
|
+
const declaredIds = new Set(declaredExtensions.map((d) => d.id));
|
|
236
|
+
for (const id of [...extensionSpaceIds].sort()) if (!declaredIds.has(id)) out.push({
|
|
107
237
|
kind: "orphanSpaceDir",
|
|
108
|
-
spaceId:
|
|
238
|
+
spaceId: id
|
|
109
239
|
});
|
|
110
|
-
for (const id of [...
|
|
240
|
+
for (const id of [...declaredIds].sort()) if (!extensionSpaceIds.has(id)) out.push({
|
|
111
241
|
kind: "declaredButUnmigrated",
|
|
112
242
|
spaceId: id
|
|
113
243
|
});
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const
|
|
119
|
-
for (const
|
|
120
|
-
|
|
121
|
-
if (headRef === null) return notOk({
|
|
122
|
-
kind: "integrityFailure",
|
|
123
|
-
spaceId: entry.id,
|
|
124
|
-
detail: `Head ref \`refs/head.json\` is missing for extension space "${entry.id}".`
|
|
125
|
-
});
|
|
126
|
-
let spaceContractRaw;
|
|
127
|
-
try {
|
|
128
|
-
spaceContractRaw = await readContractSpaceContract(input.migrationsDir, entry.id);
|
|
129
|
-
} catch (error) {
|
|
130
|
-
return notOk({
|
|
131
|
-
kind: "integrityFailure",
|
|
132
|
-
spaceId: entry.id,
|
|
133
|
-
detail: integrityDetail(error)
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
let spaceContract;
|
|
244
|
+
return out;
|
|
245
|
+
}
|
|
246
|
+
function contractViolations(input) {
|
|
247
|
+
const out = [];
|
|
248
|
+
const elementClaimedBy = /* @__PURE__ */ new Map();
|
|
249
|
+
for (const { member } of input.spaces) {
|
|
250
|
+
let contract;
|
|
137
251
|
try {
|
|
138
|
-
|
|
252
|
+
contract = member.contract();
|
|
139
253
|
} catch (error) {
|
|
140
|
-
|
|
141
|
-
kind: "
|
|
142
|
-
spaceId:
|
|
143
|
-
detail:
|
|
254
|
+
out.push({
|
|
255
|
+
kind: "contractUnreadable",
|
|
256
|
+
spaceId: member.spaceId,
|
|
257
|
+
detail: detailOf$1(error)
|
|
144
258
|
});
|
|
259
|
+
continue;
|
|
145
260
|
}
|
|
146
|
-
if (
|
|
261
|
+
if (contract.target !== input.targetId) out.push({
|
|
147
262
|
kind: "targetMismatch",
|
|
148
|
-
spaceId:
|
|
263
|
+
spaceId: member.spaceId,
|
|
149
264
|
expected: input.targetId,
|
|
150
|
-
actual:
|
|
151
|
-
});
|
|
152
|
-
let packages;
|
|
153
|
-
try {
|
|
154
|
-
packages = await readMigrationsDir(spaceMigrationDirectory(input.migrationsDir, entry.id));
|
|
155
|
-
} catch (error) {
|
|
156
|
-
return notOk({
|
|
157
|
-
kind: "integrityFailure",
|
|
158
|
-
spaceId: entry.id,
|
|
159
|
-
detail: integrityDetail(error)
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
let graph;
|
|
163
|
-
try {
|
|
164
|
-
graph = reconstructGraph(packages);
|
|
165
|
-
} catch (error) {
|
|
166
|
-
return notOk({
|
|
167
|
-
kind: "integrityFailure",
|
|
168
|
-
spaceId: entry.id,
|
|
169
|
-
detail: integrityDetail(error)
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
if (graph.nodes.size === 0) {
|
|
173
|
-
if (headRef.hash !== "sha256:empty") return notOk({
|
|
174
|
-
kind: "integrityFailure",
|
|
175
|
-
spaceId: entry.id,
|
|
176
|
-
detail: `Head ref "${headRef.hash}" is not present in the (empty) on-disk migration graph.`
|
|
177
|
-
});
|
|
178
|
-
} else if (!graph.nodes.has(headRef.hash)) return notOk({
|
|
179
|
-
kind: "integrityFailure",
|
|
180
|
-
spaceId: entry.id,
|
|
181
|
-
detail: `Head ref "${headRef.hash}" is not present in the on-disk migration graph.`
|
|
182
|
-
});
|
|
183
|
-
const packagesByMigrationHash = new Map(packages.map((p) => [p.metadata.migrationHash, p]));
|
|
184
|
-
loadedExtensions.push({
|
|
185
|
-
entry,
|
|
186
|
-
contract: spaceContract,
|
|
187
|
-
headRefHash: headRef.hash,
|
|
188
|
-
headRefInvariants: [...headRef.invariants].sort(),
|
|
189
|
-
migrations: {
|
|
190
|
-
graph,
|
|
191
|
-
packagesByMigrationHash
|
|
192
|
-
}
|
|
265
|
+
actual: contract.target
|
|
193
266
|
});
|
|
194
|
-
|
|
195
|
-
let appGraph;
|
|
196
|
-
try {
|
|
197
|
-
appGraph = reconstructGraph(input.appMigrationPackages);
|
|
198
|
-
} catch (error) {
|
|
199
|
-
return notOk({
|
|
200
|
-
kind: "integrityFailure",
|
|
201
|
-
spaceId: APP_SPACE_ID,
|
|
202
|
-
detail: error instanceof Error ? error.message : String(error)
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
const appPackagesByMigrationHash = new Map(input.appMigrationPackages.map((p) => [p.metadata.migrationHash, p]));
|
|
206
|
-
const appMember = {
|
|
207
|
-
spaceId: APP_SPACE_ID,
|
|
208
|
-
contract: input.appContract,
|
|
209
|
-
headRef: {
|
|
210
|
-
hash: input.appContract.storage.storageHash,
|
|
211
|
-
invariants: []
|
|
212
|
-
},
|
|
213
|
-
migrations: {
|
|
214
|
-
graph: appGraph,
|
|
215
|
-
packagesByMigrationHash: appPackagesByMigrationHash
|
|
216
|
-
}
|
|
217
|
-
};
|
|
218
|
-
const extensionMembers = loadedExtensions.map((s) => ({
|
|
219
|
-
spaceId: s.entry.id,
|
|
220
|
-
contract: s.contract,
|
|
221
|
-
headRef: {
|
|
222
|
-
hash: s.headRefHash,
|
|
223
|
-
invariants: s.headRefInvariants
|
|
224
|
-
},
|
|
225
|
-
migrations: s.migrations
|
|
226
|
-
}));
|
|
227
|
-
const elementClaimedBy = /* @__PURE__ */ new Map();
|
|
228
|
-
for (const member of [appMember, ...extensionMembers]) {
|
|
229
|
-
const elements = extractStorageElementNames(member.contract);
|
|
230
|
-
for (const elementName of elements) {
|
|
267
|
+
for (const elementName of extractStorageElementNames(contract)) {
|
|
231
268
|
const claimers = elementClaimedBy.get(elementName);
|
|
232
269
|
if (claimers) claimers.push(member.spaceId);
|
|
233
270
|
else elementClaimedBy.set(elementName, [member.spaceId]);
|
|
234
271
|
}
|
|
235
272
|
}
|
|
236
|
-
|
|
237
|
-
|
|
273
|
+
const disjointness = [];
|
|
274
|
+
for (const [element, claimedBy] of elementClaimedBy) if (claimedBy.length > 1) disjointness.push({
|
|
275
|
+
kind: "disjointness",
|
|
238
276
|
element,
|
|
239
277
|
claimedBy: [...claimedBy].sort()
|
|
240
278
|
});
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
279
|
+
disjointness.sort((a, b) => a.kind === "disjointness" && b.kind === "disjointness" ? a.element.localeCompare(b.element) : 0);
|
|
280
|
+
out.push(...disjointness);
|
|
281
|
+
return out;
|
|
282
|
+
}
|
|
283
|
+
function detailOf$1(error) {
|
|
284
|
+
if (MigrationToolsError.is(error)) return error.why;
|
|
285
|
+
if (error instanceof Error) return error.message;
|
|
286
|
+
return String(error);
|
|
287
|
+
}
|
|
288
|
+
//#endregion
|
|
289
|
+
//#region src/aggregate/loader.ts
|
|
290
|
+
/**
|
|
291
|
+
* Build a tolerant, queryable {@link ContractSpaceAggregate} from on-disk
|
|
292
|
+
* migration state plus the caller's live app contract.
|
|
293
|
+
*
|
|
294
|
+
* Building **never throws on disk content**: a hash- or
|
|
295
|
+
* invariants-mismatched package is retained, an unparseable package is
|
|
296
|
+
* omitted, a missing extension head ref leaves `headRef: null`, and an
|
|
297
|
+
* unreadable on-disk contract defers its failure to `member.contract()`.
|
|
298
|
+
* Every such problem is judged by {@link ContractSpaceAggregate.checkIntegrity}
|
|
299
|
+
* rather than aborting the load. The only rejections are catastrophic I/O
|
|
300
|
+
* (a `migrations/` that exists but is unreadable for reasons other than
|
|
301
|
+
* absence).
|
|
302
|
+
*
|
|
303
|
+
* The app space's head ref is synthesised from the live contract's
|
|
304
|
+
* storage hash (the app contract is authored independently of the
|
|
305
|
+
* migration graph), and `app.contract()` returns the supplied contract.
|
|
306
|
+
* Extension spaces read their contract, refs, and head ref from disk.
|
|
307
|
+
*/
|
|
308
|
+
async function loadContractSpaceAggregate(input) {
|
|
309
|
+
const { migrationsDir, deserializeContract, appContract } = input;
|
|
310
|
+
const targetId = appContract.target;
|
|
311
|
+
const appState = await loadAppSpace(migrationsDir, appContract);
|
|
312
|
+
const extensionStates = await loadExtensionSpaces(migrationsDir, deserializeContract);
|
|
313
|
+
const spaces = [appState, ...extensionStates];
|
|
314
|
+
return createContractSpaceAggregate({
|
|
315
|
+
targetId,
|
|
316
|
+
app: appState.member,
|
|
317
|
+
extensions: extensionStates.map((state) => state.member),
|
|
318
|
+
checkIntegrity: (opts) => computeIntegrityViolations({
|
|
319
|
+
targetId,
|
|
320
|
+
spaces
|
|
321
|
+
}, opts)
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
async function loadAppSpace(migrationsDir, appContract) {
|
|
325
|
+
const spaceDir = spaceMigrationDirectory(migrationsDir, APP_SPACE_ID);
|
|
326
|
+
const { packages, problems } = await readMigrationsDir(spaceDir);
|
|
327
|
+
const { refs, problems: refProblems } = await readRefsTolerant(spaceRefsDirectory(spaceDir));
|
|
328
|
+
return {
|
|
329
|
+
member: createContractSpaceMember({
|
|
330
|
+
spaceId: APP_SPACE_ID,
|
|
331
|
+
packages,
|
|
332
|
+
refs,
|
|
333
|
+
headRef: {
|
|
334
|
+
hash: appContract.storage.storageHash,
|
|
335
|
+
invariants: []
|
|
336
|
+
},
|
|
337
|
+
resolveContract: () => appContract
|
|
338
|
+
}),
|
|
339
|
+
problems,
|
|
340
|
+
refProblems,
|
|
341
|
+
headRefProblem: null,
|
|
342
|
+
isApp: true
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
async function loadExtensionSpaces(migrationsDir, deserializeContract) {
|
|
346
|
+
const extensionIds = (await listContractSpaceDirectories(migrationsDir)).filter((name) => name !== APP_SPACE_ID).filter((name) => !RESERVED_SPACE_SUBDIR_NAMES.has(name)).filter(isValidSpaceId).sort();
|
|
347
|
+
const states = [];
|
|
348
|
+
for (const spaceId of extensionIds) states.push(await loadExtensionSpace(migrationsDir, spaceId, deserializeContract));
|
|
349
|
+
return states;
|
|
350
|
+
}
|
|
351
|
+
async function loadExtensionSpace(migrationsDir, spaceId, deserializeContract) {
|
|
352
|
+
const spaceDir = spaceMigrationDirectory(migrationsDir, spaceId);
|
|
353
|
+
const { packages, problems } = await readMigrationsDir(spaceDir);
|
|
354
|
+
const { refs, problems: refProblems } = await readRefsTolerant(spaceRefsDirectory(spaceDir));
|
|
355
|
+
const { headRef, problem: headRefProblem } = await readHeadRefTolerant(migrationsDir, spaceId);
|
|
356
|
+
const rawContract = await readRawContractDeferred(migrationsDir, spaceId);
|
|
357
|
+
return {
|
|
358
|
+
member: createContractSpaceMember({
|
|
359
|
+
spaceId,
|
|
360
|
+
packages,
|
|
361
|
+
refs,
|
|
362
|
+
headRef,
|
|
363
|
+
resolveContract: () => deserializeContract(rawContract())
|
|
364
|
+
}),
|
|
365
|
+
problems,
|
|
366
|
+
refProblems,
|
|
367
|
+
headRefProblem,
|
|
368
|
+
isApp: false
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Read an extension's head ref, distinguishing a *genuinely absent*
|
|
373
|
+
* `head.json` (`headRef: null`, no problem — judged `headRefMissing`)
|
|
374
|
+
* from one that *exists but cannot be parsed* (`headRef: null` plus a
|
|
375
|
+
* problem — judged `refUnreadable`, not `headRefMissing`).
|
|
376
|
+
* `readContractSpaceHeadRef` already returns `null` only for ENOENT and
|
|
377
|
+
* throws for unparseable / schema-invalid content, so the throw is the
|
|
378
|
+
* corruption signal. Construction never throws on disk content.
|
|
379
|
+
*/
|
|
380
|
+
function isToleratedRefHeadReadError(error) {
|
|
381
|
+
if (MigrationToolsError.is(error)) return true;
|
|
382
|
+
if (!(error instanceof Error)) return false;
|
|
383
|
+
const code = error.code;
|
|
384
|
+
return code === "ENOENT" || code === "EISDIR";
|
|
385
|
+
}
|
|
386
|
+
async function readHeadRefTolerant(migrationsDir, spaceId) {
|
|
387
|
+
try {
|
|
388
|
+
return {
|
|
389
|
+
headRef: await readContractSpaceHeadRef(migrationsDir, spaceId),
|
|
390
|
+
problem: null
|
|
391
|
+
};
|
|
392
|
+
} catch (error) {
|
|
393
|
+
if (!isToleratedRefHeadReadError(error)) throw error;
|
|
394
|
+
return {
|
|
395
|
+
headRef: null,
|
|
396
|
+
problem: {
|
|
397
|
+
refName: HEAD_REF_NAME,
|
|
398
|
+
detail: detailOf(error)
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
function detailOf(error) {
|
|
404
|
+
return error instanceof Error ? error.message : String(error);
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Read the raw on-disk contract eagerly (cheap I/O) but defer its
|
|
408
|
+
* (throwing) failure to call time, so a missing or unparseable
|
|
409
|
+
* `contract.json` becomes a `contract()` throw — surfaced as
|
|
410
|
+
* `contractUnreadable` — rather than a construction failure.
|
|
411
|
+
*/
|
|
412
|
+
async function readRawContractDeferred(migrationsDir, spaceId) {
|
|
413
|
+
try {
|
|
414
|
+
const raw = await readContractSpaceContract(migrationsDir, spaceId);
|
|
415
|
+
return () => raw;
|
|
416
|
+
} catch (error) {
|
|
417
|
+
return () => {
|
|
418
|
+
throw error;
|
|
419
|
+
};
|
|
420
|
+
}
|
|
246
421
|
}
|
|
247
422
|
//#endregion
|
|
248
423
|
//#region src/aggregate/strategies/graph-walk.ts
|
|
@@ -263,11 +438,13 @@ async function loadContractSpaceAggregate(input) {
|
|
|
263
438
|
*/
|
|
264
439
|
function graphWalkStrategy(input) {
|
|
265
440
|
const { aggregateTargetId, member, currentMarker, refName } = input;
|
|
266
|
-
const
|
|
441
|
+
const headRef = requireHeadRef(member);
|
|
442
|
+
const graph = member.graph();
|
|
443
|
+
const packagesByMigrationHash = new Map(member.packages.map((pkg) => [pkg.metadata.migrationHash, pkg]));
|
|
267
444
|
const fromHash = currentMarker?.storageHash ?? "sha256:empty";
|
|
268
445
|
const markerInvariants = new Set(currentMarker?.invariants ?? []);
|
|
269
|
-
const required = new Set(
|
|
270
|
-
const outcome = findPathWithDecision(graph, fromHash,
|
|
446
|
+
const required = new Set(headRef.invariants.filter((id) => !markerInvariants.has(id)));
|
|
447
|
+
const outcome = findPathWithDecision(graph, fromHash, headRef.hash, {
|
|
271
448
|
required,
|
|
272
449
|
...refName !== void 0 ? { refName } : {}
|
|
273
450
|
});
|
|
@@ -299,12 +476,12 @@ function graphWalkStrategy(input) {
|
|
|
299
476
|
targetId: aggregateTargetId,
|
|
300
477
|
spaceId: member.spaceId,
|
|
301
478
|
origin: currentMarker === null ? null : { storageHash: currentMarker.storageHash },
|
|
302
|
-
destination: { storageHash:
|
|
479
|
+
destination: { storageHash: headRef.hash },
|
|
303
480
|
operations: pathOps,
|
|
304
481
|
providedInvariants: [...providedInvariantsSet].sort()
|
|
305
482
|
},
|
|
306
483
|
displayOps: pathOps,
|
|
307
|
-
destinationContract: member.contract,
|
|
484
|
+
destinationContract: member.contract(),
|
|
308
485
|
strategy: "graph-walk",
|
|
309
486
|
migrationEdges: edgeRefs,
|
|
310
487
|
pathDecision: outcome.decision
|
|
@@ -386,7 +563,7 @@ function collectOwnedNames(member, otherMembers) {
|
|
|
386
563
|
const owned = /* @__PURE__ */ new Set();
|
|
387
564
|
for (const other of otherMembers) {
|
|
388
565
|
if (other.spaceId === member.spaceId) continue;
|
|
389
|
-
for (const name of extractStorageElementNames(other.contract)) owned.add(name);
|
|
566
|
+
for (const name of extractStorageElementNames(other.contract())) owned.add(name);
|
|
390
567
|
}
|
|
391
568
|
return owned;
|
|
392
569
|
}
|
|
@@ -450,7 +627,7 @@ function pruneCollectionsArray(schemaObj, ownedByOthers) {
|
|
|
450
627
|
async function synthStrategy(input) {
|
|
451
628
|
const projectedSchema = projectSchemaToSpace(input.schemaIntrospection, input.member, input.otherMembers);
|
|
452
629
|
const plannerResult = await input.migrations.createPlanner(input.familyInstance).plan({
|
|
453
|
-
contract: input.member.contract,
|
|
630
|
+
contract: input.member.contract(),
|
|
454
631
|
schema: projectedSchema,
|
|
455
632
|
policy: input.operationPolicy,
|
|
456
633
|
fromContract: null,
|
|
@@ -476,7 +653,7 @@ async function synthStrategy(input) {
|
|
|
476
653
|
}
|
|
477
654
|
}),
|
|
478
655
|
displayOps: synthedPlan.operations,
|
|
479
|
-
destinationContract: input.member.contract,
|
|
656
|
+
destinationContract: input.member.contract(),
|
|
480
657
|
strategy: "synth"
|
|
481
658
|
}
|
|
482
659
|
};
|
|
@@ -491,7 +668,7 @@ async function synthStrategy(input) {
|
|
|
491
668
|
* 1. If `callerPolicy.ignoreGraphFor.has(member.spaceId)`:
|
|
492
669
|
* - If `member.headRef.invariants` is empty → synth.
|
|
493
670
|
* - Else → `policyConflict` (synth cannot satisfy authored invariants).
|
|
494
|
-
* 2. Else if `member.
|
|
671
|
+
* 2. Else if `member.graph()` is non-empty AND graph-walk
|
|
495
672
|
* succeeds → graph-walk.
|
|
496
673
|
* 3. Else if `member.headRef.invariants` is empty → synth.
|
|
497
674
|
* 4. Else → graph-walk failure → `extensionPathUnreachable` /
|
|
@@ -513,12 +690,13 @@ async function planAggregate(input) {
|
|
|
513
690
|
for (const member of orderedMembers) {
|
|
514
691
|
const otherMembers = allMembers.filter((m) => m.spaceId !== member.spaceId);
|
|
515
692
|
const currentMarker = currentDBState.markersBySpaceId.get(member.spaceId) ?? null;
|
|
693
|
+
const headRef = requireHeadRef(member);
|
|
516
694
|
const ignoreGraph = callerPolicy.ignoreGraphFor.has(member.spaceId);
|
|
517
|
-
const invariantsRequired =
|
|
695
|
+
const invariantsRequired = headRef.invariants.length > 0;
|
|
518
696
|
if (ignoreGraph && invariantsRequired) return notOk({
|
|
519
697
|
kind: "policyConflict",
|
|
520
698
|
spaceId: member.spaceId,
|
|
521
|
-
detail: `\`callerPolicy.ignoreGraphFor\` requested for space "${member.spaceId}", but the member declares non-empty head-ref invariants (${
|
|
699
|
+
detail: `\`callerPolicy.ignoreGraphFor\` requested for space "${member.spaceId}", but the member declares non-empty head-ref invariants (${headRef.invariants.join(", ")}). Synthesising a plan from the contract IR cannot satisfy authored invariants — the graph must be walked. Either remove "${member.spaceId}" from \`ignoreGraphFor\` or amend the on-disk head ref to declare zero invariants.`
|
|
522
700
|
});
|
|
523
701
|
if (ignoreGraph) {
|
|
524
702
|
const synthOutcome = await synthStrategy({
|
|
@@ -539,7 +717,7 @@ async function planAggregate(input) {
|
|
|
539
717
|
perSpace.set(member.spaceId, synthOutcome.result);
|
|
540
718
|
continue;
|
|
541
719
|
}
|
|
542
|
-
if (member.
|
|
720
|
+
if (member.graph().nodes.size > 0) {
|
|
543
721
|
const walked = graphWalkStrategy({
|
|
544
722
|
aggregateTargetId: aggregate.targetId,
|
|
545
723
|
member,
|
|
@@ -552,7 +730,7 @@ async function planAggregate(input) {
|
|
|
552
730
|
if (walked.kind === "unreachable") return notOk({
|
|
553
731
|
kind: "extensionPathUnreachable",
|
|
554
732
|
spaceId: member.spaceId,
|
|
555
|
-
target:
|
|
733
|
+
target: headRef.hash
|
|
556
734
|
});
|
|
557
735
|
return notOk({
|
|
558
736
|
kind: "extensionPathUnsatisfiable",
|
|
@@ -563,7 +741,7 @@ async function planAggregate(input) {
|
|
|
563
741
|
if (invariantsRequired) return notOk({
|
|
564
742
|
kind: "extensionPathUnsatisfiable",
|
|
565
743
|
spaceId: member.spaceId,
|
|
566
|
-
missingInvariants: [...
|
|
744
|
+
missingInvariants: [...headRef.invariants].sort()
|
|
567
745
|
});
|
|
568
746
|
const synthOutcome = await synthStrategy({
|
|
569
747
|
aggregateTargetId: aggregate.targetId,
|
|
@@ -632,16 +810,17 @@ function runVerifyAggregate(input) {
|
|
|
632
810
|
markerPerSpace.set(member.spaceId, { kind: "absent" });
|
|
633
811
|
continue;
|
|
634
812
|
}
|
|
635
|
-
|
|
813
|
+
const headRef = requireHeadRef(member);
|
|
814
|
+
if (marker.storageHash !== headRef.hash) {
|
|
636
815
|
markerPerSpace.set(member.spaceId, {
|
|
637
816
|
kind: "hashMismatch",
|
|
638
817
|
markerHash: marker.storageHash,
|
|
639
|
-
expected:
|
|
818
|
+
expected: headRef.hash
|
|
640
819
|
});
|
|
641
820
|
continue;
|
|
642
821
|
}
|
|
643
822
|
const markerInvariants = new Set(marker.invariants);
|
|
644
|
-
const missing =
|
|
823
|
+
const missing = headRef.invariants.filter((id) => !markerInvariants.has(id));
|
|
645
824
|
if (missing.length > 0) {
|
|
646
825
|
markerPerSpace.set(member.spaceId, {
|
|
647
826
|
kind: "missingInvariants",
|
|
@@ -684,7 +863,7 @@ function detectOrphanElements(schemaIntrospection, members) {
|
|
|
684
863
|
const liveTables = schemaIntrospection.tables;
|
|
685
864
|
if (typeof liveTables !== "object" || liveTables === null) return [];
|
|
686
865
|
const claimedTables = /* @__PURE__ */ new Set();
|
|
687
|
-
for (const member of members) for (const name of extractStorageElementNames(member.contract)) claimedTables.add(name);
|
|
866
|
+
for (const member of members) for (const name of extractStorageElementNames(member.contract())) claimedTables.add(name);
|
|
688
867
|
const orphans = [];
|
|
689
868
|
for (const tableName of Object.keys(liveTables)) if (!claimedTables.has(tableName)) orphans.push({
|
|
690
869
|
kind: "table",
|
|
@@ -694,6 +873,6 @@ function detectOrphanElements(schemaIntrospection, members) {
|
|
|
694
873
|
return orphans;
|
|
695
874
|
}
|
|
696
875
|
//#endregion
|
|
697
|
-
export { graphWalkStrategy, loadContractSpaceAggregate, planAggregate, projectSchemaToSpace, verifyAggregate };
|
|
876
|
+
export { computeIntegrityViolations, createContractSpaceAggregate, createContractSpaceMember, graphWalkStrategy, loadContractSpaceAggregate, loadProblemToViolation, planAggregate, projectSchemaToSpace, requireHeadRef, verifyAggregate };
|
|
698
877
|
|
|
699
878
|
//# sourceMappingURL=aggregate.mjs.map
|