@ifc-lite/flow-nodes 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -0
- package/dist/bcf-publication-capacity.d.ts +5 -0
- package/dist/bcf-publication-capacity.d.ts.map +1 -0
- package/dist/bcf-publication-capacity.js +44 -0
- package/dist/bcf-publication-capacity.js.map +1 -0
- package/dist/bcf-publication-check.d.ts +4 -0
- package/dist/bcf-publication-check.d.ts.map +1 -0
- package/dist/bcf-publication-check.js +100 -0
- package/dist/bcf-publication-check.js.map +1 -0
- package/dist/bcf-publication-codec.d.ts +53 -0
- package/dist/bcf-publication-codec.d.ts.map +1 -0
- package/dist/bcf-publication-codec.js +247 -0
- package/dist/bcf-publication-codec.js.map +1 -0
- package/dist/bcf-publication-epochs.d.ts +17 -0
- package/dist/bcf-publication-epochs.d.ts.map +1 -0
- package/dist/bcf-publication-epochs.js +104 -0
- package/dist/bcf-publication-epochs.js.map +1 -0
- package/dist/bcf-publication-history.d.ts +8 -0
- package/dist/bcf-publication-history.d.ts.map +1 -0
- package/dist/bcf-publication-history.js +84 -0
- package/dist/bcf-publication-history.js.map +1 -0
- package/dist/bcf-publication-node.d.ts +4 -0
- package/dist/bcf-publication-node.d.ts.map +1 -0
- package/dist/bcf-publication-node.js +235 -0
- package/dist/bcf-publication-node.js.map +1 -0
- package/dist/bcf-publication-owner.d.ts +7 -0
- package/dist/bcf-publication-owner.d.ts.map +1 -0
- package/dist/bcf-publication-owner.js +94 -0
- package/dist/bcf-publication-owner.js.map +1 -0
- package/dist/bcf-publication-store.d.ts +40 -0
- package/dist/bcf-publication-store.d.ts.map +1 -0
- package/dist/bcf-publication-store.js +312 -0
- package/dist/bcf-publication-store.js.map +1 -0
- package/dist/bcf-publication-types.d.ts +105 -0
- package/dist/bcf-publication-types.d.ts.map +1 -0
- package/dist/bcf-publication-types.js +5 -0
- package/dist/bcf-publication-types.js.map +1 -0
- package/dist/host.d.ts +16 -1
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js.map +1 -1
- package/dist/ids-nodes.d.ts.map +1 -1
- package/dist/ids-nodes.js +19 -5
- package/dist/ids-nodes.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/review-node.d.ts +3 -0
- package/dist/review-node.d.ts.map +1 -0
- package/dist/review-node.js +20 -0
- package/dist/review-node.js.map +1 -0
- package/package.json +14 -8
package/README.md
CHANGED
|
@@ -105,3 +105,79 @@ Direct remote failures expose only the safe failure class and valid HTTP status;
|
|
|
105
105
|
remote response bodies, detail URLs and error causes are discarded. Known local
|
|
106
106
|
preflight diagnostics and gateway refusal/recovery text are retained after
|
|
107
107
|
redacting the actual bearer, without carrying their original error cause.
|
|
108
|
+
|
|
109
|
+
`review.snapshot` is a volatile `model.read` node over a host-provided native
|
|
110
|
+
Review service. Its optional `after` input sequences the read after an analysis;
|
|
111
|
+
its bounded `snapshot` contains actual facts, coverage and producer provenance.
|
|
112
|
+
Hosts without a native capture service report the missing
|
|
113
|
+
`review` backend capability explicitly. This node does not rerun analysis or
|
|
114
|
+
write models, artifacts, publications or decisions. See
|
|
115
|
+
[the native Review read contract](../../docs/guide/flow.md#reading-captured-native-review).
|
|
116
|
+
|
|
117
|
+
## Durable native BCF publication
|
|
118
|
+
|
|
119
|
+
The Node-only `@ifc-lite/flow-nodes/bcf-publication-node` entry exports
|
|
120
|
+
`createNativeBcfWriteGateway({ directory, namespace, secrets })` for
|
|
121
|
+
`FlowHost.bcfWrites`. CLI hosts supply it automatically. An immutable,
|
|
122
|
+
fsynced intent is claimed before a create; a lost response or interrupted host
|
|
123
|
+
blocks the same target and payload across restarts. Bearers are ephemeral;
|
|
124
|
+
a payload containing a known run or write token is refused before persistence.
|
|
125
|
+
Known credentials are checked in values and dynamic keys; fixed public schema
|
|
126
|
+
keys such as `id`, `topicGuid` and `remoteGuid` are protocol names, not credential
|
|
127
|
+
data. Strict record and payload decoders still reject those names in an unknown
|
|
128
|
+
location. Authorization/token keys and credential URL/Bearer syntax remain
|
|
129
|
+
refused, and credential-bearing identifier values remain unsafe.
|
|
130
|
+
Every generated journal record is checked against the current known credentials
|
|
131
|
+
before publication. A credential that cannot be represented without appearing in
|
|
132
|
+
fixed metadata, such as the literal bearer `done`, is refused before the POST.
|
|
133
|
+
Fixed schema keys remain valid: bearers `id` and `intent` can use clean payloads
|
|
134
|
+
and receipt values. A later generated-value collision, such as a receipt timestamp
|
|
135
|
+
matching a known credential, refuses success and leaves the saved intent unresolved
|
|
136
|
+
for explicit checked recovery; it never persists that credential or retries.
|
|
137
|
+
BCF write nodes also redact the actual resolved bearer from successful server
|
|
138
|
+
responses before exposing output ports, including literal and Player tokens.
|
|
139
|
+
|
|
140
|
+
`list()` inspects one bounded local page. `listPage({cursor, limit})` returns
|
|
141
|
+
`{entries,nextCursor?}`; follow every cursor, including empty progress pages. `check(id, { token, networkGrants })` reads the
|
|
142
|
+
original HTTPS target with fresh capability grants and records matching GUID
|
|
143
|
+
candidates, including trim-equivalent title, description and comment text.
|
|
144
|
+
Server-edited or omitted fields cannot prove absence: unmatched objects make
|
|
145
|
+
the check unavailable. Only a complete empty enumeration with `confirmFullReadAccess: true` proves absence; the operator asserts complete visibility in the same access scope as the original write. Token presence alone is not proof of visibility. Without the assertion an empty enumeration is unavailable, while matching candidates remain available;
|
|
146
|
+
the create protocol supplies no stable client marker for excluding other objects.
|
|
147
|
+
It never retries a write. `reconcile(id, guid)` explicitly selects
|
|
148
|
+
a checked candidate. `confirmAbsent(id)` explicitly releases an intent only
|
|
149
|
+
after the latest complete check proved absence and persisted `fullReadAccessConfirmed: true`. Older markerless absence proofs remain readable but need a fresh asserted check before release. Anonymous genuinely public reads remain supported. No credential or principal identity is persisted. A check alone never
|
|
150
|
+
releases an unresolved create. Preserve the journal when an outcome is unknown.
|
|
151
|
+
|
|
152
|
+
Storage uses a nonempty `IFCLITE_BCF_OUTBOX_DIR` or defaults to `~/.ifc-lite/bcf-outbox`, isolated
|
|
153
|
+
by namespace. Filesystems without exclusive hard links or directory fsync
|
|
154
|
+
refuse publication. Malformed, oversized or orphaned records also refuse;
|
|
155
|
+
records are bounded to 256 KiB, with 256 generations per digest and 16 checks
|
|
156
|
+
per active immutable epoch. Immutable `v2/<prefix>/<digest>`
|
|
157
|
+
storage retains every receipt; 256 buckets each hold at most 4096 never-deleted
|
|
158
|
+
digest slots. Capacity refusal precedes sending and leaves existing recovery
|
|
159
|
+
readable. Listing streams history and bounds each page to 1000 rows, 5 MiB and
|
|
160
|
+
50,000 bounded record-work units; cursors are weakly consistent during concurrent writes. Old flat
|
|
161
|
+
journals are refused: preserve them and recover using their original build.
|
|
162
|
+
Linux boot/starttime and Windows creation-time queries distinguish process
|
|
163
|
+
instances. Unknown owners require `confirmOwnerStopped: true` on `check`, only
|
|
164
|
+
after operator verification; observed live instances always block recovery. The browser
|
|
165
|
+
continues to use its IndexedDB publication controls and the shared pure
|
|
166
|
+
`@ifc-lite/flow-nodes/bcf-publication` error classifier.
|
|
167
|
+
|
|
168
|
+
Recovery check epochs retain prior immutable files and locate the latest marker
|
|
169
|
+
with bounded exponential/binary probes over safe integer identities. Validation
|
|
170
|
+
reads the latest epoch and its embedded preceding proof; archived epoch bodies
|
|
171
|
+
are retained as evidence, without rereading their full history. An interrupted
|
|
172
|
+
check resumes the same reserved check ID with a fresh authorized read. A completed
|
|
173
|
+
unavailable check at an epoch boundary permits the next explicit check in a new
|
|
174
|
+
epoch, competing with settlement on the same exclusive transition. An empty new
|
|
175
|
+
epoch marker preserves the preceding proof. These operations never resend a create.
|
|
176
|
+
|
|
177
|
+
A native process admits at most 64 concurrent or locally completed sends whose
|
|
178
|
+
outcome could not be saved. Completed durable receipts consume no local tracking
|
|
179
|
+
slot. At this bound new creates refuse before any effect; checked reconciliation
|
|
180
|
+
or confirmed absence releases the retained local completion. No completion
|
|
181
|
+
identity is evicted to make room. Reads synchronize the visible receipt or proof's
|
|
182
|
+
directory before acknowledging it; an unresolved directory-sync fault refuses
|
|
183
|
+
recovery and subsequent creates without discarding the existing record.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const DIGEST_SLOTS = 4096;
|
|
2
|
+
export declare function capacityDigest(value: unknown): string;
|
|
3
|
+
/** Monotonic, never-deleted slots. Same-digest racers converge on the same first slot. */
|
|
4
|
+
export declare function reserveDigest(directory: string, digest: string, publish: (directory: string, filename: string, value: unknown) => Promise<void>, read: (directory: string, filename: string) => Promise<unknown>, sync: (directory: string) => Promise<void>): Promise<void>;
|
|
5
|
+
//# sourceMappingURL=bcf-publication-capacity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bcf-publication-capacity.d.ts","sourceRoot":"","sources":["../src/bcf-publication-capacity.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,YAAY,OAAQ,CAAC;AAElC,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAIrD;AAED,0FAA0F;AAC1F,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EACnE,OAAO,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,EAC/E,IAAI,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAkB5H"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
import { record, refuse } from './bcf-publication-codec.js';
|
|
5
|
+
export const DIGEST_SLOTS = 4_096;
|
|
6
|
+
const code = (error) => record(error) ? error.code : undefined;
|
|
7
|
+
export function capacityDigest(value) {
|
|
8
|
+
if (!record(value) || value.version !== 2 || typeof value.digest !== 'string' || !/^[a-f0-9]{64}$/.test(value.digest)
|
|
9
|
+
|| Object.keys(value).some(k => !['version', 'digest'].includes(k)))
|
|
10
|
+
refuse('invalid digest capacity record');
|
|
11
|
+
return value.digest;
|
|
12
|
+
}
|
|
13
|
+
/** Monotonic, never-deleted slots. Same-digest racers converge on the same first slot. */
|
|
14
|
+
export async function reserveDigest(directory, digest, publish, read, sync) {
|
|
15
|
+
for (let slot = 0; slot < DIGEST_SLOTS; slot += 1) {
|
|
16
|
+
const filename = `slot-${slot}.json`;
|
|
17
|
+
try {
|
|
18
|
+
if (capacityDigest(await read(directory, filename)) === digest) {
|
|
19
|
+
await sync(directory);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
if (code(error) !== 'ENOENT')
|
|
26
|
+
throw error;
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
await publish(directory, filename, { version: 2, digest });
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
if (code(error) !== 'EEXIST')
|
|
33
|
+
throw error;
|
|
34
|
+
// A competing same-digest claim won this exact first available slot.
|
|
35
|
+
if (capacityDigest(await read(directory, filename)) !== digest)
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
// A failed fsync leaves this immutable claim authoritative and reusable, never deleted.
|
|
39
|
+
await sync(directory);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
refuse('digest bucket capacity reached before sending; existing receipts remain readable');
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=bcf-publication-capacity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bcf-publication-capacity.js","sourceRoot":"","sources":["../src/bcf-publication-capacity.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAE5D,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,CAAC;AAClC,MAAM,IAAI,GAAG,CAAC,KAAc,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;AACxE,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;WAChH,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAAE,MAAM,CAAC,gCAAgC,CAAC,CAAC;IAChH,OAAO,KAAK,CAAC,MAAM,CAAC;AACtB,CAAC;AAED,0FAA0F;AAC1F,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,SAAiB,EAAE,MAAc,EACnE,OAA+E,EAC/E,IAA+D,EAAE,IAA0C;IAC3G,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,YAAY,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC;QAClD,MAAM,QAAQ,GAAG,QAAQ,IAAI,OAAO,CAAC;QACrC,IAAI,CAAC;YACH,IAAI,cAAc,CAAC,MAAM,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC;gBAAC,MAAM,IAAI,CAAC,SAAS,CAAC,CAAC;gBAAC,OAAO;YAAC,CAAC;YAClG,SAAS;QACX,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAAC,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,QAAQ;gBAAE,MAAM,KAAK,CAAC;QAAC,CAAC;QAC9D,IAAI,CAAC;YAAC,MAAM,OAAO,CAAC,SAAS,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;QAAC,CAAC;QACnE,OAAO,KAAK,EAAE,CAAC;YACb,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,QAAQ;gBAAE,MAAM,KAAK,CAAC;YAC1C,qEAAqE;YACrE,IAAI,cAAc,CAAC,MAAM,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,KAAK,MAAM;gBAAE,SAAS;QAC3E,CAAC;QACD,wFAAwF;QACxF,MAAM,IAAI,CAAC,SAAS,CAAC,CAAC;QACtB,OAAO;IACT,CAAC;IACD,MAAM,CAAC,kFAAkF,CAAC,CAAC;AAC7F,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { NativeBcfPublicationCheck, NativeBcfPublicationCheckOptions, StoredBcfAttempt } from './bcf-publication-types.js';
|
|
2
|
+
/** Explicit bounded, read-only lookup through the canonical capability-gated transport. */
|
|
3
|
+
export declare function lookupNativeBcfAttempt(attempt: StoredBcfAttempt, options: NativeBcfPublicationCheckOptions, secrets: readonly string[]): Promise<NativeBcfPublicationCheck>;
|
|
4
|
+
//# sourceMappingURL=bcf-publication-check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bcf-publication-check.d.ts","sourceRoot":"","sources":["../src/bcf-publication-check.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,yBAAyB,EAAE,gCAAgC,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAEhI,2FAA2F;AAC3F,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,gCAAgC,EAC/G,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAwEhE"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
import { BcfApiClient } from '@ifc-lite/bcf-api';
|
|
5
|
+
import { coreNetworkRequest } from '@ifc-lite/sandbox';
|
|
6
|
+
import { canonical, credentialFree, NATIVE_METADATA, record, refuse, text } from './bcf-publication-codec.js';
|
|
7
|
+
/** Explicit bounded, read-only lookup through the canonical capability-gated transport. */
|
|
8
|
+
export async function lookupNativeBcfAttempt(attempt, options, secrets) {
|
|
9
|
+
const intent = attempt.intent;
|
|
10
|
+
const sensitive = [...secrets, ...(options.token ? [options.token] : [])];
|
|
11
|
+
credentialFree(intent, sensitive);
|
|
12
|
+
const client = new BcfApiClient({ baseUrl: intent.baseUrl, version: intent.version, getAccessToken: () => options.token,
|
|
13
|
+
fetchFn: async (url, init) => {
|
|
14
|
+
if (init?.method && init.method !== 'GET')
|
|
15
|
+
refuse('recovery only permits reads');
|
|
16
|
+
const headers = {};
|
|
17
|
+
new Headers(init?.headers).forEach((value, key) => { headers[key] = value; });
|
|
18
|
+
const result = await coreNetworkRequest({ url, method: 'GET', headers, timeoutMs: 15_000, maxBytes: 5 * 1024 * 1024,
|
|
19
|
+
signal: options.signal }, options.networkGrants, options.networkTransport);
|
|
20
|
+
if (result.truncated)
|
|
21
|
+
refuse('server check exceeded response bound');
|
|
22
|
+
return new Response([204, 205, 304].includes(result.status) ? null : result.body, { status: result.status, headers: result.headers });
|
|
23
|
+
} });
|
|
24
|
+
const candidates = [];
|
|
25
|
+
const seen = new Set();
|
|
26
|
+
let incomparable = false;
|
|
27
|
+
const now = new Date();
|
|
28
|
+
function consider(item) {
|
|
29
|
+
if (!record(item) || !text(item.guid))
|
|
30
|
+
refuse('invalid server check response');
|
|
31
|
+
// Servers may trim free text on storage. Such a match still only proposes
|
|
32
|
+
// a manual candidate: discarding it could falsely release a duplicate write.
|
|
33
|
+
const remote = item;
|
|
34
|
+
if (!Object.entries(intent.payload).every(([key, value]) => {
|
|
35
|
+
const stored = remote[key];
|
|
36
|
+
if (['title', 'description', 'comment'].includes(key) && typeof stored === 'string' && typeof value === 'string') {
|
|
37
|
+
return stored.trim() === value.trim();
|
|
38
|
+
}
|
|
39
|
+
return canonical(stored ?? null) === canonical(value ?? null);
|
|
40
|
+
})) {
|
|
41
|
+
// A server may rewrite or omit mutable fields. Without a client marker
|
|
42
|
+
// or known remote GUID, a non-match cannot prove this is another effect.
|
|
43
|
+
incomparable = true;
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
credentialFree({ guid: item.guid }, sensitive);
|
|
47
|
+
if (!candidates.some(candidate => candidate.guid === item.guid))
|
|
48
|
+
candidates.push({ guid: item.guid });
|
|
49
|
+
if (candidates.length > 50)
|
|
50
|
+
refuse('too many matching recovery candidates');
|
|
51
|
+
}
|
|
52
|
+
if (intent.operation === 'createComment') {
|
|
53
|
+
const comments = await client.getComments(intent.projectId, intent.topicGuid ?? '', { signal: options.signal });
|
|
54
|
+
if (!Array.isArray(comments) || comments.length > 50_000)
|
|
55
|
+
refuse('comment check exceeds work bound');
|
|
56
|
+
for (const comment of comments)
|
|
57
|
+
consider(comment);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
let complete = false;
|
|
61
|
+
// Bound requests as well as entities: a server-capped one-row page must
|
|
62
|
+
// not turn the former 250-page budget into 50,000 network round trips.
|
|
63
|
+
for (let skip = 0, pages = 0; skip <= 50_000 && pages <= 250; pages += 1) {
|
|
64
|
+
const page = await client.getTopics(intent.projectId, { top: 200, skip }, { signal: options.signal });
|
|
65
|
+
if (!Array.isArray(page) || page.length > 50_000)
|
|
66
|
+
refuse('invalid topic page');
|
|
67
|
+
// $top is a maximum: a server may cap it below 200. Only an empty page
|
|
68
|
+
// proves exhaustion, and advancing by actual length avoids skipping rows.
|
|
69
|
+
if (page.length === 0) {
|
|
70
|
+
complete = true;
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
let fresh = 0;
|
|
74
|
+
for (const topic of page) {
|
|
75
|
+
if (!record(topic) || !text(topic.guid))
|
|
76
|
+
refuse('invalid server topic');
|
|
77
|
+
if (seen.has(topic.guid))
|
|
78
|
+
continue;
|
|
79
|
+
seen.add(topic.guid);
|
|
80
|
+
fresh += 1;
|
|
81
|
+
consider(topic);
|
|
82
|
+
}
|
|
83
|
+
if (seen.size > 50_000)
|
|
84
|
+
refuse('topic check exceeds work bound');
|
|
85
|
+
if (fresh === 0)
|
|
86
|
+
refuse('server ignored topic pagination');
|
|
87
|
+
skip += page.length;
|
|
88
|
+
}
|
|
89
|
+
// Partial enumeration cannot claim that an effect is absent.
|
|
90
|
+
if (!complete)
|
|
91
|
+
refuse('topic enumeration incomplete');
|
|
92
|
+
}
|
|
93
|
+
// An empty visible collection can hide a committed effect under another authority.
|
|
94
|
+
// Token presence does not prove visibility; only the operator can assert this scope.
|
|
95
|
+
const absenceConfirmed = !candidates.length && !incomparable && options.confirmFullReadAccess === true;
|
|
96
|
+
return { at: now.toISOString(), result: candidates.length ? NATIVE_METADATA.check.ambiguous
|
|
97
|
+
: absenceConfirmed ? NATIVE_METADATA.check.absent : NATIVE_METADATA.check.unavailable, candidates,
|
|
98
|
+
...(absenceConfirmed ? { fullReadAccessConfirmed: NATIVE_METADATA.fullReadAccessConfirmed } : {}) };
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=bcf-publication-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bcf-publication-check.js","sourceRoot":"","sources":["../src/bcf-publication-check.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { BcfWriteIntent } from './bcf-write-gateway.js';
|
|
2
|
+
import type { StoredBcfAttempt, StoredBcfCheck, StoredBcfOutcome } from './bcf-publication-types.js';
|
|
3
|
+
export declare const MAX_RECORD_BYTES: number;
|
|
4
|
+
export declare const MAX_GENERATIONS = 256;
|
|
5
|
+
export declare const MAX_RECORDS: number;
|
|
6
|
+
export declare const MAX_CHECKS = 16;
|
|
7
|
+
/** Shared construction/decoding values; the representability guard must not drift from saved records. */
|
|
8
|
+
export declare const NATIVE_METADATA: {
|
|
9
|
+
readonly state: {
|
|
10
|
+
readonly done: "done";
|
|
11
|
+
readonly failed: "failed";
|
|
12
|
+
readonly uncertain: "uncertain";
|
|
13
|
+
readonly sending: "sending";
|
|
14
|
+
};
|
|
15
|
+
readonly evidence: {
|
|
16
|
+
readonly response: "response";
|
|
17
|
+
readonly reconciled: "reconciled";
|
|
18
|
+
};
|
|
19
|
+
readonly check: {
|
|
20
|
+
readonly absent: "absent";
|
|
21
|
+
readonly ambiguous: "ambiguous";
|
|
22
|
+
readonly unavailable: "unavailable";
|
|
23
|
+
};
|
|
24
|
+
readonly owner: {
|
|
25
|
+
readonly alive: "alive";
|
|
26
|
+
readonly dead: "dead";
|
|
27
|
+
readonly unknown: "unknown";
|
|
28
|
+
};
|
|
29
|
+
readonly fullReadAccessConfirmed: true;
|
|
30
|
+
readonly assertion: "owner-stopped";
|
|
31
|
+
readonly confirmedAbsent: "confirmed-absent";
|
|
32
|
+
readonly failures: string[];
|
|
33
|
+
readonly message: {
|
|
34
|
+
readonly refused: "The request was refused without a confirmed effect.";
|
|
35
|
+
readonly unknown: "The server outcome is unknown; check before sending again.";
|
|
36
|
+
readonly absent: "Explicitly confirmed absent after a successful server check.";
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
export declare const record: (v: unknown) => v is Record<string, unknown>;
|
|
40
|
+
export declare const text: (v: unknown) => v is string;
|
|
41
|
+
export declare function refuse(message: string): never;
|
|
42
|
+
export declare function hash(value: string): string;
|
|
43
|
+
/** Stable object-key ordering; arrays retain their intentional order. */
|
|
44
|
+
export declare function canonical(value: unknown): string;
|
|
45
|
+
export declare function credentialFree(value: unknown, secrets: readonly string[]): void;
|
|
46
|
+
/** Refuse an unrepresentable bearer before effect, never success followed by unreadable generated metadata. */
|
|
47
|
+
export declare function assertNativeRepresentable(secrets: readonly string[]): void;
|
|
48
|
+
export declare function normalizeIntent(value: unknown, secrets?: readonly string[]): BcfWriteIntent;
|
|
49
|
+
export declare function intentDigest(intent: BcfWriteIntent): string;
|
|
50
|
+
export declare function decodeAttempt(value: unknown, filename: string): StoredBcfAttempt;
|
|
51
|
+
export declare function decodeOutcome(value: unknown, attempt: StoredBcfAttempt, checked?: boolean): StoredBcfOutcome;
|
|
52
|
+
export declare function decodeCheck(value: unknown, attempt: StoredBcfAttempt, filename: string): StoredBcfCheck;
|
|
53
|
+
//# sourceMappingURL=bcf-publication-codec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bcf-publication-codec.d.ts","sourceRoot":"","sources":["../src/bcf-publication-codec.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAErG,eAAO,MAAM,gBAAgB,QAAa,CAAC;AAC3C,eAAO,MAAM,eAAe,MAAM,CAAC;AACnC,eAAO,MAAM,WAAW,QAAsB,CAAC;AAC/C,eAAO,MAAM,UAAU,KAAK,CAAC;AAY7B,yGAAyG;AACzG,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAUlB,CAAC;AACX,eAAO,MAAM,MAAM,GAAI,GAAG,OAAO,KAAG,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAsD,CAAC;AACtH,eAAO,MAAM,IAAI,GAAI,GAAG,OAAO,KAAG,CAAC,IAAI,MAAqE,CAAC;AAE7G,wBAAgB,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,CAA4D;AAC1G,wBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAA6D;AAExG,yEAAyE;AACzE,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAIhD;AACD,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAqB/E;AAED,+GAA+G;AAC/G,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAI1E;AAcD,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,GAAE,SAAS,MAAM,EAAO,GAAG,cAAc,CA+B/F;AACD,wBAAgB,YAAY,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAG3D;AACD,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAahF;AACD,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,OAAO,UAAQ,GAAG,gBAAgB,CA2B1G;AACD,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,GAAG,cAAc,CAoBvG"}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
import { Buffer } from 'node:buffer';
|
|
5
|
+
import { createHash } from 'node:crypto';
|
|
6
|
+
import { normalizeBcfBaseUrl } from '@ifc-lite/bcf-api';
|
|
7
|
+
export const MAX_RECORD_BYTES = 256 * 1024;
|
|
8
|
+
export const MAX_GENERATIONS = 256;
|
|
9
|
+
export const MAX_RECORDS = 4 * MAX_GENERATIONS;
|
|
10
|
+
export const MAX_CHECKS = 16;
|
|
11
|
+
const TOPIC_FIELDS = ['title', 'topic_type', 'topic_status', 'priority', 'labels', 'assigned_to', 'stage', 'description', 'due_date'];
|
|
12
|
+
const COMMENT_FIELDS = ['comment', 'viewpoint_guid', 'reply_to_comment_guid'];
|
|
13
|
+
// These are protocol keys, not user credential data. Context-specific decoders
|
|
14
|
+
// still reject a known key appearing in the wrong record/payload location.
|
|
15
|
+
const SCHEMA_KEYS = new Set([...TOPIC_FIELDS, ...COMMENT_FIELDS,
|
|
16
|
+
'nodeType', 'operation', 'baseUrl', 'version', 'projectId', 'topicGuid', 'payload', 'intent',
|
|
17
|
+
'id', 'digest', 'generation', 'queuedAt', 'owner', 'pid', 'host', 'boot', 'domain', 'instance',
|
|
18
|
+
'state', 'receipt', 'remoteGuid', 'at', 'evidence', 'failure', 'code', 'message', 'status',
|
|
19
|
+
'checkId', 'check', 'result', 'candidates', 'fullReadAccessConfirmed', 'guid', 'candidateGuid', 'settlement', 'outcome', 'proofCheckId',
|
|
20
|
+
'assertion', 'epoch', 'previous', 'attempt', 'checks', 'checkPending', 'reconciled', 'ownerStatus', 'ownerConfirmed']);
|
|
21
|
+
const FAILURES = ['auth', 'permission', 'rejected', 'no-response', 'server-error', 'conflict-exists', 'interrupted'];
|
|
22
|
+
/** Shared construction/decoding values; the representability guard must not drift from saved records. */
|
|
23
|
+
export const NATIVE_METADATA = {
|
|
24
|
+
state: { done: 'done', failed: 'failed', uncertain: 'uncertain', sending: 'sending' },
|
|
25
|
+
evidence: { response: 'response', reconciled: 'reconciled' },
|
|
26
|
+
check: { absent: 'absent', ambiguous: 'ambiguous', unavailable: 'unavailable' },
|
|
27
|
+
owner: { alive: 'alive', dead: 'dead', unknown: 'unknown' },
|
|
28
|
+
fullReadAccessConfirmed: true,
|
|
29
|
+
assertion: 'owner-stopped', confirmedAbsent: 'confirmed-absent', failures: FAILURES,
|
|
30
|
+
message: { refused: 'The request was refused without a confirmed effect.',
|
|
31
|
+
unknown: 'The server outcome is unknown; check before sending again.',
|
|
32
|
+
absent: 'Explicitly confirmed absent after a successful server check.' },
|
|
33
|
+
};
|
|
34
|
+
export const record = (v) => !!v && typeof v === 'object' && !Array.isArray(v);
|
|
35
|
+
export const text = (v) => typeof v === 'string' && v.length > 0 && v.length <= 20_000;
|
|
36
|
+
const date = (v) => text(v) && Number.isFinite(Date.parse(v));
|
|
37
|
+
export function refuse(message) { throw new Error(`BCF publication refused: ${message}`); }
|
|
38
|
+
export function hash(value) { return createHash('sha256').update(value).digest('hex'); }
|
|
39
|
+
/** Stable object-key ordering; arrays retain their intentional order. */
|
|
40
|
+
export function canonical(value) {
|
|
41
|
+
if (Array.isArray(value))
|
|
42
|
+
return `[${value.map(canonical).join(',')}]`;
|
|
43
|
+
if (record(value))
|
|
44
|
+
return `{${Object.keys(value).sort().map(k => `${JSON.stringify(k)}:${canonical(value[k])}`).join(',')}}`;
|
|
45
|
+
return JSON.stringify(value);
|
|
46
|
+
}
|
|
47
|
+
export function credentialFree(value, secrets) {
|
|
48
|
+
const serialized = JSON.stringify(value);
|
|
49
|
+
if (/authorization\s*:\s*bearer\s+|"Bearer (?=[A-Za-z0-9._~+/=-]*[0-9._~+/=-])[A-Za-z0-9._~+/=-]{8,}"|[?&](?:access_token|token|password|secret)=|"(?:authorization|password|access_token|token|secret)"\s*:/i.test(serialized))
|
|
50
|
+
refuse('credentials must not enter durable state');
|
|
51
|
+
const sensitive = secrets.filter(secret => secret.length > 0).map(secret => JSON.stringify(secret).slice(1, -1));
|
|
52
|
+
if (!sensitive.length)
|
|
53
|
+
return;
|
|
54
|
+
const reject = (item) => {
|
|
55
|
+
const encoded = JSON.stringify(item);
|
|
56
|
+
if (encoded !== undefined && sensitive.some(secret => encoded.includes(secret)))
|
|
57
|
+
refuse('credentials must not enter durable state');
|
|
58
|
+
};
|
|
59
|
+
const pending = [value];
|
|
60
|
+
while (pending.length) {
|
|
61
|
+
const item = pending.pop();
|
|
62
|
+
if (Array.isArray(item)) {
|
|
63
|
+
for (const field of item)
|
|
64
|
+
pending.push(field);
|
|
65
|
+
}
|
|
66
|
+
else if (record(item)) {
|
|
67
|
+
for (const [key, field] of Object.entries(item)) {
|
|
68
|
+
if (!SCHEMA_KEYS.has(key))
|
|
69
|
+
reject(key);
|
|
70
|
+
pending.push(field);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
else
|
|
74
|
+
reject(item);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/** Refuse an unrepresentable bearer before effect, never success followed by unreadable generated metadata. */
|
|
78
|
+
export function assertNativeRepresentable(secrets) {
|
|
79
|
+
credentialFree([...Object.values(NATIVE_METADATA.state), ...Object.values(NATIVE_METADATA.evidence),
|
|
80
|
+
...Object.values(NATIVE_METADATA.check), ...Object.values(NATIVE_METADATA.owner), ...NATIVE_METADATA.failures,
|
|
81
|
+
NATIVE_METADATA.assertion, NATIVE_METADATA.fullReadAccessConfirmed, NATIVE_METADATA.confirmedAbsent, ...Object.values(NATIVE_METADATA.message)], secrets);
|
|
82
|
+
}
|
|
83
|
+
function validatePayload(value, comment) {
|
|
84
|
+
if (!record(value))
|
|
85
|
+
refuse('invalid write payload');
|
|
86
|
+
const allowed = comment ? COMMENT_FIELDS : TOPIC_FIELDS;
|
|
87
|
+
if (Object.keys(value).some(k => !allowed.includes(k)) || !text(value[comment ? 'comment' : 'title']))
|
|
88
|
+
refuse('invalid write payload');
|
|
89
|
+
for (const [key, item] of Object.entries(value)) {
|
|
90
|
+
if (item === null || item === undefined)
|
|
91
|
+
continue;
|
|
92
|
+
if (key === 'labels') {
|
|
93
|
+
if (!Array.isArray(item) || item.length > 500 || !item.every(v => typeof v === 'string' && v.length <= 20_000))
|
|
94
|
+
refuse('invalid labels');
|
|
95
|
+
}
|
|
96
|
+
else if (typeof item !== 'string' || item.length > 20_000)
|
|
97
|
+
refuse('invalid payload field');
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
export function normalizeIntent(value, secrets = []) {
|
|
101
|
+
if (!record(value) || !text(value.nodeType) || !text(value.baseUrl) || !text(value.version) || !text(value.projectId)
|
|
102
|
+
|| !['createTopic', 'createComment'].includes(String(value.operation)) || !record(value.payload))
|
|
103
|
+
refuse('invalid intent');
|
|
104
|
+
const keys = ['nodeType', 'operation', 'baseUrl', 'version', 'projectId', 'topicGuid', 'payload'];
|
|
105
|
+
if (Object.keys(value).some(k => !keys.includes(k)))
|
|
106
|
+
refuse('unknown intent fields');
|
|
107
|
+
let url;
|
|
108
|
+
try {
|
|
109
|
+
url = new URL(value.baseUrl);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
refuse('invalid server URL');
|
|
113
|
+
}
|
|
114
|
+
if (url.protocol !== 'https:' || url.username || url.password || url.search || url.hash)
|
|
115
|
+
refuse('server URL must be credential-free HTTPS');
|
|
116
|
+
if (!/^[A-Za-z0-9.-]{1,32}$/.test(value.version))
|
|
117
|
+
refuse('invalid API version');
|
|
118
|
+
const comment = value.operation === 'createComment';
|
|
119
|
+
if (value.nodeType !== (comment ? 'bcf.addComment' : 'bcf.createTopic'))
|
|
120
|
+
refuse('node and operation disagree');
|
|
121
|
+
if (comment ? !text(value.topicGuid) : value.topicGuid !== undefined)
|
|
122
|
+
refuse('invalid topic target');
|
|
123
|
+
validatePayload(value.payload, comment);
|
|
124
|
+
let serialized;
|
|
125
|
+
try {
|
|
126
|
+
serialized = JSON.stringify(value.payload);
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
refuse('invalid write payload');
|
|
130
|
+
}
|
|
131
|
+
if (serialized === undefined)
|
|
132
|
+
refuse('invalid write payload');
|
|
133
|
+
if (Buffer.byteLength(serialized) > MAX_RECORD_BYTES / 2)
|
|
134
|
+
refuse('intent exceeds storage bound');
|
|
135
|
+
let payload;
|
|
136
|
+
try {
|
|
137
|
+
payload = JSON.parse(serialized);
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
refuse('invalid write payload');
|
|
141
|
+
}
|
|
142
|
+
validatePayload(payload, comment);
|
|
143
|
+
// Custom serialization errors must not expose their message/cause to callers.
|
|
144
|
+
try {
|
|
145
|
+
credentialFree(value, secrets);
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
refuse('credentials must not enter durable state');
|
|
149
|
+
}
|
|
150
|
+
// Canonical identity must match the exact JSON schema persisted on disk.
|
|
151
|
+
// Spreading an own undefined optional key hashes data that JSON omits.
|
|
152
|
+
const normalized = { nodeType: value.nodeType, operation: comment ? 'createComment' : 'createTopic',
|
|
153
|
+
baseUrl: normalizeBcfBaseUrl(value.baseUrl), version: value.version, projectId: value.projectId,
|
|
154
|
+
...(comment && text(value.topicGuid) ? { topicGuid: value.topicGuid } : {}),
|
|
155
|
+
payload };
|
|
156
|
+
credentialFree(normalized, secrets);
|
|
157
|
+
if (Buffer.byteLength(JSON.stringify(normalized)) > MAX_RECORD_BYTES / 2)
|
|
158
|
+
refuse('intent exceeds storage bound');
|
|
159
|
+
return normalized;
|
|
160
|
+
}
|
|
161
|
+
export function intentDigest(intent) {
|
|
162
|
+
const { nodeType: _nodeType, ...effect } = intent;
|
|
163
|
+
return hash(canonical(effect));
|
|
164
|
+
}
|
|
165
|
+
export function decodeAttempt(value, filename) {
|
|
166
|
+
if (!record(value) || value.version !== 1 || !text(value.id) || !text(value.digest) || !Number.isSafeInteger(value.generation)
|
|
167
|
+
|| Number(value.generation) < 1 || !date(value.queuedAt) || !record(value.owner) || !Number.isSafeInteger(value.owner.pid)
|
|
168
|
+
|| Number(value.owner.pid) < 1 || !text(value.owner.host) || !/^[a-f0-9]{64}$/.test(value.owner.host)
|
|
169
|
+
|| Object.keys(value.owner).some(k => !['pid', 'host', 'boot', 'domain', 'instance'].includes(k)))
|
|
170
|
+
refuse('malformed attempt');
|
|
171
|
+
const owner = value.owner;
|
|
172
|
+
if (['boot', 'domain', 'instance'].some(k => owner[k] !== undefined)
|
|
173
|
+
&& !['boot', 'domain', 'instance'].every(k => typeof owner[k] === 'string' && /^[a-f0-9]{64}$/.test(owner[k])))
|
|
174
|
+
refuse('incomplete owner identity');
|
|
175
|
+
const intent = normalizeIntent(value.intent);
|
|
176
|
+
if (canonical(value.intent) !== canonical(intent) || value.digest !== intentDigest(intent)
|
|
177
|
+
|| value.id !== `${value.digest}-${value.generation}` || filename !== `${value.id}.intent.json`)
|
|
178
|
+
refuse('attempt identity mismatch');
|
|
179
|
+
if (Object.keys(value).some(k => !['version', 'id', 'digest', 'generation', 'intent', 'queuedAt', 'owner'].includes(k)))
|
|
180
|
+
refuse('unknown attempt fields');
|
|
181
|
+
return value;
|
|
182
|
+
}
|
|
183
|
+
export function decodeOutcome(value, attempt, checked = false) {
|
|
184
|
+
if (!record(value) || value.version !== 1 || value.id !== attempt.id || value.digest !== attempt.digest
|
|
185
|
+
|| !['done', 'failed', 'uncertain'].includes(String(value.state)))
|
|
186
|
+
refuse('malformed outcome');
|
|
187
|
+
if (Object.keys(value).some(k => !['version', 'id', 'digest', 'state', 'receipt', 'failure'].includes(k)))
|
|
188
|
+
refuse('unknown outcome fields');
|
|
189
|
+
if (value.state === 'done') {
|
|
190
|
+
const r = value.receipt;
|
|
191
|
+
if (value.failure !== undefined || !record(r) || !text(r.remoteGuid) || !date(r.at)
|
|
192
|
+
|| !['response', 'reconciled'].includes(String(r.evidence)) || (!checked && r.evidence !== 'response')
|
|
193
|
+
|| Object.keys(r).some(k => !['remoteGuid', 'at', 'evidence'].includes(k)))
|
|
194
|
+
refuse('invalid receipt');
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
const f = value.failure;
|
|
198
|
+
if (value.receipt !== undefined || !record(f) || !(FAILURES.includes(String(f.code)) || (checked && f.code === 'confirmed-absent')) || !text(f.message) || !date(f.at)
|
|
199
|
+
|| (f.status !== undefined && (!Number.isInteger(f.status) || Number(f.status) < 0 || Number(f.status) > 599))
|
|
200
|
+
|| Object.keys(f).some(k => !['code', 'message', 'at', 'status'].includes(k)))
|
|
201
|
+
refuse('invalid failure');
|
|
202
|
+
}
|
|
203
|
+
if (value.state === 'failed' && record(value.failure) && !['auth', 'permission', 'rejected', ...(checked ? ['confirmed-absent'] : [])].includes(String(value.failure.code)))
|
|
204
|
+
refuse('uncertain failure marked definite');
|
|
205
|
+
if (value.state === 'uncertain' && record(value.failure) && !['no-response', 'server-error', 'conflict-exists', 'interrupted'].includes(String(value.failure.code)))
|
|
206
|
+
refuse('definite failure marked uncertain');
|
|
207
|
+
if (record(value.failure)) {
|
|
208
|
+
const { code, status } = value.failure;
|
|
209
|
+
if ((code === 'auth' && status !== 401) || (code === 'permission' && status !== undefined && status !== 403)
|
|
210
|
+
|| (code === 'server-error' && (typeof status !== 'number' || status < 500))
|
|
211
|
+
|| (code === 'conflict-exists' && status !== 409)
|
|
212
|
+
|| (code === 'rejected' && (typeof status !== 'number' || status < 400 || status >= 500 || [401, 403, 409].includes(status)))
|
|
213
|
+
|| (['no-response', 'interrupted', 'confirmed-absent'].includes(String(code)) && status !== undefined && status !== 0))
|
|
214
|
+
refuse('failure status contradicts classification');
|
|
215
|
+
}
|
|
216
|
+
credentialFree(value, []);
|
|
217
|
+
return value;
|
|
218
|
+
}
|
|
219
|
+
export function decodeCheck(value, attempt, filename) {
|
|
220
|
+
if (!record(value) || value.version !== 1 || value.id !== attempt.id || value.digest !== attempt.digest || !text(value.checkId)
|
|
221
|
+
|| !/^[1-9][0-9]{0,15}$/.test(value.checkId) || !Number.isSafeInteger(Number(value.checkId)) || (filename !== `${attempt.id}.check-${value.checkId}.json` && filename !== `${attempt.id}.checked-${value.checkId}.json`) || !record(value.check))
|
|
222
|
+
refuse('malformed check');
|
|
223
|
+
const c = value.check;
|
|
224
|
+
if (!date(c.at) || !['absent', 'ambiguous', 'unavailable'].includes(String(c.result)) || !Array.isArray(c.candidates)
|
|
225
|
+
|| c.candidates.length > 50 || !c.candidates.every(v => record(v) && text(v.guid) && Object.keys(v).length === 1)
|
|
226
|
+
|| (c.result !== 'ambiguous' && c.candidates.length > 0)
|
|
227
|
+
|| (c.fullReadAccessConfirmed !== undefined && (c.fullReadAccessConfirmed !== true || c.result !== 'absent'))
|
|
228
|
+
|| Object.keys(c).some(k => !['at', 'result', 'candidates', 'fullReadAccessConfirmed'].includes(k))
|
|
229
|
+
|| Object.keys(value).some(k => !['version', 'id', 'digest', 'checkId', 'check', 'settlement'].includes(k)))
|
|
230
|
+
refuse('invalid checked candidates');
|
|
231
|
+
if (value.settlement !== undefined) {
|
|
232
|
+
const settled = value.settlement;
|
|
233
|
+
if (!record(settled) || !text(settled.proofCheckId) || Object.keys(settled).some(k => !['outcome', 'proofCheckId'].includes(k)))
|
|
234
|
+
refuse('malformed settlement');
|
|
235
|
+
const outcome = decodeOutcome(settled.outcome, attempt, true);
|
|
236
|
+
if (!(outcome.state === 'done' && outcome.receipt?.evidence === 'reconciled')
|
|
237
|
+
&& !(outcome.state === 'failed' && outcome.failure?.code === 'confirmed-absent'))
|
|
238
|
+
refuse('invalid settlement receipt');
|
|
239
|
+
if (filename.includes('.checked-'))
|
|
240
|
+
refuse('check response cannot settle an intent');
|
|
241
|
+
}
|
|
242
|
+
else if (filename.includes('.check-') && (c.result !== 'unavailable' || c.candidates.length))
|
|
243
|
+
refuse('check reservation must be unavailable');
|
|
244
|
+
credentialFree(value, []);
|
|
245
|
+
return value;
|
|
246
|
+
}
|
|
247
|
+
//# sourceMappingURL=bcf-publication-codec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bcf-publication-codec.js","sourceRoot":"","sources":["../src/bcf-publication-codec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { StoredBcfAttempt, StoredBcfCheck } from './bcf-publication-types.js';
|
|
2
|
+
export interface BcfCheckEpoch {
|
|
3
|
+
version: 1;
|
|
4
|
+
id: string;
|
|
5
|
+
digest: string;
|
|
6
|
+
epoch: number;
|
|
7
|
+
previous?: StoredBcfCheck;
|
|
8
|
+
}
|
|
9
|
+
export declare function epochOf(checkId: string): number;
|
|
10
|
+
/** Contiguous immutable markers permit bounded lookup; archived epoch bodies are never walked. */
|
|
11
|
+
export declare function loadCheckEpoch(attempt: StoredBcfAttempt, read: (epoch: number, filename: string) => Promise<unknown>): Promise<{
|
|
12
|
+
checks: StoredBcfCheck[];
|
|
13
|
+
pending: boolean;
|
|
14
|
+
}>;
|
|
15
|
+
/** Existing marker must contain the same immutable predecessor proof. */
|
|
16
|
+
export declare function verifyCheckEpoch(value: unknown, expected: BcfCheckEpoch, attempt: StoredBcfAttempt): void;
|
|
17
|
+
//# sourceMappingURL=bcf-publication-epochs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bcf-publication-epochs.d.ts","sourceRoot":"","sources":["../src/bcf-publication-epochs.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAInF,MAAM,WAAW,aAAa;IAAG,OAAO,EAAE,CAAC,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,cAAc,CAAA;CAAE;AACnH,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAM/C;AAcD,kGAAkG;AAClG,wBAAsB,cAAc,CAAC,OAAO,EAAE,gBAAgB,EAC5D,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,cAAc,EAAE,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAwCtH;AAED,yEAAyE;AACzE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAEzG"}
|