@riddledc/riddle-proof 0.8.83 → 0.8.84

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.
@@ -20,16 +20,24 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/semantic-certificate.ts
21
21
  var semantic_certificate_exports = {};
22
22
  __export(semantic_certificate_exports, {
23
+ RIDDLE_PROOF_SEMANTIC_CERTIFICATE_CLOSURE_MAX_CERTIFICATES: () => RIDDLE_PROOF_SEMANTIC_CERTIFICATE_CLOSURE_MAX_CERTIFICATES,
24
+ RIDDLE_PROOF_SEMANTIC_CERTIFICATE_CLOSURE_VERSION: () => RIDDLE_PROOF_SEMANTIC_CERTIFICATE_CLOSURE_VERSION,
23
25
  RIDDLE_PROOF_SEMANTIC_CERTIFICATE_VERSION: () => RIDDLE_PROOF_SEMANTIC_CERTIFICATE_VERSION,
26
+ composeRiddleProofSemanticCertificateClosures: () => composeRiddleProofSemanticCertificateClosures,
24
27
  composeRiddleProofSemanticCertificates: () => composeRiddleProofSemanticCertificates,
28
+ createRiddleProofSemanticAtomicCertificateClosure: () => createRiddleProofSemanticAtomicCertificateClosure,
25
29
  createRiddleProofSemanticCertificate: () => createRiddleProofSemanticCertificate,
26
30
  matchRiddleProofSemanticCertificate: () => matchRiddleProofSemanticCertificate,
31
+ matchRiddleProofSemanticCertificateClosure: () => matchRiddleProofSemanticCertificateClosure,
27
32
  parseRiddleProofSemanticCertificate: () => parseRiddleProofSemanticCertificate,
28
- riddleProofSemanticScopesEqual: () => riddleProofSemanticScopesEqual
33
+ riddleProofSemanticScopesEqual: () => riddleProofSemanticScopesEqual,
34
+ validateRiddleProofSemanticCertificateClosure: () => validateRiddleProofSemanticCertificateClosure
29
35
  });
30
36
  module.exports = __toCommonJS(semantic_certificate_exports);
31
37
  var import_node_crypto = require("crypto");
32
38
  var RIDDLE_PROOF_SEMANTIC_CERTIFICATE_VERSION = "riddle-proof.semantic-certificate.v0";
