@fabricorg/assembly 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,735 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
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
+ computeUsageContractDocumentDigest: () => computeUsageContractDocumentDigest,
31
+ parseSemverRange: () => parseSemverRange,
32
+ resolveAssembly: () => resolveAssembly,
33
+ resolveVersionRange: () => resolveVersionRange,
34
+ satisfiesRange: () => satisfiesRange,
35
+ validateAssemblyBindings: () => validateAssemblyBindings
36
+ });
37
+ module.exports = __toCommonJS(index_exports);
38
+ var import_node_crypto = require("crypto");
39
+ var import_adoption_bindings = require("@fabricorg/adoption-bindings");
40
+ var import_gen_capability = require("@fabricorg/gen-capability");
41
+ var ASSEMBLY_MANIFEST_FORMAT_VERSION = 2;
42
+ var ASSEMBLY_LOCKFILE_FORMAT_VERSION = 2;
43
+ function parseSemver(input) {
44
+ const match = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/.exec(input.trim());
45
+ if (!match) return void 0;
46
+ return {
47
+ major: Number(match[1]),
48
+ minor: Number(match[2]),
49
+ patch: Number(match[3]),
50
+ prerelease: match[4] ? match[4].split(".") : []
51
+ };
52
+ }
53
+ function comparePrerelease(a, b) {
54
+ if (a.length === 0 && b.length === 0) return 0;
55
+ if (a.length === 0) return 1;
56
+ if (b.length === 0) return -1;
57
+ for (let i = 0; i < Math.max(a.length, b.length); i++) {
58
+ const ai = a[i];
59
+ const bi = b[i];
60
+ if (ai === void 0) return -1;
61
+ if (bi === void 0) return 1;
62
+ const an = /^\d+$/.test(ai);
63
+ const bn = /^\d+$/.test(bi);
64
+ if (an && bn) {
65
+ const diff = Number(ai) - Number(bi);
66
+ if (diff !== 0) return diff;
67
+ } else if (an) {
68
+ return -1;
69
+ } else if (bn) {
70
+ return 1;
71
+ } else {
72
+ if (ai < bi) return -1;
73
+ if (ai > bi) return 1;
74
+ }
75
+ }
76
+ return 0;
77
+ }
78
+ function compareSemver(a, b) {
79
+ if (a.major !== b.major) return a.major - b.major;
80
+ if (a.minor !== b.minor) return a.minor - b.minor;
81
+ if (a.patch !== b.patch) return a.patch - b.patch;
82
+ return comparePrerelease(a.prerelease, b.prerelease);
83
+ }
84
+ function caretComparator(range) {
85
+ return (version) => {
86
+ if (compareSemver(version, range) < 0) return false;
87
+ if (version.major !== range.major) return false;
88
+ if (version.major === 0) {
89
+ if (version.minor !== range.minor) return false;
90
+ if (range.minor === 0 && version.patch !== range.patch) return false;
91
+ }
92
+ return true;
93
+ };
94
+ }
95
+ function tildeComparator(range) {
96
+ return (version) => {
97
+ if (compareSemver(version, range) < 0) return false;
98
+ return version.major === range.major && version.minor === range.minor;
99
+ };
100
+ }
101
+ function exactComparator(range) {
102
+ return (version) => compareSemver(version, range) === 0;
103
+ }
104
+ function gteComparator(range) {
105
+ return (version) => compareSemver(version, range) >= 0;
106
+ }
107
+ function gtComparator(range) {
108
+ return (version) => compareSemver(version, range) > 0;
109
+ }
110
+ function lteComparator(range) {
111
+ return (version) => compareSemver(version, range) <= 0;
112
+ }
113
+ function ltComparator(range) {
114
+ return (version) => compareSemver(version, range) < 0;
115
+ }
116
+ function anyComparator() {
117
+ return () => true;
118
+ }
119
+ function parseComparator(raw) {
120
+ const trimmed = raw.trim();
121
+ if (trimmed === "*" || trimmed === "") return { operator: "*", comparator: anyComparator(), raw: trimmed, rangeVersion: void 0 };
122
+ const caret = /^(\^)(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)$/.exec(trimmed);
123
+ if (caret) {
124
+ const version = parseSemver(caret[2]);
125
+ if (!version) return void 0;
126
+ return { operator: "^", comparator: caretComparator(version), raw: trimmed, rangeVersion: version };
127
+ }
128
+ const tilde = /^(~)(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)$/.exec(trimmed);
129
+ if (tilde) {
130
+ const version = parseSemver(tilde[2]);
131
+ if (!version) return void 0;
132
+ return { operator: "~", comparator: tildeComparator(version), raw: trimmed, rangeVersion: version };
133
+ }
134
+ const op = /^(>=|<=|>|<|=)(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)$/.exec(trimmed);
135
+ if (op) {
136
+ const version = parseSemver(op[2]);
137
+ if (!version) return void 0;
138
+ const operator = op[1];
139
+ let comparator;
140
+ switch (operator) {
141
+ case ">=":
142
+ comparator = gteComparator(version);
143
+ break;
144
+ case "<=":
145
+ comparator = lteComparator(version);
146
+ break;
147
+ case ">":
148
+ comparator = gtComparator(version);
149
+ break;
150
+ case "<":
151
+ comparator = ltComparator(version);
152
+ break;
153
+ default:
154
+ comparator = exactComparator(version);
155
+ break;
156
+ }
157
+ return { operator, comparator, raw: trimmed, rangeVersion: version };
158
+ }
159
+ const bare = /^(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)$/.exec(trimmed);
160
+ if (bare) {
161
+ const version = parseSemver(bare[1]);
162
+ if (!version) return void 0;
163
+ return { operator: "=", comparator: exactComparator(version), raw: trimmed, rangeVersion: version };
164
+ }
165
+ return void 0;
166
+ }
167
+ function comparatorRangeVersion(comparator) {
168
+ return comparator.rangeVersion;
169
+ }
170
+ function parseSemverRange(raw) {
171
+ const trimmed = raw.trim();
172
+ if (!trimmed) return void 0;
173
+ const parts = trimmed.split(/\s+/);
174
+ const comparators = [];
175
+ for (const part of parts) {
176
+ const parsed = parseComparator(part);
177
+ if (!parsed) return void 0;
178
+ comparators.push(parsed);
179
+ }
180
+ if (comparators.length === 0) return void 0;
181
+ return { comparators, raw: trimmed };
182
+ }
183
+ function satisfiesRange(version, range) {
184
+ const parsed = parseSemver(version);
185
+ if (!parsed) return false;
186
+ if (!range.comparators.every((comparator) => comparator.comparator(parsed))) return false;
187
+ if (parsed.prerelease.length > 0) {
188
+ return range.comparators.some((comparator) => {
189
+ const rangeVersion = comparatorRangeVersion(comparator);
190
+ return rangeVersion !== void 0 && rangeVersion.prerelease.length > 0 && rangeVersion.major === parsed.major && rangeVersion.minor === parsed.minor && rangeVersion.patch === parsed.patch;
191
+ });
192
+ }
193
+ return true;
194
+ }
195
+ function resolveVersionRange(range, available) {
196
+ const parsed = available.map((version) => ({ version, parsed: parseSemver(version) })).filter((entry) => entry.parsed !== void 0).filter((entry) => {
197
+ if (!range.comparators.every((comparator) => comparator.comparator(entry.parsed))) return false;
198
+ if (entry.parsed.prerelease.length > 0) {
199
+ return range.comparators.some((comparator) => {
200
+ const rangeVersion = comparatorRangeVersion(comparator);
201
+ return rangeVersion !== void 0 && rangeVersion.prerelease.length > 0 && rangeVersion.major === entry.parsed.major && rangeVersion.minor === entry.parsed.minor && rangeVersion.patch === entry.parsed.patch;
202
+ });
203
+ }
204
+ return true;
205
+ }).sort((a, b) => compareSemver(b.parsed, a.parsed));
206
+ return parsed[0]?.version;
207
+ }
208
+ function sortJson(value) {
209
+ if (Array.isArray(value)) return value.map(sortJson);
210
+ if (value && typeof value === "object") {
211
+ return Object.fromEntries(
212
+ Object.entries(value).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(([key, child]) => [key, sortJson(child)])
213
+ );
214
+ }
215
+ return value;
216
+ }
217
+ function stableStringify(value) {
218
+ return JSON.stringify(sortJson(value));
219
+ }
220
+ function assemblyDigest(value) {
221
+ return (0, import_node_crypto.createHash)("sha256").update(stableStringify(value)).digest("hex");
222
+ }
223
+ function computeUsageContractDocumentDigest(document) {
224
+ (0, import_gen_capability.assertUsageContractDocument)(document);
225
+ return assemblyDigest(document);
226
+ }
227
+ function lockfileDigestBody(lockfile) {
228
+ return {
229
+ formatVersion: lockfile.formatVersion,
230
+ application: lockfile.application,
231
+ capabilities: lockfile.capabilities,
232
+ componentPacks: lockfile.componentPacks,
233
+ bindings: lockfile.bindings
234
+ };
235
+ }
236
+ function fail(path, message) {
237
+ throw new Error(`Assembly manifest ${path} ${message}.`);
238
+ }
239
+ function requireString(value, path) {
240
+ if (typeof value !== "string" || !value.trim()) fail(path, "must be a non-empty string");
241
+ return value;
242
+ }
243
+ function requireArray(value, path) {
244
+ if (!Array.isArray(value)) fail(path, "must be an array");
245
+ return value;
246
+ }
247
+ function assertAssemblyManifest(value, path = "assembly") {
248
+ if (!value || typeof value !== "object" || Array.isArray(value)) fail(path, "must be an object");
249
+ const manifest = value;
250
+ if (manifest.formatVersion !== ASSEMBLY_MANIFEST_FORMAT_VERSION) {
251
+ fail(`${path}.formatVersion`, `must be ${ASSEMBLY_MANIFEST_FORMAT_VERSION}, received ${JSON.stringify(manifest.formatVersion)}`);
252
+ }
253
+ if (!/^[a-z][a-z0-9-]*$/.test(requireString(manifest.application, `${path}.application`))) {
254
+ fail(`${path}.application`, "must be lowercase letters, numbers and hyphens");
255
+ }
256
+ requireArray(manifest.capabilities, `${path}.capabilities`).forEach((entry, index) => {
257
+ const capability = entry;
258
+ requireString(capability?.namespace, `${path}.capabilities[${index}].namespace`);
259
+ requireString(capability?.range, `${path}.capabilities[${index}].range`);
260
+ });
261
+ requireArray(manifest.componentPacks, `${path}.componentPacks`).forEach((entry, index) => {
262
+ const pack = entry;
263
+ requireString(pack?.pack, `${path}.componentPacks[${index}].pack`);
264
+ requireString(pack?.namespace, `${path}.componentPacks[${index}].namespace`);
265
+ requireString(pack?.range, `${path}.componentPacks[${index}].range`);
266
+ });
267
+ requireArray(manifest.bindings, `${path}.bindings`).forEach((entry, index) => {
268
+ const binding = entry;
269
+ requireString(binding?.capability, `${path}.bindings[${index}].capability`);
270
+ (0, import_adoption_bindings.assertAdoptionBinding)(binding?.binding, `${path}.bindings[${index}].binding`);
271
+ });
272
+ requireArray(manifest.contracts, `${path}.contracts`).forEach((entry, index) => {
273
+ const contract = entry;
274
+ requireString(contract?.namespace, `${path}.contracts[${index}].namespace`);
275
+ try {
276
+ (0, import_gen_capability.assertUsageContractDocument)(contract?.document);
277
+ } catch (error) {
278
+ fail(`${path}.contracts[${index}].document`, error instanceof Error ? error.message : String(error));
279
+ }
280
+ });
281
+ }
282
+ function assertComponentPackManifest(value, path = "pack") {
283
+ if (!value || typeof value !== "object" || Array.isArray(value)) fail(path, "must be an object");
284
+ const pack = value;
285
+ requireString(pack.pack, `${path}.pack`);
286
+ requireString(pack.namespace, `${path}.namespace`);
287
+ requireString(pack.version, `${path}.version`);
288
+ if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(pack.version)) {
289
+ fail(`${path}.version`, "must be a semver version");
290
+ }
291
+ if (!pack.components || typeof pack.components !== "object" || Array.isArray(pack.components)) {
292
+ fail(`${path}.components`, "must be an object");
293
+ }
294
+ if (!/^[0-9a-f]{64}$/.test(requireString(pack.artifactDigest, `${path}.artifactDigest`))) {
295
+ fail(`${path}.artifactDigest`, "must be a lowercase SHA-256 digest");
296
+ }
297
+ }
298
+ function assertAssemblyLockfile(value, path = "lockfile") {
299
+ if (!value || typeof value !== "object" || Array.isArray(value)) fail(path, "must be an object");
300
+ const lockfile = value;
301
+ if (lockfile.formatVersion !== ASSEMBLY_LOCKFILE_FORMAT_VERSION) {
302
+ fail(`${path}.formatVersion`, `must be ${ASSEMBLY_LOCKFILE_FORMAT_VERSION}, received ${JSON.stringify(lockfile.formatVersion)}`);
303
+ }
304
+ if (!/^[a-z][a-z0-9-]*$/.test(requireString(lockfile.application, `${path}.application`))) {
305
+ fail(`${path}.application`, "must be lowercase letters, numbers and hyphens");
306
+ }
307
+ if (!/^[0-9a-f]{64}$/.test(requireString(lockfile.assemblyDigest, `${path}.assemblyDigest`))) {
308
+ fail(`${path}.assemblyDigest`, "must be a lowercase SHA-256 digest");
309
+ }
310
+ const capabilities = requireArray(lockfile.capabilities, `${path}.capabilities`);
311
+ const componentPacks = requireArray(lockfile.componentPacks, `${path}.componentPacks`);
312
+ const bindings = requireArray(lockfile.bindings, `${path}.bindings`);
313
+ const seenCapabilities = /* @__PURE__ */ new Set();
314
+ for (const [index, entry] of capabilities.entries()) {
315
+ const capability = entry;
316
+ const namespace = String(capability?.namespace);
317
+ if (seenCapabilities.has(namespace)) {
318
+ fail(`${path}.capabilities[${index}]`, `declares capability "${namespace}" a second time`);
319
+ }
320
+ seenCapabilities.add(namespace);
321
+ requireString(capability?.namespace, `${path}.capabilities[${index}].namespace`);
322
+ requireString(capability?.version, `${path}.capabilities[${index}].version`);
323
+ requireString(capability?.range, `${path}.capabilities[${index}].range`);
324
+ if (!/^[0-9a-f]{64}$/.test(requireString(capability?.manifestDigest, `${path}.capabilities[${index}].manifestDigest`))) {
325
+ fail(`${path}.capabilities[${index}].manifestDigest`, "must be a lowercase SHA-256 digest");
326
+ }
327
+ if (capability?.usageContractDigest !== void 0 && !/^[0-9a-f]{64}$/.test(requireString(capability.usageContractDigest, `${path}.capabilities[${index}].usageContractDigest`))) {
328
+ fail(`${path}.capabilities[${index}].usageContractDigest`, "must be a lowercase SHA-256 digest");
329
+ }
330
+ }
331
+ const seenPackIdentities = /* @__PURE__ */ new Set();
332
+ for (const [index, entry] of componentPacks.entries()) {
333
+ const pack = entry;
334
+ requireString(pack?.pack, `${path}.componentPacks[${index}].pack`);
335
+ const identity = `${String(pack?.namespace)}/${String(pack?.pack)}`;
336
+ if (seenPackIdentities.has(identity)) {
337
+ fail(`${path}.componentPacks[${index}]`, `declares component pack "${identity}" a second time; first-match resolution would make the choice arbitrary`);
338
+ }
339
+ seenPackIdentities.add(identity);
340
+ requireString(pack?.namespace, `${path}.componentPacks[${index}].namespace`);
341
+ requireString(pack?.version, `${path}.componentPacks[${index}].version`);
342
+ requireString(pack?.range, `${path}.componentPacks[${index}].range`);
343
+ if (!/^[0-9a-f]{64}$/.test(requireString(pack?.artifactDigest, `${path}.componentPacks[${index}].artifactDigest`))) {
344
+ fail(`${path}.componentPacks[${index}].artifactDigest`, "must be a lowercase SHA-256 digest");
345
+ }
346
+ if (pack?.remote !== void 0) {
347
+ const remotePath = `${path}.componentPacks[${index}].remote`;
348
+ const remote = pack.remote;
349
+ if (remote === null || typeof remote !== "object" || Array.isArray(remote)) fail(remotePath, "must be an object");
350
+ requireString(remote.entry, `${remotePath}.entry`);
351
+ if (!/^sha(256|384|512)-[A-Za-z0-9+/]+={0,2}$/.test(requireString(remote.integrity, `${remotePath}.integrity`))) {
352
+ fail(`${remotePath}.integrity`, 'must be a subresource-integrity value, e.g. "sha384-\u2026"');
353
+ }
354
+ const exposes = remote.exposes;
355
+ if (exposes === null || typeof exposes !== "object" || Array.isArray(exposes)) {
356
+ fail(`${remotePath}.exposes`, "must be an object");
357
+ }
358
+ for (const [component, modulePath] of Object.entries(exposes)) {
359
+ requireString(modulePath, `${remotePath}.exposes.${component}`);
360
+ }
361
+ }
362
+ }
363
+ for (const [index, entry] of bindings.entries()) {
364
+ const binding = entry;
365
+ requireString(binding?.capability, `${path}.bindings[${index}].capability`);
366
+ if (!/^[0-9a-f]{64}$/.test(requireString(binding?.bindingDigest, `${path}.bindings[${index}].bindingDigest`))) {
367
+ fail(`${path}.bindings[${index}].bindingDigest`, "must be a lowercase SHA-256 digest");
368
+ }
369
+ if (binding?.valid !== true) fail(`${path}.bindings[${index}].valid`, "must be true");
370
+ requireArray(binding?.findings, `${path}.bindings[${index}].findings`);
371
+ }
372
+ const expected = assemblyDigest(lockfileDigestBody({
373
+ formatVersion: ASSEMBLY_LOCKFILE_FORMAT_VERSION,
374
+ application: lockfile.application,
375
+ capabilities,
376
+ componentPacks,
377
+ bindings
378
+ }));
379
+ if (lockfile.assemblyDigest !== expected) {
380
+ fail(`${path}.assemblyDigest`, `does not match resolved lockfile content; expected "${expected}"`);
381
+ }
382
+ }
383
+ function assertAssemblyRuntimeCompatible(lockfile, loadedCapabilities) {
384
+ assertAssemblyLockfile(lockfile);
385
+ const loadedByNamespace = /* @__PURE__ */ new Map();
386
+ for (const loaded of loadedCapabilities) {
387
+ if (loadedByNamespace.has(loaded.namespace)) {
388
+ throw new Error(`Loaded capability "${loaded.namespace}" is duplicated.`);
389
+ }
390
+ loadedByNamespace.set(loaded.namespace, loaded);
391
+ }
392
+ for (const approved of lockfile.capabilities) {
393
+ const loaded = loadedByNamespace.get(approved.namespace);
394
+ if (!loaded || loaded.version !== approved.version || loaded.manifestDigest !== approved.manifestDigest) {
395
+ throw new Error(
396
+ `Loaded capability "${approved.namespace}" does not match approved capability "${approved.namespace}@${approved.version}" with manifest digest "${approved.manifestDigest}".`
397
+ );
398
+ }
399
+ }
400
+ for (const loaded of loadedCapabilities) {
401
+ if (!lockfile.capabilities.some((approved) => approved.namespace === loaded.namespace)) {
402
+ throw new Error(`Loaded capability "${loaded.namespace}" is not approved by assembly "${lockfile.assemblyDigest}".`);
403
+ }
404
+ }
405
+ }
406
+ function resolveAssembly(input) {
407
+ assertAssemblyManifest(input.manifest);
408
+ const findings = [];
409
+ const add = (code, path, message) => {
410
+ findings.push({ code, path, message });
411
+ };
412
+ const capabilityNamespaces = /* @__PURE__ */ new Set();
413
+ for (const capability of input.manifest.capabilities) {
414
+ if (capabilityNamespaces.has(capability.namespace)) {
415
+ add(
416
+ "duplicate_capability",
417
+ `assembly.capabilities.${capability.namespace}`,
418
+ `capability "${capability.namespace}" is declared more than once`
419
+ );
420
+ }
421
+ capabilityNamespaces.add(capability.namespace);
422
+ }
423
+ const packKeys = /* @__PURE__ */ new Set();
424
+ for (const pack of input.manifest.componentPacks) {
425
+ const key = `${pack.namespace}/${pack.pack}`;
426
+ if (packKeys.has(key)) {
427
+ add(
428
+ "duplicate_component_pack",
429
+ `assembly.componentPacks.${key}`,
430
+ `component pack "${key}" is declared more than once`
431
+ );
432
+ }
433
+ packKeys.add(key);
434
+ }
435
+ const contractNamespaces = /* @__PURE__ */ new Set();
436
+ for (const contract of input.manifest.contracts) {
437
+ if (contractNamespaces.has(contract.namespace)) {
438
+ add(
439
+ "duplicate_contract",
440
+ `assembly.contracts.${contract.namespace}`,
441
+ `contract for "${contract.namespace}" is declared more than once`
442
+ );
443
+ }
444
+ contractNamespaces.add(contract.namespace);
445
+ }
446
+ const resolvedCapabilities = [];
447
+ const capabilitiesByNamespace = /* @__PURE__ */ new Map();
448
+ for (const available of input.capabilities) {
449
+ const list = capabilitiesByNamespace.get(available.namespace) ?? [];
450
+ list.push(available);
451
+ capabilitiesByNamespace.set(available.namespace, list);
452
+ }
453
+ for (const ref of input.manifest.capabilities) {
454
+ const range = parseSemverRange(ref.range);
455
+ if (!range) {
456
+ add(
457
+ "invalid_range",
458
+ `assembly.capabilities.${ref.namespace}.range`,
459
+ `range "${ref.range}" is not a valid semver range`
460
+ );
461
+ continue;
462
+ }
463
+ const available = capabilitiesByNamespace.get(ref.namespace) ?? [];
464
+ const versions = available.map((entry) => entry.version);
465
+ const resolved = resolveVersionRange(range, versions);
466
+ if (!resolved) {
467
+ add(
468
+ "unresolved_capability",
469
+ `assembly.capabilities.${ref.namespace}`,
470
+ `no available version of "${ref.namespace}" satisfies range "${ref.range}"`
471
+ );
472
+ continue;
473
+ }
474
+ const matched = available.find((entry) => entry.version === resolved);
475
+ resolvedCapabilities.push({
476
+ namespace: ref.namespace,
477
+ version: resolved,
478
+ range: ref.range,
479
+ manifestDigest: matched.manifestDigest
480
+ });
481
+ }
482
+ const resolvedPacks = [];
483
+ const packsByKey = /* @__PURE__ */ new Map();
484
+ for (const available of input.componentPacks) {
485
+ const key = `${available.namespace}/${available.pack}`;
486
+ const list = packsByKey.get(key) ?? [];
487
+ list.push(available);
488
+ packsByKey.set(key, list);
489
+ }
490
+ for (const ref of input.manifest.componentPacks) {
491
+ const range = parseSemverRange(ref.range);
492
+ if (!range) {
493
+ add(
494
+ "invalid_range",
495
+ `assembly.componentPacks.${ref.namespace}/${ref.pack}.range`,
496
+ `range "${ref.range}" is not a valid semver range`
497
+ );
498
+ continue;
499
+ }
500
+ const key = `${ref.namespace}/${ref.pack}`;
501
+ const available = packsByKey.get(key) ?? [];
502
+ const versions = available.map((entry) => entry.version);
503
+ const resolved = resolveVersionRange(range, versions);
504
+ if (!resolved) {
505
+ add(
506
+ "unresolved_component_pack",
507
+ `assembly.componentPacks.${key}`,
508
+ `no available version of pack "${key}" satisfies range "${ref.range}"`
509
+ );
510
+ continue;
511
+ }
512
+ const matched = available.find((entry) => entry.version === resolved);
513
+ resolvedPacks.push({
514
+ pack: ref.pack,
515
+ namespace: ref.namespace,
516
+ version: resolved,
517
+ range: ref.range,
518
+ artifactDigest: matched.artifactDigest,
519
+ ...matched.remote ? { remote: matched.remote } : {}
520
+ });
521
+ }
522
+ const packManifests = input.packManifests ?? [];
523
+ for (const pack of packManifests) {
524
+ try {
525
+ assertComponentPackManifest(pack);
526
+ } catch (error) {
527
+ add(
528
+ "invalid_component_pack",
529
+ `packManifests.${pack.namespace}/${pack.pack}`,
530
+ error instanceof Error ? error.message : String(error)
531
+ );
532
+ }
533
+ }
534
+ const resolvedPackManifests = [];
535
+ for (const resolved of resolvedPacks) {
536
+ const key = `${resolved.namespace}/${resolved.pack}`;
537
+ const manifest = packManifests.find(
538
+ (candidate) => candidate.namespace === resolved.namespace && candidate.pack === resolved.pack && candidate.version === resolved.version
539
+ );
540
+ if (!manifest) {
541
+ add(
542
+ "missing_component_pack_manifest",
543
+ `assembly.componentPacks.${key}`,
544
+ `resolved pack "${key}@${resolved.version}" has no matching component-pack manifest`
545
+ );
546
+ continue;
547
+ }
548
+ if (manifest.artifactDigest !== resolved.artifactDigest) {
549
+ add(
550
+ "component_pack_artifact_digest_mismatch",
551
+ `assembly.componentPacks.${key}.artifactDigest`,
552
+ `component-pack manifest digest "${manifest.artifactDigest}" does not match resolved artifact digest "${resolved.artifactDigest}"`
553
+ );
554
+ continue;
555
+ }
556
+ resolvedPackManifests.push(manifest);
557
+ }
558
+ const contractsByNamespace = /* @__PURE__ */ new Map();
559
+ for (const entry of input.manifest.contracts) {
560
+ try {
561
+ (0, import_gen_capability.assertUsageContractDocument)(entry.document);
562
+ } catch (error) {
563
+ add(
564
+ "invalid_contract",
565
+ `assembly.contracts.${entry.namespace}`,
566
+ error instanceof Error ? error.message : String(error)
567
+ );
568
+ continue;
569
+ }
570
+ if (entry.document.capability.namespace !== entry.namespace) {
571
+ add(
572
+ "contract_namespace_mismatch",
573
+ `assembly.contracts.${entry.namespace}`,
574
+ `contract namespace "${entry.document.capability.namespace}" does not match assembly namespace "${entry.namespace}"`
575
+ );
576
+ }
577
+ const resolved = resolvedCapabilities.find((capability) => capability.namespace === entry.namespace);
578
+ if (resolved && entry.document.manifestDigest !== resolved.manifestDigest) {
579
+ add(
580
+ "contract_manifest_digest_mismatch",
581
+ `assembly.contracts.${entry.namespace}.manifestDigest`,
582
+ `contract manifest digest "${entry.document.manifestDigest}" does not match resolved capability digest "${resolved.manifestDigest}"`
583
+ );
584
+ } else if (resolved) {
585
+ resolved.usageContractDigest = computeUsageContractDocumentDigest(entry.document);
586
+ }
587
+ contractsByNamespace.set(entry.namespace, entry.document.capability);
588
+ }
589
+ const resolvedBindings = [];
590
+ for (const entry of input.manifest.bindings) {
591
+ const contract = contractsByNamespace.get(entry.capability);
592
+ if (!contract) {
593
+ add(
594
+ "unknown_contract_for_binding",
595
+ `assembly.bindings.${entry.capability}`,
596
+ `binding targets capability "${entry.capability}" but no contract for it is declared`
597
+ );
598
+ resolvedBindings.push({
599
+ capability: entry.capability,
600
+ bindingDigest: assemblyDigest(entry.binding),
601
+ valid: false,
602
+ findings: []
603
+ });
604
+ continue;
605
+ }
606
+ const result = (0, import_adoption_bindings.validateBinding)({
607
+ contract,
608
+ binding: entry.binding,
609
+ packs: resolvedPackManifests,
610
+ coreComponents: input.coreComponents ?? [],
611
+ availableContracts: input.availableContracts ?? []
612
+ });
613
+ resolvedBindings.push({
614
+ capability: entry.capability,
615
+ bindingDigest: assemblyDigest(entry.binding),
616
+ valid: result.valid,
617
+ findings: result.findings
618
+ });
619
+ if (!result.valid) {
620
+ for (const finding of result.findings) {
621
+ add(
622
+ "binding_validation_failed",
623
+ `assembly.bindings.${entry.capability}.${finding.path}`,
624
+ finding.message
625
+ );
626
+ }
627
+ }
628
+ }
629
+ const capabilities = [...resolvedCapabilities].sort(
630
+ (a, b) => a.namespace < b.namespace ? -1 : a.namespace > b.namespace ? 1 : 0
631
+ );
632
+ const componentPacks = [...resolvedPacks].sort((a, b) => {
633
+ const key = (e) => `${e.namespace}/${e.pack}`;
634
+ return key(a) < key(b) ? -1 : key(a) > key(b) ? 1 : 0;
635
+ });
636
+ const bindings = [...resolvedBindings].sort(
637
+ (a, b) => a.capability < b.capability ? -1 : a.capability > b.capability ? 1 : 0
638
+ );
639
+ const lockfileBody = {
640
+ formatVersion: ASSEMBLY_LOCKFILE_FORMAT_VERSION,
641
+ application: input.manifest.application,
642
+ capabilities,
643
+ componentPacks,
644
+ bindings
645
+ };
646
+ const lockfile = {
647
+ formatVersion: ASSEMBLY_LOCKFILE_FORMAT_VERSION,
648
+ application: input.manifest.application,
649
+ assemblyDigest: assemblyDigest(lockfileDigestBody(lockfileBody)),
650
+ capabilities,
651
+ componentPacks,
652
+ bindings
653
+ };
654
+ return {
655
+ lockfile: findings.length === 0 ? lockfile : void 0,
656
+ findings
657
+ };
658
+ }
659
+ function assertAssemblyResolved(input) {
660
+ const result = resolveAssembly(input);
661
+ if (result.findings.length === 0) return result.lockfile;
662
+ throw new Error([
663
+ `Assembly "${input.manifest.application}" could not be resolved:`,
664
+ ...result.findings.map((finding) => ` - [${finding.code}] ${finding.path}: ${finding.message}`)
665
+ ].join("\n"));
666
+ }
667
+ function validateAssemblyBindings(input) {
668
+ assertAssemblyManifest(input.manifest);
669
+ const findings = [];
670
+ const add = (code, path, message) => {
671
+ findings.push({ code, path, message });
672
+ };
673
+ const contractsByNamespace = /* @__PURE__ */ new Map();
674
+ for (const entry of input.manifest.contracts) {
675
+ try {
676
+ (0, import_gen_capability.assertUsageContractDocument)(entry.document);
677
+ } catch (error) {
678
+ add(
679
+ "invalid_contract",
680
+ `assembly.contracts.${entry.namespace}`,
681
+ error instanceof Error ? error.message : String(error)
682
+ );
683
+ continue;
684
+ }
685
+ contractsByNamespace.set(entry.namespace, entry.document.capability);
686
+ }
687
+ const packManifests = input.packManifests ?? [];
688
+ for (const entry of input.manifest.bindings) {
689
+ const contract = contractsByNamespace.get(entry.capability);
690
+ if (!contract) {
691
+ add(
692
+ "unknown_contract_for_binding",
693
+ `assembly.bindings.${entry.capability}`,
694
+ `binding targets capability "${entry.capability}" but no contract for it is declared`
695
+ );
696
+ continue;
697
+ }
698
+ try {
699
+ (0, import_adoption_bindings.assertBindingValid)({
700
+ contract,
701
+ binding: entry.binding,
702
+ packs: packManifests,
703
+ coreComponents: input.coreComponents ?? [],
704
+ availableContracts: input.availableContracts ?? []
705
+ });
706
+ } catch (error) {
707
+ add(
708
+ "binding_validation_failed",
709
+ `assembly.bindings.${entry.capability}`,
710
+ error instanceof Error ? error.message : String(error)
711
+ );
712
+ }
713
+ }
714
+ return {
715
+ lockfile: void 0,
716
+ findings
717
+ };
718
+ }
719
+ // Annotate the CommonJS export names for ESM import in node:
720
+ 0 && (module.exports = {
721
+ ASSEMBLY_LOCKFILE_FORMAT_VERSION,
722
+ ASSEMBLY_MANIFEST_FORMAT_VERSION,
723
+ assertAssemblyLockfile,
724
+ assertAssemblyManifest,
725
+ assertAssemblyResolved,
726
+ assertAssemblyRuntimeCompatible,
727
+ assertComponentPackManifest,
728
+ computeUsageContractDocumentDigest,
729
+ parseSemverRange,
730
+ resolveAssembly,
731
+ resolveVersionRange,
732
+ satisfiesRange,
733
+ validateAssemblyBindings
734
+ });
735
+ //# sourceMappingURL=index.cjs.map