@fabricorg/assembly 0.4.7 → 0.4.8
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/CHANGELOG.md +27 -0
- package/dist/index.cjs +725 -836
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +161 -161
- package/dist/index.d.ts +161 -161
- package/dist/index.js +708 -801
- package/dist/index.js.map +1 -1
- package/package.json +13 -12
package/dist/index.cjs
CHANGED
|
@@ -1,894 +1,783 @@
|
|
|
1
|
-
"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
|
|
20
|
-
// index.ts
|
|
21
|
-
var index_exports = {};
|
|
22
|
-
__export(index_exports, {
|
|
23
|
-
ASSEMBLY_LOCKFILE_FORMAT_VERSION: () => ASSEMBLY_LOCKFILE_FORMAT_VERSION,
|
|
24
|
-
ASSEMBLY_MANIFEST_FORMAT_VERSION: () => ASSEMBLY_MANIFEST_FORMAT_VERSION,
|
|
25
|
-
assertAssemblyLockfile: () => assertAssemblyLockfile,
|
|
26
|
-
assertAssemblyManifest: () => assertAssemblyManifest,
|
|
27
|
-
assertAssemblyResolved: () => assertAssemblyResolved,
|
|
28
|
-
assertAssemblyRuntimeCompatible: () => assertAssemblyRuntimeCompatible,
|
|
29
|
-
assertComponentPackManifest: () => assertComponentPackManifest,
|
|
30
|
-
computeAdapterCertificationDigest: () => computeAdapterCertificationDigest,
|
|
31
|
-
computeAssemblyLockfileDigest: () => computeAssemblyLockfileDigest,
|
|
32
|
-
computeUsageContractDocumentDigest: () => computeUsageContractDocumentDigest,
|
|
33
|
-
parseSemverRange: () => parseSemverRange,
|
|
34
|
-
resolveAssembly: () => resolveAssembly,
|
|
35
|
-
resolveVersionRange: () => resolveVersionRange,
|
|
36
|
-
satisfiesRange: () => satisfiesRange,
|
|
37
|
-
validateAssemblyBindings: () => validateAssemblyBindings
|
|
38
|
-
});
|
|
39
|
-
module.exports = __toCommonJS(index_exports);
|
|
40
|
-
var import_node_crypto = require("crypto");
|
|
41
|
-
var import_ports = require("@fabricorg/ports");
|
|
42
|
-
var import_adoption_bindings = require("@fabricorg/adoption-bindings");
|
|
43
|
-
var import_gen_capability = require("@fabricorg/gen-capability");
|
|
44
|
-
var ASSEMBLY_MANIFEST_FORMAT_VERSION = 2;
|
|
45
|
-
var ASSEMBLY_LOCKFILE_FORMAT_VERSION = 2;
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let crypto = require("crypto");
|
|
3
|
+
let _fabricorg_ports = require("@fabricorg/ports");
|
|
4
|
+
let _fabricorg_adoption_bindings = require("@fabricorg/adoption-bindings");
|
|
5
|
+
let _fabricorg_gen_capability = require("@fabricorg/gen-capability");
|
|
6
|
+
//#region index.ts
|
|
7
|
+
/**
|
|
8
|
+
* Version of the assembly manifest document. A vertical negotiates on this, not
|
|
9
|
+
* on this package's version, and it moves only when the serialized shape changes.
|
|
10
|
+
*/
|
|
11
|
+
const ASSEMBLY_MANIFEST_FORMAT_VERSION = 2;
|
|
12
|
+
/**
|
|
13
|
+
* Version of the resolved lockfile document. Bump when the serialized shape
|
|
14
|
+
* changes in a way a consumer must detect.
|
|
15
|
+
*/
|
|
16
|
+
const ASSEMBLY_LOCKFILE_FORMAT_VERSION = 2;
|
|
46
17
|
function parseSemver(input) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
18
|
+
const match = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/.exec(input.trim());
|
|
19
|
+
if (!match) return void 0;
|
|
20
|
+
return {
|
|
21
|
+
major: Number(match[1]),
|
|
22
|
+
minor: Number(match[2]),
|
|
23
|
+
patch: Number(match[3]),
|
|
24
|
+
prerelease: match[4] ? match[4].split(".") : []
|
|
25
|
+
};
|
|
55
26
|
}
|
|
56
27
|
function comparePrerelease(a, b) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
return 0;
|
|
28
|
+
if (a.length === 0 && b.length === 0) return 0;
|
|
29
|
+
if (a.length === 0) return 1;
|
|
30
|
+
if (b.length === 0) return -1;
|
|
31
|
+
for (let i = 0; i < Math.max(a.length, b.length); i++) {
|
|
32
|
+
const ai = a[i];
|
|
33
|
+
const bi = b[i];
|
|
34
|
+
if (ai === void 0) return -1;
|
|
35
|
+
if (bi === void 0) return 1;
|
|
36
|
+
const an = /^\d+$/.test(ai);
|
|
37
|
+
const bn = /^\d+$/.test(bi);
|
|
38
|
+
if (an && bn) {
|
|
39
|
+
const diff = Number(ai) - Number(bi);
|
|
40
|
+
if (diff !== 0) return diff;
|
|
41
|
+
} else if (an) return -1;
|
|
42
|
+
else if (bn) return 1;
|
|
43
|
+
else {
|
|
44
|
+
if (ai < bi) return -1;
|
|
45
|
+
if (ai > bi) return 1;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return 0;
|
|
80
49
|
}
|
|
81
50
|
function compareSemver(a, b) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
51
|
+
if (a.major !== b.major) return a.major - b.major;
|
|
52
|
+
if (a.minor !== b.minor) return a.minor - b.minor;
|
|
53
|
+
if (a.patch !== b.patch) return a.patch - b.patch;
|
|
54
|
+
return comparePrerelease(a.prerelease, b.prerelease);
|
|
86
55
|
}
|
|
87
56
|
function caretComparator(range) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
57
|
+
return (version) => {
|
|
58
|
+
if (compareSemver(version, range) < 0) return false;
|
|
59
|
+
if (version.major !== range.major) return false;
|
|
60
|
+
if (version.major === 0) {
|
|
61
|
+
if (version.minor !== range.minor) return false;
|
|
62
|
+
if (range.minor === 0 && version.patch !== range.patch) return false;
|
|
63
|
+
}
|
|
64
|
+
return true;
|
|
65
|
+
};
|
|
97
66
|
}
|
|
98
67
|
function tildeComparator(range) {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
68
|
+
return (version) => {
|
|
69
|
+
if (compareSemver(version, range) < 0) return false;
|
|
70
|
+
return version.major === range.major && version.minor === range.minor;
|
|
71
|
+
};
|
|
103
72
|
}
|
|
104
73
|
function exactComparator(range) {
|
|
105
|
-
|
|
74
|
+
return (version) => compareSemver(version, range) === 0;
|
|
106
75
|
}
|
|
107
76
|
function gteComparator(range) {
|
|
108
|
-
|
|
77
|
+
return (version) => compareSemver(version, range) >= 0;
|
|
109
78
|
}
|
|
110
79
|
function gtComparator(range) {
|
|
111
|
-
|
|
80
|
+
return (version) => compareSemver(version, range) > 0;
|
|
112
81
|
}
|
|
113
82
|
function lteComparator(range) {
|
|
114
|
-
|
|
83
|
+
return (version) => compareSemver(version, range) <= 0;
|
|
115
84
|
}
|
|
116
85
|
function ltComparator(range) {
|
|
117
|
-
|
|
86
|
+
return (version) => compareSemver(version, range) < 0;
|
|
118
87
|
}
|
|
119
88
|
function anyComparator() {
|
|
120
|
-
|
|
89
|
+
return () => true;
|
|
121
90
|
}
|
|
122
91
|
function parseComparator(raw) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
92
|
+
const trimmed = raw.trim();
|
|
93
|
+
if (trimmed === "*" || trimmed === "") return {
|
|
94
|
+
operator: "*",
|
|
95
|
+
comparator: anyComparator(),
|
|
96
|
+
raw: trimmed,
|
|
97
|
+
rangeVersion: void 0
|
|
98
|
+
};
|
|
99
|
+
const caret = /^(\^)(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)$/.exec(trimmed);
|
|
100
|
+
if (caret) {
|
|
101
|
+
const version = parseSemver(caret[2]);
|
|
102
|
+
if (!version) return void 0;
|
|
103
|
+
return {
|
|
104
|
+
operator: "^",
|
|
105
|
+
comparator: caretComparator(version),
|
|
106
|
+
raw: trimmed,
|
|
107
|
+
rangeVersion: version
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
const tilde = /^(~)(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)$/.exec(trimmed);
|
|
111
|
+
if (tilde) {
|
|
112
|
+
const version = parseSemver(tilde[2]);
|
|
113
|
+
if (!version) return void 0;
|
|
114
|
+
return {
|
|
115
|
+
operator: "~",
|
|
116
|
+
comparator: tildeComparator(version),
|
|
117
|
+
raw: trimmed,
|
|
118
|
+
rangeVersion: version
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
const op = /^(>=|<=|>|<|=)(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)$/.exec(trimmed);
|
|
122
|
+
if (op) {
|
|
123
|
+
const version = parseSemver(op[2]);
|
|
124
|
+
if (!version) return void 0;
|
|
125
|
+
const operator = op[1];
|
|
126
|
+
let comparator;
|
|
127
|
+
switch (operator) {
|
|
128
|
+
case ">=":
|
|
129
|
+
comparator = gteComparator(version);
|
|
130
|
+
break;
|
|
131
|
+
case "<=":
|
|
132
|
+
comparator = lteComparator(version);
|
|
133
|
+
break;
|
|
134
|
+
case ">":
|
|
135
|
+
comparator = gtComparator(version);
|
|
136
|
+
break;
|
|
137
|
+
case "<":
|
|
138
|
+
comparator = ltComparator(version);
|
|
139
|
+
break;
|
|
140
|
+
default: comparator = exactComparator(version);
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
operator,
|
|
144
|
+
comparator,
|
|
145
|
+
raw: trimmed,
|
|
146
|
+
rangeVersion: version
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
const bare = /^(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)$/.exec(trimmed);
|
|
150
|
+
if (bare) {
|
|
151
|
+
const version = parseSemver(bare[1]);
|
|
152
|
+
if (!version) return void 0;
|
|
153
|
+
return {
|
|
154
|
+
operator: "=",
|
|
155
|
+
comparator: exactComparator(version),
|
|
156
|
+
raw: trimmed,
|
|
157
|
+
rangeVersion: version
|
|
158
|
+
};
|
|
159
|
+
}
|
|
169
160
|
}
|
|
170
161
|
function comparatorRangeVersion(comparator) {
|
|
171
|
-
|
|
162
|
+
return comparator.rangeVersion;
|
|
172
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* Parse a semver range string into a {@link SemverRange}. Returns `undefined`
|
|
166
|
+
* for an unparseable range rather than throwing, so callers can distinguish
|
|
167
|
+
* "bad range" from "no satisfying version".
|
|
168
|
+
*/
|
|
173
169
|
function parseSemverRange(raw) {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
170
|
+
const trimmed = raw.trim();
|
|
171
|
+
if (!trimmed) return void 0;
|
|
172
|
+
const parts = trimmed.split(/\s+/);
|
|
173
|
+
const comparators = [];
|
|
174
|
+
for (const part of parts) {
|
|
175
|
+
const parsed = parseComparator(part);
|
|
176
|
+
if (!parsed) return void 0;
|
|
177
|
+
comparators.push(parsed);
|
|
178
|
+
}
|
|
179
|
+
if (comparators.length === 0) return void 0;
|
|
180
|
+
return {
|
|
181
|
+
comparators,
|
|
182
|
+
raw: trimmed
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Test whether a version string satisfies a parsed range.
|
|
187
|
+
*
|
|
188
|
+
* Prerelease versions only satisfy a range when a comparator in the range
|
|
189
|
+
* declares a prerelease at the same `[major, minor, patch]` tuple. This
|
|
190
|
+
* follows npm semantics: `^1.0.0` does not match `1.1.0-alpha`, but
|
|
191
|
+
* `^1.0.0-alpha` matches `1.0.0-alpha.2`.
|
|
192
|
+
*/
|
|
186
193
|
function satisfiesRange(version, range) {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
194
|
+
const parsed = parseSemver(version);
|
|
195
|
+
if (!parsed) return false;
|
|
196
|
+
if (!range.comparators.every((comparator) => comparator.comparator(parsed))) return false;
|
|
197
|
+
if (parsed.prerelease.length > 0) return range.comparators.some((comparator) => {
|
|
198
|
+
const rangeVersion = comparatorRangeVersion(comparator);
|
|
199
|
+
return rangeVersion !== void 0 && rangeVersion.prerelease.length > 0 && rangeVersion.major === parsed.major && rangeVersion.minor === parsed.minor && rangeVersion.patch === parsed.patch;
|
|
200
|
+
});
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Resolve a range against a list of available versions, returning the highest
|
|
205
|
+
* satisfying version. Returns `undefined` when no available version satisfies
|
|
206
|
+
* the range. Deterministic: the same inputs always produce the same result.
|
|
207
|
+
*/
|
|
198
208
|
function resolveVersionRange(range, available) {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
209
|
+
return available.map((version) => ({
|
|
210
|
+
version,
|
|
211
|
+
parsed: parseSemver(version)
|
|
212
|
+
})).filter((entry) => entry.parsed !== void 0).filter((entry) => {
|
|
213
|
+
if (!range.comparators.every((comparator) => comparator.comparator(entry.parsed))) return false;
|
|
214
|
+
if (entry.parsed.prerelease.length > 0) return range.comparators.some((comparator) => {
|
|
215
|
+
const rangeVersion = comparatorRangeVersion(comparator);
|
|
216
|
+
return rangeVersion !== void 0 && rangeVersion.prerelease.length > 0 && rangeVersion.major === entry.parsed.major && rangeVersion.minor === entry.parsed.minor && rangeVersion.patch === entry.parsed.patch;
|
|
217
|
+
});
|
|
218
|
+
return true;
|
|
219
|
+
}).sort((a, b) => compareSemver(b.parsed, a.parsed))[0]?.version;
|
|
210
220
|
}
|
|
211
221
|
function sortJson(value) {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
Object.entries(value).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(([key, child]) => [key, sortJson(child)])
|
|
216
|
-
);
|
|
217
|
-
}
|
|
218
|
-
return value;
|
|
222
|
+
if (Array.isArray(value)) return value.map(sortJson);
|
|
223
|
+
if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(([key, child]) => [key, sortJson(child)]));
|
|
224
|
+
return value;
|
|
219
225
|
}
|
|
220
226
|
function stableStringify(value) {
|
|
221
|
-
|
|
227
|
+
return JSON.stringify(sortJson(value));
|
|
222
228
|
}
|
|
223
229
|
function assemblyDigest(value) {
|
|
224
|
-
|
|
230
|
+
return (0, crypto.createHash)("sha256").update(stableStringify(value)).digest("hex");
|
|
225
231
|
}
|
|
232
|
+
/** Compute the digest stored for an exact generated usage-contract document. */
|
|
226
233
|
function computeUsageContractDocumentDigest(document) {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
}
|
|
234
|
+
(0, _fabricorg_gen_capability.assertUsageContractDocument)(document);
|
|
235
|
+
return assemblyDigest(document);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* The canonical digest of a lockfile's resolved content.
|
|
239
|
+
*
|
|
240
|
+
* `assertAssemblyLockfile` recomputes this on every load, so publishing it
|
|
241
|
+
* grants nothing an attacker did not already have; it lets tooling and tests
|
|
242
|
+
* construct a self-consistent lockfile instead of hand-fitting a hash. A
|
|
243
|
+
* forged lockfile still fails downstream, because a release binds the assembly
|
|
244
|
+
* digest it was validated against.
|
|
245
|
+
*/
|
|
230
246
|
function computeAssemblyLockfileDigest(lockfile) {
|
|
231
|
-
|
|
247
|
+
return assemblyDigest(lockfileDigestBody(lockfile));
|
|
232
248
|
}
|
|
233
249
|
function lockfileDigestBody(lockfile) {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
}
|
|
250
|
+
return {
|
|
251
|
+
formatVersion: lockfile.formatVersion,
|
|
252
|
+
application: lockfile.application,
|
|
253
|
+
capabilities: lockfile.capabilities,
|
|
254
|
+
componentPacks: lockfile.componentPacks,
|
|
255
|
+
bindings: lockfile.bindings,
|
|
256
|
+
...lockfile.adapters ? { adapters: lockfile.adapters } : {}
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
/** The digest a certification record locks under: the whole record, keys sorted, checks sorted. */
|
|
245
260
|
function computeAdapterCertificationDigest(certification) {
|
|
246
|
-
|
|
261
|
+
return assemblyDigest({
|
|
262
|
+
...certification,
|
|
263
|
+
checks: [...certification.checks].sort()
|
|
264
|
+
});
|
|
247
265
|
}
|
|
248
266
|
function fail(path, message) {
|
|
249
|
-
|
|
267
|
+
throw new Error(`Assembly manifest ${path} ${message}.`);
|
|
250
268
|
}
|
|
251
269
|
function requireString(value, path) {
|
|
252
|
-
|
|
253
|
-
|
|
270
|
+
if (typeof value !== "string" || !value.trim()) fail(path, "must be a non-empty string");
|
|
271
|
+
return value;
|
|
254
272
|
}
|
|
255
273
|
function requireArray(value, path) {
|
|
256
|
-
|
|
257
|
-
|
|
274
|
+
if (!Array.isArray(value)) fail(path, "must be an array");
|
|
275
|
+
return value;
|
|
258
276
|
}
|
|
277
|
+
/**
|
|
278
|
+
* Structural validation of an assembly manifest. Checks the document's own
|
|
279
|
+
* shape; does not resolve ranges or validate contracts/bindings.
|
|
280
|
+
*/
|
|
259
281
|
function assertAssemblyManifest(value, path = "assembly") {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
});
|
|
293
|
-
if (manifest.adapters !== void 0) {
|
|
294
|
-
requireArray(manifest.adapters, `${path}.adapters`).forEach((entry, index) => {
|
|
295
|
-
assertAdapterEntry(entry, `${path}.adapters[${index}]`);
|
|
296
|
-
});
|
|
297
|
-
}
|
|
282
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) fail(path, "must be an object");
|
|
283
|
+
const manifest = value;
|
|
284
|
+
if (manifest.formatVersion !== 2) fail(`${path}.formatVersion`, `must be 2, received ${JSON.stringify(manifest.formatVersion)}`);
|
|
285
|
+
if (!/^[a-z][a-z0-9-]*$/.test(requireString(manifest.application, `${path}.application`))) fail(`${path}.application`, "must be lowercase letters, numbers and hyphens");
|
|
286
|
+
requireArray(manifest.capabilities, `${path}.capabilities`).forEach((entry, index) => {
|
|
287
|
+
const capability = entry;
|
|
288
|
+
requireString(capability?.namespace, `${path}.capabilities[${index}].namespace`);
|
|
289
|
+
requireString(capability?.range, `${path}.capabilities[${index}].range`);
|
|
290
|
+
});
|
|
291
|
+
requireArray(manifest.componentPacks, `${path}.componentPacks`).forEach((entry, index) => {
|
|
292
|
+
const pack = entry;
|
|
293
|
+
requireString(pack?.pack, `${path}.componentPacks[${index}].pack`);
|
|
294
|
+
requireString(pack?.namespace, `${path}.componentPacks[${index}].namespace`);
|
|
295
|
+
requireString(pack?.range, `${path}.componentPacks[${index}].range`);
|
|
296
|
+
});
|
|
297
|
+
requireArray(manifest.bindings, `${path}.bindings`).forEach((entry, index) => {
|
|
298
|
+
const binding = entry;
|
|
299
|
+
requireString(binding?.capability, `${path}.bindings[${index}].capability`);
|
|
300
|
+
(0, _fabricorg_adoption_bindings.assertAdoptionBinding)(binding?.binding, `${path}.bindings[${index}].binding`);
|
|
301
|
+
});
|
|
302
|
+
requireArray(manifest.contracts, `${path}.contracts`).forEach((entry, index) => {
|
|
303
|
+
const contract = entry;
|
|
304
|
+
requireString(contract?.namespace, `${path}.contracts[${index}].namespace`);
|
|
305
|
+
try {
|
|
306
|
+
(0, _fabricorg_gen_capability.assertUsageContractDocument)(contract?.document);
|
|
307
|
+
} catch (error) {
|
|
308
|
+
fail(`${path}.contracts[${index}].document`, error instanceof Error ? error.message : String(error));
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
if (manifest.adapters !== void 0) requireArray(manifest.adapters, `${path}.adapters`).forEach((entry, index) => {
|
|
312
|
+
assertAdapterEntry(entry, `${path}.adapters[${index}]`);
|
|
313
|
+
});
|
|
298
314
|
}
|
|
299
315
|
function assertAdapterEntry(value, path) {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
if (certification.portId !== adapter.port) {
|
|
326
|
-
fail(`${path}.certification.portId`, `must match the adapter's port "${String(adapter.port)}"`);
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
}
|
|
316
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) fail(path, "must be an object");
|
|
317
|
+
const adapter = value;
|
|
318
|
+
requireString(adapter.port, `${path}.port`);
|
|
319
|
+
requireString(adapter.vendor, `${path}.vendor`);
|
|
320
|
+
if (adapter.version !== void 0) requireString(adapter.version, `${path}.version`);
|
|
321
|
+
if (adapter.standard !== void 0) {
|
|
322
|
+
const standard = adapter.standard;
|
|
323
|
+
if (!standard || typeof standard !== "object" || Array.isArray(standard)) fail(`${path}.standard`, "must be an object");
|
|
324
|
+
requireString(standard.name, `${path}.standard.name`);
|
|
325
|
+
if (standard.version !== void 0) requireString(standard.version, `${path}.standard.version`);
|
|
326
|
+
}
|
|
327
|
+
if (adapter.certification !== void 0) {
|
|
328
|
+
const certification = adapter.certification;
|
|
329
|
+
if (!certification || typeof certification !== "object" || Array.isArray(certification)) fail(`${path}.certification`, "must be an object");
|
|
330
|
+
requireString(certification.portId, `${path}.certification.portId`);
|
|
331
|
+
if (!/^\d+\.\d+\.\d+$/.test(requireString(certification.portVersion, `${path}.certification.portVersion`))) fail(`${path}.certification.portVersion`, "must be a semver version");
|
|
332
|
+
const checks = requireArray(certification.checks, `${path}.certification.checks`);
|
|
333
|
+
if (checks.length === 0 || !checks.every((check) => typeof check === "string" && check.trim())) fail(`${path}.certification.checks`, "must name at least one check");
|
|
334
|
+
if (Number.isNaN(Date.parse(requireString(certification.certifiedAt, `${path}.certification.certifiedAt`)))) fail(`${path}.certification.certifiedAt`, "must be an instant");
|
|
335
|
+
if (certification.portId !== adapter.port) fail(`${path}.certification.portId`, `must match the adapter's port "${String(adapter.port)}"`);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Structural validation of a component pack manifest.
|
|
340
|
+
*/
|
|
330
341
|
function assertComponentPackManifest(value, path = "pack") {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
}
|
|
345
|
-
}
|
|
342
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) fail(path, "must be an object");
|
|
343
|
+
const pack = value;
|
|
344
|
+
requireString(pack.pack, `${path}.pack`);
|
|
345
|
+
requireString(pack.namespace, `${path}.namespace`);
|
|
346
|
+
requireString(pack.version, `${path}.version`);
|
|
347
|
+
if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(pack.version)) fail(`${path}.version`, "must be a semver version");
|
|
348
|
+
if (!pack.components || typeof pack.components !== "object" || Array.isArray(pack.components)) fail(`${path}.components`, "must be an object");
|
|
349
|
+
if (!/^[0-9a-f]{64}$/.test(requireString(pack.artifactDigest, `${path}.artifactDigest`))) fail(`${path}.artifactDigest`, "must be a lowercase SHA-256 digest");
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Validate a resolved lockfile and verify that its digest still identifies its
|
|
353
|
+
* exact application, capability, pack, contract, and binding content.
|
|
354
|
+
*/
|
|
346
355
|
function assertAssemblyLockfile(value, path = "lockfile") {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
if (!standard || typeof standard !== "object" || Array.isArray(standard)) fail(`${path}.adapters[${index}].standard`, "must be an object");
|
|
435
|
-
requireString(standard.name, `${path}.adapters[${index}].standard.name`);
|
|
436
|
-
if (standard.version !== void 0) requireString(standard.version, `${path}.adapters[${index}].standard.version`);
|
|
437
|
-
}
|
|
438
|
-
if (typeof adapter?.certified !== "boolean") fail(`${path}.adapters[${index}].certified`, "must be a boolean");
|
|
439
|
-
if (adapter.certified) {
|
|
440
|
-
if (!/^\d+\.\d+\.\d+$/.test(requireString(adapter?.portVersion, `${path}.adapters[${index}].portVersion`))) {
|
|
441
|
-
fail(`${path}.adapters[${index}].portVersion`, "must be a semver version");
|
|
442
|
-
}
|
|
443
|
-
if (Number.isNaN(Date.parse(requireString(adapter?.certifiedAt, `${path}.adapters[${index}].certifiedAt`)))) {
|
|
444
|
-
fail(`${path}.adapters[${index}].certifiedAt`, "must be an instant");
|
|
445
|
-
}
|
|
446
|
-
if (!/^[0-9a-f]{64}$/.test(requireString(adapter?.certificationDigest, `${path}.adapters[${index}].certificationDigest`))) {
|
|
447
|
-
fail(`${path}.adapters[${index}].certificationDigest`, "must be a lowercase SHA-256 digest");
|
|
448
|
-
}
|
|
449
|
-
} else if (adapter.portVersion !== void 0 || adapter.certifiedAt !== void 0 || adapter.certificationDigest !== void 0) {
|
|
450
|
-
fail(`${path}.adapters[${index}]`, "carries certification fields but is marked uncertified");
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
const expected = assemblyDigest(lockfileDigestBody({
|
|
455
|
-
formatVersion: ASSEMBLY_LOCKFILE_FORMAT_VERSION,
|
|
456
|
-
application: lockfile.application,
|
|
457
|
-
capabilities,
|
|
458
|
-
componentPacks,
|
|
459
|
-
bindings,
|
|
460
|
-
...adapters ? { adapters } : {}
|
|
461
|
-
}));
|
|
462
|
-
if (lockfile.assemblyDigest !== expected) {
|
|
463
|
-
fail(`${path}.assemblyDigest`, `does not match resolved lockfile content; expected "${expected}"`);
|
|
464
|
-
}
|
|
465
|
-
}
|
|
356
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) fail(path, "must be an object");
|
|
357
|
+
const lockfile = value;
|
|
358
|
+
if (lockfile.formatVersion !== 2) fail(`${path}.formatVersion`, `must be 2, received ${JSON.stringify(lockfile.formatVersion)}`);
|
|
359
|
+
if (!/^[a-z][a-z0-9-]*$/.test(requireString(lockfile.application, `${path}.application`))) fail(`${path}.application`, "must be lowercase letters, numbers and hyphens");
|
|
360
|
+
if (!/^[0-9a-f]{64}$/.test(requireString(lockfile.assemblyDigest, `${path}.assemblyDigest`))) fail(`${path}.assemblyDigest`, "must be a lowercase SHA-256 digest");
|
|
361
|
+
const capabilities = requireArray(lockfile.capabilities, `${path}.capabilities`);
|
|
362
|
+
const componentPacks = requireArray(lockfile.componentPacks, `${path}.componentPacks`);
|
|
363
|
+
const bindings = requireArray(lockfile.bindings, `${path}.bindings`);
|
|
364
|
+
const seenCapabilities = /* @__PURE__ */ new Set();
|
|
365
|
+
for (const [index, entry] of capabilities.entries()) {
|
|
366
|
+
const capability = entry;
|
|
367
|
+
const namespace = String(capability?.namespace);
|
|
368
|
+
if (seenCapabilities.has(namespace)) fail(`${path}.capabilities[${index}]`, `declares capability "${namespace}" a second time`);
|
|
369
|
+
seenCapabilities.add(namespace);
|
|
370
|
+
requireString(capability?.namespace, `${path}.capabilities[${index}].namespace`);
|
|
371
|
+
requireString(capability?.version, `${path}.capabilities[${index}].version`);
|
|
372
|
+
requireString(capability?.range, `${path}.capabilities[${index}].range`);
|
|
373
|
+
if (!/^[0-9a-f]{64}$/.test(requireString(capability?.manifestDigest, `${path}.capabilities[${index}].manifestDigest`))) fail(`${path}.capabilities[${index}].manifestDigest`, "must be a lowercase SHA-256 digest");
|
|
374
|
+
if (capability?.usageContractDigest !== void 0 && !/^[0-9a-f]{64}$/.test(requireString(capability.usageContractDigest, `${path}.capabilities[${index}].usageContractDigest`))) fail(`${path}.capabilities[${index}].usageContractDigest`, "must be a lowercase SHA-256 digest");
|
|
375
|
+
}
|
|
376
|
+
const seenPackIdentities = /* @__PURE__ */ new Set();
|
|
377
|
+
for (const [index, entry] of componentPacks.entries()) {
|
|
378
|
+
const pack = entry;
|
|
379
|
+
requireString(pack?.pack, `${path}.componentPacks[${index}].pack`);
|
|
380
|
+
const identity = `${String(pack?.namespace)}/${String(pack?.pack)}`;
|
|
381
|
+
if (seenPackIdentities.has(identity)) fail(`${path}.componentPacks[${index}]`, `declares component pack "${identity}" a second time; first-match resolution would make the choice arbitrary`);
|
|
382
|
+
seenPackIdentities.add(identity);
|
|
383
|
+
requireString(pack?.namespace, `${path}.componentPacks[${index}].namespace`);
|
|
384
|
+
requireString(pack?.version, `${path}.componentPacks[${index}].version`);
|
|
385
|
+
requireString(pack?.range, `${path}.componentPacks[${index}].range`);
|
|
386
|
+
if (!/^[0-9a-f]{64}$/.test(requireString(pack?.artifactDigest, `${path}.componentPacks[${index}].artifactDigest`))) fail(`${path}.componentPacks[${index}].artifactDigest`, "must be a lowercase SHA-256 digest");
|
|
387
|
+
if (pack?.remote !== void 0) {
|
|
388
|
+
const remotePath = `${path}.componentPacks[${index}].remote`;
|
|
389
|
+
const remote = pack.remote;
|
|
390
|
+
if (remote === null || typeof remote !== "object" || Array.isArray(remote)) fail(remotePath, "must be an object");
|
|
391
|
+
requireString(remote.entry, `${remotePath}.entry`);
|
|
392
|
+
if (!/^sha(256|384|512)-[A-Za-z0-9+/]+={0,2}$/.test(requireString(remote.integrity, `${remotePath}.integrity`))) fail(`${remotePath}.integrity`, "must be a subresource-integrity value, e.g. \"sha384-…\"");
|
|
393
|
+
const exposes = remote.exposes;
|
|
394
|
+
if (exposes === null || typeof exposes !== "object" || Array.isArray(exposes)) fail(`${remotePath}.exposes`, "must be an object");
|
|
395
|
+
for (const [component, modulePath] of Object.entries(exposes)) requireString(modulePath, `${remotePath}.exposes.${component}`);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
for (const [index, entry] of bindings.entries()) {
|
|
399
|
+
const binding = entry;
|
|
400
|
+
requireString(binding?.capability, `${path}.bindings[${index}].capability`);
|
|
401
|
+
if (!/^[0-9a-f]{64}$/.test(requireString(binding?.bindingDigest, `${path}.bindings[${index}].bindingDigest`))) fail(`${path}.bindings[${index}].bindingDigest`, "must be a lowercase SHA-256 digest");
|
|
402
|
+
if (binding?.valid !== true) fail(`${path}.bindings[${index}].valid`, "must be true");
|
|
403
|
+
requireArray(binding?.findings, `${path}.bindings[${index}].findings`);
|
|
404
|
+
}
|
|
405
|
+
let adapters;
|
|
406
|
+
if (lockfile.adapters !== void 0) {
|
|
407
|
+
adapters = requireArray(lockfile.adapters, `${path}.adapters`);
|
|
408
|
+
const seen = /* @__PURE__ */ new Set();
|
|
409
|
+
for (const [index, entry] of adapters.entries()) {
|
|
410
|
+
const adapter = entry;
|
|
411
|
+
const key = `${requireString(adapter?.port, `${path}.adapters[${index}].port`)}/${requireString(adapter?.vendor, `${path}.adapters[${index}].vendor`)}`;
|
|
412
|
+
if (seen.has(key)) fail(`${path}.adapters[${index}]`, `declares adapter "${key}" a second time`);
|
|
413
|
+
seen.add(key);
|
|
414
|
+
if (adapter?.version !== void 0) requireString(adapter.version, `${path}.adapters[${index}].version`);
|
|
415
|
+
if (adapter?.standard !== void 0) {
|
|
416
|
+
const standard = adapter.standard;
|
|
417
|
+
if (!standard || typeof standard !== "object" || Array.isArray(standard)) fail(`${path}.adapters[${index}].standard`, "must be an object");
|
|
418
|
+
requireString(standard.name, `${path}.adapters[${index}].standard.name`);
|
|
419
|
+
if (standard.version !== void 0) requireString(standard.version, `${path}.adapters[${index}].standard.version`);
|
|
420
|
+
}
|
|
421
|
+
if (typeof adapter?.certified !== "boolean") fail(`${path}.adapters[${index}].certified`, "must be a boolean");
|
|
422
|
+
if (adapter.certified) {
|
|
423
|
+
if (!/^\d+\.\d+\.\d+$/.test(requireString(adapter?.portVersion, `${path}.adapters[${index}].portVersion`))) fail(`${path}.adapters[${index}].portVersion`, "must be a semver version");
|
|
424
|
+
if (Number.isNaN(Date.parse(requireString(adapter?.certifiedAt, `${path}.adapters[${index}].certifiedAt`)))) fail(`${path}.adapters[${index}].certifiedAt`, "must be an instant");
|
|
425
|
+
if (!/^[0-9a-f]{64}$/.test(requireString(adapter?.certificationDigest, `${path}.adapters[${index}].certificationDigest`))) fail(`${path}.adapters[${index}].certificationDigest`, "must be a lowercase SHA-256 digest");
|
|
426
|
+
} else if (adapter.portVersion !== void 0 || adapter.certifiedAt !== void 0 || adapter.certificationDigest !== void 0) fail(`${path}.adapters[${index}]`, "carries certification fields but is marked uncertified");
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
const expected = assemblyDigest(lockfileDigestBody({
|
|
430
|
+
formatVersion: 2,
|
|
431
|
+
application: lockfile.application,
|
|
432
|
+
capabilities,
|
|
433
|
+
componentPacks,
|
|
434
|
+
bindings,
|
|
435
|
+
...adapters ? { adapters } : {}
|
|
436
|
+
}));
|
|
437
|
+
if (lockfile.assemblyDigest !== expected) fail(`${path}.assemblyDigest`, `does not match resolved lockfile content; expected "${expected}"`);
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Verify that the capability artifacts loaded by a runtime are exactly those
|
|
441
|
+
* approved by an integrity-checked assembly lockfile.
|
|
442
|
+
*/
|
|
466
443
|
function assertAssemblyRuntimeCompatible(lockfile, loadedCapabilities, registeredAdapters) {
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
for (const loaded of loadedCapabilities) {
|
|
507
|
-
if (!lockfile.capabilities.some((approved) => approved.namespace === loaded.namespace)) {
|
|
508
|
-
throw new Error(`Loaded capability "${loaded.namespace}" is not approved by assembly "${lockfile.assemblyDigest}".`);
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
}
|
|
444
|
+
assertAssemblyLockfile(lockfile);
|
|
445
|
+
if (lockfile.adapters !== void 0 && registeredAdapters !== void 0) {
|
|
446
|
+
const registeredByKey = new Map(registeredAdapters.map((adapter) => [`${adapter.port}/${adapter.vendor}`, adapter]));
|
|
447
|
+
for (const approved of lockfile.adapters) {
|
|
448
|
+
const key = `${approved.port}/${approved.vendor}`;
|
|
449
|
+
const registered = registeredByKey.get(key);
|
|
450
|
+
if (!registered) throw new Error(`Approved adapter "${key}" is not registered by this runtime.`);
|
|
451
|
+
if (approved.version !== void 0 && registered.version !== approved.version) throw new Error(`Registered adapter "${key}" is version "${registered.version ?? "unversioned"}"; the assembly approved "${approved.version}".`);
|
|
452
|
+
if (approved.certified) {
|
|
453
|
+
if ((registered.certification ? computeAdapterCertificationDigest(registered.certification) : void 0) !== approved.certificationDigest) throw new Error(`Registered adapter "${key}" does not carry the certification the assembly approved (digest "${approved.certificationDigest}").`);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
for (const registered of registeredAdapters) {
|
|
457
|
+
const key = `${registered.port}/${registered.vendor}`;
|
|
458
|
+
if (!lockfile.adapters.some((approved) => approved.port === registered.port && approved.vendor === registered.vendor)) throw new Error(`Registered adapter "${key}" is not approved by assembly "${lockfile.assemblyDigest}".`);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
const loadedByNamespace = /* @__PURE__ */ new Map();
|
|
462
|
+
for (const loaded of loadedCapabilities) {
|
|
463
|
+
if (loadedByNamespace.has(loaded.namespace)) throw new Error(`Loaded capability "${loaded.namespace}" is duplicated.`);
|
|
464
|
+
loadedByNamespace.set(loaded.namespace, loaded);
|
|
465
|
+
}
|
|
466
|
+
for (const approved of lockfile.capabilities) {
|
|
467
|
+
const loaded = loadedByNamespace.get(approved.namespace);
|
|
468
|
+
if (!loaded || loaded.version !== approved.version || loaded.manifestDigest !== approved.manifestDigest) throw new Error(`Loaded capability "${approved.namespace}" does not match approved capability "${approved.namespace}@${approved.version}" with manifest digest "${approved.manifestDigest}".`);
|
|
469
|
+
}
|
|
470
|
+
for (const loaded of loadedCapabilities) if (!lockfile.capabilities.some((approved) => approved.namespace === loaded.namespace)) throw new Error(`Loaded capability "${loaded.namespace}" is not approved by assembly "${lockfile.assemblyDigest}".`);
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Resolve an assembly manifest into a deterministic lockfile. This is the
|
|
474
|
+
* single build gate: it resolves version ranges, rejects unresolved or
|
|
475
|
+
* duplicate capability identities, validates usage contracts, component
|
|
476
|
+
* packs, and adoption bindings, and produces a lockfile with a canonical
|
|
477
|
+
* digest.
|
|
478
|
+
*
|
|
479
|
+
* Returns every finding rather than throwing, so one CI run tells a vertical
|
|
480
|
+
* everything it has to change. Use {@link assertAssemblyResolved} to throw
|
|
481
|
+
* on the first invalid result.
|
|
482
|
+
*/
|
|
512
483
|
function resolveAssembly(input) {
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
for (const entry of input.manifest.bindings) {
|
|
742
|
-
const contract = contractsByNamespace.get(entry.capability);
|
|
743
|
-
if (!contract) {
|
|
744
|
-
add(
|
|
745
|
-
"unknown_contract_for_binding",
|
|
746
|
-
`assembly.bindings.${entry.capability}`,
|
|
747
|
-
`binding targets capability "${entry.capability}" but no contract for it is declared`
|
|
748
|
-
);
|
|
749
|
-
resolvedBindings.push({
|
|
750
|
-
capability: entry.capability,
|
|
751
|
-
bindingDigest: assemblyDigest(entry.binding),
|
|
752
|
-
valid: false,
|
|
753
|
-
findings: []
|
|
754
|
-
});
|
|
755
|
-
continue;
|
|
756
|
-
}
|
|
757
|
-
const result = (0, import_adoption_bindings.validateBinding)({
|
|
758
|
-
contract,
|
|
759
|
-
binding: entry.binding,
|
|
760
|
-
packs: resolvedPackManifests,
|
|
761
|
-
coreComponents: input.coreComponents ?? [],
|
|
762
|
-
availableContracts: input.availableContracts ?? []
|
|
763
|
-
});
|
|
764
|
-
resolvedBindings.push({
|
|
765
|
-
capability: entry.capability,
|
|
766
|
-
bindingDigest: assemblyDigest(entry.binding),
|
|
767
|
-
valid: result.valid,
|
|
768
|
-
findings: result.findings
|
|
769
|
-
});
|
|
770
|
-
if (!result.valid) {
|
|
771
|
-
for (const finding of result.findings) {
|
|
772
|
-
add(
|
|
773
|
-
"binding_validation_failed",
|
|
774
|
-
`assembly.bindings.${entry.capability}.${finding.path}`,
|
|
775
|
-
finding.message
|
|
776
|
-
);
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
}
|
|
780
|
-
const capabilities = [...resolvedCapabilities].sort(
|
|
781
|
-
(a, b) => a.namespace < b.namespace ? -1 : a.namespace > b.namespace ? 1 : 0
|
|
782
|
-
);
|
|
783
|
-
const componentPacks = [...resolvedPacks].sort((a, b) => {
|
|
784
|
-
const key = (e) => `${e.namespace}/${e.pack}`;
|
|
785
|
-
return key(a) < key(b) ? -1 : key(a) > key(b) ? 1 : 0;
|
|
786
|
-
});
|
|
787
|
-
const bindings = [...resolvedBindings].sort(
|
|
788
|
-
(a, b) => a.capability < b.capability ? -1 : a.capability > b.capability ? 1 : 0
|
|
789
|
-
);
|
|
790
|
-
const adapters = input.manifest.adapters === void 0 ? void 0 : [...resolvedAdapters].sort((a, b) => {
|
|
791
|
-
const key = (e) => `${e.port}/${e.vendor}`;
|
|
792
|
-
return key(a) < key(b) ? -1 : key(a) > key(b) ? 1 : 0;
|
|
793
|
-
});
|
|
794
|
-
const lockfileBody = {
|
|
795
|
-
formatVersion: ASSEMBLY_LOCKFILE_FORMAT_VERSION,
|
|
796
|
-
application: input.manifest.application,
|
|
797
|
-
capabilities,
|
|
798
|
-
componentPacks,
|
|
799
|
-
bindings,
|
|
800
|
-
...adapters ? { adapters } : {}
|
|
801
|
-
};
|
|
802
|
-
const lockfile = {
|
|
803
|
-
formatVersion: ASSEMBLY_LOCKFILE_FORMAT_VERSION,
|
|
804
|
-
application: input.manifest.application,
|
|
805
|
-
assemblyDigest: assemblyDigest(lockfileDigestBody(lockfileBody)),
|
|
806
|
-
capabilities,
|
|
807
|
-
componentPacks,
|
|
808
|
-
bindings,
|
|
809
|
-
...adapters ? { adapters } : {}
|
|
810
|
-
};
|
|
811
|
-
return {
|
|
812
|
-
lockfile: findings.length === 0 ? lockfile : void 0,
|
|
813
|
-
findings
|
|
814
|
-
};
|
|
815
|
-
}
|
|
484
|
+
assertAssemblyManifest(input.manifest);
|
|
485
|
+
const findings = [];
|
|
486
|
+
const add = (code, path, message) => {
|
|
487
|
+
findings.push({
|
|
488
|
+
code,
|
|
489
|
+
path,
|
|
490
|
+
message
|
|
491
|
+
});
|
|
492
|
+
};
|
|
493
|
+
const capabilityNamespaces = /* @__PURE__ */ new Set();
|
|
494
|
+
for (const capability of input.manifest.capabilities) {
|
|
495
|
+
if (capabilityNamespaces.has(capability.namespace)) add("duplicate_capability", `assembly.capabilities.${capability.namespace}`, `capability "${capability.namespace}" is declared more than once`);
|
|
496
|
+
capabilityNamespaces.add(capability.namespace);
|
|
497
|
+
}
|
|
498
|
+
const packKeys = /* @__PURE__ */ new Set();
|
|
499
|
+
for (const pack of input.manifest.componentPacks) {
|
|
500
|
+
const key = `${pack.namespace}/${pack.pack}`;
|
|
501
|
+
if (packKeys.has(key)) add("duplicate_component_pack", `assembly.componentPacks.${key}`, `component pack "${key}" is declared more than once`);
|
|
502
|
+
packKeys.add(key);
|
|
503
|
+
}
|
|
504
|
+
const contractNamespaces = /* @__PURE__ */ new Set();
|
|
505
|
+
for (const contract of input.manifest.contracts) {
|
|
506
|
+
if (contractNamespaces.has(contract.namespace)) add("duplicate_contract", `assembly.contracts.${contract.namespace}`, `contract for "${contract.namespace}" is declared more than once`);
|
|
507
|
+
contractNamespaces.add(contract.namespace);
|
|
508
|
+
}
|
|
509
|
+
const resolvedCapabilities = [];
|
|
510
|
+
const capabilitiesByNamespace = /* @__PURE__ */ new Map();
|
|
511
|
+
for (const available of input.capabilities) {
|
|
512
|
+
const list = capabilitiesByNamespace.get(available.namespace) ?? [];
|
|
513
|
+
list.push(available);
|
|
514
|
+
capabilitiesByNamespace.set(available.namespace, list);
|
|
515
|
+
}
|
|
516
|
+
for (const ref of input.manifest.capabilities) {
|
|
517
|
+
const range = parseSemverRange(ref.range);
|
|
518
|
+
if (!range) {
|
|
519
|
+
add("invalid_range", `assembly.capabilities.${ref.namespace}.range`, `range "${ref.range}" is not a valid semver range`);
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
522
|
+
const available = capabilitiesByNamespace.get(ref.namespace) ?? [];
|
|
523
|
+
const resolved = resolveVersionRange(range, available.map((entry) => entry.version));
|
|
524
|
+
if (!resolved) {
|
|
525
|
+
add("unresolved_capability", `assembly.capabilities.${ref.namespace}`, `no available version of "${ref.namespace}" satisfies range "${ref.range}"`);
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
528
|
+
const matched = available.find((entry) => entry.version === resolved);
|
|
529
|
+
resolvedCapabilities.push({
|
|
530
|
+
namespace: ref.namespace,
|
|
531
|
+
version: resolved,
|
|
532
|
+
range: ref.range,
|
|
533
|
+
manifestDigest: matched.manifestDigest
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
const resolvedPacks = [];
|
|
537
|
+
const packsByKey = /* @__PURE__ */ new Map();
|
|
538
|
+
for (const available of input.componentPacks) {
|
|
539
|
+
const key = `${available.namespace}/${available.pack}`;
|
|
540
|
+
const list = packsByKey.get(key) ?? [];
|
|
541
|
+
list.push(available);
|
|
542
|
+
packsByKey.set(key, list);
|
|
543
|
+
}
|
|
544
|
+
for (const ref of input.manifest.componentPacks) {
|
|
545
|
+
const range = parseSemverRange(ref.range);
|
|
546
|
+
if (!range) {
|
|
547
|
+
add("invalid_range", `assembly.componentPacks.${ref.namespace}/${ref.pack}.range`, `range "${ref.range}" is not a valid semver range`);
|
|
548
|
+
continue;
|
|
549
|
+
}
|
|
550
|
+
const key = `${ref.namespace}/${ref.pack}`;
|
|
551
|
+
const available = packsByKey.get(key) ?? [];
|
|
552
|
+
const resolved = resolveVersionRange(range, available.map((entry) => entry.version));
|
|
553
|
+
if (!resolved) {
|
|
554
|
+
add("unresolved_component_pack", `assembly.componentPacks.${key}`, `no available version of pack "${key}" satisfies range "${ref.range}"`);
|
|
555
|
+
continue;
|
|
556
|
+
}
|
|
557
|
+
const matched = available.find((entry) => entry.version === resolved);
|
|
558
|
+
resolvedPacks.push({
|
|
559
|
+
pack: ref.pack,
|
|
560
|
+
namespace: ref.namespace,
|
|
561
|
+
version: resolved,
|
|
562
|
+
range: ref.range,
|
|
563
|
+
artifactDigest: matched.artifactDigest,
|
|
564
|
+
...matched.remote ? { remote: matched.remote } : {}
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
const packManifests = input.packManifests ?? [];
|
|
568
|
+
for (const pack of packManifests) try {
|
|
569
|
+
assertComponentPackManifest(pack);
|
|
570
|
+
} catch (error) {
|
|
571
|
+
add("invalid_component_pack", `packManifests.${pack.namespace}/${pack.pack}`, error instanceof Error ? error.message : String(error));
|
|
572
|
+
}
|
|
573
|
+
const resolvedPackManifests = [];
|
|
574
|
+
for (const resolved of resolvedPacks) {
|
|
575
|
+
const key = `${resolved.namespace}/${resolved.pack}`;
|
|
576
|
+
const manifest = packManifests.find((candidate) => candidate.namespace === resolved.namespace && candidate.pack === resolved.pack && candidate.version === resolved.version);
|
|
577
|
+
if (!manifest) {
|
|
578
|
+
add("missing_component_pack_manifest", `assembly.componentPacks.${key}`, `resolved pack "${key}@${resolved.version}" has no matching component-pack manifest`);
|
|
579
|
+
continue;
|
|
580
|
+
}
|
|
581
|
+
if (manifest.artifactDigest !== resolved.artifactDigest) {
|
|
582
|
+
add("component_pack_artifact_digest_mismatch", `assembly.componentPacks.${key}.artifactDigest`, `component-pack manifest digest "${manifest.artifactDigest}" does not match resolved artifact digest "${resolved.artifactDigest}"`);
|
|
583
|
+
continue;
|
|
584
|
+
}
|
|
585
|
+
resolvedPackManifests.push(manifest);
|
|
586
|
+
}
|
|
587
|
+
const contractsByNamespace = /* @__PURE__ */ new Map();
|
|
588
|
+
for (const entry of input.manifest.contracts) {
|
|
589
|
+
try {
|
|
590
|
+
(0, _fabricorg_gen_capability.assertUsageContractDocument)(entry.document);
|
|
591
|
+
} catch (error) {
|
|
592
|
+
add("invalid_contract", `assembly.contracts.${entry.namespace}`, error instanceof Error ? error.message : String(error));
|
|
593
|
+
continue;
|
|
594
|
+
}
|
|
595
|
+
if (entry.document.capability.namespace !== entry.namespace) add("contract_namespace_mismatch", `assembly.contracts.${entry.namespace}`, `contract namespace "${entry.document.capability.namespace}" does not match assembly namespace "${entry.namespace}"`);
|
|
596
|
+
const resolved = resolvedCapabilities.find((capability) => capability.namespace === entry.namespace);
|
|
597
|
+
if (resolved && entry.document.manifestDigest !== resolved.manifestDigest) add("contract_manifest_digest_mismatch", `assembly.contracts.${entry.namespace}.manifestDigest`, `contract manifest digest "${entry.document.manifestDigest}" does not match resolved capability digest "${resolved.manifestDigest}"`);
|
|
598
|
+
else if (resolved) resolved.usageContractDigest = computeUsageContractDocumentDigest(entry.document);
|
|
599
|
+
contractsByNamespace.set(entry.namespace, entry.document.capability);
|
|
600
|
+
}
|
|
601
|
+
const resolvedAdapters = [];
|
|
602
|
+
const declaredAdapters = input.manifest.adapters ?? [];
|
|
603
|
+
const adapterKeys = /* @__PURE__ */ new Set();
|
|
604
|
+
for (const adapter of declaredAdapters) {
|
|
605
|
+
const key = `${adapter.port}/${adapter.vendor}`;
|
|
606
|
+
if (adapterKeys.has(key)) {
|
|
607
|
+
add("duplicate_adapter", `assembly.adapters.${key}`, `adapter "${key}" is declared a second time`);
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
adapterKeys.add(key);
|
|
611
|
+
resolvedAdapters.push({
|
|
612
|
+
port: adapter.port,
|
|
613
|
+
vendor: adapter.vendor,
|
|
614
|
+
...adapter.version === void 0 ? {} : { version: adapter.version },
|
|
615
|
+
...adapter.standard === void 0 ? {} : { standard: adapter.standard },
|
|
616
|
+
certified: adapter.certification !== void 0,
|
|
617
|
+
...adapter.certification === void 0 ? {} : {
|
|
618
|
+
portVersion: adapter.certification.portVersion,
|
|
619
|
+
certifiedAt: adapter.certification.certifiedAt,
|
|
620
|
+
certificationDigest: computeAdapterCertificationDigest(adapter.certification)
|
|
621
|
+
}
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
const portDefinitions = (input.portDefinitions ?? []).map((definition, index) => {
|
|
625
|
+
if (typeof definition.id !== "string" || !_fabricorg_ports.PORT_ID.test(definition.id)) fail(`portDefinitions[${index}].id`, `must be a namespaced lowercase port id, received ${JSON.stringify(definition.id)}`);
|
|
626
|
+
if (typeof definition.version !== "string" || !/^\d+\.\d+\.\d+$/.test(definition.version)) fail(`portDefinitions[${index}].version`, `must be a semver version, received ${JSON.stringify(definition.version)}`);
|
|
627
|
+
return {
|
|
628
|
+
id: definition.id,
|
|
629
|
+
version: definition.version,
|
|
630
|
+
description: "",
|
|
631
|
+
checks: () => []
|
|
632
|
+
};
|
|
633
|
+
});
|
|
634
|
+
for (const [namespace, capability] of contractsByNamespace) {
|
|
635
|
+
if (input.manifest.adapters === void 0) break;
|
|
636
|
+
if (!capability.requirements?.ports?.length) continue;
|
|
637
|
+
const result = (0, _fabricorg_ports.validatePortRequirements)({
|
|
638
|
+
capability: {
|
|
639
|
+
namespace,
|
|
640
|
+
requirements: { ports: capability.requirements.ports }
|
|
641
|
+
},
|
|
642
|
+
adapters: declaredAdapters,
|
|
643
|
+
definitions: portDefinitions,
|
|
644
|
+
requireCertification: true
|
|
645
|
+
});
|
|
646
|
+
for (const finding of result.findings) add(finding.code, `assembly.contracts.${namespace}.requirements.ports.${finding.port}`, finding.message);
|
|
647
|
+
}
|
|
648
|
+
const resolvedBindings = [];
|
|
649
|
+
for (const entry of input.manifest.bindings) {
|
|
650
|
+
const contract = contractsByNamespace.get(entry.capability);
|
|
651
|
+
if (!contract) {
|
|
652
|
+
add("unknown_contract_for_binding", `assembly.bindings.${entry.capability}`, `binding targets capability "${entry.capability}" but no contract for it is declared`);
|
|
653
|
+
resolvedBindings.push({
|
|
654
|
+
capability: entry.capability,
|
|
655
|
+
bindingDigest: assemblyDigest(entry.binding),
|
|
656
|
+
valid: false,
|
|
657
|
+
findings: []
|
|
658
|
+
});
|
|
659
|
+
continue;
|
|
660
|
+
}
|
|
661
|
+
const result = (0, _fabricorg_adoption_bindings.validateBinding)({
|
|
662
|
+
contract,
|
|
663
|
+
binding: entry.binding,
|
|
664
|
+
packs: resolvedPackManifests,
|
|
665
|
+
coreComponents: input.coreComponents ?? [],
|
|
666
|
+
availableContracts: input.availableContracts ?? []
|
|
667
|
+
});
|
|
668
|
+
resolvedBindings.push({
|
|
669
|
+
capability: entry.capability,
|
|
670
|
+
bindingDigest: assemblyDigest(entry.binding),
|
|
671
|
+
valid: result.valid,
|
|
672
|
+
findings: result.findings
|
|
673
|
+
});
|
|
674
|
+
if (!result.valid) for (const finding of result.findings) add("binding_validation_failed", `assembly.bindings.${entry.capability}.${finding.path}`, finding.message);
|
|
675
|
+
}
|
|
676
|
+
const capabilities = [...resolvedCapabilities].sort((a, b) => a.namespace < b.namespace ? -1 : a.namespace > b.namespace ? 1 : 0);
|
|
677
|
+
const componentPacks = [...resolvedPacks].sort((a, b) => {
|
|
678
|
+
const key = (e) => `${e.namespace}/${e.pack}`;
|
|
679
|
+
return key(a) < key(b) ? -1 : key(a) > key(b) ? 1 : 0;
|
|
680
|
+
});
|
|
681
|
+
const bindings = [...resolvedBindings].sort((a, b) => a.capability < b.capability ? -1 : a.capability > b.capability ? 1 : 0);
|
|
682
|
+
const adapters = input.manifest.adapters === void 0 ? void 0 : [...resolvedAdapters].sort((a, b) => {
|
|
683
|
+
const key = (e) => `${e.port}/${e.vendor}`;
|
|
684
|
+
return key(a) < key(b) ? -1 : key(a) > key(b) ? 1 : 0;
|
|
685
|
+
});
|
|
686
|
+
const lockfileBody = {
|
|
687
|
+
formatVersion: 2,
|
|
688
|
+
application: input.manifest.application,
|
|
689
|
+
capabilities,
|
|
690
|
+
componentPacks,
|
|
691
|
+
bindings,
|
|
692
|
+
...adapters ? { adapters } : {}
|
|
693
|
+
};
|
|
694
|
+
const lockfile = {
|
|
695
|
+
formatVersion: 2,
|
|
696
|
+
application: input.manifest.application,
|
|
697
|
+
assemblyDigest: assemblyDigest(lockfileDigestBody(lockfileBody)),
|
|
698
|
+
capabilities,
|
|
699
|
+
componentPacks,
|
|
700
|
+
bindings,
|
|
701
|
+
...adapters ? { adapters } : {}
|
|
702
|
+
};
|
|
703
|
+
return {
|
|
704
|
+
lockfile: findings.length === 0 ? lockfile : void 0,
|
|
705
|
+
findings
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
/**
|
|
709
|
+
* Throw with every finding listed, for use as a build step. Returns the
|
|
710
|
+
* lockfile when valid.
|
|
711
|
+
*/
|
|
816
712
|
function assertAssemblyResolved(input) {
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
713
|
+
const result = resolveAssembly(input);
|
|
714
|
+
if (result.findings.length === 0) return result.lockfile;
|
|
715
|
+
throw new Error([`Assembly "${input.manifest.application}" could not be resolved:`, ...result.findings.map((finding) => ` - [${finding.code}] ${finding.path}: ${finding.message}`)].join("\n"));
|
|
716
|
+
}
|
|
717
|
+
/**
|
|
718
|
+
* Validate an assembly manifest's bindings as a standalone build gate,
|
|
719
|
+
* without resolving versions. Useful when versions are already locked and
|
|
720
|
+
* only the binding contracts need checking.
|
|
721
|
+
*/
|
|
824
722
|
function validateAssemblyBindings(input) {
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
computeAdapterCertificationDigest,
|
|
886
|
-
computeAssemblyLockfileDigest,
|
|
887
|
-
computeUsageContractDocumentDigest,
|
|
888
|
-
parseSemverRange,
|
|
889
|
-
resolveAssembly,
|
|
890
|
-
resolveVersionRange,
|
|
891
|
-
satisfiesRange,
|
|
892
|
-
validateAssemblyBindings
|
|
893
|
-
});
|
|
723
|
+
assertAssemblyManifest(input.manifest);
|
|
724
|
+
const findings = [];
|
|
725
|
+
const add = (code, path, message) => {
|
|
726
|
+
findings.push({
|
|
727
|
+
code,
|
|
728
|
+
path,
|
|
729
|
+
message
|
|
730
|
+
});
|
|
731
|
+
};
|
|
732
|
+
const contractsByNamespace = /* @__PURE__ */ new Map();
|
|
733
|
+
for (const entry of input.manifest.contracts) {
|
|
734
|
+
try {
|
|
735
|
+
(0, _fabricorg_gen_capability.assertUsageContractDocument)(entry.document);
|
|
736
|
+
} catch (error) {
|
|
737
|
+
add("invalid_contract", `assembly.contracts.${entry.namespace}`, error instanceof Error ? error.message : String(error));
|
|
738
|
+
continue;
|
|
739
|
+
}
|
|
740
|
+
contractsByNamespace.set(entry.namespace, entry.document.capability);
|
|
741
|
+
}
|
|
742
|
+
const packManifests = input.packManifests ?? [];
|
|
743
|
+
for (const entry of input.manifest.bindings) {
|
|
744
|
+
const contract = contractsByNamespace.get(entry.capability);
|
|
745
|
+
if (!contract) {
|
|
746
|
+
add("unknown_contract_for_binding", `assembly.bindings.${entry.capability}`, `binding targets capability "${entry.capability}" but no contract for it is declared`);
|
|
747
|
+
continue;
|
|
748
|
+
}
|
|
749
|
+
try {
|
|
750
|
+
(0, _fabricorg_adoption_bindings.assertBindingValid)({
|
|
751
|
+
contract,
|
|
752
|
+
binding: entry.binding,
|
|
753
|
+
packs: packManifests,
|
|
754
|
+
coreComponents: input.coreComponents ?? [],
|
|
755
|
+
availableContracts: input.availableContracts ?? []
|
|
756
|
+
});
|
|
757
|
+
} catch (error) {
|
|
758
|
+
add("binding_validation_failed", `assembly.bindings.${entry.capability}`, error instanceof Error ? error.message : String(error));
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
return {
|
|
762
|
+
lockfile: void 0,
|
|
763
|
+
findings
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
//#endregion
|
|
767
|
+
exports.ASSEMBLY_LOCKFILE_FORMAT_VERSION = ASSEMBLY_LOCKFILE_FORMAT_VERSION;
|
|
768
|
+
exports.ASSEMBLY_MANIFEST_FORMAT_VERSION = ASSEMBLY_MANIFEST_FORMAT_VERSION;
|
|
769
|
+
exports.assertAssemblyLockfile = assertAssemblyLockfile;
|
|
770
|
+
exports.assertAssemblyManifest = assertAssemblyManifest;
|
|
771
|
+
exports.assertAssemblyResolved = assertAssemblyResolved;
|
|
772
|
+
exports.assertAssemblyRuntimeCompatible = assertAssemblyRuntimeCompatible;
|
|
773
|
+
exports.assertComponentPackManifest = assertComponentPackManifest;
|
|
774
|
+
exports.computeAdapterCertificationDigest = computeAdapterCertificationDigest;
|
|
775
|
+
exports.computeAssemblyLockfileDigest = computeAssemblyLockfileDigest;
|
|
776
|
+
exports.computeUsageContractDocumentDigest = computeUsageContractDocumentDigest;
|
|
777
|
+
exports.parseSemverRange = parseSemverRange;
|
|
778
|
+
exports.resolveAssembly = resolveAssembly;
|
|
779
|
+
exports.resolveVersionRange = resolveVersionRange;
|
|
780
|
+
exports.satisfiesRange = satisfiesRange;
|
|
781
|
+
exports.validateAssemblyBindings = validateAssemblyBindings;
|
|
782
|
+
|
|
894
783
|
//# sourceMappingURL=index.cjs.map
|