wire-mesh-core 4.0.3 → 4.0.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/dist/adapters/byte-stream-connection.cjs +15 -4
- package/dist/adapters/byte-stream-connection.mjs +15 -4
- package/dist/adapters/memory-storage.d.cts +1 -1
- package/dist/adapters/memory-storage.d.mts +1 -1
- package/dist/adapters/node-fs-storage.cjs +4 -3
- package/dist/adapters/node-fs-storage.d.cts +1 -1
- package/dist/adapters/node-fs-storage.d.mts +1 -1
- package/dist/adapters/node-fs-storage.mjs +4 -3
- package/dist/adapters/system-clock.d.cts +1 -1
- package/dist/adapters/system-clock.d.mts +1 -1
- package/dist/clock-DhUPRfLh.d.cts +10 -0
- package/dist/clock-DhUPRfLh.d.mts +10 -0
- package/dist/domain/bulk.cjs +21 -12
- package/dist/domain/bulk.d.cts +1 -1
- package/dist/domain/bulk.d.mts +1 -1
- package/dist/domain/bulk.mjs +21 -12
- package/dist/domain/capability-grant.d.cts +1 -1
- package/dist/domain/capability-grant.d.mts +1 -1
- package/dist/domain/capability-request.d.cts +2 -2
- package/dist/domain/capability-request.d.mts +2 -2
- package/dist/domain/data-sync.cjs +22 -19
- package/dist/domain/data-sync.d.cts +1 -1
- package/dist/domain/data-sync.d.mts +1 -1
- package/dist/domain/data-sync.mjs +22 -19
- package/dist/domain/gossip-expansion.cjs +11 -5
- package/dist/domain/gossip-expansion.mjs +11 -5
- package/dist/domain/hub-mailbox.d.cts +1 -1
- package/dist/domain/hub-mailbox.d.mts +1 -1
- package/dist/domain/mesh-session.cjs +27 -14
- package/dist/domain/mesh-session.d.cts +1 -1
- package/dist/domain/mesh-session.d.mts +1 -1
- package/dist/domain/mesh-session.mjs +27 -14
- package/dist/domain/notice-board.cjs +4 -1
- package/dist/domain/notice-board.d.cts +2 -2
- package/dist/domain/notice-board.d.mts +2 -2
- package/dist/domain/notice-board.mjs +4 -1
- package/dist/domain/path-trace.d.cts +1 -1
- package/dist/domain/path-trace.d.mts +1 -1
- package/dist/domain/relay-hub.cjs +15 -10
- package/dist/domain/relay-hub.mjs +15 -10
- package/dist/domain/room-rekey.d.cts +1 -1
- package/dist/domain/room-rekey.d.mts +1 -1
- package/dist/domain/room.d.cts +1 -1
- package/dist/domain/room.d.mts +1 -1
- package/dist/domain/token-predicates.cjs +10 -10
- package/dist/domain/token-predicates.d.cts +1 -1
- package/dist/domain/token-predicates.d.mts +1 -1
- package/dist/domain/token-predicates.mjs +10 -10
- package/dist/domain/tokens.cjs +16 -4
- package/dist/domain/tokens.d.cts +2 -2
- package/dist/domain/tokens.d.mts +2 -2
- package/dist/domain/tokens.mjs +16 -4
- package/dist/ports/clock.d.cts +1 -1
- package/dist/ports/clock.d.mts +1 -1
- package/dist/ports/storage.d.cts +1 -1
- package/dist/ports/storage.d.mts +1 -1
- package/dist/sequential-CtQVlW8M.cjs +31 -0
- package/dist/sequential-D_W3THly.mjs +20 -0
- package/dist/storage-jbGFAYIV.d.cts +13 -0
- package/dist/storage-jbGFAYIV.d.mts +13 -0
- package/dist/{token-predicates-DqpG8VZZ.d.mts → token-predicates-CrieP1WI.d.mts} +1 -1
- package/dist/{token-predicates-D4NNPLnp.d.cts → token-predicates-lg4TtUxm.d.cts} +1 -1
- package/package.json +2 -2
- package/dist/clock-DiSx-WKM.d.cts +0 -7
- package/dist/clock-DiSx-WKM.d.mts +0 -7
- package/dist/storage-B403CRyu.d.cts +0 -10
- package/dist/storage-B403CRyu.d.mts +0 -10
|
@@ -22,16 +22,27 @@ function concat(first, second) {
|
|
|
22
22
|
joined.set(second, first.length);
|
|
23
23
|
return joined;
|
|
24
24
|
}
|
|
25
|
+
/** A reader's chunks as an async iterable, which ends with the stream and leaves releasing or cancelling the reader to its owner. */
|
|
26
|
+
function chunksOf(reader) {
|
|
27
|
+
return { [Symbol.asyncIterator]: () => ({ next: async () => {
|
|
28
|
+
const chunk = await reader.read();
|
|
29
|
+
return chunk.done ? {
|
|
30
|
+
done: true,
|
|
31
|
+
value: void 0
|
|
32
|
+
} : {
|
|
33
|
+
done: false,
|
|
34
|
+
value: chunk.value
|
|
35
|
+
};
|
|
36
|
+
} }) };
|
|
37
|
+
}
|
|
25
38
|
/**
|
|
26
39
|
* Frames arriving on a byte stream, in order, until it ends. An empty body (STREAM_OPEN_MARKER) is skipped. A body that does not decode as CBOR, or a length prefix above MAX_FRAME_BYTES, rejects the iteration (a connection-level failure); a body that decodes but fails frame validation is dropped and the stream continues, since an unrecognised frame from a newer peer is what version negotiation exists to tolerate.
|
|
27
40
|
*/
|
|
28
41
|
async function* readFrames(reader) {
|
|
29
42
|
let buffered = /* @__PURE__ */ new Uint8Array(0);
|
|
30
43
|
try {
|
|
31
|
-
for (
|
|
32
|
-
|
|
33
|
-
if (chunk.done) return;
|
|
34
|
-
buffered = concat(buffered, chunk.value);
|
|
44
|
+
for await (const chunk of chunksOf(reader)) {
|
|
45
|
+
buffered = concat(buffered, chunk);
|
|
35
46
|
while (buffered.length >= LENGTH_PREFIX_BYTES) {
|
|
36
47
|
const bodyLength = new DataView(buffered.buffer, buffered.byteOffset).getUint32(0);
|
|
37
48
|
if (bodyLength > 104857600) throw new Error(`frame of ${String(bodyLength)} bytes exceeds the ${String(MAX_FRAME_BYTES)}-byte limit`);
|
|
@@ -21,16 +21,27 @@ function concat(first, second) {
|
|
|
21
21
|
joined.set(second, first.length);
|
|
22
22
|
return joined;
|
|
23
23
|
}
|
|
24
|
+
/** A reader's chunks as an async iterable, which ends with the stream and leaves releasing or cancelling the reader to its owner. */
|
|
25
|
+
function chunksOf(reader) {
|
|
26
|
+
return { [Symbol.asyncIterator]: () => ({ next: async () => {
|
|
27
|
+
const chunk = await reader.read();
|
|
28
|
+
return chunk.done ? {
|
|
29
|
+
done: true,
|
|
30
|
+
value: void 0
|
|
31
|
+
} : {
|
|
32
|
+
done: false,
|
|
33
|
+
value: chunk.value
|
|
34
|
+
};
|
|
35
|
+
} }) };
|
|
36
|
+
}
|
|
24
37
|
/**
|
|
25
38
|
* Frames arriving on a byte stream, in order, until it ends. An empty body (STREAM_OPEN_MARKER) is skipped. A body that does not decode as CBOR, or a length prefix above MAX_FRAME_BYTES, rejects the iteration (a connection-level failure); a body that decodes but fails frame validation is dropped and the stream continues, since an unrecognised frame from a newer peer is what version negotiation exists to tolerate.
|
|
26
39
|
*/
|
|
27
40
|
async function* readFrames(reader) {
|
|
28
41
|
let buffered = /* @__PURE__ */ new Uint8Array(0);
|
|
29
42
|
try {
|
|
30
|
-
for (
|
|
31
|
-
|
|
32
|
-
if (chunk.done) return;
|
|
33
|
-
buffered = concat(buffered, chunk.value);
|
|
43
|
+
for await (const chunk of chunksOf(reader)) {
|
|
44
|
+
buffered = concat(buffered, chunk);
|
|
34
45
|
while (buffered.length >= LENGTH_PREFIX_BYTES) {
|
|
35
46
|
const bodyLength = new DataView(buffered.buffer, buffered.byteOffset).getUint32(0);
|
|
36
47
|
if (bodyLength > 104857600) throw new Error(`frame of ${String(bodyLength)} bytes exceeds the ${String(MAX_FRAME_BYTES)}-byte limit`);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as KeyValueStorage } from "../storage-
|
|
1
|
+
import { t as KeyValueStorage } from "../storage-jbGFAYIV.cjs";
|
|
2
2
|
//#region src/adapters/memory-storage.d.ts
|
|
3
3
|
/** An in-process KeyValueStorage backed by a Map -- for tests and single-process nodes; a real deployment substitutes a persistent adapter behind the same contract without touching anything that depends on the port. Not declared `async`: every operation is genuinely synchronous under the hood, so the contract's Promise return is satisfied directly via Promise.resolve() rather than an async function with no await in its body. */
|
|
4
4
|
export declare function createMemoryStorage(): KeyValueStorage;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as KeyValueStorage } from "../storage-
|
|
1
|
+
import { t as KeyValueStorage } from "../storage-jbGFAYIV.mjs";
|
|
2
2
|
//#region src/adapters/memory-storage.d.ts
|
|
3
3
|
/** An in-process KeyValueStorage backed by a Map -- for tests and single-process nodes; a real deployment substitutes a persistent adapter behind the same contract without touching anything that depends on the port. Not declared `async`: every operation is genuinely synchronous under the hood, so the contract's Promise return is satisfied directly via Promise.resolve() rather than an async function with no await in its body. */
|
|
4
4
|
export declare function createMemoryStorage(): KeyValueStorage;
|
|
@@ -23,9 +23,10 @@ async function listAllRelativePaths(baseDir) {
|
|
|
23
23
|
if (isEnoent(error)) return [];
|
|
24
24
|
throw error;
|
|
25
25
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
return (await Promise.all(entries.map(async (entry) => ({
|
|
27
|
+
entry,
|
|
28
|
+
stat: await node_fs_promises.stat(node_path.join(baseDir, entry)).catch(() => null)
|
|
29
|
+
})))).filter(({ stat }) => stat?.isFile() === true).map(({ entry }) => entry.split(node_path.sep).join("/"));
|
|
29
30
|
}
|
|
30
31
|
function createNodeFsStorage(options) {
|
|
31
32
|
const baseDir = options.dir;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as KeyValueStorage } from "../storage-
|
|
1
|
+
import { t as KeyValueStorage } from "../storage-jbGFAYIV.cjs";
|
|
2
2
|
//#region src/adapters/node-fs-storage.d.ts
|
|
3
3
|
export interface NodeFsStorageOptions {
|
|
4
4
|
/** Directory to store entries under. Created on first write if it doesn't already exist. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as KeyValueStorage } from "../storage-
|
|
1
|
+
import { t as KeyValueStorage } from "../storage-jbGFAYIV.mjs";
|
|
2
2
|
//#region src/adapters/node-fs-storage.d.ts
|
|
3
3
|
export interface NodeFsStorageOptions {
|
|
4
4
|
/** Directory to store entries under. Created on first write if it doesn't already exist. */
|
|
@@ -19,9 +19,10 @@ async function listAllRelativePaths(baseDir) {
|
|
|
19
19
|
if (isEnoent(error)) return [];
|
|
20
20
|
throw error;
|
|
21
21
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
return (await Promise.all(entries.map(async (entry) => ({
|
|
23
|
+
entry,
|
|
24
|
+
stat: await fs.stat(path.join(baseDir, entry)).catch(() => null)
|
|
25
|
+
})))).filter(({ stat }) => stat?.isFile() === true).map(({ entry }) => entry.split(path.sep).join("/"));
|
|
25
26
|
}
|
|
26
27
|
function createNodeFsStorage(options) {
|
|
27
28
|
const baseDir = options.dir;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as Clock } from "../clock-
|
|
1
|
+
import { t as Clock } from "../clock-DhUPRfLh.cjs";
|
|
2
2
|
//#region src/adapters/system-clock.d.ts
|
|
3
3
|
/** Wraps the system wall clock -- the only place Date.now() appears in this package. */
|
|
4
4
|
export declare function createSystemClock(): Clock;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as Clock } from "../clock-
|
|
1
|
+
import { t as Clock } from "../clock-DhUPRfLh.mjs";
|
|
2
2
|
//#region src/adapters/system-clock.d.ts
|
|
3
3
|
/** Wraps the system wall clock -- the only place Date.now() appears in this package. */
|
|
4
4
|
export declare function createSystemClock(): Clock;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region src/ports/clock.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Injected time source, never pulled from global Date.now() directly in domain logic -- lets token-expiry and revocation-timestamp checks be tested deterministically and lets a consumer substitute a synchronised network clock later without touching domain code.
|
|
4
|
+
*/
|
|
5
|
+
interface Clock {
|
|
6
|
+
/** Current time as Unix milliseconds, matching every uint timestamp field in the protocol (token-claims.expires, revocation-entry.revoked-at, handshake params, etc.). */
|
|
7
|
+
now: () => number;
|
|
8
|
+
}
|
|
9
|
+
//#endregion
|
|
10
|
+
export { Clock as t };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region src/ports/clock.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Injected time source, never pulled from global Date.now() directly in domain logic -- lets token-expiry and revocation-timestamp checks be tested deterministically and lets a consumer substitute a synchronised network clock later without touching domain code.
|
|
4
|
+
*/
|
|
5
|
+
interface Clock {
|
|
6
|
+
/** Current time as Unix milliseconds, matching every uint timestamp field in the protocol (token-claims.expires, revocation-entry.revoked-at, handshake params, etc.). */
|
|
7
|
+
now: () => number;
|
|
8
|
+
}
|
|
9
|
+
//#endregion
|
|
10
|
+
export { Clock as t };
|
package/dist/domain/bulk.cjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_generated_protocol = require("../generated/protocol.cjs");
|
|
3
3
|
const require_domain_device_id = require("./device-id.cjs");
|
|
4
|
+
const require_sequential = require("../sequential-CtQVlW8M.cjs");
|
|
4
5
|
//#region src/domain/bulk.ts
|
|
5
6
|
/**
|
|
6
7
|
* core/bulk (wire-mesh#34/#123): resumable, flow-controlled bulk transfer. `bulk.open`/`bulk.resume`/`bulk.cancel` ride manage-request-frame as ordinary manage-command params; `bulk-data`/`bulk-ack`/`bulk-end` are their own frame kind, riding the connection directly rather than a manage-response -- see spec/bulk.cddl's own header comment for the full design, including the three previously-open questions this module resolves in code (chunk-size negotiation is not a wire concept at all; transfer-id/issuer pairing is a verifier obligation, not a wire field; token expiry is checked once per manage-command, never per data frame).
|
|
@@ -103,22 +104,30 @@ function createBulkSender(options) {
|
|
|
103
104
|
let window = initialWindow;
|
|
104
105
|
let bytesInFlight = 0;
|
|
105
106
|
const inFlight = [];
|
|
107
|
+
/** Takes one ack's credit in: it replaces the window, and every chunk it covers is retired. */
|
|
108
|
+
const applyAck = (ack) => {
|
|
109
|
+
ackedCount = ack["ack-seq"];
|
|
110
|
+
window = ack.window;
|
|
111
|
+
for (let head = inFlight[0]; head !== void 0 && head.seq < ackedCount; head = inFlight[0]) {
|
|
112
|
+
inFlight.shift();
|
|
113
|
+
bytesInFlight -= head.length;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
const exceedsWindow = (length) => bytesInFlight + length > window;
|
|
117
|
+
/** Takes acks until `length` more bytes fit in the window. */
|
|
118
|
+
const awaitCredit = async (length) => {
|
|
119
|
+
applyAck(await waitForAck(id));
|
|
120
|
+
if (exceedsWindow(length)) await awaitCredit(length);
|
|
121
|
+
};
|
|
106
122
|
try {
|
|
107
123
|
for await (const chunk of options.source(fromSeq)) {
|
|
108
124
|
if (cancelled.has(id)) return {
|
|
109
125
|
status: "incomplete",
|
|
110
126
|
ackedCount
|
|
111
127
|
};
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
window = ack.window;
|
|
116
|
-
for (;;) {
|
|
117
|
-
const head = inFlight[0];
|
|
118
|
-
if (head === void 0 || head.seq >= ackedCount) break;
|
|
119
|
-
inFlight.shift();
|
|
120
|
-
bytesInFlight -= head.length;
|
|
121
|
-
}
|
|
128
|
+
if (exceedsWindow(chunk.length)) {
|
|
129
|
+
applyAck(await waitForAck(id));
|
|
130
|
+
if (exceedsWindow(chunk.length)) await awaitCredit(chunk.length);
|
|
122
131
|
}
|
|
123
132
|
const dataFrame = {
|
|
124
133
|
type: "bulk-data",
|
|
@@ -306,10 +315,10 @@ function createBulkReceiver(options) {
|
|
|
306
315
|
}
|
|
307
316
|
const ackedCount = await readAckSeq(idHex);
|
|
308
317
|
const chunks = [];
|
|
309
|
-
|
|
318
|
+
await require_sequential.eachInOrder(Array.from({ length: ackedCount }, (_, seq) => seq), async (seq) => {
|
|
310
319
|
const chunk = await storage.get(chunkKey(idHex, seq));
|
|
311
320
|
if (chunk !== void 0) chunks.push(chunk);
|
|
312
|
-
}
|
|
321
|
+
});
|
|
313
322
|
const ok = bytesEqual(await digestOf(concatChunks(chunks)), frame.digest);
|
|
314
323
|
reg.onComplete?.({
|
|
315
324
|
ok,
|
package/dist/domain/bulk.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { D as DeviceId, N as Frame, V as ManageCommand, f as CapabilityScope, p as CapabilityToken } from "../protocol-DzmpxxUr.cjs";
|
|
2
2
|
import { t as Connection } from "../transport-gDbSHO-r.cjs";
|
|
3
|
-
import { t as KeyValueStorage } from "../storage-
|
|
3
|
+
import { t as KeyValueStorage } from "../storage-jbGFAYIV.cjs";
|
|
4
4
|
import { IncomingManageRequest, MeshSession } from "./mesh-session.cjs";
|
|
5
5
|
//#region src/domain/bulk.d.ts
|
|
6
6
|
/** The two capabilities matching the direction each manage-command actually flows (spec/bulk.cddl): the sender proves write authority to open a transfer, the receiver proves read authority to ask for one to continue. */
|
package/dist/domain/bulk.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { D as DeviceId, N as Frame, V as ManageCommand, f as CapabilityScope, p as CapabilityToken } from "../protocol-DzmpxxUr.mjs";
|
|
2
2
|
import { t as Connection } from "../transport-bD3p_TO_.mjs";
|
|
3
|
-
import { t as KeyValueStorage } from "../storage-
|
|
3
|
+
import { t as KeyValueStorage } from "../storage-jbGFAYIV.mjs";
|
|
4
4
|
import { IncomingManageRequest, MeshSession } from "./mesh-session.mjs";
|
|
5
5
|
//#region src/domain/bulk.d.ts
|
|
6
6
|
/** The two capabilities matching the direction each manage-command actually flows (spec/bulk.cddl): the sender proves write authority to open a transfer, the receiver proves read authority to ask for one to continue. */
|
package/dist/domain/bulk.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { bulkCancelSchema, bulkOpenSchema, bulkResumeSchema } from "../generated/protocol.mjs";
|
|
2
2
|
import { bytesToHex } from "./device-id.mjs";
|
|
3
|
+
import { t as eachInOrder } from "../sequential-D_W3THly.mjs";
|
|
3
4
|
//#region src/domain/bulk.ts
|
|
4
5
|
/**
|
|
5
6
|
* core/bulk (wire-mesh#34/#123): resumable, flow-controlled bulk transfer. `bulk.open`/`bulk.resume`/`bulk.cancel` ride manage-request-frame as ordinary manage-command params; `bulk-data`/`bulk-ack`/`bulk-end` are their own frame kind, riding the connection directly rather than a manage-response -- see spec/bulk.cddl's own header comment for the full design, including the three previously-open questions this module resolves in code (chunk-size negotiation is not a wire concept at all; transfer-id/issuer pairing is a verifier obligation, not a wire field; token expiry is checked once per manage-command, never per data frame).
|
|
@@ -102,22 +103,30 @@ function createBulkSender(options) {
|
|
|
102
103
|
let window = initialWindow;
|
|
103
104
|
let bytesInFlight = 0;
|
|
104
105
|
const inFlight = [];
|
|
106
|
+
/** Takes one ack's credit in: it replaces the window, and every chunk it covers is retired. */
|
|
107
|
+
const applyAck = (ack) => {
|
|
108
|
+
ackedCount = ack["ack-seq"];
|
|
109
|
+
window = ack.window;
|
|
110
|
+
for (let head = inFlight[0]; head !== void 0 && head.seq < ackedCount; head = inFlight[0]) {
|
|
111
|
+
inFlight.shift();
|
|
112
|
+
bytesInFlight -= head.length;
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
const exceedsWindow = (length) => bytesInFlight + length > window;
|
|
116
|
+
/** Takes acks until `length` more bytes fit in the window. */
|
|
117
|
+
const awaitCredit = async (length) => {
|
|
118
|
+
applyAck(await waitForAck(id));
|
|
119
|
+
if (exceedsWindow(length)) await awaitCredit(length);
|
|
120
|
+
};
|
|
105
121
|
try {
|
|
106
122
|
for await (const chunk of options.source(fromSeq)) {
|
|
107
123
|
if (cancelled.has(id)) return {
|
|
108
124
|
status: "incomplete",
|
|
109
125
|
ackedCount
|
|
110
126
|
};
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
window = ack.window;
|
|
115
|
-
for (;;) {
|
|
116
|
-
const head = inFlight[0];
|
|
117
|
-
if (head === void 0 || head.seq >= ackedCount) break;
|
|
118
|
-
inFlight.shift();
|
|
119
|
-
bytesInFlight -= head.length;
|
|
120
|
-
}
|
|
127
|
+
if (exceedsWindow(chunk.length)) {
|
|
128
|
+
applyAck(await waitForAck(id));
|
|
129
|
+
if (exceedsWindow(chunk.length)) await awaitCredit(chunk.length);
|
|
121
130
|
}
|
|
122
131
|
const dataFrame = {
|
|
123
132
|
type: "bulk-data",
|
|
@@ -305,10 +314,10 @@ function createBulkReceiver(options) {
|
|
|
305
314
|
}
|
|
306
315
|
const ackedCount = await readAckSeq(idHex);
|
|
307
316
|
const chunks = [];
|
|
308
|
-
|
|
317
|
+
await eachInOrder(Array.from({ length: ackedCount }, (_, seq) => seq), async (seq) => {
|
|
309
318
|
const chunk = await storage.get(chunkKey(idHex, seq));
|
|
310
319
|
if (chunk !== void 0) chunks.push(chunk);
|
|
311
|
-
}
|
|
320
|
+
});
|
|
312
321
|
const ok = bytesEqual(await digestOf(concatChunks(chunks)), frame.digest);
|
|
313
322
|
reg.onComplete?.({
|
|
314
323
|
ok,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { D as DeviceId, V as ManageCommand, f as CapabilityScope, p as CapabilityToken } from "../protocol-DzmpxxUr.cjs";
|
|
2
2
|
import { t as IdentityPort } from "../identity-B6nhlSOT.cjs";
|
|
3
|
-
import { t as Clock } from "../clock-
|
|
3
|
+
import { t as Clock } from "../clock-DhUPRfLh.cjs";
|
|
4
4
|
import { IncomingManageRequest, ManageOutcome, MeshSession } from "./mesh-session.cjs";
|
|
5
5
|
import { RevocationCheck } from "./tokens.cjs";
|
|
6
6
|
//#region src/domain/capability-grant.d.ts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { D as DeviceId, V as ManageCommand, f as CapabilityScope, p as CapabilityToken } from "../protocol-DzmpxxUr.mjs";
|
|
2
2
|
import { t as IdentityPort } from "../identity-EV-HHv_D.mjs";
|
|
3
|
-
import { t as Clock } from "../clock-
|
|
3
|
+
import { t as Clock } from "../clock-DhUPRfLh.mjs";
|
|
4
4
|
import { IncomingManageRequest, ManageOutcome, MeshSession } from "./mesh-session.mjs";
|
|
5
5
|
import { RevocationCheck } from "./tokens.mjs";
|
|
6
6
|
//#region src/domain/capability-grant.d.ts
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { D as DeviceId, V as ManageCommand, f as CapabilityScope, p as CapabilityToken, u as CapabilityGrantOk } from "../protocol-DzmpxxUr.cjs";
|
|
2
2
|
import { t as IdentityPort } from "../identity-B6nhlSOT.cjs";
|
|
3
|
-
import { t as Clock } from "../clock-
|
|
3
|
+
import { t as Clock } from "../clock-DhUPRfLh.cjs";
|
|
4
4
|
import { IncomingManageRequest, MeshSession } from "./mesh-session.cjs";
|
|
5
|
-
import { m as TokenDelegateHandler, r as ConditionsContext } from "../token-predicates-
|
|
5
|
+
import { m as TokenDelegateHandler, r as ConditionsContext } from "../token-predicates-lg4TtUxm.cjs";
|
|
6
6
|
import { RevocationCheck } from "./tokens.cjs";
|
|
7
7
|
//#region src/domain/capability-request.d.ts
|
|
8
8
|
/**
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { D as DeviceId, V as ManageCommand, f as CapabilityScope, p as CapabilityToken, u as CapabilityGrantOk } from "../protocol-DzmpxxUr.mjs";
|
|
2
2
|
import { t as IdentityPort } from "../identity-EV-HHv_D.mjs";
|
|
3
|
-
import { t as Clock } from "../clock-
|
|
3
|
+
import { t as Clock } from "../clock-DhUPRfLh.mjs";
|
|
4
4
|
import { IncomingManageRequest, MeshSession } from "./mesh-session.mjs";
|
|
5
|
-
import { m as TokenDelegateHandler, r as ConditionsContext } from "../token-predicates-
|
|
5
|
+
import { m as TokenDelegateHandler, r as ConditionsContext } from "../token-predicates-CrieP1WI.mjs";
|
|
6
6
|
import { RevocationCheck } from "./tokens.mjs";
|
|
7
7
|
//#region src/domain/capability-request.d.ts
|
|
8
8
|
/**
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_domain_device_id = require("./device-id.cjs");
|
|
3
|
+
const require_sequential = require("../sequential-CtQVlW8M.cjs");
|
|
3
4
|
//#region src/domain/data-sync.ts
|
|
4
5
|
function headKey(peer) {
|
|
5
6
|
return `data/${require_domain_device_id.deviceIdToHex(peer)}/head`;
|
|
@@ -50,18 +51,28 @@ async function handleDataHave(storage, frame) {
|
|
|
50
51
|
};
|
|
51
52
|
}
|
|
52
53
|
/**
|
|
54
|
+
* The entries stored for `peer` from sequence `first` to `last` inclusive, read one after another and cut at the first hole: a hole is a record missing from a range the head claims is held, which should not happen while entryKey and headKey stay consistent, and stopping there is better than returning a run with a silent gap that a receiver's own check would only catch after the fact. Keys past a hole are never read.
|
|
55
|
+
*/
|
|
56
|
+
async function readRun(storage, peer, first, last) {
|
|
57
|
+
const run = [];
|
|
58
|
+
const readFrom = async (seq) => {
|
|
59
|
+
if (seq > last) return;
|
|
60
|
+
const entry = await storage.get(entryKey(peer, seq));
|
|
61
|
+
if (entry === void 0) return;
|
|
62
|
+
run.push(entry);
|
|
63
|
+
await readFrom(seq + 1);
|
|
64
|
+
};
|
|
65
|
+
await readFrom(first);
|
|
66
|
+
return run;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
53
69
|
* Handles an incoming data-request: the entries this store actually holds for `frame.peer` after `frame["from-seq"]`, capped at `limit`, or null if it holds none past that point. The responder need not be `frame.peer` itself -- any peer holding a replica may answer on the original author's behalf, per data-domain.cddl's own fan-out rule; this function makes no distinction.
|
|
54
70
|
*/
|
|
55
71
|
async function handleDataRequest(storage, frame, limit) {
|
|
56
72
|
const ourHead = await readHeadSeq(storage, frame.peer);
|
|
57
73
|
if (ourHead <= frame["from-seq"]) return null;
|
|
58
74
|
const lastSeq = Math.min(ourHead, frame["from-seq"] + limit);
|
|
59
|
-
const entries = [];
|
|
60
|
-
for (let seq = frame["from-seq"] + 1; seq <= lastSeq; seq += 1) {
|
|
61
|
-
const entry = await storage.get(entryKey(frame.peer, seq));
|
|
62
|
-
if (entry === void 0) break;
|
|
63
|
-
entries.push(entry);
|
|
64
|
-
}
|
|
75
|
+
const entries = await readRun(storage, frame.peer, frame["from-seq"] + 1, lastSeq);
|
|
65
76
|
if (entries.length === 0) return null;
|
|
66
77
|
return {
|
|
67
78
|
type: "data-entries",
|
|
@@ -79,12 +90,10 @@ async function handleDataEntries(storage, frame) {
|
|
|
79
90
|
ok: false,
|
|
80
91
|
reason: "gap"
|
|
81
92
|
};
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
await writeHeadSeq(storage, frame.peer, seq);
|
|
93
|
+
await require_sequential.eachInOrder(frame.entries, async (entry, index) => {
|
|
94
|
+
await storage.set(entryKey(frame.peer, frame["from-seq"] + 1 + index), entry);
|
|
95
|
+
});
|
|
96
|
+
await writeHeadSeq(storage, frame.peer, frame["from-seq"] + frame.entries.length);
|
|
88
97
|
return { ok: true };
|
|
89
98
|
}
|
|
90
99
|
/**
|
|
@@ -92,13 +101,7 @@ async function handleDataEntries(storage, frame) {
|
|
|
92
101
|
*/
|
|
93
102
|
async function readEntries(storage, peer, fromSeqExclusive) {
|
|
94
103
|
const head = await readHeadSeq(storage, peer);
|
|
95
|
-
|
|
96
|
-
for (let seq = fromSeqExclusive + 1; seq <= head; seq += 1) {
|
|
97
|
-
const entry = await storage.get(entryKey(peer, seq));
|
|
98
|
-
if (entry === void 0) break;
|
|
99
|
-
entries.push(entry);
|
|
100
|
-
}
|
|
101
|
-
return entries;
|
|
104
|
+
return readRun(storage, peer, fromSeqExclusive + 1, head);
|
|
102
105
|
}
|
|
103
106
|
//#endregion
|
|
104
107
|
exports.appendOwnEntry = appendOwnEntry;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { D as DeviceId, E as DataRequestFrame, T as DataHaveFrame, w as DataEntriesFrame } from "../protocol-DzmpxxUr.cjs";
|
|
2
|
-
import { t as KeyValueStorage } from "../storage-
|
|
2
|
+
import { t as KeyValueStorage } from "../storage-jbGFAYIV.cjs";
|
|
3
3
|
import { t as IdentityPort } from "../identity-B6nhlSOT.cjs";
|
|
4
4
|
//#region src/domain/data-sync.d.ts
|
|
5
5
|
export interface DataSyncOptions {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { D as DeviceId, E as DataRequestFrame, T as DataHaveFrame, w as DataEntriesFrame } from "../protocol-DzmpxxUr.mjs";
|
|
2
|
-
import { t as KeyValueStorage } from "../storage-
|
|
2
|
+
import { t as KeyValueStorage } from "../storage-jbGFAYIV.mjs";
|
|
3
3
|
import { t as IdentityPort } from "../identity-EV-HHv_D.mjs";
|
|
4
4
|
//#region src/domain/data-sync.d.ts
|
|
5
5
|
export interface DataSyncOptions {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { deviceIdToHex } from "./device-id.mjs";
|
|
2
|
+
import { t as eachInOrder } from "../sequential-D_W3THly.mjs";
|
|
2
3
|
//#region src/domain/data-sync.ts
|
|
3
4
|
function headKey(peer) {
|
|
4
5
|
return `data/${deviceIdToHex(peer)}/head`;
|
|
@@ -49,18 +50,28 @@ async function handleDataHave(storage, frame) {
|
|
|
49
50
|
};
|
|
50
51
|
}
|
|
51
52
|
/**
|
|
53
|
+
* The entries stored for `peer` from sequence `first` to `last` inclusive, read one after another and cut at the first hole: a hole is a record missing from a range the head claims is held, which should not happen while entryKey and headKey stay consistent, and stopping there is better than returning a run with a silent gap that a receiver's own check would only catch after the fact. Keys past a hole are never read.
|
|
54
|
+
*/
|
|
55
|
+
async function readRun(storage, peer, first, last) {
|
|
56
|
+
const run = [];
|
|
57
|
+
const readFrom = async (seq) => {
|
|
58
|
+
if (seq > last) return;
|
|
59
|
+
const entry = await storage.get(entryKey(peer, seq));
|
|
60
|
+
if (entry === void 0) return;
|
|
61
|
+
run.push(entry);
|
|
62
|
+
await readFrom(seq + 1);
|
|
63
|
+
};
|
|
64
|
+
await readFrom(first);
|
|
65
|
+
return run;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
52
68
|
* Handles an incoming data-request: the entries this store actually holds for `frame.peer` after `frame["from-seq"]`, capped at `limit`, or null if it holds none past that point. The responder need not be `frame.peer` itself -- any peer holding a replica may answer on the original author's behalf, per data-domain.cddl's own fan-out rule; this function makes no distinction.
|
|
53
69
|
*/
|
|
54
70
|
async function handleDataRequest(storage, frame, limit) {
|
|
55
71
|
const ourHead = await readHeadSeq(storage, frame.peer);
|
|
56
72
|
if (ourHead <= frame["from-seq"]) return null;
|
|
57
73
|
const lastSeq = Math.min(ourHead, frame["from-seq"] + limit);
|
|
58
|
-
const entries = [];
|
|
59
|
-
for (let seq = frame["from-seq"] + 1; seq <= lastSeq; seq += 1) {
|
|
60
|
-
const entry = await storage.get(entryKey(frame.peer, seq));
|
|
61
|
-
if (entry === void 0) break;
|
|
62
|
-
entries.push(entry);
|
|
63
|
-
}
|
|
74
|
+
const entries = await readRun(storage, frame.peer, frame["from-seq"] + 1, lastSeq);
|
|
64
75
|
if (entries.length === 0) return null;
|
|
65
76
|
return {
|
|
66
77
|
type: "data-entries",
|
|
@@ -78,12 +89,10 @@ async function handleDataEntries(storage, frame) {
|
|
|
78
89
|
ok: false,
|
|
79
90
|
reason: "gap"
|
|
80
91
|
};
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
await writeHeadSeq(storage, frame.peer, seq);
|
|
92
|
+
await eachInOrder(frame.entries, async (entry, index) => {
|
|
93
|
+
await storage.set(entryKey(frame.peer, frame["from-seq"] + 1 + index), entry);
|
|
94
|
+
});
|
|
95
|
+
await writeHeadSeq(storage, frame.peer, frame["from-seq"] + frame.entries.length);
|
|
87
96
|
return { ok: true };
|
|
88
97
|
}
|
|
89
98
|
/**
|
|
@@ -91,13 +100,7 @@ async function handleDataEntries(storage, frame) {
|
|
|
91
100
|
*/
|
|
92
101
|
async function readEntries(storage, peer, fromSeqExclusive) {
|
|
93
102
|
const head = await readHeadSeq(storage, peer);
|
|
94
|
-
|
|
95
|
-
for (let seq = fromSeqExclusive + 1; seq <= head; seq += 1) {
|
|
96
|
-
const entry = await storage.get(entryKey(peer, seq));
|
|
97
|
-
if (entry === void 0) break;
|
|
98
|
-
entries.push(entry);
|
|
99
|
-
}
|
|
100
|
-
return entries;
|
|
103
|
+
return readRun(storage, peer, fromSeqExclusive + 1, head);
|
|
101
104
|
}
|
|
102
105
|
//#endregion
|
|
103
106
|
export { appendOwnEntry, handleDataEntries, handleDataHave, handleDataRequest, headSeqFor, readEntries };
|
|
@@ -20,15 +20,21 @@ function createGossipExpansion(options) {
|
|
|
20
20
|
options.onExpansionDeclined?.(candidate);
|
|
21
21
|
return;
|
|
22
22
|
}
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
await dialInOrder(candidate, candidate.addresses, []);
|
|
24
|
+
}
|
|
25
|
+
/** Dials the addresses one at a time, in order, stopping at the first that connects: a later address is only tried once every earlier one has failed, and when none connects the failures, in order, are reported through `onExpansionFailed`. */
|
|
26
|
+
async function dialInOrder(candidate, addresses, failures) {
|
|
27
|
+
const [address, ...remaining] = addresses;
|
|
28
|
+
if (address === void 0) {
|
|
29
|
+
options.onExpansionFailed?.(candidate, failures);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
25
33
|
const session = await options.dial(address);
|
|
26
34
|
options.onExpanded(candidate, address, session);
|
|
27
|
-
return;
|
|
28
35
|
} catch (error) {
|
|
29
|
-
|
|
36
|
+
await dialInOrder(candidate, remaining, [...failures, toError(error)]);
|
|
30
37
|
}
|
|
31
|
-
options.onExpansionFailed?.(candidate, errors);
|
|
32
38
|
}
|
|
33
39
|
return { considerAdvert(advert) {
|
|
34
40
|
const hex = require_domain_device_id.deviceIdToHex(advert.device);
|
|
@@ -19,15 +19,21 @@ function createGossipExpansion(options) {
|
|
|
19
19
|
options.onExpansionDeclined?.(candidate);
|
|
20
20
|
return;
|
|
21
21
|
}
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
await dialInOrder(candidate, candidate.addresses, []);
|
|
23
|
+
}
|
|
24
|
+
/** Dials the addresses one at a time, in order, stopping at the first that connects: a later address is only tried once every earlier one has failed, and when none connects the failures, in order, are reported through `onExpansionFailed`. */
|
|
25
|
+
async function dialInOrder(candidate, addresses, failures) {
|
|
26
|
+
const [address, ...remaining] = addresses;
|
|
27
|
+
if (address === void 0) {
|
|
28
|
+
options.onExpansionFailed?.(candidate, failures);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
24
32
|
const session = await options.dial(address);
|
|
25
33
|
options.onExpanded(candidate, address, session);
|
|
26
|
-
return;
|
|
27
34
|
} catch (error) {
|
|
28
|
-
|
|
35
|
+
await dialInOrder(candidate, remaining, [...failures, toError(error)]);
|
|
29
36
|
}
|
|
30
|
-
options.onExpansionFailed?.(candidate, errors);
|
|
31
37
|
}
|
|
32
38
|
return { considerAdvert(advert) {
|
|
33
39
|
const hex = deviceIdToHex(advert.device);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { D as DeviceId, E as DataRequestFrame, N as Frame, T as DataHaveFrame, w as DataEntriesFrame } from "../protocol-DzmpxxUr.cjs";
|
|
2
|
-
import { t as KeyValueStorage } from "../storage-
|
|
2
|
+
import { t as KeyValueStorage } from "../storage-jbGFAYIV.cjs";
|
|
3
3
|
//#region src/domain/hub-mailbox.d.ts
|
|
4
4
|
export interface MailboxLimits {
|
|
5
5
|
/** How many devices' logs the mailbox will hold at once. */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { D as DeviceId, E as DataRequestFrame, N as Frame, T as DataHaveFrame, w as DataEntriesFrame } from "../protocol-DzmpxxUr.mjs";
|
|
2
|
-
import { t as KeyValueStorage } from "../storage-
|
|
2
|
+
import { t as KeyValueStorage } from "../storage-jbGFAYIV.mjs";
|
|
3
3
|
//#region src/domain/hub-mailbox.d.ts
|
|
4
4
|
export interface MailboxLimits {
|
|
5
5
|
/** How many devices' logs the mailbox will hold at once. */
|