@ixo/domain.md 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.
Files changed (70) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +6 -0
  3. package/README.md +30 -0
  4. package/assets/domain-md.schema.json +1103 -0
  5. package/assets/oracle-capsule-jcs-vectors.json +57 -0
  6. package/assets/oracle-capsule-manifest-contract.md +72 -0
  7. package/assets/oracle-capsule-source-lock.schema.json +45 -0
  8. package/assets/oracle-capsule.schema.json +265 -0
  9. package/assets/rules.json +275 -0
  10. package/assets/spec.md +1434 -0
  11. package/assets/template-manifest.schema.json +57 -0
  12. package/dist/capsule/index.d.ts +75 -0
  13. package/dist/capsule/index.js +20 -0
  14. package/dist/capsule/index.js.map +1 -0
  15. package/dist/chunk-BYBC2TSS.js +597 -0
  16. package/dist/chunk-BYBC2TSS.js.map +1 -0
  17. package/dist/chunk-G7PZJYAJ.js +31826 -0
  18. package/dist/chunk-G7PZJYAJ.js.map +1 -0
  19. package/dist/chunk-R6QZRNEX.js +164 -0
  20. package/dist/chunk-R6QZRNEX.js.map +1 -0
  21. package/dist/chunk-T3STBWXP.js +46 -0
  22. package/dist/chunk-T3STBWXP.js.map +1 -0
  23. package/dist/chunk-W7MRQP7I.js +862 -0
  24. package/dist/chunk-W7MRQP7I.js.map +1 -0
  25. package/dist/chunk-XDIWMZVF.js +1150 -0
  26. package/dist/chunk-XDIWMZVF.js.map +1 -0
  27. package/dist/cli-KcUIQEiE.d.ts +171 -0
  28. package/dist/cli.d.ts +2 -0
  29. package/dist/cli.js +400 -0
  30. package/dist/cli.js.map +1 -0
  31. package/dist/index.d.ts +12 -0
  32. package/dist/index.js +83 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/resolver-B3mwnjJq.d.ts +96 -0
  35. package/dist/spec.d.ts +20 -0
  36. package/dist/spec.js +23 -0
  37. package/dist/spec.js.map +1 -0
  38. package/dist/templates/index.d.ts +7 -0
  39. package/dist/templates/index.js +24 -0
  40. package/dist/templates/index.js.map +1 -0
  41. package/dist/workers-Dr8B8a2O.d.ts +19 -0
  42. package/dist/workers.d.ts +3 -0
  43. package/dist/workers.js +31 -0
  44. package/dist/workers.js.map +1 -0
  45. package/package.json +97 -0
  46. package/src/capsule/canonical.ts +74 -0
  47. package/src/capsule/index.ts +10 -0
  48. package/src/capsule/json.ts +373 -0
  49. package/src/capsule/types.ts +63 -0
  50. package/src/capsule/validate.ts +551 -0
  51. package/src/cli.ts +405 -0
  52. package/src/compiled-validators.d.ts +7 -0
  53. package/src/compiled-validators.js +32512 -0
  54. package/src/constants.ts +250 -0
  55. package/src/diff.ts +111 -0
  56. package/src/export.ts +28 -0
  57. package/src/index.ts +36 -0
  58. package/src/lint.ts +42 -0
  59. package/src/parser.ts +174 -0
  60. package/src/sarif.ts +49 -0
  61. package/src/semantic.ts +1156 -0
  62. package/src/spec.ts +48 -0
  63. package/src/templates/errors.ts +3 -0
  64. package/src/templates/index.ts +11 -0
  65. package/src/templates/manifest.ts +94 -0
  66. package/src/templates/render.ts +435 -0
  67. package/src/templates/resolver.ts +200 -0
  68. package/src/templates/types.ts +98 -0
  69. package/src/types.ts +195 -0
  70. package/src/workers.ts +6 -0
