review-council 4.4.15 → 4.4.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/converge/correction-anchors.d.ts +1 -0
- package/dist/converge/correction-anchors.js +2 -0
- package/dist/converge/correction-anchors.js.map +1 -0
- package/dist/converge/ordinary-pending-package.js +5 -2
- package/dist/converge/ordinary-pending-package.js.map +1 -1
- package/dist/evidence/claim-recovery/authenticated-receipts.d.ts +46 -0
- package/dist/evidence/claim-recovery/authenticated-receipts.js +120 -0
- package/dist/evidence/claim-recovery/authenticated-receipts.js.map +1 -0
- package/dist/evidence/claim-recovery/carrier-inventory.d.ts +49 -0
- package/dist/evidence/claim-recovery/carrier-inventory.js +340 -0
- package/dist/evidence/claim-recovery/carrier-inventory.js.map +1 -0
- package/dist/evidence/claim-recovery/validation/anchors.d.ts +20 -0
- package/dist/evidence/claim-recovery/validation/anchors.js +22 -0
- package/dist/evidence/claim-recovery/validation/anchors.js.map +1 -0
- package/dist/evidence/claim-recovery/validation/carrier-projection.d.ts +2 -0
- package/dist/evidence/claim-recovery/validation/carrier-projection.js +3 -0
- package/dist/evidence/claim-recovery/validation/carrier-projection.js.map +1 -0
- package/dist/evidence/claim-recovery/validation/carrier-types.d.ts +83 -0
- package/dist/evidence/claim-recovery/validation/carrier-types.js +2 -0
- package/dist/evidence/claim-recovery/validation/carrier-types.js.map +1 -0
- package/dist/evidence/claim-recovery/validation/claim-split.d.ts +57 -0
- package/dist/evidence/claim-recovery/validation/claim-split.js +209 -0
- package/dist/evidence/claim-recovery/validation/claim-split.js.map +1 -0
- package/dist/evidence/claim-recovery/validation/current-projection.d.ts +20 -0
- package/dist/evidence/claim-recovery/validation/current-projection.js +2 -0
- package/dist/evidence/claim-recovery/validation/current-projection.js.map +1 -0
- package/dist/evidence/claim-recovery/validation/native-material.d.ts +17 -0
- package/dist/evidence/claim-recovery/validation/native-material.js +2 -0
- package/dist/evidence/claim-recovery/validation/native-material.js.map +1 -0
- package/dist/evidence/claim-recovery/validation/native-occurrences.d.ts +62 -0
- package/dist/evidence/claim-recovery/validation/native-occurrences.js +2 -0
- package/dist/evidence/claim-recovery/validation/native-occurrences.js.map +1 -0
- package/dist/evidence/claim-recovery/validation/native-state.d.ts +21 -0
- package/dist/evidence/claim-recovery/validation/native-state.js +162 -0
- package/dist/evidence/claim-recovery/validation/native-state.js.map +1 -0
- package/dist/evidence/claim-recovery/validation/obligations.d.ts +15 -0
- package/dist/evidence/claim-recovery/validation/obligations.js +65 -0
- package/dist/evidence/claim-recovery/validation/obligations.js.map +1 -0
- package/dist/evidence/claim-recovery/validation/occurrence-source.d.ts +45 -0
- package/dist/evidence/claim-recovery/validation/occurrence-source.js +208 -0
- package/dist/evidence/claim-recovery/validation/occurrence-source.js.map +1 -0
- package/dist/evidence/claim-recovery/validation/occurrence-types.d.ts +101 -0
- package/dist/evidence/claim-recovery/validation/occurrence-types.js +2 -0
- package/dist/evidence/claim-recovery/validation/occurrence-types.js.map +1 -0
- package/dist/evidence/claim-recovery/validation/sighting.d.ts +2 -0
- package/dist/evidence/claim-recovery/validation/sighting.js +15 -0
- package/dist/evidence/claim-recovery/validation/sighting.js.map +1 -0
- package/dist/evidence/claim-recovery/validation/sources.d.ts +15 -0
- package/dist/evidence/claim-recovery/validation/sources.js +2 -0
- package/dist/evidence/claim-recovery/validation/sources.js.map +1 -0
- package/dist/evidence/claim-recovery/validation/types.d.ts +126 -0
- package/dist/evidence/claim-recovery/validation/types.js +2 -0
- package/dist/evidence/claim-recovery/validation/types.js.map +1 -0
- package/dist/evidence/claim-split.d.ts +1 -0
- package/dist/evidence/claim-split.js +2 -0
- package/dist/evidence/claim-split.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../evidence/claim-recovery/validation/anchors.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"correction-anchors.js","sourceRoot":"","sources":["../../src/converge/correction-anchors.ts"],"names":[],"mappings":"AAAA,cAAc,kDAAkD,CAAC"}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { guardedInputSha256, stableStringify } from '../report/run-header.js';
|
|
2
2
|
export function validateOrdinaryPendingPackage(value, expected) {
|
|
3
|
-
const input = value?.guardedInput
|
|
3
|
+
const input = value?.guardedInput;
|
|
4
|
+
const specPresent = !!input && Object.hasOwn(input, 'spec');
|
|
5
|
+
const keys = ['asyncRoles', 'config', 'diff', 'head', 'kind', 'pr', 'prompts', 'repo', 'roster',
|
|
6
|
+
...(specPresent ? ['spec'] : [])];
|
|
4
7
|
const digest = (candidate) => typeof candidate === 'string' && /^[a-f0-9]{64}$/.test(candidate);
|
|
5
8
|
const structuredInput = input && input.kind === 'patch' && typeof input.repo === 'string' && /^[^/\s]+\/[^/\s]+$/.test(input.repo) &&
|
|
6
9
|
Number.isSafeInteger(input.pr) && input.pr > 0 && digest(input.diff) && digest(input.config) &&
|
|
@@ -11,7 +14,7 @@ export function validateOrdinaryPendingPackage(value, expected) {
|
|
|
11
14
|
const role = item;
|
|
12
15
|
return item && typeof item === 'object' && typeof role.name === 'string' && role.name.length > 0;
|
|
13
16
|
}) &&
|
|
14
|
-
input.spec && typeof input.spec === 'object';
|
|
17
|
+
(!specPresent || (input.spec && typeof input.spec === 'object'));
|
|
15
18
|
if (!value || value.target !== expected.target || value.headSha !== expected.headSha || value.baseSha !== expected.baseSha || value.attempt !== expected.attempt || value.round !== expected.round || value.pid !== expected.pid || !/^[a-f0-9]{40}$/.test(value.baseSha) ||
|
|
16
19
|
!Number.isSafeInteger(value.attempt) || value.attempt < 1 || !Number.isSafeInteger(value.round) || value.round < 1 ||
|
|
17
20
|
!Number.isSafeInteger(value.pid) || value.pid < 1 || !input || Object.keys(input).sort().join(',') !== keys.join(',') ||
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ordinary-pending-package.js","sourceRoot":"","sources":["../../src/converge/ordinary-pending-package.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAK9E,MAAM,UAAU,8BAA8B,CAAC,KAA6B,EAAE,QAA+B;IAC3G,MAAM,KAAK,GAAG,KAAK,EAAE,YAAY,EAAE,IAAI,GAAG,CAAC,YAAY,EAAC,QAAQ,EAAC,MAAM,EAAC,MAAM,EAAC,MAAM,EAAC,IAAI,EAAC,SAAS,EAAC,MAAM,EAAC,QAAQ,
|
|
1
|
+
{"version":3,"file":"ordinary-pending-package.js","sourceRoot":"","sources":["../../src/converge/ordinary-pending-package.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type StoredEventReceipt } from '../event-receipts.js';
|
|
2
|
+
import { type ClaimRecoverySelection } from './context.js';
|
|
3
|
+
import type { NativeOccurrenceReadRequirement } from './validation/native-occurrences.js';
|
|
4
|
+
import type { HarnessSink } from '../../telemetry/sink.js';
|
|
5
|
+
type SelectedReceiptRequirement = Extract<NativeOccurrenceReadRequirement, {
|
|
6
|
+
kind: 'selected-event-receipts';
|
|
7
|
+
}>;
|
|
8
|
+
/** Explicit original source and exact server receipts expected for that source. */
|
|
9
|
+
export interface AuthenticatedReceiptSelection {
|
|
10
|
+
selection: ClaimRecoverySelection;
|
|
11
|
+
expectedReceipts: readonly StoredEventReceipt[];
|
|
12
|
+
/** Selected receipt requirements produced by native occurrence preparation. */
|
|
13
|
+
readRequirements: readonly SelectedReceiptRequirement[];
|
|
14
|
+
}
|
|
15
|
+
/** A live token only the verifier can create. Use the accessor for defensive content. */
|
|
16
|
+
export interface AuthenticatedSelectedReceipts {
|
|
17
|
+
readonly qualification: 'authenticated-selected-receipts';
|
|
18
|
+
}
|
|
19
|
+
export interface AuthenticatedReceiptContent {
|
|
20
|
+
actorUserId: string;
|
|
21
|
+
selections: Array<{
|
|
22
|
+
selection: ClaimRecoverySelection;
|
|
23
|
+
eventIds: string[];
|
|
24
|
+
eventSequence: number;
|
|
25
|
+
receipts: StoredEventReceipt[];
|
|
26
|
+
}>;
|
|
27
|
+
}
|
|
28
|
+
/** Reject serialized or caller-forged tokens and return only a defensive snapshot. */
|
|
29
|
+
export declare function authenticatedReceiptContent(verification: AuthenticatedSelectedReceipts): AuthenticatedReceiptContent;
|
|
30
|
+
export type AuthenticatedReceiptVerification = {
|
|
31
|
+
kind: 'verified';
|
|
32
|
+
value: AuthenticatedSelectedReceipts;
|
|
33
|
+
} | {
|
|
34
|
+
kind: 'conflict';
|
|
35
|
+
reason: string;
|
|
36
|
+
} | {
|
|
37
|
+
kind: 'unknown';
|
|
38
|
+
reason: string;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Authenticate exact selected receipts in one stable, ordinary-authenticated
|
|
42
|
+
* read window. This is read-only and establishes neither inventory closure nor
|
|
43
|
+
* disposition standing.
|
|
44
|
+
*/
|
|
45
|
+
export declare function verifyAuthenticatedSelectedReceipts(sink: HarnessSink, expectedActorUserId: string, selections: readonly AuthenticatedReceiptSelection[]): Promise<AuthenticatedReceiptVerification>;
|
|
46
|
+
export {};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
2
|
+
import { MAX_SELECTED_EVENT_RECEIPTS, readEventReceipts } from '../event-receipts.js';
|
|
3
|
+
import { readClaimRecoveryContext } from './context.js';
|
|
4
|
+
import { isStoredEventReceipt } from './validation/receipts.js';
|
|
5
|
+
import { uuidSchema } from './validation/primitives.js';
|
|
6
|
+
const accepted = new WeakMap();
|
|
7
|
+
/** Reject serialized or caller-forged tokens and return only a defensive snapshot. */
|
|
8
|
+
export function authenticatedReceiptContent(verification) {
|
|
9
|
+
const content = accepted.get(verification);
|
|
10
|
+
if (!content)
|
|
11
|
+
throw new Error('unverified_authenticated_receipts');
|
|
12
|
+
return structuredClone(content);
|
|
13
|
+
}
|
|
14
|
+
const sameScope = (a, b) => a.base_url === b.base_url && a.org_id === b.org_id && a.run_id === b.run_id && a.repo === b.repo && a.pr_number === b.pr_number;
|
|
15
|
+
const sameOperation = (a, b) => a.scope.base_url === b.scope.base_url && a.scope.org_id === b.scope.org_id && a.scope.repo.toLowerCase() === b.scope.repo.toLowerCase() &&
|
|
16
|
+
a.scope.pr_number === b.scope.pr_number && a.target === b.target;
|
|
17
|
+
function selectedIds(input) {
|
|
18
|
+
const requirements = input.readRequirements;
|
|
19
|
+
if (!requirements.length || !input.expectedReceipts.length)
|
|
20
|
+
throw new Error('invalid_authenticated_receipt_selection');
|
|
21
|
+
const ids = [];
|
|
22
|
+
for (const requirement of requirements) {
|
|
23
|
+
if (!sameScope(input.selection.scope, requirement.scope) || !Array.isArray(requirement.eventIds) ||
|
|
24
|
+
requirement.eventIds.length < 1)
|
|
25
|
+
throw new Error('invalid_authenticated_receipt_selection');
|
|
26
|
+
ids.push(...requirement.eventIds);
|
|
27
|
+
}
|
|
28
|
+
const uniqueIds = [...new Set(ids)];
|
|
29
|
+
if (new Set(input.expectedReceipts.map(receipt => receipt.id)).size !== input.expectedReceipts.length ||
|
|
30
|
+
!input.expectedReceipts.every(receipt => isStoredEventReceipt(receipt, input.selection.scope)) ||
|
|
31
|
+
!isDeepStrictEqual([...uniqueIds].sort(), input.expectedReceipts.map(receipt => receipt.id).sort())) {
|
|
32
|
+
throw new Error('invalid_authenticated_receipt_selection');
|
|
33
|
+
}
|
|
34
|
+
return uniqueIds;
|
|
35
|
+
}
|
|
36
|
+
function unknown(reason) { return { kind: 'unknown', reason }; }
|
|
37
|
+
function conflict(reason) { return { kind: 'conflict', reason }; }
|
|
38
|
+
/**
|
|
39
|
+
* Authenticate exact selected receipts in one stable, ordinary-authenticated
|
|
40
|
+
* read window. This is read-only and establishes neither inventory closure nor
|
|
41
|
+
* disposition standing.
|
|
42
|
+
*/
|
|
43
|
+
export async function verifyAuthenticatedSelectedReceipts(sink, expectedActorUserId, selections) {
|
|
44
|
+
if (!uuidSchema.safeParse(expectedActorUserId).success || !Array.isArray(selections) || !selections.length) {
|
|
45
|
+
throw new Error('invalid_authenticated_receipt_selection');
|
|
46
|
+
}
|
|
47
|
+
// Pin every nested source before the first await. The caller retains no live
|
|
48
|
+
// path to later receipt, context, or operation comparisons.
|
|
49
|
+
const pinned = structuredClone(selections);
|
|
50
|
+
const seenSourceRuns = new Set();
|
|
51
|
+
const seenEvents = new Map();
|
|
52
|
+
const operations = pinned.map(input => input.selection);
|
|
53
|
+
if (!operations.every(selection => sameOperation(operations[0], selection)))
|
|
54
|
+
throw new Error('authenticated_receipt_operation_conflict');
|
|
55
|
+
const entries = pinned.map(input => {
|
|
56
|
+
const ids = selectedIds(input);
|
|
57
|
+
const sourceRunKey = JSON.stringify([input.selection.scope.base_url, input.selection.scope.org_id, input.selection.scope.run_id]);
|
|
58
|
+
if (seenSourceRuns.has(sourceRunKey))
|
|
59
|
+
throw new Error('duplicate_authenticated_receipt_selection');
|
|
60
|
+
seenSourceRuns.add(sourceRunKey);
|
|
61
|
+
const sequences = new Map();
|
|
62
|
+
for (const receipt of input.expectedReceipts) {
|
|
63
|
+
const sequenceId = sequences.get(receipt.sequence);
|
|
64
|
+
if (sequenceId && sequenceId !== receipt.id)
|
|
65
|
+
throw new Error('authenticated_receipt_sequence_conflict');
|
|
66
|
+
sequences.set(receipt.sequence, receipt.id);
|
|
67
|
+
const eventScope = JSON.stringify([input.selection.scope.base_url, input.selection.scope.org_id, input.selection.scope.run_id, receipt.id]);
|
|
68
|
+
const previousScope = seenEvents.get(receipt.id);
|
|
69
|
+
if (previousScope && previousScope !== eventScope)
|
|
70
|
+
throw new Error('authenticated_receipt_event_scope_conflict');
|
|
71
|
+
if (previousScope)
|
|
72
|
+
throw new Error('authenticated_receipt_event_scope_conflict');
|
|
73
|
+
seenEvents.set(receipt.id, eventScope);
|
|
74
|
+
}
|
|
75
|
+
return { input, ids };
|
|
76
|
+
});
|
|
77
|
+
// All source contexts precede any receipt material, yielding one operation-wide window.
|
|
78
|
+
for (const entry of entries) {
|
|
79
|
+
const before = await readClaimRecoveryContext(sink, entry.input.selection);
|
|
80
|
+
if (before.kind !== 'ok')
|
|
81
|
+
return unknown('authenticated_context_unavailable');
|
|
82
|
+
if (before.value.actorUserId !== expectedActorUserId)
|
|
83
|
+
return conflict('authenticated_actor_changed');
|
|
84
|
+
if (entry.input.expectedReceipts.some(receipt => receipt.sequence > before.value.eventSequence)) {
|
|
85
|
+
return conflict('selected_receipt_sequence_conflict');
|
|
86
|
+
}
|
|
87
|
+
entry.before = before.value;
|
|
88
|
+
}
|
|
89
|
+
for (const entry of entries) {
|
|
90
|
+
const expected = new Map(entry.input.expectedReceipts.map((receipt) => [receipt.id, receipt]));
|
|
91
|
+
for (let offset = 0; offset < entry.ids.length; offset += MAX_SELECTED_EVENT_RECEIPTS) {
|
|
92
|
+
const batch = entry.ids.slice(offset, offset + MAX_SELECTED_EVENT_RECEIPTS);
|
|
93
|
+
const result = await readEventReceipts(sink, entry.input.selection.scope, batch);
|
|
94
|
+
if (result.kind !== 'ok')
|
|
95
|
+
return unknown('selected_receipt_read_unavailable');
|
|
96
|
+
if (result.value.missing.length)
|
|
97
|
+
return conflict('selected_receipt_missing');
|
|
98
|
+
if (result.value.receipts.length !== batch.length || result.value.receipts.some(receipt => !expected.has(receipt.id) || !isDeepStrictEqual(receipt, expected.get(receipt.id)))) {
|
|
99
|
+
return conflict('selected_receipt_mismatch');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
for (const entry of entries) {
|
|
104
|
+
const after = await readClaimRecoveryContext(sink, entry.input.selection);
|
|
105
|
+
if (after.kind !== 'ok')
|
|
106
|
+
return unknown('authenticated_context_unavailable');
|
|
107
|
+
if (after.value.actorUserId !== expectedActorUserId || after.value.actorUserId !== entry.before.actorUserId) {
|
|
108
|
+
return conflict('authenticated_actor_changed');
|
|
109
|
+
}
|
|
110
|
+
if (after.value.eventSequence !== entry.before.eventSequence)
|
|
111
|
+
return conflict('source_event_sequence_changed');
|
|
112
|
+
}
|
|
113
|
+
const token = Object.freeze({ qualification: 'authenticated-selected-receipts' });
|
|
114
|
+
accepted.set(token, structuredClone({ actorUserId: expectedActorUserId, selections: entries.map(entry => ({
|
|
115
|
+
selection: entry.input.selection, eventIds: entry.ids, eventSequence: entry.before.eventSequence,
|
|
116
|
+
receipts: [...entry.input.expectedReceipts],
|
|
117
|
+
})) }));
|
|
118
|
+
return { kind: 'verified', value: token };
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=authenticated-receipts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authenticated-receipts.js","sourceRoot":"","sources":["../../../src/evidence/claim-recovery/authenticated-receipts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { HarnessSink, SinkOutcome } from '../../telemetry/sink.js';
|
|
2
|
+
import type { StoredEventReceipt } from '../event-receipts.js';
|
|
3
|
+
import type { CarrierSourceInventory, OccurrenceCarrierSelector, OccurrenceRunSelector } from './validation/carrier-types.js';
|
|
4
|
+
import type { NativeCarrierInventory } from './validation/native-occurrences.js';
|
|
5
|
+
/** A live read token. Serialized/caller-supplied objects cannot recreate it. */
|
|
6
|
+
export interface AuthenticatedCarrierInventory {
|
|
7
|
+
readonly qualification: 'authenticated-carrier-inventory-read';
|
|
8
|
+
}
|
|
9
|
+
export interface CarrierInventoryContent {
|
|
10
|
+
actorUserId: string;
|
|
11
|
+
inventory: NativeCarrierInventory;
|
|
12
|
+
runSequences: Array<{
|
|
13
|
+
runId: string;
|
|
14
|
+
eventSequence: number;
|
|
15
|
+
}>;
|
|
16
|
+
}
|
|
17
|
+
export declare const MAX_CARRIER_READ_BYTES: number;
|
|
18
|
+
/** Return defensive content, never a persisted flag authorizing native mutation. */
|
|
19
|
+
export declare function carrierInventoryContent(read: AuthenticatedCarrierInventory): CarrierInventoryContent;
|
|
20
|
+
/**
|
|
21
|
+
* Enumerate the exact PR's retained original sources and selected receipts. All
|
|
22
|
+
* initial contexts precede material reads; all final contexts and re-enumeration
|
|
23
|
+
* follow them. This authenticates a stable read window, never global historical
|
|
24
|
+
* absence, carrier retirement, disposition standing or permission to mutate.
|
|
25
|
+
* Missing source parts remain explicit inputs to the residual projection.
|
|
26
|
+
*/
|
|
27
|
+
export declare function readCarrierInventory(sink: HarnessSink, carrier: OccurrenceCarrierSelector, actorUserId: string): Promise<SinkOutcome<AuthenticatedCarrierInventory>>;
|
|
28
|
+
export interface AuthenticatedClaimHistory {
|
|
29
|
+
readonly qualification: 'authenticated-claim-history';
|
|
30
|
+
}
|
|
31
|
+
export interface ClaimHistoryContent {
|
|
32
|
+
readWindow: {
|
|
33
|
+
startedAt: string;
|
|
34
|
+
completedAt: string;
|
|
35
|
+
};
|
|
36
|
+
actorUserId: string;
|
|
37
|
+
sources: CarrierSourceInventory[];
|
|
38
|
+
histories: Array<{
|
|
39
|
+
runId: string;
|
|
40
|
+
eventSequence: number;
|
|
41
|
+
receipts: StoredEventReceipt[];
|
|
42
|
+
}>;
|
|
43
|
+
}
|
|
44
|
+
export declare function claimHistoryContent(token: AuthenticatedClaimHistory): ClaimHistoryContent;
|
|
45
|
+
/** All retained same-target sources, including later rounds, with a complete pinned
|
|
46
|
+
* index followed by exact selected receipt reads. Inline truncation alone never
|
|
47
|
+
* authenticates completeness. Both source and event ceilings are read again. */
|
|
48
|
+
export declare function readClaimTargetHistory(sink: HarnessSink, selection: OccurrenceRunSelector, expectedActor?: string): Promise<SinkOutcome<AuthenticatedClaimHistory>>;
|
|
49
|
+
export declare function sameClaimHistoryEvidence(first: ClaimHistoryContent, second: ClaimHistoryContent): boolean;
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
2
|
+
import { readClaimEventIndex, claimEventSelector } from './claim-index.js';
|
|
3
|
+
import { readEventReceipts } from '../event-receipts.js';
|
|
4
|
+
import { object, uuidSchema } from './validation/primitives.js';
|
|
5
|
+
import { isEventReceiptScope } from './validation/receipts.js';
|
|
6
|
+
import { MAX_CARRIER_INVENTORY, MAX_CARRIER_PREFIX_ROUNDS } from './validation/carrier-projection.js';
|
|
7
|
+
export const MAX_CARRIER_READ_BYTES = 64 * 1024 * 1024;
|
|
8
|
+
const accepted = new WeakMap();
|
|
9
|
+
/** Return defensive content, never a persisted flag authorizing native mutation. */
|
|
10
|
+
export function carrierInventoryContent(read) {
|
|
11
|
+
const content = accepted.get(read);
|
|
12
|
+
if (!content)
|
|
13
|
+
throw new Error('unverified_carrier_inventory');
|
|
14
|
+
return structuredClone(content);
|
|
15
|
+
}
|
|
16
|
+
class FailedRead extends Error {
|
|
17
|
+
result;
|
|
18
|
+
constructor(result) {
|
|
19
|
+
super('carrier_inventory_read_failed');
|
|
20
|
+
this.result = result;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function conflict(message) { throw new FailedRead({ kind: 'conflict', message }); }
|
|
24
|
+
function value(result) { if (result.kind !== 'ok')
|
|
25
|
+
throw new FailedRead(result); return result.value; }
|
|
26
|
+
const uuid = (x) => uuidSchema.safeParse(x).success;
|
|
27
|
+
const count = (x) => typeof x === 'number' && Number.isSafeInteger(x) && x >= 0;
|
|
28
|
+
const sha = (x) => typeof x === 'string' && x.length === 64 && /^[a-f0-9]{64}$/.test(x);
|
|
29
|
+
const head = (x) => typeof x === 'string' && [40, 64].includes(x.length) && /^[a-f0-9]{40}(?:[a-f0-9]{24})?$/.test(x) && !/^0+$/.test(x);
|
|
30
|
+
function scopedTarget(target, carrier) {
|
|
31
|
+
return object(target) && typeof target.repo === 'string' && target.repo.toLowerCase() === carrier.scope.repo.toLowerCase() &&
|
|
32
|
+
target.pr_number === carrier.scope.pr_number;
|
|
33
|
+
}
|
|
34
|
+
function sameRunSelection(a, b) {
|
|
35
|
+
return isDeepStrictEqual({ ...a, scope: { ...a.scope, repo: a.scope.repo.toLowerCase() } }, { ...b, scope: { ...b.scope, repo: b.scope.repo.toLowerCase() } });
|
|
36
|
+
}
|
|
37
|
+
async function listRuns(sink, carrier) {
|
|
38
|
+
const rows = [];
|
|
39
|
+
let total;
|
|
40
|
+
let pages = 1;
|
|
41
|
+
for (let page = 1; page <= Math.max(pages, 1); page++) {
|
|
42
|
+
const query = new URLSearchParams({ repo: carrier.scope.repo, pr: String(carrier.scope.pr_number),
|
|
43
|
+
page: String(page), page_size: '100', order_by: 'received_at', order_dir: 'asc' });
|
|
44
|
+
const answer = value(await sink.getJson(`/api/v1/reviews/runs?${query}`, (data, meta) => {
|
|
45
|
+
if (!object(meta) || meta.org_id !== carrier.scope.org_id || meta.evidence_protocol_version !== 2 ||
|
|
46
|
+
meta.page !== page || meta.page_size !== 100 || !count(meta.total) || meta.total > MAX_CARRIER_INVENTORY ||
|
|
47
|
+
meta.total_pages !== Math.ceil(meta.total / 100) || !Array.isArray(data) ||
|
|
48
|
+
data.length !== Math.min(100, Math.max(0, meta.total - (page - 1) * 100)))
|
|
49
|
+
return null;
|
|
50
|
+
const runs = [];
|
|
51
|
+
for (const row of data) {
|
|
52
|
+
if (!object(row) || !uuid(row.id) || !scopedTarget(row.target, carrier) ||
|
|
53
|
+
!(row.converge === null || object(row.converge) && typeof row.converge.target === 'string' &&
|
|
54
|
+
row.converge.target.trim() === row.converge.target && row.converge.target.length > 0 &&
|
|
55
|
+
count(row.converge.round) && row.converge.round > 0))
|
|
56
|
+
return null;
|
|
57
|
+
runs.push({ id: row.id, target: row.target, converge: row.converge });
|
|
58
|
+
}
|
|
59
|
+
return { runs, total: meta.total, pages: meta.total_pages };
|
|
60
|
+
}, { requireCompleteRead: true }));
|
|
61
|
+
if (total !== undefined && total !== answer.total)
|
|
62
|
+
conflict('carrier_inventory_pagination_changed');
|
|
63
|
+
total = answer.total;
|
|
64
|
+
pages = answer.pages;
|
|
65
|
+
rows.push(...answer.runs);
|
|
66
|
+
}
|
|
67
|
+
if (rows.length !== total || new Set(rows.map(r => r.id)).size !== rows.length)
|
|
68
|
+
conflict('carrier_inventory_duplicate_or_missing_run');
|
|
69
|
+
return rows.sort((a, b) => a.id.localeCompare(b.id));
|
|
70
|
+
}
|
|
71
|
+
async function runView(sink, carrier, listed, actor, fullHistory = false) {
|
|
72
|
+
return value(await sink.getJson(`/api/v1/reviews/runs/${listed.id}`, (data, meta) => {
|
|
73
|
+
if (!object(meta) || meta.org_id !== carrier.scope.org_id || meta.actor_user_id !== actor ||
|
|
74
|
+
meta.evidence_protocol_version !== 2 || meta.claim_recovery_version !== 1 || !object(meta.recovery) ||
|
|
75
|
+
(fullHistory ? typeof meta.recovery.truncated !== 'boolean' : meta.recovery.truncated !== false) || !count(meta.recovery.event_sequence) ||
|
|
76
|
+
!object(data) || data.id !== listed.id || !scopedTarget(data.target, carrier) ||
|
|
77
|
+
!['pr', 'patch'].includes(data.target.kind) || !head(data.target.head_sha) ||
|
|
78
|
+
!isDeepStrictEqual(data.target, listed.target) || !isDeepStrictEqual(data.converge, listed.converge) ||
|
|
79
|
+
!object(data.converge) || !count(data.converge.round) || data.converge.round < 1 ||
|
|
80
|
+
data.converge.target !== carrier.target || !Array.isArray(data.artifacts))
|
|
81
|
+
return null;
|
|
82
|
+
const artifacts = data.artifacts.filter(a => object(a) && a.kind === 'report_json');
|
|
83
|
+
if (artifacts.length !== 1 || !sha(artifacts[0].declared_sha256) || !count(artifacts[0].declared_bytes) ||
|
|
84
|
+
artifacts[0].declared_bytes > 25_000_000 || typeof artifacts[0].stored !== 'boolean')
|
|
85
|
+
return null;
|
|
86
|
+
const sequence = meta.recovery.event_sequence;
|
|
87
|
+
const recovery = meta.recovery;
|
|
88
|
+
const classification = recovery.classification_event;
|
|
89
|
+
if (!(classification === null || object(classification) && uuid(classification.id) && count(classification.sequence) &&
|
|
90
|
+
classification.sequence > 0 && classification.sequence <= sequence && classification.round === data.converge.round) ||
|
|
91
|
+
!Array.isArray(recovery.native_corrections) || recovery.native_corrections.length > MAX_CARRIER_INVENTORY)
|
|
92
|
+
return null;
|
|
93
|
+
const corrections = [];
|
|
94
|
+
const eventSelectors = classification === null ? [] : [{ id: classification.id,
|
|
95
|
+
sequence: classification.sequence, kind: 'round_processed' }];
|
|
96
|
+
const correctionIds = new Set();
|
|
97
|
+
const eventSequences = new Set(eventSelectors.map(event => event.sequence));
|
|
98
|
+
for (const correction of recovery.native_corrections) {
|
|
99
|
+
if (!object(correction) || !uuid(correction.id) || !count(correction.sequence) || correction.sequence < 1 ||
|
|
100
|
+
correction.sequence > sequence || correctionIds.has(correction.id) ||
|
|
101
|
+
classification !== null && correction.id === classification.id)
|
|
102
|
+
return null;
|
|
103
|
+
if (eventSequences.has(correction.sequence))
|
|
104
|
+
return null;
|
|
105
|
+
correctionIds.add(correction.id);
|
|
106
|
+
eventSequences.add(correction.sequence);
|
|
107
|
+
corrections.push(correction.id);
|
|
108
|
+
eventSelectors.push({ id: correction.id, sequence: correction.sequence, kind: 'finding_identity_corrected' });
|
|
109
|
+
}
|
|
110
|
+
return { data, recovery, sequence,
|
|
111
|
+
classificationId: classification === null ? null : classification.id,
|
|
112
|
+
correctionIds: corrections, eventSelectors, bytes: artifacts[0].declared_bytes, stored: artifacts[0].stored,
|
|
113
|
+
source: { selector: { scope: { ...carrier.scope, repo: data.target.repo, run_id: listed.id },
|
|
114
|
+
target: carrier.target, round: data.converge.round, headSha: data.target.head_sha,
|
|
115
|
+
reportSha256: artifacts[0].declared_sha256 }, reportJson: null, storedRun: data,
|
|
116
|
+
classifications: [], correctionIds: corrections, corrections: [] } };
|
|
117
|
+
}, { requireCompleteRead: true }));
|
|
118
|
+
}
|
|
119
|
+
async function material(sink, view, retainedBytes) {
|
|
120
|
+
const source = view.source;
|
|
121
|
+
if (view.stored) {
|
|
122
|
+
const read = value(await sink.getArtifact(source.selector.scope.run_id, 'report_json', view.bytes));
|
|
123
|
+
if (read.sha256 !== source.selector.reportSha256 || read.bytes.length !== view.bytes)
|
|
124
|
+
conflict('carrier_inventory_artifact_changed');
|
|
125
|
+
const report = read.bytes.toString('utf8');
|
|
126
|
+
if (!Buffer.from(report, 'utf8').equals(read.bytes))
|
|
127
|
+
conflict('carrier_inventory_invalid_utf8');
|
|
128
|
+
source.reportJson = report;
|
|
129
|
+
}
|
|
130
|
+
const ids = [...(view.classificationId ? [view.classificationId] : []), ...view.correctionIds];
|
|
131
|
+
const selectorsById = new Map(view.eventSelectors.map(selector => [selector.id, selector]));
|
|
132
|
+
for (let offset = 0; offset < ids.length; offset += 50) {
|
|
133
|
+
const read = value(await readEventReceipts(sink, source.selector.scope, ids.slice(offset, offset + 50)));
|
|
134
|
+
for (const receipt of read.receipts) {
|
|
135
|
+
retainedBytes += Buffer.byteLength(JSON.stringify(receipt), 'utf8');
|
|
136
|
+
if (retainedBytes > MAX_CARRIER_READ_BYTES)
|
|
137
|
+
conflict('carrier_inventory_read_limit');
|
|
138
|
+
const selector = selectorsById.get(receipt.id);
|
|
139
|
+
if (!selector || receipt.sequence !== selector.sequence || receipt.kind !== selector.kind ||
|
|
140
|
+
receipt.sequence > view.sequence || receipt.converge_target !== source.selector.target || receipt.round !== source.selector.round) {
|
|
141
|
+
conflict('carrier_inventory_receipt_selector_conflict');
|
|
142
|
+
}
|
|
143
|
+
if (receipt.id === view.classificationId)
|
|
144
|
+
source.classifications.push(receipt);
|
|
145
|
+
else
|
|
146
|
+
source.corrections.push(receipt);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return retainedBytes;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Enumerate the exact PR's retained original sources and selected receipts. All
|
|
153
|
+
* initial contexts precede material reads; all final contexts and re-enumeration
|
|
154
|
+
* follow them. This authenticates a stable read window, never global historical
|
|
155
|
+
* absence, carrier retirement, disposition standing or permission to mutate.
|
|
156
|
+
* Missing source parts remain explicit inputs to the residual projection.
|
|
157
|
+
*/
|
|
158
|
+
export async function readCarrierInventory(sink, carrier, actorUserId) {
|
|
159
|
+
carrier = structuredClone(carrier);
|
|
160
|
+
if (!carrier || !isEventReceiptScope(carrier.scope) || !uuid(actorUserId) || !uuid(carrier.classificationId) ||
|
|
161
|
+
!head(carrier.headSha) || !sha(carrier.reportSha256) || !/^[a-f0-9]{16}$/.test(carrier.identity) ||
|
|
162
|
+
!['classified_group', 'legacy_pending'].includes(carrier.kind) || !count(carrier.round) || carrier.round < 1 ||
|
|
163
|
+
carrier.round > MAX_CARRIER_PREFIX_ROUNDS || typeof carrier.target !== 'string' ||
|
|
164
|
+
!carrier.target || carrier.target.trim() !== carrier.target || carrier.target.length > 500) {
|
|
165
|
+
throw new Error('invalid_carrier_inventory_selection');
|
|
166
|
+
}
|
|
167
|
+
if (sink.credentialSource === 'attest')
|
|
168
|
+
throw new Error('unsupported_attested_recovery');
|
|
169
|
+
if (sink.baseUrl !== carrier.scope.base_url)
|
|
170
|
+
throw new Error('carrier_inventory_destination_conflict');
|
|
171
|
+
try {
|
|
172
|
+
const listed = await listRuns(sink, carrier);
|
|
173
|
+
const selected = listed.filter(run => {
|
|
174
|
+
if (carrier.kind === 'classified_group')
|
|
175
|
+
return run.id === carrier.scope.run_id;
|
|
176
|
+
if (run.converge?.target !== carrier.target)
|
|
177
|
+
return false;
|
|
178
|
+
if (!count(run.converge.round) || run.converge.round < 1)
|
|
179
|
+
conflict('carrier_inventory_invalid_round');
|
|
180
|
+
return run.converge.round <= carrier.round;
|
|
181
|
+
});
|
|
182
|
+
if (!selected.some(run => run.id === carrier.scope.run_id))
|
|
183
|
+
conflict('carrier_inventory_carrier_unavailable');
|
|
184
|
+
const views = [];
|
|
185
|
+
let retainedBytes = 0;
|
|
186
|
+
for (const run of selected) {
|
|
187
|
+
const view = await runView(sink, carrier, run, actorUserId);
|
|
188
|
+
retainedBytes += view.bytes + Buffer.byteLength(JSON.stringify([view.data, view.recovery]), 'utf8');
|
|
189
|
+
if (retainedBytes > MAX_CARRIER_READ_BYTES)
|
|
190
|
+
conflict('carrier_inventory_read_limit');
|
|
191
|
+
views.push(view);
|
|
192
|
+
}
|
|
193
|
+
const own = views.find(view => view.source.selector.scope.run_id === carrier.scope.run_id);
|
|
194
|
+
if (own.classificationId !== carrier.classificationId || !sameRunSelection(own.source.selector, { scope: carrier.scope, target: carrier.target, round: carrier.round,
|
|
195
|
+
headSha: carrier.headSha, reportSha256: carrier.reportSha256 }))
|
|
196
|
+
conflict('carrier_inventory_carrier_binding_conflict');
|
|
197
|
+
for (const view of views)
|
|
198
|
+
retainedBytes = await material(sink, view, retainedBytes);
|
|
199
|
+
for (let index = 0; index < selected.length; index++) {
|
|
200
|
+
const after = await runView(sink, carrier, selected[index], actorUserId);
|
|
201
|
+
const before = views[index];
|
|
202
|
+
if (!isDeepStrictEqual(after.data, before.data) || !isDeepStrictEqual(after.recovery, before.recovery)) {
|
|
203
|
+
conflict('carrier_inventory_source_changed');
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (!isDeepStrictEqual(await listRuns(sink, carrier), listed))
|
|
207
|
+
conflict('carrier_inventory_run_set_changed');
|
|
208
|
+
const token = Object.freeze({ qualification: 'authenticated-carrier-inventory-read' });
|
|
209
|
+
accepted.set(token, structuredClone({ actorUserId, inventory: { carrier, inventoryStatus: 'complete',
|
|
210
|
+
sources: views.map(view => view.source) }, runSequences: views.map(view => ({
|
|
211
|
+
runId: view.source.selector.scope.run_id, eventSequence: view.sequence
|
|
212
|
+
})) }));
|
|
213
|
+
return { kind: 'ok', httpStatus: 200, value: token };
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
if (error instanceof FailedRead)
|
|
217
|
+
return error.result;
|
|
218
|
+
return { kind: 'unavailable', reason: 'carrier_inventory_read_failed' };
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
const histories = new WeakMap();
|
|
222
|
+
export function claimHistoryContent(token) {
|
|
223
|
+
const content = histories.get(token);
|
|
224
|
+
if (!content)
|
|
225
|
+
throw new Error('unverified_claim_history');
|
|
226
|
+
return structuredClone(content);
|
|
227
|
+
}
|
|
228
|
+
function prefix(actual, complete, exhaustive) {
|
|
229
|
+
if (!Array.isArray(actual) || !actual.every(claimEventSelector) || actual.length > complete.length)
|
|
230
|
+
return false;
|
|
231
|
+
return isDeepStrictEqual(actual, complete.slice(0, actual.length)) && (!exhaustive || actual.length === complete.length);
|
|
232
|
+
}
|
|
233
|
+
/** All retained same-target sources, including later rounds, with a complete pinned
|
|
234
|
+
* index followed by exact selected receipt reads. Inline truncation alone never
|
|
235
|
+
* authenticates completeness. Both source and event ceilings are read again. */
|
|
236
|
+
export async function readClaimTargetHistory(sink, selection, expectedActor) {
|
|
237
|
+
selection = structuredClone(selection);
|
|
238
|
+
if (!isEventReceiptScope(selection.scope) || !head(selection.headSha) || !sha(selection.reportSha256) ||
|
|
239
|
+
!count(selection.round) || selection.round < 1 || !selection.target || selection.target.trim() !== selection.target ||
|
|
240
|
+
expectedActor !== undefined && !uuid(expectedActor))
|
|
241
|
+
throw new Error('invalid_claim_history_selection');
|
|
242
|
+
if (sink.credentialSource === 'attest' || sink.baseUrl !== selection.scope.base_url)
|
|
243
|
+
throw new Error('claim_history_destination_conflict');
|
|
244
|
+
const startedAt = new Date().toISOString();
|
|
245
|
+
try {
|
|
246
|
+
// Bootstrap only the actor; this response grants no mutation authority.
|
|
247
|
+
const actor = value(await sink.getJson(`/api/v1/reviews/runs/${selection.scope.run_id}`, (data, meta) => object(data) && data.id === selection.scope.run_id && object(meta) && meta.org_id === selection.scope.org_id &&
|
|
248
|
+
meta.evidence_protocol_version === 2 && meta.claim_recovery_version === 1 && uuid(meta.actor_user_id) &&
|
|
249
|
+
(expectedActor === undefined || expectedActor === meta.actor_user_id) ? meta.actor_user_id : null, { requireCompleteRead: true }));
|
|
250
|
+
// Only scope fields are used by list/run readers. No synthetic carrier proof
|
|
251
|
+
// is exposed or accepted: the result contains original sources and receipts.
|
|
252
|
+
const scope = selection;
|
|
253
|
+
const listed = await listRuns(sink, scope);
|
|
254
|
+
const selected = listed.filter(run => run.converge?.target === selection.target);
|
|
255
|
+
if (!selected.some(run => run.id === selection.scope.run_id))
|
|
256
|
+
conflict('claim_history_source_unavailable');
|
|
257
|
+
const views = [];
|
|
258
|
+
let bytes = 0;
|
|
259
|
+
for (const row of selected) {
|
|
260
|
+
const view = await runView(sink, scope, row, actor, true);
|
|
261
|
+
bytes += view.bytes + Buffer.byteLength(JSON.stringify([view.data, view.recovery]));
|
|
262
|
+
if (bytes > MAX_CARRIER_READ_BYTES)
|
|
263
|
+
conflict('claim_history_read_limit');
|
|
264
|
+
views.push(view);
|
|
265
|
+
}
|
|
266
|
+
const own = views.find(v => v.source.selector.scope.run_id === selection.scope.run_id);
|
|
267
|
+
if (!sameRunSelection(own.source.selector, selection))
|
|
268
|
+
conflict('claim_history_source_binding_conflict');
|
|
269
|
+
const content = { readWindow: { startedAt, completedAt: startedAt }, actorUserId: actor, sources: [], histories: [] };
|
|
270
|
+
for (const view of views) {
|
|
271
|
+
const index = value(await readClaimEventIndex(sink, view.source.selector.scope, view.sequence));
|
|
272
|
+
if (typeof view.recovery.claim_events_complete !== 'boolean' ||
|
|
273
|
+
!prefix(view.recovery.claim_events, index, view.recovery.claim_events_complete))
|
|
274
|
+
conflict('claim_history_inline_index_conflict');
|
|
275
|
+
const corrections = index.filter(e => e.kind === 'finding_identity_corrected');
|
|
276
|
+
const inline = view.eventSelectors.filter(e => e.kind === 'finding_identity_corrected').sort((a, b) => a.sequence - b.sequence);
|
|
277
|
+
if (!prefix(inline, corrections, view.recovery.truncated === false))
|
|
278
|
+
conflict('claim_history_correction_prefix_conflict');
|
|
279
|
+
const classifications = index.filter(e => e.kind === 'round_processed');
|
|
280
|
+
if (view.classificationId === null ? classifications.length !== 0 :
|
|
281
|
+
!classifications.some(e => e.id === view.classificationId && view.eventSelectors.some(i => isDeepStrictEqual(i, e)))) {
|
|
282
|
+
conflict('claim_history_classification_selector_conflict');
|
|
283
|
+
}
|
|
284
|
+
const receipts = [];
|
|
285
|
+
for (let offset = 0; offset < index.length; offset += 50) {
|
|
286
|
+
const chunk = index.slice(offset, offset + 50);
|
|
287
|
+
const read = value(await readEventReceipts(sink, view.source.selector.scope, chunk.map(e => e.id)));
|
|
288
|
+
if (read.missing.length || read.receipts.length !== chunk.length)
|
|
289
|
+
conflict('claim_history_receipt_unavailable');
|
|
290
|
+
for (const receipt of read.receipts) {
|
|
291
|
+
const selected = chunk.find(e => e.id === receipt.id);
|
|
292
|
+
if (receipt.sequence !== selected.sequence || receipt.kind !== selected.kind ||
|
|
293
|
+
receipt.converge_target !== selection.target || receipt.round !== view.source.selector.round) {
|
|
294
|
+
conflict('claim_history_receipt_selector_conflict');
|
|
295
|
+
}
|
|
296
|
+
bytes += Buffer.byteLength(JSON.stringify(receipt));
|
|
297
|
+
if (bytes > MAX_CARRIER_READ_BYTES)
|
|
298
|
+
conflict('claim_history_read_limit');
|
|
299
|
+
receipts.push(receipt);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
receipts.sort((a, b) => a.sequence - b.sequence);
|
|
303
|
+
view.source.classifications = receipts.filter(e => e.kind === 'round_processed');
|
|
304
|
+
view.source.corrections = receipts.filter(e => e.kind === 'finding_identity_corrected');
|
|
305
|
+
view.source.correctionIds = view.source.corrections.map(e => e.id);
|
|
306
|
+
if (view.stored) {
|
|
307
|
+
const raw = value(await sink.getArtifact(view.source.selector.scope.run_id, 'report_json', view.bytes));
|
|
308
|
+
if (raw.sha256 !== view.source.selector.reportSha256 || raw.bytes.length !== view.bytes ||
|
|
309
|
+
!Buffer.from(raw.bytes.toString('utf8')).equals(raw.bytes))
|
|
310
|
+
conflict('claim_history_artifact_conflict');
|
|
311
|
+
view.source.reportJson = raw.bytes.toString('utf8');
|
|
312
|
+
}
|
|
313
|
+
content.sources.push(view.source);
|
|
314
|
+
content.histories.push({ runId: view.source.selector.scope.run_id, eventSequence: view.sequence, receipts });
|
|
315
|
+
}
|
|
316
|
+
for (let i = 0; i < selected.length; i++) {
|
|
317
|
+
const after = await runView(sink, scope, selected[i], actor, true);
|
|
318
|
+
const before = views[i];
|
|
319
|
+
if (!isDeepStrictEqual(after.data, before.data) || !isDeepStrictEqual(after.recovery, before.recovery))
|
|
320
|
+
conflict('claim_history_changed');
|
|
321
|
+
}
|
|
322
|
+
if (!isDeepStrictEqual(await listRuns(sink, scope), listed))
|
|
323
|
+
conflict('claim_history_run_set_changed');
|
|
324
|
+
content.readWindow.completedAt = new Date().toISOString();
|
|
325
|
+
const token = Object.freeze({ qualification: 'authenticated-claim-history' });
|
|
326
|
+
histories.set(token, structuredClone(content));
|
|
327
|
+
return { kind: 'ok', httpStatus: 200, value: token };
|
|
328
|
+
}
|
|
329
|
+
catch (error) {
|
|
330
|
+
if (error instanceof FailedRead)
|
|
331
|
+
return error.result;
|
|
332
|
+
return { kind: 'unavailable', reason: 'claim_history_read_failed' };
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
export function sameClaimHistoryEvidence(first, second) {
|
|
336
|
+
const { readWindow: _first, ...a } = first;
|
|
337
|
+
const { readWindow: _second, ...b } = second;
|
|
338
|
+
return isDeepStrictEqual(a, b);
|
|
339
|
+
}
|
|
340
|
+
//# sourceMappingURL=carrier-inventory.js.map
|