39
+ var RIDDLE_PROOF_SEMANTIC_CERTIFICATE_CLOSURE_VERSION = "riddle-proof.semantic-certificate-closure.v0";
40
+ var RIDDLE_PROOF_SEMANTIC_CERTIFICATE_CLOSURE_MAX_CERTIFICATES = 4096;
33
41
  var SCOPE_FIELDS = [
34
42
  "repository",
35
43
  "revision",
@@ -61,7 +69,9 @@ var CERTIFICATE_FIELDS = /* @__PURE__ */ new Set([
61
69
  "issued_at"
62
70
  ]);
63
71
  function isRecord(value) {
64
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
72
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
73
+ const prototype = Object.getPrototypeOf(value);
74
+ return prototype === Object.prototype || prototype === null;
65
75
  }
66
76
  function safeErrorMessage(error) {
67
77
  try {
@@ -76,51 +86,129 @@ function safeErrorMessage(error) {
76
86
  }
77
87
  function assertOnlyKeys(record, allowed, context) {
78
88
  const allowedSet = new Set(allowed);
79
- for (const key of Object.keys(record)) {
89
+ for (const key of Reflect.ownKeys(record)) {
90
+ if (typeof key !== "string") {
91
+ throw new Error(`${context} contains an unsupported symbol field.`);
92
+ }
80
93
  if (!allowedSet.has(key)) {
81
94
  throw new Error(`${context} contains unsupported field ${key}.`);
82
95
  }
96
+ const descriptor = Object.getOwnPropertyDescriptor(record, key);
97
+ if (!descriptor || descriptor.enumerable !== true || descriptor.get !== void 0 || descriptor.set !== void 0) {
98
+ throw new Error(`${context}.${key} must be an enumerable data field.`);
99
+ }
100
+ }
101
+ }
102
+ function readDenseDataArray(value, context, maximumLength) {
103
+ if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype) {
104
+ throw new Error(`${context} must be a plain array.`);
105
+ }
106
+ const lengthDescriptor = Object.getOwnPropertyDescriptor(value, "length");
107
+ if (!lengthDescriptor || typeof lengthDescriptor.value !== "number" || !Number.isSafeInteger(lengthDescriptor.value) || lengthDescriptor.value < 0) {
108
+ throw new Error(`${context}.length must be an own data field.`);
109
+ }
110
+ const length = lengthDescriptor.value;
111
+ if (maximumLength !== void 0 && length > maximumLength) {
112
+ throw new Error(`${context} exceeds ${maximumLength} entries.`);
113
+ }
114
+ const indexedElements = [];
115
+ let elementCount = 0;
116
+ for (const key of Reflect.ownKeys(value)) {
117
+ if (key === "length") continue;
118
+ if (typeof key !== "string") {
119
+ throw new Error(`${context} contains an unsupported symbol field.`);
120
+ }
121
+ const index = Number(key);
122
+ if (!Number.isInteger(index) || index < 0 || index >= length || index >= 2 ** 32 - 1 || String(index) !== key) {
123
+ throw new Error(`${context} contains unsupported array field ${key}.`);
124
+ }
125
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
126
+ if (!descriptor || descriptor.enumerable !== true || descriptor.get !== void 0 || descriptor.set !== void 0) {
127
+ throw new Error(`${context}[${key}] must be an enumerable data field.`);
128
+ }
129
+ indexedElements.push([index, descriptor.value]);
130
+ elementCount += 1;
131
+ }
132
+ if (elementCount !== length) {
133
+ throw new Error(`${context} must not contain sparse or inherited entries.`);
134
+ }
135
+ indexedElements.sort(([left], [right]) => left - right);
136
+ return indexedElements.map(([, entry]) => entry);
137
+ }
138
+ function requiredField(record, key, context) {
139
+ const descriptor = Object.getOwnPropertyDescriptor(record, key);
140
+ if (!descriptor) {
141
+ throw new Error(`${context}.${key} is required.`);
142
+ }
143
+ if (descriptor.enumerable !== true || descriptor.get !== void 0 || descriptor.set !== void 0) {
144
+ throw new Error(`${context}.${key} must be an enumerable data field.`);
83
145
  }
146
+ return descriptor.value;
147
+ }
148
+ function optionalField(record, key) {
149
+ const descriptor = Object.getOwnPropertyDescriptor(record, key);
150
+ if (!descriptor) return void 0;
151
+ if (descriptor.enumerable !== true || descriptor.get !== void 0 || descriptor.set !== void 0) {
152
+ throw new Error(`${key} must be an enumerable data field.`);
153
+ }
154
+ return descriptor.value;
84
155
  }
85
156
  function requiredString(record, key, context) {
86
- const value = record[key];
157
+ const value = requiredField(record, key, context);
87
158
  if (typeof value !== "string" || !value.trim()) {
88
159
  throw new Error(`${context}.${key} must be a non-empty string.`);
89
160
  }
90
161
  return value.trim();
91
162
  }
92
163
  function optionalString(record, key, context) {
93
- if (record[key] === void 0) return void 0;
164
+ if (optionalField(record, key) === void 0) return void 0;
94
165
  return requiredString(record, key, context);
95
166
  }
96
- function isJsonValue(value, ancestors = /* @__PURE__ */ new Set()) {
167
+ function cloneJsonValue(value, context, ancestors = /* @__PURE__ */ new Set()) {
97
168
  if (value === null || typeof value === "string" || typeof value === "boolean") {
98
- return true;
169
+ return value;
170
+ }
171
+ if (typeof value === "number") {
172
+ if (!Number.isFinite(value)) throw new Error(`${context} must contain only finite numbers.`);
173
+ return value;
99
174
  }
100
- if (typeof value === "number") return Number.isFinite(value);
101
175
  if (Array.isArray(value)) {
102
- if (ancestors.has(value)) return false;
176
+ const elements = readDenseDataArray(value, context);
177
+ if (ancestors.has(value)) throw new Error(`${context} must not be cyclic.`);
103
178
  ancestors.add(value);
104
- const valid2 = value.every((entry) => isJsonValue(entry, ancestors));
179
+ const cloned2 = elements.map((entry, index) => cloneJsonValue(entry, `${context}[${index}]`, ancestors));
105
180
  ancestors.delete(value);
106
- return valid2;
181
+ return cloned2;
107
182
  }
108
- if (!isRecord(value)) return false;
109
- const prototype = Object.getPrototypeOf(value);
110
- if (prototype !== Object.prototype && prototype !== null) return false;
111
- if (ancestors.has(value)) return false;
183
+ if (!isRecord(value)) throw new Error(`${context} must contain only JSON values.`);
184
+ if (ancestors.has(value)) throw new Error(`${context} must not be cyclic.`);
112
185
  ancestors.add(value);
113
- const valid = Object.values(value).every((entry) => isJsonValue(entry, ancestors));
186
+ const cloned = {};
187
+ for (const key of Reflect.ownKeys(value)) {
188
+ if (typeof key !== "string") {
189
+ throw new Error(`${context} contains an unsupported symbol field.`);
190
+ }
191
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
192
+ if (!descriptor || descriptor.enumerable !== true || descriptor.get !== void 0 || descriptor.set !== void 0) {
193
+ throw new Error(`${context}.${key} must be an enumerable data field.`);
194
+ }
195
+ Object.defineProperty(cloned, key, {
196
+ value: cloneJsonValue(descriptor.value, `${context}.${key}`, ancestors),
197
+ enumerable: true,
198
+ configurable: true,
199
+ writable: true
200
+ });
201
+ }
114
202
  ancestors.delete(value);
115
- return valid;
203
+ return cloned;
116
204
  }
117
205
  function parseJsonObject(value, context) {
118
206
  if (value === void 0) return void 0;
119
- if (!isRecord(value) || !isJsonValue(value)) {
207
+ if (!isRecord(value)) {
120
208
  throw new Error(`${context} must be a JSON object.`);
121
209
  }
122
- const cloned = JSON.parse(JSON.stringify(value));
123
- if (!isRecord(cloned) || !isJsonValue(cloned)) {
210
+ const cloned = cloneJsonValue(value, context);
211
+ if (!isRecord(cloned)) {
124
212
  throw new Error(`${context} must remain a JSON object when serialized.`);
125
213
  }
126
214
  return cloned;
@@ -145,7 +233,7 @@ function parseScope(value, context) {
145
233
  function parseClaimRef(value, context, allowedExtras = []) {
146
234
  if (!isRecord(value)) throw new Error(`${context} must be an object.`);
147
235
  assertOnlyKeys(value, ["claim_id", "claim_version", "parameters", ...allowedExtras], context);
148
- const parameters = parseJsonObject(value.parameters, `${context}.parameters`);
236
+ const parameters = parseJsonObject(optionalField(value, "parameters"), `${context}.parameters`);
149
237
  return {
150
238
  claim_id: requiredString(value, "claim_id", context),
151
239
  claim_version: requiredString(value, "claim_version", context),
@@ -181,10 +269,11 @@ function parseEvidenceRef(value, context) {
181
269
  };
182
270
  }
183
271
  function parseEvidenceBundle(value, context) {
184
- if (!Array.isArray(value) || value.length === 0) {
272
+ const entries = readDenseDataArray(value, context);
273
+ if (entries.length === 0) {
185
274
  throw new Error(`${context} must contain at least one evidence reference.`);
186
275
  }
187
- return value.map((entry, index) => parseEvidenceRef(entry, `${context}[${index}]`));
276
+ return entries.map((entry, index) => parseEvidenceRef(entry, `${context}[${index}]`));
188
277
  }
189
278
  function parseContractRef(value, context) {
190
279
  if (!isRecord(value)) throw new Error(`${context} must be an object.`);
@@ -201,30 +290,32 @@ function parseContract(value, context, allowRuntimePredicate = false) {
201
290
  ["contract_id", "contract_version", "label", "claim", ...allowRuntimePredicate ? ["accepts"] : []],
202
291
  context
203
292
  );
204
- if (allowRuntimePredicate && typeof value.accepts !== "function") {
293
+ if (allowRuntimePredicate && typeof requiredField(value, "accepts", context) !== "function") {
205
294
  throw new Error(`${context}.accepts must be a function.`);
206
295
  }
207
296
  return {
208
297
  ...parseContractRef(value, context),
209
- claim: parseClaim(value.claim, `${context}.claim`)
298
+ claim: parseClaim(requiredField(value, "claim", context), `${context}.claim`)
210
299
  };
211
300
  }
212
301
  function parseRule(value, context, allowFullPremises = false) {
213
302
  if (!isRecord(value)) throw new Error(`${context} must be an object.`);
214
303
  assertOnlyKeys(value, ["rule_id", "rule_version", "label", "premises", "conclusion"], context);
215
- if (!Array.isArray(value.premises) || value.premises.length === 0) {
304
+ const premises = requiredField(value, "premises", context);
305
+ const premiseValues = readDenseDataArray(premises, `${context}.premises`);
306
+ if (premiseValues.length === 0) {
216
307
  throw new Error(`${context}.premises must contain at least one claim reference.`);
217
308
  }
218
309
  return {
219
310
  rule_id: requiredString(value, "rule_id", context),
220
311
  rule_version: requiredString(value, "rule_version", context),
221
312
  label: requiredString(value, "label", context),
222
- premises: value.premises.map((premise, index) => parseClaimRef(
313
+ premises: premiseValues.map((premise, index) => parseClaimRef(
223
314
  premise,
224
315
  `${context}.premises[${index}]`,
225
316
  allowFullPremises ? ["label"] : []
226
317
  )),
227
- conclusion: parseClaim(value.conclusion, `${context}.conclusion`)
318
+ conclusion: parseClaim(requiredField(value, "conclusion", context), `${context}.conclusion`)
228
319
  };
229
320
  }
230
321
  function parsePremise(value, context) {
@@ -244,9 +335,9 @@ function parsePremise(value, context) {
244
335
  certificate_id: requiredString(value, "certificate_id", context),
245
336
  derivation_kind: derivationKind,
246
337
  assurance,
247
- scope: parseScope(value.scope, `${context}.scope`),
248
- claim: parseClaim(value.claim, `${context}.claim`),
249
- evidence: parseEvidenceBundle(value.evidence, `${context}.evidence`)
338
+ scope: parseScope(requiredField(value, "scope", context), `${context}.scope`),
339
+ claim: parseClaim(requiredField(value, "claim", context), `${context}.claim`),
340
+ evidence: parseEvidenceBundle(requiredField(value, "evidence", context), `${context}.evidence`)
250
341
  };
251
342
  }
252
343
  function stableJson(value) {
@@ -276,28 +367,30 @@ function parseDerivation(value, context) {
276
367
  const kind = requiredString(value, "kind", context);
277
368
  if (kind === "contract") {
278
369
  assertOnlyKeys(value, ["kind", "assurance", "contract"], context);
279
- if (value.assurance !== "runtime_contract_accepted") {
370
+ if (requiredField(value, "assurance", context) !== "runtime_contract_accepted") {
280
371
  throw new Error(`${context}.assurance must be runtime_contract_accepted.`);
281
372
  }
282
373
  return {
283
374
  kind,
284
375
  assurance: "runtime_contract_accepted",
285
- contract: parseContract(value.contract, `${context}.contract`)
376
+ contract: parseContract(requiredField(value, "contract", context), `${context}.contract`)
286
377
  };
287
378
  }
288
379
  if (kind === "composition") {
289
380
  assertOnlyKeys(value, ["kind", "assurance", "rule", "premises"], context);
290
- if (value.assurance !== "declared_runtime_rule") {
381
+ if (requiredField(value, "assurance", context) !== "declared_runtime_rule") {
291
382
  throw new Error(`${context}.assurance must be declared_runtime_rule.`);
292
383
  }
293
- if (!Array.isArray(value.premises) || value.premises.length === 0) {
384
+ const premises = requiredField(value, "premises", context);
385
+ const premiseValues = readDenseDataArray(premises, `${context}.premises`);
386
+ if (premiseValues.length === 0) {
294
387
  throw new Error(`${context}.premises must contain at least one certificate premise.`);
295
388
  }
296
389
  return {
297
390
  kind,
298
391
  assurance: "declared_runtime_rule",
299
- rule: parseRule(value.rule, `${context}.rule`),
300
- premises: value.premises.map((premise, index) => parsePremise(premise, `${context}.premises[${index}]`))
392
+ rule: parseRule(requiredField(value, "rule", context), `${context}.rule`),
393
+ premises: premiseValues.map((premise, index) => parsePremise(premise, `${context}.premises[${index}]`))
301
394
  };
302
395
  }
303
396
  throw new Error(`${context}.kind must be contract or composition.`);
@@ -312,9 +405,25 @@ function createRiddleProofSemanticCertificate(input) {
312
405
  ["scope", "evidence", "observation", "contract", "issued_at"],
313
406
  "semantic certificate input"
314
407
  );
315
- const scope = parseScope(input.scope, "semantic certificate scope");
316
- const evidence = parseEvidenceBundle(input.evidence, "semantic certificate evidence");
317
- const contract = parseContract(input.contract, "semantic certificate contract", true);
408
+ const scope = parseScope(requiredField(input, "scope", "semantic certificate input"), "semantic certificate scope");
409
+ const evidence = parseEvidenceBundle(
410
+ requiredField(input, "evidence", "semantic certificate input"),
411
+ "semantic certificate evidence"
412
+ );
413
+ const runtimeContract = requiredField(
414
+ input,
415
+ "contract",
416
+ "semantic certificate input"
417
+ );
418
+ const contract = parseContract(runtimeContract, "semantic certificate contract", true);
419
+ const accepts = requiredField(
420
+ runtimeContract,
421
+ "accepts",
422
+ "semantic certificate contract"
423
+ );
424
+ if (typeof accepts !== "function") {
425
+ throw new Error("semantic certificate contract.accepts must be a function.");
426
+ }
318
427
  const contractRef = {
319
428
  contract_id: contract.contract_id,
320
429
  contract_version: contract.contract_version,
@@ -323,14 +432,17 @@ function createRiddleProofSemanticCertificate(input) {
323
432
  const claim = contract.claim;
324
433
  let accepted;
325
434
  try {
326
- accepted = input.contract.accepts({ ...scope }, input.observation) === true;
435
+ accepted = accepts(
436
+ { ...scope },
437
+ requiredField(input, "observation", "semantic certificate input")
438
+ ) === true;
327
439
  } catch (error) {
328
440
  return {
329
441
  ok: false,
330
442
  error: {
331
443
  code: "contract_error",
332
444
  contract: contractRef,
333
- message: `Semantic contract evaluation failed: ${error instanceof Error ? error.message : String(error)}.`
445
+ message: `Semantic contract evaluation failed: ${safeErrorMessage(error)}.`
334
446
  }
335
447
  };
336
448
  }
@@ -350,29 +462,35 @@ function createRiddleProofSemanticCertificate(input) {
350
462
  claim,
351
463
  evidence,
352
464
  derivation: { kind: "contract", assurance: "runtime_contract_accepted", contract },
353
- issued_at: parseIssuedAt(input.issued_at || (/* @__PURE__ */ new Date()).toISOString(), "semantic certificate issued_at")
465
+ issued_at: parseIssuedAt(
466
+ optionalField(input, "issued_at") || (/* @__PURE__ */ new Date()).toISOString(),
467
+ "semantic certificate issued_at"
468
+ )
354
469
  };
355
470
  return { ok: true, certificate: withCertificateId(body) };
356
471
  }
357
472
  function parseRiddleProofSemanticCertificate(value) {
358
473
  if (!isRecord(value)) throw new Error("Semantic certificate must be an object.");
359
- if (value.version !== RIDDLE_PROOF_SEMANTIC_CERTIFICATE_VERSION) {
360
- throw new Error(`Unsupported Semantic certificate version ${String(value.version || "missing")}.`);
474
+ const version = optionalField(value, "version");
475
+ if (version !== RIDDLE_PROOF_SEMANTIC_CERTIFICATE_VERSION) {
476
+ throw new Error(`Unsupported Semantic certificate version ${String(version || "missing")}.`);
361
477
  }
362
478
  for (const field of AUTHORITY_FIELDS) {
363
479
  if (Object.prototype.hasOwnProperty.call(value, field)) {
364
480
  throw new Error(`Semantic certificate must not contain authority field ${field}.`);
365
481
  }
366
482
  }
367
- for (const field of Object.keys(value)) {
368
- if (!CERTIFICATE_FIELDS.has(field)) {
369
- throw new Error(`Semantic certificate contains unsupported field ${field}.`);
370
- }
371
- }
372
- const scope = parseScope(value.scope, "semantic certificate scope");
373
- const claim = parseClaim(value.claim, "semantic certificate claim");
374
- const evidence = parseEvidenceBundle(value.evidence, "semantic certificate evidence");
375
- const derivation = parseDerivation(value.derivation, "semantic certificate derivation");
483
+ assertOnlyKeys(value, [...CERTIFICATE_FIELDS], "Semantic certificate");
484
+ const scope = parseScope(requiredField(value, "scope", "semantic certificate"), "semantic certificate scope");
485
+ const claim = parseClaim(requiredField(value, "claim", "semantic certificate"), "semantic certificate claim");
486
+ const evidence = parseEvidenceBundle(
487
+ requiredField(value, "evidence", "semantic certificate"),
488
+ "semantic certificate evidence"
489
+ );
490
+ const derivation = parseDerivation(
491
+ requiredField(value, "derivation", "semantic certificate"),
492
+ "semantic certificate derivation"
493
+ );
376
494
  if (derivation.kind === "contract" && !sameClaimRef(claim, derivation.contract.claim)) {
377
495
  throw new Error("Semantic contract-derived claim must match its contract claim.");
378
496
  }
@@ -402,7 +520,10 @@ function parseRiddleProofSemanticCertificate(value) {
402
520
  claim,
403
521
  evidence,
404
522
  derivation,
405
- issued_at: parseIssuedAt(value.issued_at, "semantic certificate issued_at")
523
+ issued_at: parseIssuedAt(
524
+ requiredField(value, "issued_at", "semantic certificate"),
525
+ "semantic certificate issued_at"
526
+ )
406
527
  };
407
528
  const observedId = requiredString(value, "certificate_id", "semantic certificate");
408
529
  const expectedId = certificateId(body);
@@ -412,7 +533,7 @@ function parseRiddleProofSemanticCertificate(value) {
412
533
  return { ...body, certificate_id: observedId };
413
534
  }
414
535
  function matchRiddleProofSemanticCertificate(input) {
415
- if (!isRecord(input)) throw new Error("Semantic certificate match input must be an object.");
536
+ if (!isRecord(input)) throw new Error("Semantic certificate match input must be a plain object.");
416
537
  assertOnlyKeys(
417
538
  input,
418
539
  [
@@ -435,11 +556,11 @@ function matchRiddleProofSemanticCertificate(input) {
435
556
  );
436
557
  }
437
558
  const expectedScope = parseScope(
438
- input.expected_scope,
559
+ requiredField(input, "expected_scope", "semantic certificate match input"),
439
560
  "semantic certificate match expected_scope"
440
561
  );
441
562
  const expectedClaim = parseClaimRef(
442
- input.expected_claim,
563
+ requiredField(input, "expected_claim", "semantic certificate match input"),
443
564
  "semantic certificate match expected_claim",
444
565
  ["label"]
445
566
  );
@@ -455,7 +576,9 @@ function matchRiddleProofSemanticCertificate(input) {
455
576
  }
456
577
  let certificate;
457
578
  try {
458
- certificate = parseRiddleProofSemanticCertificate(input.certificate);
579
+ certificate = parseRiddleProofSemanticCertificate(
580
+ requiredField(input, "certificate", "semantic certificate match input")
581
+ );
459
582
  } catch (error) {
460
583
  return {
461
584
  ok: false,
@@ -540,11 +663,20 @@ function composeRiddleProofSemanticCertificates(input) {
540
663
  ["rule", "certificates", "issued_at"],
541
664
  "semantic composition input"
542
665
  );
543
- const rule = parseRule(input.rule, "semantic composition rule", true);
544
- if (!Array.isArray(input.certificates) || input.certificates.length === 0) {
666
+ const rule = parseRule(
667
+ requiredField(input, "rule", "semantic composition input"),
668
+ "semantic composition rule",
669
+ true
670
+ );
671
+ const inputCertificates = requiredField(input, "certificates", "semantic composition input");
672
+ const certificateValues = readDenseDataArray(
673
+ inputCertificates,
674
+ "semantic composition input.certificates"
675
+ );
676
+ if (certificateValues.length === 0) {
545
677
  throw new Error("Semantic composition requires at least one certificate.");
546
678
  }
547
- const certificates = input.certificates.map((certificate) => parseRiddleProofSemanticCertificate(certificate));
679
+ const certificates = certificateValues.map((certificate) => parseRiddleProofSemanticCertificate(certificate));
548
680
  if (certificates.length !== rule.premises.length) {
549
681
  return {
550
682
  ok: false,
@@ -599,16 +731,391 @@ function composeRiddleProofSemanticCertificates(input) {
599
731
  rule,
600
732
  premises: certificates.map(premiseFromCertificate)
601
733
  },
602
- issued_at: parseIssuedAt(input.issued_at || (/* @__PURE__ */ new Date()).toISOString(), "semantic certificate issued_at")
734
+ issued_at: parseIssuedAt(
735
+ optionalField(input, "issued_at") || (/* @__PURE__ */ new Date()).toISOString(),
736
+ "semantic certificate issued_at"
737
+ )
603
738
  };
604
739
  return { ok: true, certificate: withCertificateId(body) };
605
740
  }
741
+ function invalidClosure(message) {
742
+ return { ok: false, error: { code: "invalid_closure", message } };
743
+ }
744
+ function firstPremiseSnapshotMismatch(snapshot, certificate) {
745
+ const observed = premiseFromCertificate(certificate);
746
+ if (snapshot.derivation_kind !== observed.derivation_kind) return "derivation_kind";
747
+ if (snapshot.assurance !== observed.assurance) return "assurance";
748
+ if (stableJson(snapshot.scope) !== stableJson(observed.scope)) return "scope";
749
+ if (stableJson(snapshot.claim) !== stableJson(observed.claim)) return "claim";
750
+ if (stableJson(snapshot.evidence) !== stableJson(observed.evidence)) return "evidence";
751
+ return void 0;
752
+ }
753
+ function validateSemanticCertificateClosureInternal(value) {
754
+ if (!isRecord(value)) {
755
+ return invalidClosure("Semantic certificate closure must be a plain object.");
756
+ }
757
+ assertOnlyKeys(
758
+ value,
759
+ ["version", "root_certificate_id", "certificates"],
760
+ "Semantic certificate closure"
761
+ );
762
+ if (requiredField(value, "version", "Semantic certificate closure") !== RIDDLE_PROOF_SEMANTIC_CERTIFICATE_CLOSURE_VERSION) {
763
+ return invalidClosure("Unsupported Semantic certificate closure version.");
764
+ }
765
+ const rootCertificateId = requiredString(
766
+ value,
767
+ "root_certificate_id",
768
+ "Semantic certificate closure"
769
+ );
770
+ if (!/^rpsc_[0-9a-f]{64}$/u.test(rootCertificateId)) {
771
+ return invalidClosure(
772
+ "Semantic certificate closure.root_certificate_id must be a full rpsc content ID."
773
+ );
774
+ }
775
+ const inputCertificates = requiredField(
776
+ value,
777
+ "certificates",
778
+ "Semantic certificate closure"
779
+ );
780
+ const certificateValues = readDenseDataArray(
781
+ inputCertificates,
782
+ "Semantic certificate closure.certificates",
783
+ RIDDLE_PROOF_SEMANTIC_CERTIFICATE_CLOSURE_MAX_CERTIFICATES
784
+ );
785
+ if (certificateValues.length === 0) {
786
+ return invalidClosure("Semantic certificate closure must contain at least one certificate.");
787
+ }
788
+ const certificates = [];
789
+ for (let index = 0; index < certificateValues.length; index += 1) {
790
+ try {
791
+ certificates.push(parseRiddleProofSemanticCertificate(certificateValues[index]));
792
+ } catch (error) {
793
+ return {
794
+ ok: false,
795
+ error: {
796
+ code: "invalid_closure_certificate",
797
+ input_index: index,
798
+ message: `Semantic certificate closure certificate ${index} did not parse: ${safeErrorMessage(error)}`
799
+ }
800
+ };
801
+ }
802
+ }
803
+ const certificatesById = /* @__PURE__ */ new Map();
804
+ const firstIndexById = /* @__PURE__ */ new Map();
805
+ for (let index = 0; index < certificates.length; index += 1) {
806
+ const certificate = certificates[index];
807
+ const firstIndex = firstIndexById.get(certificate.certificate_id);
808
+ if (firstIndex !== void 0) {
809
+ return {
810
+ ok: false,
811
+ error: {
812
+ code: "duplicate_certificate_id",
813
+ certificate_id: certificate.certificate_id,
814
+ first_index: firstIndex,
815
+ duplicate_index: index,
816
+ message: `Semantic certificate closure repeats certificate ${certificate.certificate_id}.`
817
+ }
818
+ };
819
+ }
820
+ firstIndexById.set(certificate.certificate_id, index);
821
+ certificatesById.set(certificate.certificate_id, certificate);
822
+ }
823
+ const rootCertificate = certificatesById.get(rootCertificateId);
824
+ if (!rootCertificate) {
825
+ return {
826
+ ok: false,
827
+ error: {
828
+ code: "root_certificate_missing",
829
+ root_certificate_id: rootCertificateId,
830
+ message: "Semantic certificate closure does not contain its declared root certificate."
831
+ }
832
+ };
833
+ }
834
+ const states = /* @__PURE__ */ new Map();
835
+ const stack = [{ certificate: rootCertificate, next_premise_index: 0 }];
836
+ const dependencyFirst = [];
837
+ states.set(rootCertificateId, "visiting");
838
+ while (stack.length > 0) {
839
+ const frame = stack[stack.length - 1];
840
+ const premises = frame.certificate.derivation.kind === "composition" ? frame.certificate.derivation.premises : [];
841
+ if (frame.next_premise_index < premises.length) {
842
+ const premiseIndex = frame.next_premise_index;
843
+ frame.next_premise_index += 1;
844
+ const snapshot = premises[premiseIndex];
845
+ const child = certificatesById.get(snapshot.certificate_id);
846
+ if (!child) {
847
+ return {
848
+ ok: false,
849
+ error: {
850
+ code: "dangling_premise",
851
+ parent_certificate_id: frame.certificate.certificate_id,
852
+ premise_index: premiseIndex,
853
+ premise_certificate_id: snapshot.certificate_id,
854
+ message: `Semantic certificate ${frame.certificate.certificate_id} premise ${premiseIndex} has no full certificate body.`
855
+ }
856
+ };
857
+ }
858
+ const mismatch = firstPremiseSnapshotMismatch(snapshot, child);
859
+ if (mismatch) {
860
+ return {
861
+ ok: false,
862
+ error: {
863
+ code: "premise_snapshot_mismatch",
864
+ parent_certificate_id: frame.certificate.certificate_id,
865
+ premise_index: premiseIndex,
866
+ premise_certificate_id: snapshot.certificate_id,
867
+ field: mismatch,
868
+ message: `Semantic certificate ${frame.certificate.certificate_id} premise ${premiseIndex} has a different ${mismatch} than its full certificate body.`
869
+ }
870
+ };
871
+ }
872
+ const childState = states.get(child.certificate_id);
873
+ if (childState === "visiting") {
874
+ const cycleStart = stack.findIndex(
875
+ (candidate) => candidate.certificate.certificate_id === child.certificate_id
876
+ );
877
+ const certificateIds = stack.slice(Math.max(0, cycleStart)).map((candidate) => candidate.certificate.certificate_id);
878
+ certificateIds.push(child.certificate_id);
879
+ return {
880
+ ok: false,
881
+ error: {
882
+ code: "certificate_cycle",
883
+ certificate_ids: certificateIds,
884
+ message: "Semantic certificate closure contains a certificate cycle."
885
+ }
886
+ };
887
+ }
888
+ if (childState !== "visited") {
889
+ states.set(child.certificate_id, "visiting");
890
+ stack.push({ certificate: child, next_premise_index: 0 });
891
+ }
892
+ continue;
893
+ }
894
+ stack.pop();
895
+ states.set(frame.certificate.certificate_id, "visited");
896
+ dependencyFirst.push(frame.certificate);
897
+ }
898
+ if (dependencyFirst.length !== certificates.length) {
899
+ const reachable = new Set(dependencyFirst.map((certificate) => certificate.certificate_id));
900
+ const unreachable = certificates.filter((certificate) => !reachable.has(certificate.certificate_id)).map((certificate) => certificate.certificate_id).sort();
901
+ return {
902
+ ok: false,
903
+ error: {
904
+ code: "unreachable_certificates",
905
+ certificate_ids: unreachable,
906
+ message: "Semantic certificate closure contains certificates unreachable from its root."
907
+ }
908
+ };
909
+ }
910
+ const closure = {
911
+ version: RIDDLE_PROOF_SEMANTIC_CERTIFICATE_CLOSURE_VERSION,
912
+ root_certificate_id: rootCertificateId,
913
+ certificates: dependencyFirst
914
+ };
915
+ return { ok: true, closure, root_certificate: rootCertificate };
916
+ }
917
+ function validateRiddleProofSemanticCertificateClosure(value) {
918
+ try {
919
+ return validateSemanticCertificateClosureInternal(value);
920
+ } catch (error) {
921
+ return invalidClosure(
922
+ `Semantic certificate closure did not parse: ${safeErrorMessage(error)}`
923
+ );
924
+ }
925
+ }
926
+ function createRiddleProofSemanticAtomicCertificateClosure(input) {
927
+ if (!isRecord(input)) throw new Error("Semantic certificate closure input must be a plain object.");
928
+ assertOnlyKeys(input, ["certificate"], "Semantic certificate closure input");
929
+ const certificate = requiredField(input, "certificate", "Semantic certificate closure input");
930
+ let parsedCertificate;
931
+ try {
932
+ parsedCertificate = parseRiddleProofSemanticCertificate(certificate);
933
+ } catch (error) {
934
+ return {
935
+ ok: false,
936
+ error: {
937
+ code: "invalid_closure_certificate",
938
+ input_index: 0,
939
+ message: `Semantic certificate closure certificate 0 did not parse: ${safeErrorMessage(error)}`
940
+ }
941
+ };
942
+ }
943
+ if (parsedCertificate.derivation.kind !== "contract") {
944
+ return invalidClosure(
945
+ "An atomic Semantic certificate closure requires a contract-derived certificate."
946
+ );
947
+ }
948
+ return validateRiddleProofSemanticCertificateClosure({
949
+ version: RIDDLE_PROOF_SEMANTIC_CERTIFICATE_CLOSURE_VERSION,
950
+ root_certificate_id: parsedCertificate.certificate_id,
951
+ certificates: [certificate]
952
+ });
953
+ }
954
+ function composeRiddleProofSemanticCertificateClosures(input) {
955
+ if (!isRecord(input)) throw new Error("Semantic closure composition input must be a plain object.");
956
+ assertOnlyKeys(
957
+ input,
958
+ ["rule", "closures", "issued_at"],
959
+ "Semantic closure composition input"
960
+ );
961
+ const inputClosures = requiredField(
962
+ input,
963
+ "closures",
964
+ "Semantic closure composition input"
965
+ );
966
+ const closureValues = readDenseDataArray(
967
+ inputClosures,
968
+ "Semantic closure composition input.closures"
969
+ );
970
+ if (closureValues.length === 0) {
971
+ throw new Error("Semantic closure composition requires at least one closure.");
972
+ }
973
+ const validatedClosures = [];
974
+ for (let index = 0; index < closureValues.length; index += 1) {
975
+ const result = validateRiddleProofSemanticCertificateClosure(closureValues[index]);
976
+ if (!result.ok) {
977
+ return {
978
+ ok: false,
979
+ error: {
980
+ code: "input_closure_invalid",
981
+ input_index: index,
982
+ cause: result.error,
983
+ message: `Semantic input closure ${index} is invalid: ${result.error.message}`
984
+ }
985
+ };
986
+ }
987
+ validatedClosures.push(result);
988
+ }
989
+ const roots = validatedClosures.map((result) => {
990
+ if (!result.ok) throw new Error("Validated Semantic closure unexpectedly became invalid.");
991
+ return result.root_certificate;
992
+ });
993
+ const composition = composeRiddleProofSemanticCertificates({
994
+ rule: requiredField(input, "rule", "Semantic closure composition input"),
995
+ certificates: roots,
996
+ issued_at: optionalField(input, "issued_at")
997
+ });
998
+ if (!composition.ok) return composition;
999
+ const merged = [];
1000
+ const byId = /* @__PURE__ */ new Map();
1001
+ for (const result of validatedClosures) {
1002
+ if (!result.ok) continue;
1003
+ for (const certificate of result.closure.certificates) {
1004
+ const existing = byId.get(certificate.certificate_id);
1005
+ if (existing) {
1006
+ if (stableJson(existing) !== stableJson(certificate)) {
1007
+ return {
1008
+ ok: false,
1009
+ error: {
1010
+ code: "certificate_id_collision",
1011
+ certificate_id: certificate.certificate_id,
1012
+ message: `Semantic closures contain unequal bodies for ${certificate.certificate_id}.`
1013
+ }
1014
+ };
1015
+ }
1016
+ continue;
1017
+ }
1018
+ byId.set(certificate.certificate_id, certificate);
1019
+ merged.push(certificate);
1020
+ }
1021
+ }
1022
+ if (byId.has(composition.certificate.certificate_id)) {
1023
+ return {
1024
+ ok: false,
1025
+ error: {
1026
+ code: "certificate_id_collision",
1027
+ certificate_id: composition.certificate.certificate_id,
1028
+ message: "Semantic composition produced a root ID already present in its premise closures."
1029
+ }
1030
+ };
1031
+ }
1032
+ merged.push(composition.certificate);
1033
+ const validation = validateRiddleProofSemanticCertificateClosure({
1034
+ version: RIDDLE_PROOF_SEMANTIC_CERTIFICATE_CLOSURE_VERSION,
1035
+ root_certificate_id: composition.certificate.certificate_id,
1036
+ certificates: merged
1037
+ });
1038
+ if (!validation.ok) {
1039
+ return {
1040
+ ok: false,
1041
+ error: {
1042
+ code: "closure_construction_failed",
1043
+ cause: validation.error,
1044
+ message: `Semantic closure construction failed: ${validation.error.message}`
1045
+ }
1046
+ };
1047
+ }
1048
+ return {
1049
+ ok: true,
1050
+ certificate: composition.certificate,
1051
+ closure: validation.closure
1052
+ };
1053
+ }
1054
+ function matchRiddleProofSemanticCertificateClosure(input) {
1055
+ if (!isRecord(input)) throw new Error("Semantic certificate closure match input must be a plain object.");
1056
+ assertOnlyKeys(
1057
+ input,
1058
+ [
1059
+ "closure",
1060
+ "expected_root_certificate_id",
1061
+ "expected_scope",
1062
+ "expected_claim",
1063
+ "expected_assurance"
1064
+ ],
1065
+ "Semantic certificate closure match input"
1066
+ );
1067
+ const validation = validateRiddleProofSemanticCertificateClosure(
1068
+ requiredField(input, "closure", "Semantic certificate closure match input")
1069
+ );
1070
+ if (!validation.ok) return validation;
1071
+ const expectedRootCertificateId = requiredString(
1072
+ input,
1073
+ "expected_root_certificate_id",
1074
+ "Semantic certificate closure match input"
1075
+ );
1076
+ if (!/^rpsc_[0-9a-f]{64}$/u.test(expectedRootCertificateId)) {
1077
+ throw new Error(
1078
+ "Semantic certificate closure match input.expected_root_certificate_id must be a full rpsc content ID."
1079
+ );
1080
+ }
1081
+ const rootMatch = matchRiddleProofSemanticCertificate({
1082
+ certificate: validation.root_certificate,
1083
+ expected_certificate_id: expectedRootCertificateId,
1084
+ expected_scope: requiredField(
1085
+ input,
1086
+ "expected_scope",
1087
+ "Semantic certificate closure match input"
1088
+ ),
1089
+ expected_claim: requiredField(
1090
+ input,
1091
+ "expected_claim",
1092
+ "Semantic certificate closure match input"
1093
+ ),
1094
+ expected_assurance: requiredField(
1095
+ input,
1096
+ "expected_assurance",
1097
+ "Semantic certificate closure match input"
1098
+ )
1099
+ });
1100
+ if (!rootMatch.ok) return rootMatch;
1101
+ return {
1102
+ ok: true,
1103
+ closure: validation.closure,
1104
+ root_certificate: rootMatch.certificate
1105
+ };
1106
+ }
606
1107
  // Annotate the CommonJS export names for ESM import in node:
607
1108
  0 && (module.exports = {
1109
+ RIDDLE_PROOF_SEMANTIC_CERTIFICATE_CLOSURE_MAX_CERTIFICATES,
1110
+ RIDDLE_PROOF_SEMANTIC_CERTIFICATE_CLOSURE_VERSION,
608
1111
  RIDDLE_PROOF_SEMANTIC_CERTIFICATE_VERSION,
1112
+ composeRiddleProofSemanticCertificateClosures,
609
1113
  composeRiddleProofSemanticCertificates,
1114
+ createRiddleProofSemanticAtomicCertificateClosure,
610
1115
  createRiddleProofSemanticCertificate,
611
1116
  matchRiddleProofSemanticCertificate,
1117
+ matchRiddleProofSemanticCertificateClosure,
612
1118
  parseRiddleProofSemanticCertificate,
613
- riddleProofSemanticScopesEqual
1119
+ riddleProofSemanticScopesEqual,
1120
+ validateRiddleProofSemanticCertificateClosure
614
1121
  });