@@ -0,0 +1,862 @@
1
+ import {
2
+ ORACLE_CAPSULE_CONTRACT,
3
+ ORACLE_CAPSULE_EXTERNAL_CHECKS,
4
+ ORACLE_CAPSULE_LIMITS,
5
+ compiled_validators_default
6
+ } from "./chunk-G7PZJYAJ.js";
7
+
8
+ // src/capsule/canonical.ts
9
+ import { createHash } from "crypto";
10
+ var CapsuleCanonicalizationError = class extends Error {
11
+ code = "capsule-canonicalization";
12
+ };
13
+ function canonical(value, path) {
14
+ if (value === null) return "null";
15
+ if (typeof value === "boolean") return value ? "true" : "false";
16
+ if (typeof value === "string") {
17
+ for (let index = 0; index < value.length; index += 1) {
18
+ const code = value.charCodeAt(index);
19
+ if (code >= 55296 && code <= 56319) {
20
+ const low = value.charCodeAt(index + 1);
21
+ if (!Number.isInteger(low) || low < 56320 || low > 57343)
22
+ throw new CapsuleCanonicalizationError(`Lone high surrogate at ${path}.`);
23
+ index += 1;
24
+ } else if (code >= 56320 && code <= 57343) {
25
+ throw new CapsuleCanonicalizationError(`Lone low surrogate at ${path}.`);
26
+ }
27
+ }
28
+ return JSON.stringify(value);
29
+ }
30
+ if (typeof value === "number") {
31
+ if (!Number.isFinite(value))
32
+ throw new CapsuleCanonicalizationError(`Non-finite number at ${path}.`);
33
+ if (Number.isInteger(value) && !Number.isSafeInteger(value))
34
+ throw new CapsuleCanonicalizationError(`Unsafe integer at ${path}.`);
35
+ return JSON.stringify(value);
36
+ }
37
+ if (Array.isArray(value)) {
38
+ return `[${value.map((child, index) => canonical(child, `${path}/${index}`)).join(",")}]`;
39
+ }
40
+ if (typeof value === "object" && value !== void 0) {
41
+ const record = value;
42
+ return `{${Object.keys(record).sort().map((key) => `${canonical(key, path)}:${canonical(record[key], `${path}/${key}`)}`).join(",")}}`;
43
+ }
44
+ throw new CapsuleCanonicalizationError(`Unsupported JSON value at ${path}.`);
45
+ }
46
+ function canonicalizeCapsuleJson(value) {
47
+ return canonical(value, "/");
48
+ }
49
+ function capsuleJcsSha256(value) {
50
+ return createHash("sha256").update(canonicalizeCapsuleJson(value), "utf8").digest("hex");
51
+ }
52
+ function oracleCapsuleReleaseProjection(value) {
53
+ if (typeof value !== "object" || value === null || Array.isArray(value))
54
+ throw new CapsuleCanonicalizationError("A capsule release must be a JSON object.");
55
+ const root = structuredClone(value);
56
+ const metadata = root.metadata;
57
+ if (typeof metadata !== "object" || metadata === null || Array.isArray(metadata))
58
+ throw new CapsuleCanonicalizationError("A capsule release must contain metadata.");
59
+ const projectedMetadata = { ...metadata };
60
+ delete projectedMetadata.release_digest;
61
+ root.metadata = projectedMetadata;
62
+ return root;
63
+ }
64
+ function oracleCapsuleReleaseDigest(value) {
65
+ return capsuleJcsSha256(oracleCapsuleReleaseProjection(value));
66
+ }
67
+
68
+ // src/capsule/json.ts
69
+ var JsonFailure = class extends Error {
70
+ constructor(code, message, path, offset) {
71
+ super(message);
72
+ this.code = code;
73
+ this.path = path;
74
+ this.offset = offset;
75
+ }
76
+ code;
77
+ path;
78
+ offset;
79
+ };
80
+ function pointerSegment(value) {
81
+ return value.replace(/~/g, "~0").replace(/\//g, "~1");
82
+ }
83
+ function decodeInput(input) {
84
+ if (typeof input === "string") return input;
85
+ if (input[0] === 239 && input[1] === 187 && input[2] === 191)
86
+ throw new JsonFailure("capsule-encoding", "UTF-8 byte-order marks are forbidden.", "/", 0);
87
+ try {
88
+ return new TextDecoder("utf-8", { fatal: true, ignoreBOM: false }).decode(input);
89
+ } catch {
90
+ throw new JsonFailure("capsule-encoding", "Input must be valid UTF-8.", "/", 0);
91
+ }
92
+ }
93
+ var StrictJsonParser = class {
94
+ constructor(source, options) {
95
+ this.source = source;
96
+ this.options = options;
97
+ for (let index = 0; index < source.length; index += 1) {
98
+ if (source.charCodeAt(index) === 10) this.lineStarts.push(index + 1);
99
+ }
100
+ }
101
+ source;
102
+ options;
103
+ offset = 0;
104
+ nodes = 0;
105
+ lineStarts = [0];
106
+ locations = /* @__PURE__ */ new Map();
107
+ parse() {
108
+ this.skipWhitespace();
109
+ const value = this.parseValue("/", 0);
110
+ this.skipWhitespace();
111
+ if (this.offset !== this.source.length)
112
+ this.fail("capsule-json-syntax", "Unexpected trailing content.", "/");
113
+ return value;
114
+ }
115
+ location(offset) {
116
+ let low = 0;
117
+ let high = this.lineStarts.length;
118
+ while (low + 1 < high) {
119
+ const middle = Math.floor((low + high) / 2);
120
+ if ((this.lineStarts[middle] ?? 0) <= offset) low = middle;
121
+ else high = middle;
122
+ }
123
+ return { line: low + 1, column: offset - (this.lineStarts[low] ?? 0) + 1 };
124
+ }
125
+ failureLocation(offset) {
126
+ return this.location(offset);
127
+ }
128
+ fail(code, message, path, offset = this.offset) {
129
+ throw new JsonFailure(code, message, path, offset);
130
+ }
131
+ node(path, depth) {
132
+ if (depth > this.options.maxDepth)
133
+ this.fail("capsule-limit", `JSON exceeds maximum depth ${this.options.maxDepth}.`, path);
134
+ this.nodes += 1;
135
+ if (this.nodes > this.options.maxNodes)
136
+ this.fail("capsule-limit", `JSON exceeds maximum node count ${this.options.maxNodes}.`, path);
137
+ this.locations.set(path, this.location(this.offset));
138
+ }
139
+ skipWhitespace() {
140
+ while (/[\t\n\r ]/.test(this.source[this.offset] ?? "")) this.offset += 1;
141
+ }
142
+ parseValue(path, depth) {
143
+ this.node(path, depth);
144
+ const character = this.source[this.offset];
145
+ if (character === "{") return this.parseObject(path, depth);
146
+ if (character === "[") return this.parseArray(path, depth);
147
+ if (character === '"') return this.parseString(path);
148
+ if (character === "-" || character !== void 0 && /[0-9]/.test(character))
149
+ return this.parseNumber(path);
150
+ if (this.source.startsWith("true", this.offset)) {
151
+ this.offset += 4;
152
+ return true;
153
+ }
154
+ if (this.source.startsWith("false", this.offset)) {
155
+ this.offset += 5;
156
+ return false;
157
+ }
158
+ if (this.source.startsWith("null", this.offset)) {
159
+ this.offset += 4;
160
+ return null;
161
+ }
162
+ return this.fail("capsule-json-syntax", "Expected a JSON value.", path);
163
+ }
164
+ parseObject(path, depth) {
165
+ const result = {};
166
+ const seen = /* @__PURE__ */ new Set();
167
+ this.offset += 1;
168
+ this.skipWhitespace();
169
+ if (this.source[this.offset] === "}") {
170
+ this.offset += 1;
171
+ return result;
172
+ }
173
+ while (true) {
174
+ if (this.source[this.offset] !== '"')
175
+ this.fail("capsule-json-syntax", "Object names must be JSON strings.", path);
176
+ const keyOffset = this.offset;
177
+ const key = this.parseString(path);
178
+ const childPath = path === "/" ? `/${pointerSegment(key)}` : `${path}/${pointerSegment(key)}`;
179
+ if (seen.has(key))
180
+ this.fail(
181
+ "capsule-duplicate-key",
182
+ `Duplicate object name ${JSON.stringify(key)}.`,
183
+ childPath,
184
+ keyOffset
185
+ );
186
+ seen.add(key);
187
+ this.skipWhitespace();
188
+ if (this.source[this.offset] !== ":")
189
+ this.fail("capsule-json-syntax", "Expected a colon after the object name.", childPath);
190
+ this.offset += 1;
191
+ this.skipWhitespace();
192
+ result[key] = this.parseValue(childPath, depth + 1);
193
+ this.skipWhitespace();
194
+ const separator = this.source[this.offset];
195
+ if (separator === "}") {
196
+ this.offset += 1;
197
+ return result;
198
+ }
199
+ if (separator !== ",")
200
+ this.fail("capsule-json-syntax", "Expected a comma or closing brace.", path);
201
+ this.offset += 1;
202
+ this.skipWhitespace();
203
+ }
204
+ }
205
+ parseArray(path, depth) {
206
+ const result = [];
207
+ this.offset += 1;
208
+ this.skipWhitespace();
209
+ if (this.source[this.offset] === "]") {
210
+ this.offset += 1;
211
+ return result;
212
+ }
213
+ while (true) {
214
+ const childPath = path === "/" ? `/${result.length}` : `${path}/${result.length}`;
215
+ result.push(this.parseValue(childPath, depth + 1));
216
+ this.skipWhitespace();
217
+ const separator = this.source[this.offset];
218
+ if (separator === "]") {
219
+ this.offset += 1;
220
+ return result;
221
+ }
222
+ if (separator !== ",")
223
+ this.fail("capsule-json-syntax", "Expected a comma or closing bracket.", path);
224
+ this.offset += 1;
225
+ this.skipWhitespace();
226
+ }
227
+ }
228
+ parseString(path) {
229
+ this.offset += 1;
230
+ let value = "";
231
+ while (this.offset < this.source.length) {
232
+ const code = this.source.charCodeAt(this.offset);
233
+ if (code === 34) {
234
+ this.offset += 1;
235
+ if (value.length > this.options.maxScalarLength)
236
+ this.fail(
237
+ "capsule-limit",
238
+ `String exceeds maximum length ${this.options.maxScalarLength}.`,
239
+ path
240
+ );
241
+ return value;
242
+ }
243
+ if (code < 32)
244
+ this.fail("capsule-json-syntax", "Unescaped control character in string.", path);
245
+ if (code === 92) {
246
+ value += this.parseEscape(path);
247
+ continue;
248
+ }
249
+ if (code >= 55296 && code <= 56319) {
250
+ const low = this.source.charCodeAt(this.offset + 1);
251
+ if (!Number.isInteger(low) || low < 56320 || low > 57343)
252
+ this.fail("capsule-unicode", "Lone high surrogate is not I-JSON compatible.", path);
253
+ value += this.source.slice(this.offset, this.offset + 2);
254
+ this.offset += 2;
255
+ continue;
256
+ }
257
+ if (code >= 56320 && code <= 57343)
258
+ this.fail("capsule-unicode", "Lone low surrogate is not I-JSON compatible.", path);
259
+ value += this.source[this.offset] ?? "";
260
+ this.offset += 1;
261
+ }
262
+ return this.fail("capsule-json-syntax", "Unterminated JSON string.", path);
263
+ }
264
+ parseEscape(path) {
265
+ this.offset += 1;
266
+ const escape = this.source[this.offset];
267
+ const simple = {
268
+ '"': '"',
269
+ "\\": "\\",
270
+ "/": "/",
271
+ b: "\b",
272
+ f: "\f",
273
+ n: "\n",
274
+ r: "\r",
275
+ t: " "
276
+ };
277
+ if (escape !== void 0 && escape in simple) {
278
+ this.offset += 1;
279
+ return simple[escape] ?? "";
280
+ }
281
+ if (escape !== "u") return this.fail("capsule-json-syntax", "Invalid JSON escape.", path);
282
+ const first = this.readUnicodeEscape(path);
283
+ if (first >= 55296 && first <= 56319) {
284
+ if (this.source.slice(this.offset, this.offset + 2) !== "\\u")
285
+ return this.fail(
286
+ "capsule-unicode",
287
+ "Escaped high surrogate must be followed by a low surrogate.",
288
+ path
289
+ );
290
+ const second = this.readUnicodeEscape(path);
291
+ if (second < 56320 || second > 57343)
292
+ return this.fail(
293
+ "capsule-unicode",
294
+ "Escaped high surrogate must be followed by a low surrogate.",
295
+ path
296
+ );
297
+ return String.fromCharCode(first, second);
298
+ }
299
+ if (first >= 56320 && first <= 57343)
300
+ return this.fail("capsule-unicode", "Lone escaped low surrogate is forbidden.", path);
301
+ return String.fromCharCode(first);
302
+ }
303
+ readUnicodeEscape(path) {
304
+ const digitsAt = this.source[this.offset] === "u" ? this.offset + 1 : this.source.slice(this.offset, this.offset + 2) === "\\u" ? this.offset + 2 : -1;
305
+ if (digitsAt < 0) return this.fail("capsule-json-syntax", "Invalid Unicode escape.", path);
306
+ const hex = this.source.slice(digitsAt, digitsAt + 4);
307
+ if (!/^[0-9a-fA-F]{4}$/.test(hex))
308
+ return this.fail(
309
+ "capsule-json-syntax",
310
+ "Unicode escapes require four hexadecimal digits.",
311
+ path
312
+ );
313
+ this.offset = digitsAt + 4;
314
+ return Number.parseInt(hex, 16);
315
+ }
316
+ parseNumber(path) {
317
+ const remaining = this.source.slice(this.offset);
318
+ const match = /^-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?/.exec(remaining);
319
+ if (!match) return this.fail("capsule-json-syntax", "Invalid JSON number.", path);
320
+ const token = match[0];
321
+ const following = remaining[token.length];
322
+ if (following !== void 0 && !/[\t\n\r ,\]}]/.test(following))
323
+ return this.fail("capsule-json-syntax", "Invalid character after JSON number.", path);
324
+ this.offset += token.length;
325
+ const value = Number(token);
326
+ if (!Number.isFinite(value))
327
+ return this.fail("capsule-number", "Non-finite numbers are not I-JSON compatible.", path);
328
+ if (Number.isInteger(value) && !Number.isSafeInteger(value))
329
+ return this.fail(
330
+ "capsule-number",
331
+ "Integers must be within the interoperable safe-integer range.",
332
+ path
333
+ );
334
+ return value;
335
+ }
336
+ };
337
+ function finding(error, location2) {
338
+ return {
339
+ severity: "error",
340
+ code: error.code,
341
+ message: error.message,
342
+ path: error.path,
343
+ location: location2
344
+ };
345
+ }
346
+ function parseCapsuleJson(input, options = {}) {
347
+ const sourceName = options.sourceName ?? "<memory>";
348
+ let source;
349
+ try {
350
+ source = decodeInput(input);
351
+ } catch (error) {
352
+ const failure = error;
353
+ return { ok: false, findings: [finding(failure, { line: 1, column: 1 })] };
354
+ }
355
+ const maxBytes = options.maxBytes ?? ORACLE_CAPSULE_LIMITS.maxBytes;
356
+ if (Buffer.byteLength(source, "utf8") > maxBytes) {
357
+ return {
358
+ ok: false,
359
+ findings: [
360
+ {
361
+ severity: "error",
362
+ code: "capsule-file-too-large",
363
+ message: `Input exceeds ${maxBytes} bytes.`,
364
+ path: "/",
365
+ location: { line: 1, column: 1 }
366
+ }
367
+ ]
368
+ };
369
+ }
370
+ if (source.charCodeAt(0) === 65279) {
371
+ return {
372
+ ok: false,
373
+ findings: [
374
+ {
375
+ severity: "error",
376
+ code: "capsule-encoding",
377
+ message: "UTF-8 byte-order marks are forbidden.",
378
+ path: "/",
379
+ location: { line: 1, column: 1 }
380
+ }
381
+ ]
382
+ };
383
+ }
384
+ const parser = new StrictJsonParser(source, {
385
+ maxDepth: options.maxDepth ?? ORACLE_CAPSULE_LIMITS.maxDepth,
386
+ maxNodes: options.maxNodes ?? ORACLE_CAPSULE_LIMITS.maxNodes,
387
+ maxScalarLength: options.maxScalarLength ?? ORACLE_CAPSULE_LIMITS.maxScalarLength
388
+ });
389
+ try {
390
+ const value = parser.parse();
391
+ const document = {
392
+ value,
393
+ raw: source,
394
+ sourceName,
395
+ locations: parser.locations
396
+ };
397
+ return { ok: true, document, findings: [] };
398
+ } catch (error) {
399
+ const failure = error instanceof JsonFailure ? error : new JsonFailure("capsule-json-syntax", String(error), "/", 0);
400
+ return { ok: false, findings: [finding(failure, parser.failureLocation(failure.offset))] };
401
+ }
402
+ }
403
+
404
+ // src/capsule/validate.ts
405
+ import { createHash as createHash2 } from "crypto";
406
+ import { CID } from "multiformats/cid";
407
+ import { sha256 } from "multiformats/hashes/sha2";
408
+ function isRecord(value) {
409
+ return typeof value === "object" && value !== null && !Array.isArray(value);
410
+ }
411
+ function pointerSegment2(value) {
412
+ return value.replace(/~/g, "~0").replace(/\//g, "~1");
413
+ }
414
+ function location(document, path) {
415
+ let candidate = path.length > 0 ? path : "/";
416
+ while (candidate !== "/") {
417
+ const found = document.locations.get(candidate);
418
+ if (found) return found;
419
+ candidate = candidate.slice(0, candidate.lastIndexOf("/")) || "/";
420
+ }
421
+ return document.locations.get("/") ?? { line: 1, column: 1 };
422
+ }
423
+ function add(findings, document, code, message, path = "/") {
424
+ findings.push({ severity: "error", code, message, path, location: location(document, path) });
425
+ }
426
+ function schemaPath(error) {
427
+ const base = error.instancePath || "/";
428
+ if (error.keyword !== "required") return base;
429
+ const missing = error.params.missingProperty;
430
+ if (!missing) return base;
431
+ return base === "/" || base === "" ? `/${pointerSegment2(missing)}` : `${base}/${pointerSegment2(missing)}`;
432
+ }
433
+ function addSchemaFindings(findings, document, errors, label) {
434
+ for (const error of errors ?? []) {
435
+ const path = schemaPath(error);
436
+ add(findings, document, "capsule-schema", `${label}: ${error.message ?? error.keyword}.`, path);
437
+ }
438
+ }
439
+ function sha(bytes2) {
440
+ return createHash2("sha256").update(bytes2).digest("hex");
441
+ }
442
+ function bytes(value) {
443
+ return typeof value === "string" ? Buffer.from(value, "utf8") : value;
444
+ }
445
+ function records(value) {
446
+ return Array.isArray(value) ? value.filter(isRecord) : [];
447
+ }
448
+ function artifactRecords(value, path = "/") {
449
+ const result = [];
450
+ if (Array.isArray(value)) {
451
+ value.forEach((child, index) => result.push(...artifactRecords(child, `${path}/${index}`)));
452
+ } else if (isRecord(value)) {
453
+ if (typeof value.cid === "string" && typeof value.sha256 === "string" && typeof value.uri === "string") {
454
+ result.push([value, path]);
455
+ }
456
+ for (const [key, child] of Object.entries(value)) {
457
+ result.push(...artifactRecords(child, `${path === "/" ? "" : path}/${pointerSegment2(key)}`));
458
+ }
459
+ }
460
+ return result;
461
+ }
462
+ function validateArtifactIdentity(artifact, path, document, findings) {
463
+ const declaredCid = artifact.cid;
464
+ const declaredSha = artifact.sha256;
465
+ if (typeof declaredCid !== "string" || typeof declaredSha !== "string") return;
466
+ try {
467
+ const cid = CID.parse(declaredCid);
468
+ if (cid.version !== 1 || cid.code !== 85 || cid.multihash.code !== sha256.code) {
469
+ add(
470
+ findings,
471
+ document,
472
+ "capsule-cid",
473
+ "Only CIDv1 raw sha2-256 content addresses are supported.",
474
+ `${path}/cid`
475
+ );
476
+ return;
477
+ }
478
+ if (!Buffer.from(cid.multihash.digest).equals(Buffer.from(declaredSha, "hex"))) {
479
+ add(
480
+ findings,
481
+ document,
482
+ "capsule-integrity",
483
+ "CID multihash and declared SHA-256 identify different bytes.",
484
+ path
485
+ );
486
+ }
487
+ } catch {
488
+ add(findings, document, "capsule-cid", "CID is not a valid CIDv1 value.", `${path}/cid`);
489
+ }
490
+ const uri = artifact.uri;
491
+ if (typeof uri === "string") {
492
+ try {
493
+ const parsed = new URL(uri);
494
+ if (parsed.username || parsed.password || parsed.search) {
495
+ add(
496
+ findings,
497
+ document,
498
+ "capsule-uri-policy",
499
+ "Credential-bearing and query-bearing artifact URIs are forbidden.",
500
+ `${path}/uri`
501
+ );
502
+ }
503
+ } catch {
504
+ add(findings, document, "capsule-uri-policy", "Artifact URI is invalid.", `${path}/uri`);
505
+ }
506
+ }
507
+ }
508
+ function validateExpectedIdentity(input, expected, document, findings) {
509
+ const content = bytes(input);
510
+ const actualSha = sha(content);
511
+ if (actualSha !== expected.sha256.toLowerCase() || expected.bytes !== void 0 && expected.bytes !== content.byteLength) {
512
+ add(
513
+ findings,
514
+ document,
515
+ "capsule-integrity",
516
+ "Manifest exact bytes do not match the expected SHA-256 or byte length.",
517
+ "/"
518
+ );
519
+ }
520
+ try {
521
+ const cid = CID.parse(expected.cid);
522
+ if (cid.version !== 1 || cid.code !== 85 || cid.multihash.code !== sha256.code || !Buffer.from(cid.multihash.digest).equals(Buffer.from(actualSha, "hex"))) {
523
+ add(
524
+ findings,
525
+ document,
526
+ "capsule-integrity",
527
+ "Manifest exact bytes do not match the expected CIDv1 raw sha2-256 address.",
528
+ "/"
529
+ );
530
+ }
531
+ } catch {
532
+ add(findings, document, "capsule-cid", "Expected manifest CID is invalid.", "/");
533
+ }
534
+ }
535
+ function uniqueIds(entries, path, document, findings) {
536
+ const ids = /* @__PURE__ */ new Set();
537
+ entries.forEach((entry, index) => {
538
+ if (typeof entry.id !== "string") return;
539
+ if (ids.has(entry.id))
540
+ add(
541
+ findings,
542
+ document,
543
+ "capsule-duplicate-id",
544
+ `Duplicate id ${JSON.stringify(entry.id)}.`,
545
+ `${path}/${index}/id`
546
+ );
547
+ ids.add(entry.id);
548
+ });
549
+ return ids;
550
+ }
551
+ function validateComponentGraph(root, document, findings) {
552
+ const components = records(root.components);
553
+ const ids = uniqueIds(components, "/components", document, findings);
554
+ const graph = /* @__PURE__ */ new Map();
555
+ components.forEach((component, index) => {
556
+ if (typeof component.id !== "string") return;
557
+ const dependencies = Array.isArray(component.dependencies) ? component.dependencies.filter((item) => typeof item === "string") : [];
558
+ graph.set(component.id, dependencies);
559
+ for (const dependency of dependencies) {
560
+ if (!ids.has(dependency))
561
+ add(
562
+ findings,
563
+ document,
564
+ "capsule-reference",
565
+ `Unknown component dependency ${JSON.stringify(dependency)}.`,
566
+ `/components/${index}/dependencies`
567
+ );
568
+ if (dependency === component.id)
569
+ add(
570
+ findings,
571
+ document,
572
+ "capsule-dependency-cycle",
573
+ "A component cannot depend on itself.",
574
+ `/components/${index}/dependencies`
575
+ );
576
+ }
577
+ });
578
+ const visiting = /* @__PURE__ */ new Set();
579
+ const visited = /* @__PURE__ */ new Set();
580
+ const visit = (id) => {
581
+ if (visiting.has(id)) return true;
582
+ if (visited.has(id)) return false;
583
+ visiting.add(id);
584
+ for (const dependency of graph.get(id) ?? []) {
585
+ if (graph.has(dependency) && visit(dependency)) return true;
586
+ }
587
+ visiting.delete(id);
588
+ visited.add(id);
589
+ return false;
590
+ };
591
+ for (const id of graph.keys()) {
592
+ if (visit(id)) {
593
+ add(
594
+ findings,
595
+ document,
596
+ "capsule-dependency-cycle",
597
+ "Component dependency graph contains a cycle.",
598
+ "/components"
599
+ );
600
+ break;
601
+ }
602
+ }
603
+ const toolIds = uniqueIds(records(root.tools), "/tools", document, findings);
604
+ const capabilityIds = uniqueIds(
605
+ records(root.requestedCapabilities),
606
+ "/requestedCapabilities",
607
+ document,
608
+ findings
609
+ );
610
+ void capabilityIds;
611
+ records(root.requestedCapabilities).forEach((request, index) => {
612
+ if (typeof request.tool === "string" && !toolIds.has(request.tool))
613
+ add(
614
+ findings,
615
+ document,
616
+ "capsule-reference",
617
+ `Requested capability names unknown tool ${JSON.stringify(request.tool)}.`,
618
+ `/requestedCapabilities/${index}/tool`
619
+ );
620
+ });
621
+ }
622
+ function externalChecks() {
623
+ const definitions = {
624
+ "oracle-identity-current": [
625
+ "identity",
626
+ "Resolve the current Oracle DID/IID document and revocation state."
627
+ ],
628
+ "subject-domain-current": [
629
+ "identity",
630
+ "Resolve each subject domain and its current canonical resources."
631
+ ],
632
+ "capability-current-and-unrevoked": [
633
+ "authorization",
634
+ "Verify live grants, audience, scope, expiry and revocation before each call."
635
+ ],
636
+ "trusted-time-available": [
637
+ "time",
638
+ "Evaluate time-bound policy against an approved trusted clock."
639
+ ],
640
+ "private-resource-access-authorized": [
641
+ "privacy",
642
+ "Verify consent, disclosure and decryption rights before reading private evidence."
643
+ ],
644
+ "human-review-authorized": [
645
+ "review",
646
+ "Verify the named independent reviewer and exact approved output digest."
647
+ ],
648
+ "runtime-distribution-trusted": [
649
+ "distribution",
650
+ "Verify the installed kernel and adapters against an external trust anchor."
651
+ ],
652
+ "receipt-signer-current-and-unrevoked": [
653
+ "signer",
654
+ "Verify the external receipt signer key and current revocation status."
655
+ ]
656
+ };
657
+ return ORACLE_CAPSULE_EXTERNAL_CHECKS.map((code) => ({
658
+ code,
659
+ category: definitions[code]?.[0] ?? "authorization",
660
+ reason: definitions[code]?.[1] ?? "Runtime verification is required."
661
+ }));
662
+ }
663
+ function validateSourceLock(manifest, manifestDocument, sourceLock, lockedFiles, findings) {
664
+ const parsed = parseCapsuleJson(sourceLock, {
665
+ sourceName: `${manifestDocument.sourceName}#source-lock`
666
+ });
667
+ if (!parsed.document) {
668
+ findings.push(...parsed.findings);
669
+ return;
670
+ }
671
+ const lockDocument = parsed.document;
672
+ const valid = compiled_validators_default.sourceLock(lockDocument.value);
673
+ if (!valid) {
674
+ addSchemaFindings(findings, lockDocument, compiled_validators_default.sourceLock.errors, "Source lock schema");
675
+ return;
676
+ }
677
+ if (!isRecord(lockDocument.value)) return;
678
+ const lock = lockDocument.value;
679
+ const componentId = lock.component;
680
+ const component = records(manifest.components).find((entry) => entry.id === componentId);
681
+ if (!component || !isRecord(component.artifact)) {
682
+ add(
683
+ findings,
684
+ lockDocument,
685
+ "capsule-lock",
686
+ "Source lock component does not resolve in the manifest.",
687
+ "/component"
688
+ );
689
+ return;
690
+ }
691
+ const lockBytes = bytes(sourceLock);
692
+ const sourceLockReference = component.artifact.source_lock;
693
+ if (isRecord(sourceLockReference)) {
694
+ if (sourceLockReference.sha256 !== sha(lockBytes) || sourceLockReference.bytes !== lockBytes.byteLength) {
695
+ add(
696
+ findings,
697
+ manifestDocument,
698
+ "capsule-integrity",
699
+ "Source lock exact bytes do not match the manifest reference.",
700
+ "/components"
701
+ );
702
+ }
703
+ }
704
+ const lockArtifact = lock.artifact;
705
+ if (isRecord(lockArtifact) && (lockArtifact.cid !== component.artifact.cid || lockArtifact.sha256 !== component.artifact.sha256 || lockArtifact.bytes !== component.artifact.bytes)) {
706
+ add(
707
+ findings,
708
+ lockDocument,
709
+ "capsule-lock",
710
+ "Source lock artifact identity differs from its manifest component.",
711
+ "/artifact"
712
+ );
713
+ }
714
+ const files = records(lock.files);
715
+ const paths = /* @__PURE__ */ new Set();
716
+ const portablePaths = /* @__PURE__ */ new Set();
717
+ let totalBytes = 0;
718
+ files.forEach((file, index) => {
719
+ if (typeof file.path !== "string" || typeof file.bytes !== "number" || typeof file.sha256 !== "string")
720
+ return;
721
+ if (paths.has(file.path))
722
+ add(
723
+ findings,
724
+ lockDocument,
725
+ "capsule-duplicate-path",
726
+ `Duplicate locked path ${JSON.stringify(file.path)}.`,
727
+ `/files/${index}/path`
728
+ );
729
+ const portablePath = file.path.toLowerCase();
730
+ if (portablePaths.has(portablePath) && !paths.has(file.path))
731
+ add(
732
+ findings,
733
+ lockDocument,
734
+ "capsule-path-collision",
735
+ `Locked path ${JSON.stringify(file.path)} collides on case-insensitive hosts.`,
736
+ `/files/${index}/path`
737
+ );
738
+ paths.add(file.path);
739
+ portablePaths.add(portablePath);
740
+ totalBytes += file.bytes;
741
+ if (totalBytes > ORACLE_CAPSULE_LIMITS.maxLockedBytes)
742
+ add(
743
+ findings,
744
+ lockDocument,
745
+ "capsule-limit",
746
+ `Locked files exceed ${ORACLE_CAPSULE_LIMITS.maxLockedBytes} bytes.`,
747
+ "/files"
748
+ );
749
+ if (lockedFiles) {
750
+ const content = lockedFiles[file.path];
751
+ if (content === void 0) {
752
+ add(
753
+ findings,
754
+ lockDocument,
755
+ "capsule-lock",
756
+ `Locked file ${JSON.stringify(file.path)} is missing.`,
757
+ `/files/${index}/path`
758
+ );
759
+ } else {
760
+ const contentBytes = bytes(content);
761
+ if (contentBytes.byteLength !== file.bytes || sha(contentBytes) !== file.sha256)
762
+ add(
763
+ findings,
764
+ lockDocument,
765
+ "capsule-integrity",
766
+ `Locked file ${JSON.stringify(file.path)} failed byte-length or SHA-256 verification.`,
767
+ `/files/${index}`
768
+ );
769
+ }
770
+ }
771
+ });
772
+ if (lockedFiles) {
773
+ for (const path of Object.keys(lockedFiles)) {
774
+ if (!paths.has(path))
775
+ add(
776
+ findings,
777
+ lockDocument,
778
+ "capsule-lock",
779
+ `Unlocked file ${JSON.stringify(path)} is forbidden.`,
780
+ "/files"
781
+ );
782
+ }
783
+ }
784
+ }
785
+ function validateOracleCapsule(input, options = {}) {
786
+ const parsed = parseCapsuleJson(input, options);
787
+ const checks = externalChecks();
788
+ if (!parsed.document) {
789
+ return {
790
+ tool: "@ixo/domain.md",
791
+ contract: ORACLE_CAPSULE_CONTRACT,
792
+ sourceName: options.sourceName ?? "<memory>",
793
+ status: "fail",
794
+ ok: false,
795
+ findings: parsed.findings,
796
+ externalChecksRequired: checks
797
+ };
798
+ }
799
+ const findings = [...parsed.findings];
800
+ if (options.expectedIdentity)
801
+ validateExpectedIdentity(input, options.expectedIdentity, parsed.document, findings);
802
+ const validator = compiled_validators_default.manifest;
803
+ if (!validator(parsed.document.value))
804
+ addSchemaFindings(findings, parsed.document, validator.errors, "Manifest schema");
805
+ if (isRecord(parsed.document.value)) {
806
+ for (const [artifact, path] of artifactRecords(parsed.document.value))
807
+ validateArtifactIdentity(artifact, path, parsed.document, findings);
808
+ validateComponentGraph(parsed.document.value, parsed.document, findings);
809
+ const metadata = parsed.document.value.metadata;
810
+ if (isRecord(metadata) && typeof metadata.release_digest === "string") {
811
+ try {
812
+ const actual = oracleCapsuleReleaseDigest(parsed.document.value);
813
+ if (metadata.release_digest !== actual)
814
+ add(
815
+ findings,
816
+ parsed.document,
817
+ "capsule-release-digest",
818
+ `Release digest mismatch; expected ${actual}.`,
819
+ "/metadata/release_digest"
820
+ );
821
+ } catch (error) {
822
+ add(
823
+ findings,
824
+ parsed.document,
825
+ "capsule-canonicalization",
826
+ error instanceof Error ? error.message : String(error),
827
+ "/"
828
+ );
829
+ }
830
+ }
831
+ if (options.sourceLock)
832
+ validateSourceLock(
833
+ parsed.document.value,
834
+ parsed.document,
835
+ options.sourceLock,
836
+ options.lockedFiles,
837
+ findings
838
+ );
839
+ }
840
+ const ok = !findings.some((finding2) => finding2.severity === "error");
841
+ return {
842
+ tool: "@ixo/domain.md",
843
+ contract: ORACLE_CAPSULE_CONTRACT,
844
+ sourceName: parsed.document.sourceName,
845
+ status: ok ? "static-pass" : "fail",
846
+ ok,
847
+ findings,
848
+ externalChecksRequired: checks,
849
+ manifest: parsed.document.value
850
+ };
851
+ }
852
+
853
+ export {
854
+ CapsuleCanonicalizationError,
855
+ canonicalizeCapsuleJson,
856
+ capsuleJcsSha256,
857
+ oracleCapsuleReleaseProjection,
858
+ oracleCapsuleReleaseDigest,
859
+ parseCapsuleJson,
860
+ validateOracleCapsule
861
+ };
862
+ //# sourceMappingURL=chunk-W7MRQP7I.js.map