@disclosureos/scoring 2.0.0-beta.3 → 2.0.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/README.md +70 -36
- package/dist/compellingness/index.d.ts +7 -12
- package/dist/compellingness/index.js +7 -26
- package/dist/compellingness/index.js.map +1 -1
- package/dist/experimental/v2/index.d.ts +226 -1
- package/dist/experimental/v2/index.js +493 -10
- package/dist/experimental/v2/index.js.map +1 -1
- package/dist/index.js +7 -26
- package/dist/index.js.map +1 -1
- package/package.json +11 -7
- package/schema/experimental/provider-score-policy-0.1.0.schema.json +307 -0
- package/schema/experimental/provider-score-result-0.1.0.schema.json +343 -0
|
@@ -19,7 +19,7 @@ function collapse(claims) {
|
|
|
19
19
|
const groups = /* @__PURE__ */ new Map();
|
|
20
20
|
for (const claim of claims) {
|
|
21
21
|
const {
|
|
22
|
-
id:
|
|
22
|
+
id: id3,
|
|
23
23
|
recordedAt: _recordedAt,
|
|
24
24
|
supersedes: _supersedes,
|
|
25
25
|
...body
|
|
@@ -27,8 +27,8 @@ function collapse(claims) {
|
|
|
27
27
|
const declaration = claim.kind === "assessment" ? { ...body, inputRefs: [...claim.inputRefs].sort() } : body;
|
|
28
28
|
const key = canonical(declaration);
|
|
29
29
|
const group = groups.get(key);
|
|
30
|
-
if (group) group.claimRefs.push(`claim:${
|
|
31
|
-
else groups.set(key, { claimRefs: [`claim:${
|
|
30
|
+
if (group) group.claimRefs.push(`claim:${id3}`);
|
|
31
|
+
else groups.set(key, { claimRefs: [`claim:${id3}`], declaration });
|
|
32
32
|
}
|
|
33
33
|
return [...groups.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([, group]) => ({ ...group, claimRefs: group.claimRefs.sort() }));
|
|
34
34
|
}
|
|
@@ -154,8 +154,8 @@ function summarizeClaimHistory(input) {
|
|
|
154
154
|
}
|
|
155
155
|
}
|
|
156
156
|
result.sharedInputs = [...shared.entries()].filter(([, refs]) => refs.size > 1).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([ref, refs]) => ({ ref, assessmentRefs: [...refs].sort() }));
|
|
157
|
-
result.sharedDeclaredArtifacts = [...digestIndex.entries()].filter(([, entry]) => entry.assessmentRefs.size > 1).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([
|
|
158
|
-
sha256,
|
|
157
|
+
result.sharedDeclaredArtifacts = [...digestIndex.entries()].filter(([, entry]) => entry.assessmentRefs.size > 1).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([sha2562, entry]) => ({
|
|
158
|
+
sha256: sha2562,
|
|
159
159
|
artifactRefs: [...entry.artifactRefs].sort(),
|
|
160
160
|
assessmentRefs: [...entry.assessmentRefs].sort()
|
|
161
161
|
}));
|
|
@@ -163,15 +163,498 @@ function summarizeClaimHistory(input) {
|
|
|
163
163
|
}
|
|
164
164
|
var text = z.string().min(1).regex(/\S/);
|
|
165
165
|
var id = z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]*(?![\s\S])/);
|
|
166
|
+
var finite = z.number().finite();
|
|
166
167
|
var pin = z.strictObject({
|
|
167
168
|
sha256: z.string().regex(/^[a-f0-9]{64}(?![\s\S])/),
|
|
168
169
|
byteLength: z.number().int().min(1).max(Number.MAX_SAFE_INTEGER)
|
|
169
170
|
});
|
|
170
|
-
var
|
|
171
|
+
var artifact = z.strictObject({
|
|
171
172
|
ref: id,
|
|
173
|
+
title: text,
|
|
174
|
+
mediaType: text,
|
|
175
|
+
...pin.shape
|
|
176
|
+
});
|
|
177
|
+
var subjectKind = z.enum([
|
|
178
|
+
"case",
|
|
179
|
+
"record",
|
|
180
|
+
"claim",
|
|
181
|
+
"measurement",
|
|
182
|
+
"product",
|
|
183
|
+
"source",
|
|
184
|
+
"collection"
|
|
185
|
+
]);
|
|
186
|
+
var direction = z.enum(["higher", "lower", "none"]);
|
|
187
|
+
var grade = z.strictObject({ id, label: text, min: finite, max: finite });
|
|
188
|
+
var PROVIDER_SCORE_POLICY_SCHEMA_ID = "urn:disclosureos:experimental:provider-score-policy:0.1.0";
|
|
189
|
+
var PROVIDER_SCORE_RESULT_SCHEMA_ID = "urn:disclosureos:experimental:provider-score-result:0.1.0";
|
|
190
|
+
var ProviderScorePolicySchema = z.strictObject({
|
|
191
|
+
kind: z.literal("provider_score_policy"),
|
|
192
|
+
schemaVersion: z.literal("0.1.0"),
|
|
172
193
|
id: text,
|
|
173
194
|
version: text,
|
|
174
|
-
|
|
195
|
+
title: text,
|
|
196
|
+
metric: z.strictObject({ id: text, label: text, question: text }),
|
|
197
|
+
subjectKinds: z.array(subjectKind).min(1),
|
|
198
|
+
method: z.strictObject({
|
|
199
|
+
description: text,
|
|
200
|
+
artifacts: z.array(
|
|
201
|
+
z.strictObject({
|
|
202
|
+
ref: id,
|
|
203
|
+
id: text,
|
|
204
|
+
version: text,
|
|
205
|
+
mediaType: text,
|
|
206
|
+
...pin.shape
|
|
207
|
+
})
|
|
208
|
+
).min(1)
|
|
209
|
+
}),
|
|
210
|
+
applicability: text,
|
|
211
|
+
missingData: z.strictObject({
|
|
212
|
+
handling: z.enum(["withhold", "allow_partial"]),
|
|
213
|
+
description: text
|
|
214
|
+
}),
|
|
215
|
+
limitations: z.array(text).min(1),
|
|
216
|
+
scale: z.discriminatedUnion("kind", [
|
|
217
|
+
z.strictObject({
|
|
218
|
+
kind: z.literal("numeric"),
|
|
219
|
+
min: finite,
|
|
220
|
+
max: finite,
|
|
221
|
+
direction,
|
|
222
|
+
unit: text,
|
|
223
|
+
grades: z.array(grade)
|
|
224
|
+
}),
|
|
225
|
+
z.strictObject({
|
|
226
|
+
kind: z.literal("ordinal"),
|
|
227
|
+
direction,
|
|
228
|
+
levels: z.array(z.strictObject({ id, label: text, description: text })).min(2)
|
|
229
|
+
})
|
|
230
|
+
])
|
|
231
|
+
});
|
|
232
|
+
var ProviderScoreResultSchema = z.strictObject({
|
|
233
|
+
kind: z.literal("provider_score_result"),
|
|
234
|
+
schemaVersion: z.literal("0.1.0"),
|
|
235
|
+
id,
|
|
236
|
+
provider: z.strictObject({
|
|
237
|
+
id: text,
|
|
238
|
+
name: text,
|
|
239
|
+
url: z.url().regex(/^https?:\/\//i).optional()
|
|
240
|
+
}),
|
|
241
|
+
producedAt: z.iso.datetime({ offset: true }),
|
|
242
|
+
policy: z.strictObject({ id: text, version: text, ...pin.shape }),
|
|
243
|
+
subject: z.strictObject({ kind: subjectKind, id: text }),
|
|
244
|
+
inputScope: z.strictObject({ id: text, description: text }),
|
|
245
|
+
inputs: z.array(artifact).min(1),
|
|
246
|
+
derivation: z.array(artifact),
|
|
247
|
+
applicability: z.strictObject({
|
|
248
|
+
status: z.enum(["applicable", "not_applicable", "undetermined"]),
|
|
249
|
+
reason: text
|
|
250
|
+
}),
|
|
251
|
+
missingInputs: z.array(z.strictObject({ ref: id, description: text })),
|
|
252
|
+
limitations: z.array(text),
|
|
253
|
+
outcome: z.discriminatedUnion("status", [
|
|
254
|
+
z.strictObject({
|
|
255
|
+
status: z.literal("scored"),
|
|
256
|
+
value: z.union([finite, id]),
|
|
257
|
+
gradeId: id.optional(),
|
|
258
|
+
explanation: text
|
|
259
|
+
}),
|
|
260
|
+
z.strictObject({
|
|
261
|
+
status: z.literal("unavailable"),
|
|
262
|
+
reasonCode: z.enum([
|
|
263
|
+
"missing_inputs",
|
|
264
|
+
"not_applicable",
|
|
265
|
+
"undetermined",
|
|
266
|
+
"not_calculated"
|
|
267
|
+
]),
|
|
268
|
+
explanation: text
|
|
269
|
+
})
|
|
270
|
+
])
|
|
271
|
+
});
|
|
272
|
+
function providerScorePolicyJsonSchema() {
|
|
273
|
+
return {
|
|
274
|
+
...z.toJSONSchema(ProviderScorePolicySchema, { target: "draft-2020-12" }),
|
|
275
|
+
$id: PROVIDER_SCORE_POLICY_SCHEMA_ID
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
function providerScoreResultJsonSchema() {
|
|
279
|
+
return {
|
|
280
|
+
...z.toJSONSchema(ProviderScoreResultSchema, { target: "draft-2020-12" }),
|
|
281
|
+
$id: PROVIDER_SCORE_RESULT_SCHEMA_ID
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
function structuralIssues(error) {
|
|
285
|
+
return error.issues.map((issue) => ({
|
|
286
|
+
code: "PROVIDER_SCORE.STRUCTURE",
|
|
287
|
+
pointer: "/" + issue.path.map((p) => String(p).replace(/~/g, "~0").replace(/\//g, "~1")).join("/"),
|
|
288
|
+
message: issue.message
|
|
289
|
+
}));
|
|
290
|
+
}
|
|
291
|
+
function duplicateIssues(values, pointer, issues) {
|
|
292
|
+
const seen = /* @__PURE__ */ new Set();
|
|
293
|
+
values.forEach((value, index) => {
|
|
294
|
+
if (seen.has(value))
|
|
295
|
+
issues.push({
|
|
296
|
+
code: "PROVIDER_SCORE.DUPLICATE",
|
|
297
|
+
pointer: `${pointer}/${index}`,
|
|
298
|
+
message: "Identifiers must be unique within this list"
|
|
299
|
+
});
|
|
300
|
+
seen.add(value);
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
function validateProviderScorePolicy(input) {
|
|
304
|
+
const parsed = ProviderScorePolicySchema.safeParse(input);
|
|
305
|
+
if (!parsed.success)
|
|
306
|
+
return {
|
|
307
|
+
success: false,
|
|
308
|
+
checks: { structural: "failed", semantic: "not_checked" },
|
|
309
|
+
issues: structuralIssues(parsed.error),
|
|
310
|
+
policy: null
|
|
311
|
+
};
|
|
312
|
+
const policy = parsed.data;
|
|
313
|
+
const issues = [];
|
|
314
|
+
duplicateIssues(policy.subjectKinds, "/subjectKinds", issues);
|
|
315
|
+
duplicateIssues(
|
|
316
|
+
policy.method.artifacts.map((a) => a.ref),
|
|
317
|
+
"/method/artifacts",
|
|
318
|
+
issues
|
|
319
|
+
);
|
|
320
|
+
if (policy.scale.kind === "numeric") {
|
|
321
|
+
const scale = policy.scale;
|
|
322
|
+
if (scale.min >= scale.max)
|
|
323
|
+
issues.push({
|
|
324
|
+
code: "PROVIDER_SCORE.SCALE",
|
|
325
|
+
pointer: "/scale",
|
|
326
|
+
message: "Numeric scale minimum must be less than its maximum"
|
|
327
|
+
});
|
|
328
|
+
duplicateIssues(
|
|
329
|
+
scale.grades.map((g) => g.id),
|
|
330
|
+
"/scale/grades",
|
|
331
|
+
issues
|
|
332
|
+
);
|
|
333
|
+
scale.grades.forEach((band, index) => {
|
|
334
|
+
const previous = scale.grades[index - 1];
|
|
335
|
+
if (band.min >= band.max || band.min < scale.min || band.max > scale.max || band.min !== (previous?.max ?? scale.min) || index === scale.grades.length - 1 && band.max !== scale.max) {
|
|
336
|
+
issues.push({
|
|
337
|
+
code: "PROVIDER_SCORE.GRADE_BANDS",
|
|
338
|
+
pointer: `/scale/grades/${index}`,
|
|
339
|
+
message: "Grade bands must be ordered, contiguous and cover the scale exactly; lower bounds are inclusive, upper bounds exclusive except the final maximum"
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
} else
|
|
344
|
+
duplicateIssues(
|
|
345
|
+
policy.scale.levels.map((level) => level.id),
|
|
346
|
+
"/scale/levels",
|
|
347
|
+
issues
|
|
348
|
+
);
|
|
349
|
+
return {
|
|
350
|
+
success: issues.length === 0,
|
|
351
|
+
checks: {
|
|
352
|
+
structural: "passed",
|
|
353
|
+
semantic: issues.length ? "failed" : "passed"
|
|
354
|
+
},
|
|
355
|
+
issues,
|
|
356
|
+
policy: issues.length ? null : policy
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
function providerScoreGrade(policy, value) {
|
|
360
|
+
if (!validateProviderScorePolicy(policy).success) return null;
|
|
361
|
+
if (policy.scale.kind === "ordinal") {
|
|
362
|
+
const level = policy.scale.levels.find((entry) => entry.id === value);
|
|
363
|
+
return level ? { id: level.id, label: level.label } : null;
|
|
364
|
+
}
|
|
365
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return null;
|
|
366
|
+
const bands = policy.scale.grades;
|
|
367
|
+
const band = bands.find(
|
|
368
|
+
(entry, index) => value >= entry.min && (value < entry.max || index === bands.length - 1 && value === entry.max)
|
|
369
|
+
);
|
|
370
|
+
return band ? { id: band.id, label: band.label } : null;
|
|
371
|
+
}
|
|
372
|
+
async function sha256(bytes) {
|
|
373
|
+
return [
|
|
374
|
+
...new Uint8Array(
|
|
375
|
+
await globalThis.crypto.subtle.digest("SHA-256", Uint8Array.from(bytes))
|
|
376
|
+
)
|
|
377
|
+
].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
378
|
+
}
|
|
379
|
+
function snapshot(files) {
|
|
380
|
+
return new Map(
|
|
381
|
+
[...files ?? []].map(([ref, bytes]) => [ref, Uint8Array.from(bytes)])
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
async function evaluateProviderScore(input, options = {}) {
|
|
385
|
+
const evaluation = {
|
|
386
|
+
success: false,
|
|
387
|
+
checks: {
|
|
388
|
+
structural: "passed",
|
|
389
|
+
semantic: "not_checked",
|
|
390
|
+
external: "not_checked"
|
|
391
|
+
},
|
|
392
|
+
issues: [],
|
|
393
|
+
files: [],
|
|
394
|
+
policy: null,
|
|
395
|
+
result: null,
|
|
396
|
+
grade: null,
|
|
397
|
+
policyExecution: "not_checked",
|
|
398
|
+
providerAuthentication: "not_checked",
|
|
399
|
+
scientificValidity: "not_checked",
|
|
400
|
+
subjectResolution: "not_checked"
|
|
401
|
+
};
|
|
402
|
+
const parsed = ProviderScoreResultSchema.safeParse(input);
|
|
403
|
+
if (!parsed.success) {
|
|
404
|
+
evaluation.checks.structural = "failed";
|
|
405
|
+
evaluation.issues = structuralIssues(parsed.error);
|
|
406
|
+
return evaluation;
|
|
407
|
+
}
|
|
408
|
+
const result = parsed.data;
|
|
409
|
+
evaluation.result = result;
|
|
410
|
+
const policyBytes = options.policyBytes && Uint8Array.from(options.policyBytes);
|
|
411
|
+
const inputFiles = snapshot(options.inputFiles), derivationFiles = snapshot(options.derivationFiles), methodFiles = snapshot(options.methodFiles);
|
|
412
|
+
const issue = (code, pointer, message) => evaluation.issues.push({
|
|
413
|
+
code: `PROVIDER_SCORE.${code}`,
|
|
414
|
+
pointer,
|
|
415
|
+
message
|
|
416
|
+
});
|
|
417
|
+
duplicateIssues(
|
|
418
|
+
[...result.inputs, ...result.derivation, ...result.missingInputs].map(
|
|
419
|
+
(entry) => entry.ref
|
|
420
|
+
),
|
|
421
|
+
"/artifactRefs",
|
|
422
|
+
evaluation.issues
|
|
423
|
+
);
|
|
424
|
+
async function checkFile(ref, declared, bytes, pointer) {
|
|
425
|
+
const check = {
|
|
426
|
+
ref,
|
|
427
|
+
status: "not_checked",
|
|
428
|
+
expectedSha256: declared.sha256,
|
|
429
|
+
expectedByteLength: declared.byteLength
|
|
430
|
+
};
|
|
431
|
+
evaluation.files.push(check);
|
|
432
|
+
if (!bytes) {
|
|
433
|
+
issue("MISSING_FILE", pointer, "Declared bytes were not supplied");
|
|
434
|
+
return false;
|
|
435
|
+
}
|
|
436
|
+
check.actualByteLength = bytes.byteLength;
|
|
437
|
+
if (check.actualByteLength !== declared.byteLength) {
|
|
438
|
+
check.status = "failed";
|
|
439
|
+
issue(
|
|
440
|
+
"PIN_MISMATCH",
|
|
441
|
+
pointer,
|
|
442
|
+
"Supplied bytes do not match the declared length"
|
|
443
|
+
);
|
|
444
|
+
return false;
|
|
445
|
+
}
|
|
446
|
+
try {
|
|
447
|
+
check.actualSha256 = await sha256(bytes);
|
|
448
|
+
} catch {
|
|
449
|
+
issue(
|
|
450
|
+
"HASH_UNAVAILABLE",
|
|
451
|
+
pointer,
|
|
452
|
+
"SHA-256 could not be computed in this environment"
|
|
453
|
+
);
|
|
454
|
+
return false;
|
|
455
|
+
}
|
|
456
|
+
check.status = check.actualByteLength === declared.byteLength && check.actualSha256 === declared.sha256 ? "passed" : "failed";
|
|
457
|
+
if (check.status === "failed")
|
|
458
|
+
issue(
|
|
459
|
+
"PIN_MISMATCH",
|
|
460
|
+
pointer,
|
|
461
|
+
"Supplied bytes do not match the declared digest and length"
|
|
462
|
+
);
|
|
463
|
+
return check.status === "passed";
|
|
464
|
+
}
|
|
465
|
+
const policyMatches = await checkFile(
|
|
466
|
+
"policy",
|
|
467
|
+
result.policy,
|
|
468
|
+
policyBytes,
|
|
469
|
+
"/policy"
|
|
470
|
+
);
|
|
471
|
+
if (!policyMatches) {
|
|
472
|
+
evaluation.checks.external = evaluation.files.some(
|
|
473
|
+
(file) => file.status === "failed"
|
|
474
|
+
) ? "failed" : "not_checked";
|
|
475
|
+
if (evaluation.issues.some(
|
|
476
|
+
(entry) => entry.code === "PROVIDER_SCORE.DUPLICATE"
|
|
477
|
+
))
|
|
478
|
+
evaluation.checks.semantic = "failed";
|
|
479
|
+
return evaluation;
|
|
480
|
+
}
|
|
481
|
+
let policyInput;
|
|
482
|
+
try {
|
|
483
|
+
const serialized = new TextDecoder("utf-8", {
|
|
484
|
+
fatal: true,
|
|
485
|
+
ignoreBOM: true
|
|
486
|
+
}).decode(policyBytes);
|
|
487
|
+
policyInput = JSON.parse(serialized);
|
|
488
|
+
} catch {
|
|
489
|
+
issue(
|
|
490
|
+
"POLICY_JSON",
|
|
491
|
+
"/policy",
|
|
492
|
+
"Policy bytes must contain valid UTF-8 JSON without a byte-order mark"
|
|
493
|
+
);
|
|
494
|
+
evaluation.checks.structural = "failed";
|
|
495
|
+
evaluation.checks.external = "passed";
|
|
496
|
+
return evaluation;
|
|
497
|
+
}
|
|
498
|
+
const policyValidation = validateProviderScorePolicy(policyInput);
|
|
499
|
+
if (!policyValidation.success || !policyValidation.policy) {
|
|
500
|
+
evaluation.checks.structural = policyValidation.checks.structural;
|
|
501
|
+
evaluation.checks.semantic = policyValidation.checks.semantic;
|
|
502
|
+
evaluation.issues.push(
|
|
503
|
+
...policyValidation.issues.map((entry) => ({
|
|
504
|
+
...entry,
|
|
505
|
+
pointer: `/policy${entry.pointer}`
|
|
506
|
+
}))
|
|
507
|
+
);
|
|
508
|
+
return evaluation;
|
|
509
|
+
}
|
|
510
|
+
const policy = policyValidation.policy;
|
|
511
|
+
evaluation.policy = policy;
|
|
512
|
+
if (policy.id !== result.policy.id || policy.version !== result.policy.version)
|
|
513
|
+
issue(
|
|
514
|
+
"POLICY_IDENTITY",
|
|
515
|
+
"/policy",
|
|
516
|
+
"Policy identity and revision must match the pinned document"
|
|
517
|
+
);
|
|
518
|
+
if (!policy.subjectKinds.includes(result.subject.kind))
|
|
519
|
+
issue(
|
|
520
|
+
"SUBJECT_KIND",
|
|
521
|
+
"/subject/kind",
|
|
522
|
+
"The policy does not apply to this subject kind"
|
|
523
|
+
);
|
|
524
|
+
if (result.outcome.status === "scored") {
|
|
525
|
+
if (result.applicability.status !== "applicable")
|
|
526
|
+
issue(
|
|
527
|
+
"APPLICABILITY",
|
|
528
|
+
"/applicability",
|
|
529
|
+
"A score requires an applicable policy"
|
|
530
|
+
);
|
|
531
|
+
if (result.missingInputs.length && policy.missingData.handling === "withhold")
|
|
532
|
+
issue(
|
|
533
|
+
"MISSING_INPUTS",
|
|
534
|
+
"/missingInputs",
|
|
535
|
+
"This policy withholds a score when required inputs are missing"
|
|
536
|
+
);
|
|
537
|
+
const value = result.outcome.value;
|
|
538
|
+
if (policy.scale.kind === "numeric") {
|
|
539
|
+
if (typeof value !== "number" || value < policy.scale.min || value > policy.scale.max)
|
|
540
|
+
issue(
|
|
541
|
+
"VALUE",
|
|
542
|
+
"/outcome/value",
|
|
543
|
+
"Value must be a finite number within the policy scale"
|
|
544
|
+
);
|
|
545
|
+
} else if (typeof value !== "string" || !policy.scale.levels.some((entry) => entry.id === value))
|
|
546
|
+
issue(
|
|
547
|
+
"VALUE",
|
|
548
|
+
"/outcome/value",
|
|
549
|
+
"Value must identify a declared ordinal level"
|
|
550
|
+
);
|
|
551
|
+
evaluation.grade = providerScoreGrade(policy, value);
|
|
552
|
+
if (result.outcome.gradeId !== void 0 && result.outcome.gradeId !== evaluation.grade?.id)
|
|
553
|
+
issue(
|
|
554
|
+
"GRADE",
|
|
555
|
+
"/outcome/gradeId",
|
|
556
|
+
"Declared grade does not match the policy band or ordinal level"
|
|
557
|
+
);
|
|
558
|
+
} else {
|
|
559
|
+
const reason = result.outcome.reasonCode;
|
|
560
|
+
if (reason === "missing_inputs" && !result.missingInputs.length || reason === "not_applicable" && result.applicability.status !== "not_applicable" || reason === "undetermined" && result.applicability.status !== "undetermined")
|
|
561
|
+
issue(
|
|
562
|
+
"UNAVAILABLE_REASON",
|
|
563
|
+
"/outcome/reasonCode",
|
|
564
|
+
"Unavailable reason must agree with the recorded applicability or missing inputs"
|
|
565
|
+
);
|
|
566
|
+
if (result.applicability.status === "not_applicable" && reason !== "not_applicable" || result.applicability.status === "undetermined" && reason !== "undetermined")
|
|
567
|
+
issue(
|
|
568
|
+
"UNAVAILABLE_REASON",
|
|
569
|
+
"/outcome/reasonCode",
|
|
570
|
+
"Unavailable reason must retain the policy applicability state"
|
|
571
|
+
);
|
|
572
|
+
}
|
|
573
|
+
evaluation.checks.semantic = evaluation.issues.length ? "failed" : "passed";
|
|
574
|
+
for (const [index, entry] of policy.method.artifacts.entries())
|
|
575
|
+
await checkFile(
|
|
576
|
+
`method:${entry.ref}`,
|
|
577
|
+
entry,
|
|
578
|
+
methodFiles.get(entry.ref),
|
|
579
|
+
`/policy/method/artifacts/${index}`
|
|
580
|
+
);
|
|
581
|
+
for (const [index, entry] of result.inputs.entries())
|
|
582
|
+
await checkFile(
|
|
583
|
+
`input:${entry.ref}`,
|
|
584
|
+
entry,
|
|
585
|
+
inputFiles.get(entry.ref),
|
|
586
|
+
`/inputs/${index}`
|
|
587
|
+
);
|
|
588
|
+
for (const [index, entry] of result.derivation.entries())
|
|
589
|
+
await checkFile(
|
|
590
|
+
`derivation:${entry.ref}`,
|
|
591
|
+
entry,
|
|
592
|
+
derivationFiles.get(entry.ref),
|
|
593
|
+
`/derivation/${index}`
|
|
594
|
+
);
|
|
595
|
+
evaluation.checks.external = evaluation.files.some(
|
|
596
|
+
(file) => file.status === "failed"
|
|
597
|
+
) ? "failed" : evaluation.files.every((file) => file.status === "passed") ? "passed" : "not_checked";
|
|
598
|
+
evaluation.success = evaluation.checks.semantic === "passed" && evaluation.checks.external === "passed";
|
|
599
|
+
return evaluation;
|
|
600
|
+
}
|
|
601
|
+
async function compareProviderScores(leftInput, rightInput, options = {}) {
|
|
602
|
+
const [left, right] = await Promise.all([
|
|
603
|
+
evaluateProviderScore(leftInput, options.left),
|
|
604
|
+
evaluateProviderScore(rightInput, options.right)
|
|
605
|
+
]);
|
|
606
|
+
const reasons = [];
|
|
607
|
+
const a = left.result, b = right.result;
|
|
608
|
+
if (!left.success || !right.success) reasons.push("unverified_result");
|
|
609
|
+
if (a && b) {
|
|
610
|
+
if (a.outcome.status !== "scored" || b.outcome.status !== "scored")
|
|
611
|
+
reasons.push("unavailable_result");
|
|
612
|
+
if (a.policy.id !== b.policy.id || a.policy.version !== b.policy.version || a.policy.sha256 !== b.policy.sha256 || a.policy.byteLength !== b.policy.byteLength)
|
|
613
|
+
reasons.push("different_policy");
|
|
614
|
+
if (a.subject.id !== b.subject.id || a.subject.kind !== b.subject.kind)
|
|
615
|
+
reasons.push("different_subject");
|
|
616
|
+
if (a.inputScope.id !== b.inputScope.id || a.inputScope.description !== b.inputScope.description)
|
|
617
|
+
reasons.push("different_input_scope");
|
|
618
|
+
const basis = (value) => JSON.stringify(
|
|
619
|
+
value.inputs.map((entry) => [
|
|
620
|
+
entry.ref,
|
|
621
|
+
entry.sha256,
|
|
622
|
+
entry.byteLength,
|
|
623
|
+
entry.mediaType
|
|
624
|
+
]).sort((x, y) => String(x[0]).localeCompare(String(y[0])))
|
|
625
|
+
);
|
|
626
|
+
if (basis(a) !== basis(b)) reasons.push("different_inputs");
|
|
627
|
+
if (a.missingInputs.length || b.missingInputs.length)
|
|
628
|
+
reasons.push("incomplete_inputs");
|
|
629
|
+
}
|
|
630
|
+
const sharedInputDigests = a && b ? [
|
|
631
|
+
...new Set(
|
|
632
|
+
a.inputs.map((entry) => entry.sha256).filter(
|
|
633
|
+
(digest2) => b.inputs.some((entry) => entry.sha256 === digest2)
|
|
634
|
+
)
|
|
635
|
+
)
|
|
636
|
+
].sort() : [];
|
|
637
|
+
return {
|
|
638
|
+
eligibleForComparison: reasons.length === 0,
|
|
639
|
+
status: reasons.length ? "not_eligible" : "eligible_for_comparison",
|
|
640
|
+
reasons,
|
|
641
|
+
left,
|
|
642
|
+
right,
|
|
643
|
+
sharedInputDigests,
|
|
644
|
+
statisticalIndependence: "not_checked"
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
var text2 = z.string().min(1).regex(/\S/);
|
|
648
|
+
var id2 = z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]*(?![\s\S])/);
|
|
649
|
+
var pin2 = z.strictObject({
|
|
650
|
+
sha256: z.string().regex(/^[a-f0-9]{64}(?![\s\S])/),
|
|
651
|
+
byteLength: z.number().int().min(1).max(Number.MAX_SAFE_INTEGER)
|
|
652
|
+
});
|
|
653
|
+
var dependency = z.strictObject({
|
|
654
|
+
ref: id2,
|
|
655
|
+
id: text2,
|
|
656
|
+
version: text2,
|
|
657
|
+
...pin2.shape
|
|
175
658
|
});
|
|
176
659
|
var ASSESSMENT_SUMMARY_EVALUATION_SCHEMA_ID = "urn:disclosureos:experimental:assessment-summary-evaluation:0.1.0";
|
|
177
660
|
var ASSESSMENT_SUMMARY_EVALUATION_POLICY = Object.freeze({
|
|
@@ -182,9 +665,9 @@ var ASSESSMENT_SUMMARY_EVALUATION_POLICY = Object.freeze({
|
|
|
182
665
|
var AssessmentSummaryEvaluationSchema = z.strictObject({
|
|
183
666
|
kind: z.literal("assessment_summary_evaluation"),
|
|
184
667
|
schemaVersion: z.literal("0.1.0"),
|
|
185
|
-
id,
|
|
668
|
+
id: id2,
|
|
186
669
|
workflow: z.literal("assessment-summary:0.1.0"),
|
|
187
|
-
history:
|
|
670
|
+
history: pin2,
|
|
188
671
|
dependencies: z.strictObject({
|
|
189
672
|
vocabularies: z.array(dependency).min(1),
|
|
190
673
|
implementation: dependency,
|
|
@@ -428,6 +911,6 @@ async function evaluateAssessmentSummaryEvaluation(input, options = {}) {
|
|
|
428
911
|
return result;
|
|
429
912
|
}
|
|
430
913
|
|
|
431
|
-
export { ASSESSMENT_SUMMARY_EVALUATION_POLICY, ASSESSMENT_SUMMARY_EVALUATION_SCHEMA_ID, ASSESSMENT_SUMMARY_POLICY, AssessmentSummaryEvaluationSchema, assessmentSummaryEvaluationJsonSchema, evaluateAssessmentSummaryEvaluation, summarizeClaimHistory };
|
|
914
|
+
export { ASSESSMENT_SUMMARY_EVALUATION_POLICY, ASSESSMENT_SUMMARY_EVALUATION_SCHEMA_ID, ASSESSMENT_SUMMARY_POLICY, AssessmentSummaryEvaluationSchema, PROVIDER_SCORE_POLICY_SCHEMA_ID, PROVIDER_SCORE_RESULT_SCHEMA_ID, ProviderScorePolicySchema, ProviderScoreResultSchema, assessmentSummaryEvaluationJsonSchema, compareProviderScores, evaluateAssessmentSummaryEvaluation, evaluateProviderScore, providerScoreGrade, providerScorePolicyJsonSchema, providerScoreResultJsonSchema, summarizeClaimHistory, validateProviderScorePolicy };
|
|
432
915
|
//# sourceMappingURL=index.js.map
|
|
433
916
|
//# sourceMappingURL=index.js.map
|