@actuarial-ts/compliance 0.6.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -9
- package/dist/bundle.d.ts +1 -1
- package/dist/bundle.d.ts.map +1 -1
- package/dist/bundle.js +163 -21
- package/dist/bundle.js.map +1 -1
- package/dist/diagnosticRun.d.ts +52 -29
- package/dist/diagnosticRun.d.ts.map +1 -1
- package/dist/diagnosticRun.js +691 -109
- package/dist/diagnosticRun.js.map +1 -1
- package/dist/ledger.d.ts.map +1 -1
- package/dist/ledger.js +47 -6
- package/dist/ledger.js.map +1 -1
- package/dist/metadata.d.ts.map +1 -1
- package/dist/metadata.js +27 -3
- package/dist/metadata.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +6 -5
- package/src/bundle.ts +272 -32
- package/src/diagnosticRun.ts +1396 -93
- package/src/ledger.ts +59 -8
- package/src/metadata.ts +48 -7
- package/src/version.ts +1 -1
package/src/bundle.ts
CHANGED
|
@@ -26,10 +26,15 @@
|
|
|
26
26
|
* responsibility for compliance remains with the credentialed actuary.
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
canonicalJson,
|
|
31
|
+
fnv1a64,
|
|
32
|
+
isDiagnosticPlainRecord,
|
|
33
|
+
} from "@actuarial-ts/core";
|
|
30
34
|
import {
|
|
31
35
|
INTERCHANGE_SPEC_VERSION,
|
|
32
36
|
diagnosticDefinitionToDoc,
|
|
37
|
+
docToDiagnosticDefinition,
|
|
33
38
|
type DiagnosticDefinitionDoc,
|
|
34
39
|
BundleDoc,
|
|
35
40
|
MethodResultDoc,
|
|
@@ -37,11 +42,22 @@ import {
|
|
|
37
42
|
StochasticResultDoc,
|
|
38
43
|
TriangleDoc,
|
|
39
44
|
} from "@actuarial-ts/interchange";
|
|
40
|
-
import type {
|
|
41
|
-
|
|
45
|
+
import type {
|
|
46
|
+
DiagnosticRunProvenance,
|
|
47
|
+
VerifiedDiagnosticRunProvenance,
|
|
48
|
+
} from "./diagnosticRun.js";
|
|
49
|
+
import {
|
|
50
|
+
assertVerifiedDiagnosticRunProvenance,
|
|
51
|
+
serializedDiagnosticRunMismatch,
|
|
52
|
+
verifiedDefinitionForBundle,
|
|
53
|
+
} from "./diagnosticRun.js";
|
|
42
54
|
import { ComplianceError } from "./errors.js";
|
|
43
55
|
import { COMPLIANCE_PACKAGE_VERSION } from "./version.js";
|
|
44
|
-
export {
|
|
56
|
+
export {
|
|
57
|
+
ComplianceError,
|
|
58
|
+
COMPLIANCE_ERROR_CODES,
|
|
59
|
+
type ComplianceErrorCode,
|
|
60
|
+
} from "./errors.js";
|
|
45
61
|
export { COMPLIANCE_PACKAGE_VERSION } from "./version.js";
|
|
46
62
|
|
|
47
63
|
export { canonicalJson, fnv1a64 };
|
|
@@ -64,8 +80,6 @@ export type WrappedBundleDoc = BundleDoc;
|
|
|
64
80
|
* (UNSUPPORTED_VALUE moved to core's registry with canonicalJson in 0.2.0.)
|
|
65
81
|
*/
|
|
66
82
|
|
|
67
|
-
|
|
68
|
-
|
|
69
83
|
/**
|
|
70
84
|
* The interchange version the wrapped form is written under. Kept in literal
|
|
71
85
|
* sync with `@actuarial-ts/interchange`'s INTERCHANGE_SPEC_VERSION (the dep
|
|
@@ -158,9 +172,13 @@ export interface WrappedBundleResult extends ReproducibilityBundle {
|
|
|
158
172
|
* ALSO returned as a wrapped BundleDoc; the unwrapped `{ payload, hash }` is
|
|
159
173
|
* byte-identical either way (the v0.1.x compat fixture pins this).
|
|
160
174
|
*/
|
|
161
|
-
export function createBundle(
|
|
175
|
+
export function createBundle(
|
|
176
|
+
input: CreateBundleInput & { wrap: BundleWrapInput },
|
|
177
|
+
): WrappedBundleResult;
|
|
162
178
|
export function createBundle(input: CreateBundleInput): ReproducibilityBundle;
|
|
163
|
-
export function createBundle(
|
|
179
|
+
export function createBundle(
|
|
180
|
+
input: CreateBundleInput,
|
|
181
|
+
): ReproducibilityBundle | WrappedBundleResult {
|
|
164
182
|
const body: Record<string, unknown> = {
|
|
165
183
|
createdAt: input.createdAt,
|
|
166
184
|
inputs: input.inputs,
|
|
@@ -170,8 +188,60 @@ export function createBundle(input: CreateBundleInput): ReproducibilityBundle |
|
|
|
170
188
|
};
|
|
171
189
|
if (input.seeds !== undefined) body["seeds"] = input.seeds;
|
|
172
190
|
if (input.diagnosticRuns !== undefined) {
|
|
173
|
-
for (const run of input.diagnosticRuns)
|
|
174
|
-
|
|
191
|
+
for (const run of input.diagnosticRuns)
|
|
192
|
+
assertVerifiedDiagnosticRunProvenance(run);
|
|
193
|
+
const seenBindings = new Set<string>();
|
|
194
|
+
for (const [runIndex, run] of input.diagnosticRuns.entries()) {
|
|
195
|
+
if (seenBindings.has(run.runResultFingerprint))
|
|
196
|
+
throw new ComplianceError(
|
|
197
|
+
"BAD_DIAGNOSTIC_RUN",
|
|
198
|
+
"Duplicate diagnostic run-result fingerprint",
|
|
199
|
+
`$.diagnosticRuns[${runIndex}].runResultFingerprint`,
|
|
200
|
+
);
|
|
201
|
+
seenBindings.add(run.runResultFingerprint);
|
|
202
|
+
}
|
|
203
|
+
if (input.diagnosticRuns.length > 0) {
|
|
204
|
+
if (!isDiagnosticPlainRecord(input.sdkVersions))
|
|
205
|
+
throw new ComplianceError(
|
|
206
|
+
"BAD_DIAGNOSTIC_RUN",
|
|
207
|
+
"Diagnostic bundles require a plain SDK-version map",
|
|
208
|
+
"$.sdkVersions",
|
|
209
|
+
);
|
|
210
|
+
const expected = input.diagnosticRuns[0]!.manifest.engine.packages;
|
|
211
|
+
for (const [runIndex, run] of input.diagnosticRuns.entries()) {
|
|
212
|
+
if (
|
|
213
|
+
canonicalJson(run.manifest.engine.packages) !==
|
|
214
|
+
canonicalJson(expected)
|
|
215
|
+
)
|
|
216
|
+
throw new ComplianceError(
|
|
217
|
+
"BAD_DIAGNOSTIC_RUN",
|
|
218
|
+
"Diagnostic runs disagree on SDK package versions",
|
|
219
|
+
`$.diagnosticRuns[${runIndex}].manifest.engine.packages`,
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
for (const [name, version] of Object.entries({
|
|
223
|
+
"@actuarial-ts/core": expected.core,
|
|
224
|
+
"@actuarial-ts/data": expected.data,
|
|
225
|
+
"@actuarial-ts/compliance": expected.compliance,
|
|
226
|
+
}))
|
|
227
|
+
if (input.sdkVersions[name] !== version)
|
|
228
|
+
throw new ComplianceError(
|
|
229
|
+
"BAD_DIAGNOSTIC_RUN",
|
|
230
|
+
`Outer sdkVersions must record ${name} at ${version}`,
|
|
231
|
+
`$.sdkVersions.${name}`,
|
|
232
|
+
);
|
|
233
|
+
if (
|
|
234
|
+
input.wrap !== undefined &&
|
|
235
|
+
input.generator !== undefined &&
|
|
236
|
+
(input.generator.name !== "@actuarial-ts/compliance" ||
|
|
237
|
+
input.generator.version !== COMPLIANCE_PACKAGE_VERSION)
|
|
238
|
+
)
|
|
239
|
+
throw new ComplianceError(
|
|
240
|
+
"BAD_DIAGNOSTIC_RUN",
|
|
241
|
+
"Wrapped diagnostic bundles require the actual compliance generator stamp",
|
|
242
|
+
"$.generator",
|
|
243
|
+
);
|
|
244
|
+
}
|
|
175
245
|
body["diagnosticRuns"] = input.diagnosticRuns;
|
|
176
246
|
}
|
|
177
247
|
const payload = canonicalJson(body);
|
|
@@ -181,27 +251,48 @@ export function createBundle(input: CreateBundleInput): ReproducibilityBundle |
|
|
|
181
251
|
// The inner record is carried opaquely as the `bundle` segment; the outer
|
|
182
252
|
// tag is fnv1a64(canonicalJson({ bundle, interchange })) — exactly
|
|
183
253
|
// interchange's semanticBodyOf for kind "bundle" (spec 3.2).
|
|
184
|
-
const bundleSegment: Record<string, unknown> = {
|
|
254
|
+
const bundleSegment: Record<string, unknown> = {
|
|
255
|
+
hash: inner.hash,
|
|
256
|
+
payload: inner.payload,
|
|
257
|
+
};
|
|
185
258
|
const definitions = new Map<string, DiagnosticDefinitionDoc>();
|
|
186
259
|
for (const run of input.diagnosticRuns ?? []) {
|
|
187
260
|
const compiled = verifiedDefinitionForBundle(run);
|
|
188
|
-
definitions.set(
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
261
|
+
definitions.set(
|
|
262
|
+
run.definition.identities.definition,
|
|
263
|
+
diagnosticDefinitionToDoc(compiled, {
|
|
264
|
+
createdAt: input.createdAt,
|
|
265
|
+
generator: {
|
|
266
|
+
name: "@actuarial-ts/compliance",
|
|
267
|
+
version: COMPLIANCE_PACKAGE_VERSION,
|
|
268
|
+
},
|
|
269
|
+
}),
|
|
270
|
+
);
|
|
192
271
|
}
|
|
193
272
|
const interchange = {
|
|
194
273
|
triangles: [...input.wrap.triangles],
|
|
195
274
|
selections: [...input.wrap.selections],
|
|
196
275
|
results: [...input.wrap.results],
|
|
197
|
-
...(definitions.size === 0
|
|
276
|
+
...(definitions.size === 0
|
|
277
|
+
? {}
|
|
278
|
+
: {
|
|
279
|
+
diagnosticDefinitions: [...definitions.values()].sort((a, b) =>
|
|
280
|
+
a.diagnosticDefinition.identities.definition <
|
|
281
|
+
b.diagnosticDefinition.identities.definition
|
|
282
|
+
? -1
|
|
283
|
+
: 1,
|
|
284
|
+
),
|
|
285
|
+
}),
|
|
198
286
|
};
|
|
199
287
|
const wrapped: WrappedBundleDoc = {
|
|
200
288
|
interchangeVersion: WRAPPED_BUNDLE_INTERCHANGE_VERSION,
|
|
201
289
|
kind: "bundle",
|
|
202
290
|
generator: input.generator
|
|
203
291
|
? { ...input.generator }
|
|
204
|
-
: {
|
|
292
|
+
: {
|
|
293
|
+
name: "@actuarial-ts/compliance",
|
|
294
|
+
version: COMPLIANCE_PACKAGE_VERSION,
|
|
295
|
+
},
|
|
205
296
|
createdAt: input.createdAt,
|
|
206
297
|
bundle: bundleSegment,
|
|
207
298
|
interchange,
|
|
@@ -236,18 +327,29 @@ export interface VerifyBundleResult {
|
|
|
236
327
|
}
|
|
237
328
|
|
|
238
329
|
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
239
|
-
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
330
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
331
|
+
return false;
|
|
240
332
|
const proto: unknown = Object.getPrototypeOf(value);
|
|
241
333
|
return proto === Object.prototype || proto === null;
|
|
242
334
|
}
|
|
243
335
|
|
|
244
336
|
/** First differing path between two canonicalizable structures, or null when equal. */
|
|
245
|
-
function firstDifference(
|
|
337
|
+
function firstDifference(
|
|
338
|
+
stored: unknown,
|
|
339
|
+
rerun: unknown,
|
|
340
|
+
path: string,
|
|
341
|
+
): string | null {
|
|
246
342
|
if (isPlainObject(stored) && isPlainObject(rerun)) {
|
|
247
|
-
const keys = [
|
|
343
|
+
const keys = [
|
|
344
|
+
...new Set([...Object.keys(stored), ...Object.keys(rerun)]),
|
|
345
|
+
].sort();
|
|
248
346
|
for (const key of keys) {
|
|
249
347
|
const keyPath = `${path}.${key}`;
|
|
250
|
-
if (
|
|
348
|
+
if (
|
|
349
|
+
!Object.prototype.hasOwnProperty.call(stored, key) ||
|
|
350
|
+
!Object.prototype.hasOwnProperty.call(rerun, key)
|
|
351
|
+
)
|
|
352
|
+
return keyPath;
|
|
251
353
|
const diff = firstDifference(stored[key], rerun[key], keyPath);
|
|
252
354
|
if (diff !== null) return diff;
|
|
253
355
|
}
|
|
@@ -265,7 +367,10 @@ function firstDifference(stored: unknown, rerun: unknown, path: string): string
|
|
|
265
367
|
}
|
|
266
368
|
|
|
267
369
|
/** Inner verification — the pre-wrapped behavior, unchanged byte for byte. */
|
|
268
|
-
function verifyUnwrapped(
|
|
370
|
+
function verifyUnwrapped(
|
|
371
|
+
bundle: ReproducibilityBundle,
|
|
372
|
+
rerunResults: unknown,
|
|
373
|
+
): VerifyBundleResult {
|
|
269
374
|
// The bundle's own hash first. Without this, the attestation ("these results
|
|
270
375
|
// came from exactly these inputs, parameters, and SDK versions") checked the
|
|
271
376
|
// results segment only — a bundle with rewritten inputs and hash "deadbeef"
|
|
@@ -281,21 +386,68 @@ function verifyUnwrapped(bundle: ReproducibilityBundle, rerunResults: unknown):
|
|
|
281
386
|
throw new ComplianceError("BAD_BUNDLE", "bundle payload is not valid JSON");
|
|
282
387
|
}
|
|
283
388
|
if (!isPlainObject(stored) || !("results" in stored)) {
|
|
284
|
-
throw new ComplianceError(
|
|
389
|
+
throw new ComplianceError(
|
|
390
|
+
"BAD_BUNDLE",
|
|
391
|
+
'bundle payload has no "results" segment',
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
if (Object.prototype.hasOwnProperty.call(stored, "diagnosticRuns")) {
|
|
395
|
+
if (!Array.isArray(stored["diagnosticRuns"]))
|
|
396
|
+
throw new ComplianceError(
|
|
397
|
+
"BAD_BUNDLE",
|
|
398
|
+
'bundle payload "diagnosticRuns" must be an array',
|
|
399
|
+
);
|
|
400
|
+
const seen = new Set<string>();
|
|
401
|
+
for (const [index, run] of stored["diagnosticRuns"].entries()) {
|
|
402
|
+
const path = `$.diagnosticRuns[${index}]`;
|
|
403
|
+
const mismatch = serializedDiagnosticRunMismatch(run, path);
|
|
404
|
+
if (mismatch !== null)
|
|
405
|
+
return { reproduced: false, mismatchPath: mismatch };
|
|
406
|
+
const fingerprint = (run as Record<string, unknown>)[
|
|
407
|
+
"runResultFingerprint"
|
|
408
|
+
] as string;
|
|
409
|
+
if (seen.has(fingerprint))
|
|
410
|
+
return {
|
|
411
|
+
reproduced: false,
|
|
412
|
+
mismatchPath: `${path}.runResultFingerprint`,
|
|
413
|
+
};
|
|
414
|
+
seen.add(fingerprint);
|
|
415
|
+
const packages = (run as DiagnosticRunProvenance).manifest.engine
|
|
416
|
+
.packages;
|
|
417
|
+
for (const [name, shortName] of [
|
|
418
|
+
["@actuarial-ts/core", "core"],
|
|
419
|
+
["@actuarial-ts/data", "data"],
|
|
420
|
+
["@actuarial-ts/compliance", "compliance"],
|
|
421
|
+
] as const) {
|
|
422
|
+
if (
|
|
423
|
+
!isPlainObject(stored["sdkVersions"]) ||
|
|
424
|
+
stored["sdkVersions"][name] !== packages?.[shortName]
|
|
425
|
+
)
|
|
426
|
+
return { reproduced: false, mismatchPath: `$.sdkVersions.${name}` };
|
|
427
|
+
}
|
|
428
|
+
}
|
|
285
429
|
}
|
|
286
430
|
const storedResults = stored["results"];
|
|
287
431
|
const rerunCanonical = canonicalJson(rerunResults);
|
|
288
432
|
const storedCanonical = canonicalJson(storedResults);
|
|
289
433
|
if (rerunCanonical === storedCanonical) return { reproduced: true };
|
|
290
|
-
return {
|
|
434
|
+
return {
|
|
435
|
+
reproduced: false,
|
|
436
|
+
mismatchPath: firstDifference(storedResults, rerunResults, "$") ?? "$",
|
|
437
|
+
};
|
|
291
438
|
}
|
|
292
439
|
|
|
293
|
-
function isWrappedBundleDoc(
|
|
440
|
+
function isWrappedBundleDoc(
|
|
441
|
+
value: ReproducibilityBundle | WrappedBundleDoc,
|
|
442
|
+
): value is WrappedBundleDoc {
|
|
294
443
|
return (value as { kind?: unknown }).kind === "bundle";
|
|
295
444
|
}
|
|
296
445
|
|
|
297
446
|
/** Wrapped mode: the outer tag (spec 3.2) AND the inner bundle exactly as today. */
|
|
298
|
-
function verifyWrapped(
|
|
447
|
+
function verifyWrapped(
|
|
448
|
+
doc: WrappedBundleDoc,
|
|
449
|
+
rerunResults: unknown,
|
|
450
|
+
): VerifyBundleResult {
|
|
299
451
|
const innerRaw: unknown = doc.bundle;
|
|
300
452
|
if (
|
|
301
453
|
!isPlainObject(innerRaw) ||
|
|
@@ -308,18 +460,104 @@ function verifyWrapped(doc: WrappedBundleDoc, rerunResults: unknown): VerifyBund
|
|
|
308
460
|
);
|
|
309
461
|
}
|
|
310
462
|
if (doc.interchange === undefined || doc.interchange === null) {
|
|
311
|
-
throw new ComplianceError(
|
|
463
|
+
throw new ComplianceError(
|
|
464
|
+
"BAD_BUNDLE",
|
|
465
|
+
'wrapped bundle is missing its "interchange" mirror',
|
|
466
|
+
);
|
|
312
467
|
}
|
|
313
468
|
// OUTER tag over the two-field semantic body { bundle, interchange } —
|
|
314
469
|
// exactly interchange's semanticBodyOf for kind "bundle" (spec 3.2).
|
|
315
|
-
const expected = fnv1a64(
|
|
470
|
+
const expected = fnv1a64(
|
|
471
|
+
canonicalJson({ bundle: doc.bundle, interchange: doc.interchange }),
|
|
472
|
+
);
|
|
316
473
|
const actual = typeof doc.integrity === "string" ? doc.integrity : null;
|
|
317
|
-
const outerIntegrity: OuterIntegrityCheck = {
|
|
474
|
+
const outerIntegrity: OuterIntegrityCheck = {
|
|
475
|
+
ok: expected === actual,
|
|
476
|
+
expected,
|
|
477
|
+
actual,
|
|
478
|
+
};
|
|
318
479
|
if (!outerIntegrity.ok) {
|
|
319
480
|
return { reproduced: false, mismatchPath: "$.integrity", outerIntegrity };
|
|
320
481
|
}
|
|
321
|
-
const inner: ReproducibilityBundle = {
|
|
322
|
-
|
|
482
|
+
const inner: ReproducibilityBundle = {
|
|
483
|
+
payload: innerRaw["payload"],
|
|
484
|
+
hash: innerRaw["hash"],
|
|
485
|
+
};
|
|
486
|
+
const innerResult = verifyUnwrapped(inner, rerunResults);
|
|
487
|
+
if (!innerResult.reproduced) return { ...innerResult, outerIntegrity };
|
|
488
|
+
const body = JSON.parse(inner.payload) as Record<string, unknown>;
|
|
489
|
+
const runs = Array.isArray(body["diagnosticRuns"])
|
|
490
|
+
? body["diagnosticRuns"]
|
|
491
|
+
: [];
|
|
492
|
+
const docs = (doc.interchange as { diagnosticDefinitions?: unknown })
|
|
493
|
+
.diagnosticDefinitions;
|
|
494
|
+
if (runs.length > 0) {
|
|
495
|
+
const packages = (runs[0] as DiagnosticRunProvenance).manifest.engine
|
|
496
|
+
.packages;
|
|
497
|
+
if (doc.generator?.name !== "@actuarial-ts/compliance")
|
|
498
|
+
return {
|
|
499
|
+
reproduced: false,
|
|
500
|
+
mismatchPath: "$.generator.name",
|
|
501
|
+
outerIntegrity,
|
|
502
|
+
};
|
|
503
|
+
if (doc.generator.version !== packages.compliance)
|
|
504
|
+
return {
|
|
505
|
+
reproduced: false,
|
|
506
|
+
mismatchPath: "$.generator.version",
|
|
507
|
+
outerIntegrity,
|
|
508
|
+
};
|
|
509
|
+
if (!Array.isArray(docs))
|
|
510
|
+
return {
|
|
511
|
+
reproduced: false,
|
|
512
|
+
mismatchPath: "$.interchange.diagnosticDefinitions",
|
|
513
|
+
outerIntegrity,
|
|
514
|
+
};
|
|
515
|
+
const expected = new Set(
|
|
516
|
+
runs.map(
|
|
517
|
+
(run) =>
|
|
518
|
+
(run as DiagnosticRunProvenance).definition.identities.definition,
|
|
519
|
+
),
|
|
520
|
+
);
|
|
521
|
+
const actual = new Set<string>();
|
|
522
|
+
for (const [index, raw] of docs.entries()) {
|
|
523
|
+
try {
|
|
524
|
+
const document = raw as DiagnosticDefinitionDoc;
|
|
525
|
+
const integrity =
|
|
526
|
+
docToDiagnosticDefinition(document).definition.definitionIntegrity;
|
|
527
|
+
if (actual.has(integrity))
|
|
528
|
+
return {
|
|
529
|
+
reproduced: false,
|
|
530
|
+
mismatchPath: `$.interchange.diagnosticDefinitions[${index}]`,
|
|
531
|
+
outerIntegrity,
|
|
532
|
+
};
|
|
533
|
+
if (
|
|
534
|
+
document.generator.name !== doc.generator.name ||
|
|
535
|
+
document.generator.version !== doc.generator.version
|
|
536
|
+
)
|
|
537
|
+
return {
|
|
538
|
+
reproduced: false,
|
|
539
|
+
mismatchPath: `$.interchange.diagnosticDefinitions[${index}].generator`,
|
|
540
|
+
outerIntegrity,
|
|
541
|
+
};
|
|
542
|
+
actual.add(integrity);
|
|
543
|
+
} catch {
|
|
544
|
+
return {
|
|
545
|
+
reproduced: false,
|
|
546
|
+
mismatchPath: `$.interchange.diagnosticDefinitions[${index}]`,
|
|
547
|
+
outerIntegrity,
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
if (
|
|
552
|
+
canonicalJson([...actual].sort()) !== canonicalJson([...expected].sort())
|
|
553
|
+
)
|
|
554
|
+
return {
|
|
555
|
+
reproduced: false,
|
|
556
|
+
mismatchPath: "$.interchange.diagnosticDefinitions",
|
|
557
|
+
outerIntegrity,
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
return { ...innerResult, outerIntegrity };
|
|
323
561
|
}
|
|
324
562
|
|
|
325
563
|
/**
|
|
@@ -344,5 +582,7 @@ export function verifyBundle(
|
|
|
344
582
|
bundle: ReproducibilityBundle | WrappedBundleDoc,
|
|
345
583
|
rerunResults: unknown,
|
|
346
584
|
): VerifyBundleResult {
|
|
347
|
-
return isWrappedBundleDoc(bundle)
|
|
585
|
+
return isWrappedBundleDoc(bundle)
|
|
586
|
+
? verifyWrapped(bundle, rerunResults)
|
|
587
|
+
: verifyUnwrapped(bundle, rerunResults);
|
|
348
588
|
}
|