wire-mesh-core 4.1.1 → 4.1.2
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.
|
@@ -280,73 +280,129 @@ async function establishChannelFrom(identity, { pending, remote, peer, firstSend
|
|
|
280
280
|
function createRelayChannels(identity, sendHello, options = {}) {
|
|
281
281
|
if (options.firstSendCounter !== void 0) assertFirstSendCounter(options.firstSendCounter);
|
|
282
282
|
const channels = /* @__PURE__ */ new Map();
|
|
283
|
-
/**
|
|
284
|
-
function
|
|
283
|
+
/** Tells the owner of a failure that is local to this side. The callback is the owner's code: its own failure, thrown or as a rejected promise it returns, must not end the session or go unobserved, so it is reported once and swallowed. */
|
|
284
|
+
function reportFault(error) {
|
|
285
|
+
const report = (faultError) => {
|
|
286
|
+
console.error("the local fault callback failed", faultError);
|
|
287
|
+
};
|
|
288
|
+
try {
|
|
289
|
+
const result = options.onFault?.(error);
|
|
290
|
+
if (typeof result === "object" && result !== null && "then" in result && typeof result.then === "function") Promise.resolve(result).catch(report);
|
|
291
|
+
} catch (faultError) {
|
|
292
|
+
report(faultError);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
/** Abandons a handshake that failed: the entry goes if it is still the one held for the peer, and whoever awaits it is told. */
|
|
296
|
+
function abandon(entry, error) {
|
|
297
|
+
const failure = error instanceof Error ? error : new Error(String(error));
|
|
298
|
+
if (channels.get(entry.key) === entry) channels.delete(entry.key);
|
|
299
|
+
entry.settle.reject(failure);
|
|
300
|
+
entry.sent.reject(failure);
|
|
301
|
+
}
|
|
302
|
+
/** Starts a handshake with the peer: sends this side's hello, and registers the peer as awaiting at once, so a hello arriving meanwhile completes this handshake instead of starting a second. `answering` is true when a received hello started this, so nobody awaits the outcome and a failure to send is reported to the owner. `track` is false for a handshake answering a hello while a channel with that peer is established: it is not registered, so the established channel stays in place and in use until the new one is up and this side's hello has been sent. A hello that could not be sent leaves no channel behind: the peer would never learn this side's key. */
|
|
303
|
+
function start(peer, { answering, track } = {
|
|
304
|
+
answering: false,
|
|
305
|
+
track: true
|
|
306
|
+
}) {
|
|
285
307
|
const key = require_domain_device_id.deviceIdToHex(peer);
|
|
286
|
-
const settle = Promise.withResolvers();
|
|
287
308
|
const pending = beginHandshake(identity, peer);
|
|
288
309
|
const entry = {
|
|
289
310
|
status: "awaiting",
|
|
290
311
|
pending,
|
|
291
|
-
settle
|
|
312
|
+
settle: Promise.withResolvers(),
|
|
313
|
+
key,
|
|
314
|
+
sent: Promise.withResolvers()
|
|
292
315
|
};
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
316
|
+
entry.settle.promise.catch(() => void 0);
|
|
317
|
+
entry.sent.promise.catch(() => void 0);
|
|
318
|
+
pending.then(async ({ hello }) => {
|
|
319
|
+
try {
|
|
320
|
+
await sendHello(peer, hello);
|
|
321
|
+
} catch (error) {
|
|
322
|
+
abandon(entry, error);
|
|
323
|
+
entry.settle.promise.then((channel) => {
|
|
324
|
+
const held = channels.get(key);
|
|
325
|
+
if (held?.status === "established" && held.channel === channel) channels.delete(key);
|
|
326
|
+
}, () => void 0);
|
|
327
|
+
if (answering && track) reportFault(error);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
entry.sent.resolve(void 0);
|
|
331
|
+
}, (error) => {
|
|
332
|
+
abandon(entry, error);
|
|
297
333
|
});
|
|
334
|
+
if (track) channels.set(key, entry);
|
|
298
335
|
return entry;
|
|
299
336
|
}
|
|
337
|
+
/** Completes the handshake a verified hello belongs to. Throws on any failure of this side; the caller abandons the handshake. */
|
|
338
|
+
async function complete({ entry, hello, peer, replaces }) {
|
|
339
|
+
const { key } = entry;
|
|
340
|
+
const established = await establishChannelFrom(identity, {
|
|
341
|
+
pending: await entry.pending,
|
|
342
|
+
remote: hello,
|
|
343
|
+
peer,
|
|
344
|
+
...options.firstSendCounter === void 0 ? {} : { firstSendCounter: options.firstSendCounter }
|
|
345
|
+
});
|
|
346
|
+
if (established === void 0) {
|
|
347
|
+
abandon(entry, /* @__PURE__ */ new Error("the peer sent an unusable ephemeral key"));
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
if (replaces !== void 0) await entry.sent.promise;
|
|
351
|
+
if (channels.get(key) !== (replaces ?? entry)) return;
|
|
352
|
+
const channel = {
|
|
353
|
+
...established,
|
|
354
|
+
seal: async (frame) => {
|
|
355
|
+
try {
|
|
356
|
+
return await established.seal(frame);
|
|
357
|
+
} catch (error) {
|
|
358
|
+
const held = channels.get(key);
|
|
359
|
+
if (error instanceof require_domain_channel_exhausted_error.ChannelExhaustedError && held?.status === "established" && held.channel === channel) channels.delete(key);
|
|
360
|
+
throw error;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
channels.set(key, {
|
|
365
|
+
status: "established",
|
|
366
|
+
channel,
|
|
367
|
+
remoteEphemeralKey: hello["ephemeral-key"]
|
|
368
|
+
});
|
|
369
|
+
entry.settle.resolve(channel);
|
|
370
|
+
}
|
|
300
371
|
return {
|
|
301
372
|
ensure: async (peer) => {
|
|
302
|
-
const
|
|
373
|
+
const key = require_domain_device_id.deviceIdToHex(peer);
|
|
374
|
+
const existing = channels.get(key);
|
|
303
375
|
if (existing?.status === "established") return existing.channel;
|
|
304
376
|
return (existing ?? start(peer)).settle.promise;
|
|
305
377
|
},
|
|
306
378
|
applyHello: async (hello) => {
|
|
307
|
-
let
|
|
379
|
+
let entry;
|
|
308
380
|
try {
|
|
309
|
-
verdict = await verifyHello(identity, hello);
|
|
381
|
+
const verdict = await verifyHello(identity, hello);
|
|
382
|
+
if (!verdict.ok) return;
|
|
383
|
+
const key = require_domain_device_id.deviceIdToHex(verdict.peer.deviceId);
|
|
384
|
+
const current = channels.get(key);
|
|
385
|
+
if (current?.status === "established" && require_domain_token_scope.bytesEqual(current.remoteEphemeralKey, hello["ephemeral-key"])) return;
|
|
386
|
+
const replaces = current?.status === "established" ? current : void 0;
|
|
387
|
+
entry = current?.status === "awaiting" ? current : start(verdict.peer.deviceId, {
|
|
388
|
+
answering: true,
|
|
389
|
+
track: replaces === void 0
|
|
390
|
+
});
|
|
391
|
+
await complete({
|
|
392
|
+
entry,
|
|
393
|
+
hello,
|
|
394
|
+
peer: verdict.peer,
|
|
395
|
+
replaces
|
|
396
|
+
});
|
|
310
397
|
} catch (error) {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
}
|
|
314
|
-
if (!verdict.ok) return;
|
|
315
|
-
const key = require_domain_device_id.deviceIdToHex(verdict.peer.deviceId);
|
|
316
|
-
const current = channels.get(key);
|
|
317
|
-
const entry = current?.status === "awaiting" ? current : start(verdict.peer.deviceId);
|
|
318
|
-
const established = await establishChannelFrom(identity, {
|
|
319
|
-
pending: await entry.pending,
|
|
320
|
-
remote: hello,
|
|
321
|
-
peer: verdict.peer,
|
|
322
|
-
...options.firstSendCounter === void 0 ? {} : { firstSendCounter: options.firstSendCounter }
|
|
323
|
-
});
|
|
324
|
-
if (established === void 0) {
|
|
325
|
-
entry.settle.reject(/* @__PURE__ */ new Error("the peer sent an unusable ephemeral key"));
|
|
326
|
-
if (channels.get(key) === entry) channels.delete(key);
|
|
327
|
-
return;
|
|
398
|
+
if (entry !== void 0) abandon(entry, error);
|
|
399
|
+
reportFault(error);
|
|
328
400
|
}
|
|
329
|
-
const channel = {
|
|
330
|
-
...established,
|
|
331
|
-
seal: async (frame) => {
|
|
332
|
-
try {
|
|
333
|
-
return await established.seal(frame);
|
|
334
|
-
} catch (error) {
|
|
335
|
-
const held = channels.get(key);
|
|
336
|
-
if (error instanceof require_domain_channel_exhausted_error.ChannelExhaustedError && held?.status === "established" && held.channel === channel) channels.delete(key);
|
|
337
|
-
throw error;
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
};
|
|
341
|
-
channels.set(key, {
|
|
342
|
-
status: "established",
|
|
343
|
-
channel
|
|
344
|
-
});
|
|
345
|
-
entry.settle.resolve(channel);
|
|
346
401
|
},
|
|
347
402
|
open: async (data, claimedFrom) => {
|
|
348
403
|
if (claimedFrom === void 0) return;
|
|
349
|
-
const
|
|
404
|
+
const key = require_domain_device_id.deviceIdToHex(claimedFrom);
|
|
405
|
+
const entry = channels.get(key);
|
|
350
406
|
if (entry?.status !== "established") return;
|
|
351
407
|
const frame = await entry.channel.open(data);
|
|
352
408
|
return frame === void 0 ? void 0 : {
|
|
@@ -355,13 +411,13 @@ function createRelayChannels(identity, sendHello, options = {}) {
|
|
|
355
411
|
};
|
|
356
412
|
},
|
|
357
413
|
reset: (reason) => {
|
|
358
|
-
for (const entry of channels.values()) if (entry.status === "awaiting") entry
|
|
414
|
+
for (const entry of [...channels.values()]) if (entry.status === "awaiting") abandon(entry, new Error(reason));
|
|
359
415
|
channels.clear();
|
|
360
416
|
},
|
|
361
417
|
forget: (peer) => {
|
|
362
418
|
const key = require_domain_device_id.deviceIdToHex(peer);
|
|
363
419
|
const entry = channels.get(key);
|
|
364
|
-
if (entry?.status === "awaiting") entry
|
|
420
|
+
if (entry?.status === "awaiting") abandon(entry, /* @__PURE__ */ new Error("the relay pairing was lost"));
|
|
365
421
|
channels.delete(key);
|
|
366
422
|
}
|
|
367
423
|
};
|
|
@@ -137,8 +137,8 @@ addresses?: readonly string[],
|
|
|
137
137
|
onPeerAdvert?: (advert: PeerAdvert) => void,
|
|
138
138
|
/** Fired once per frame received on this session's connection, with the Connection it arrived on: a new Connection after a reconnect is how a caller notices it has to announce itself again, and a core/data reply is how it receives a log it asked a hub for. Omit for a caller with no use for it, exactly today's behaviour. */
|
|
139
139
|
onFrame?: (connection: Readonly<Connection>, frame: Frame) => void | Promise<void>,
|
|
140
|
-
/** Told of a failure that is local to this session and did not end it: an identity port that failed
|
|
141
|
-
onLocalFault?: (error: unknown) => void): MeshSession;
|
|
140
|
+
/** Told of a failure that is local to this session and did not end it, while handling a hello from a peer: an identity port that failed to check or sign, an answering hello that could not be sent, a channel that could not be established. The hello is dropped, any half-made handshake for that peer is discarded, and the peer is told nothing, so no failure in handling a hello ends the session. Called once per failing hello; if it throws, or returns a promise that rejects, that is reported once to the console and swallowed. Omit for a caller with no use for it. */
|
|
141
|
+
onLocalFault?: (error: unknown) => void | Promise<void>): MeshSession;
|
|
142
142
|
/** A MeshSession built over a connection that already exists (a Transport's own listen() handed it to onConnection), extended with the one thing a dial-side session can't offer: the device-id of the specific peer at the other end. Unlike createMeshSession, which may end up talking to a relay gossiping about many devices at once, an accepted connection is the agent-comms case -- exactly two peers, directly connected -- so "the peer" is well-defined here in a way it structurally isn't for the dial side. */
|
|
143
143
|
export interface AcceptedMeshSession extends MeshSession {
|
|
144
144
|
/** Resolves with the device-id carried by the first peer-advert this connection's remote sends -- the same self-advertisement mechanism createMeshSession's own directory already relies on for every peer, just narrowed to "the one peer this specific connection is with" rather than accumulated into a directory of possibly many. There is no transport-level authentication behind this yet (see wire-mesh#45's own createTlsTransport item): it is only as trustworthy as the remote's own gossip, exactly the same trust level the dial-side directory already has for every entry in it. */
|
|
@@ -137,8 +137,8 @@ addresses?: readonly string[],
|
|
|
137
137
|
onPeerAdvert?: (advert: PeerAdvert) => void,
|
|
138
138
|
/** Fired once per frame received on this session's connection, with the Connection it arrived on: a new Connection after a reconnect is how a caller notices it has to announce itself again, and a core/data reply is how it receives a log it asked a hub for. Omit for a caller with no use for it, exactly today's behaviour. */
|
|
139
139
|
onFrame?: (connection: Readonly<Connection>, frame: Frame) => void | Promise<void>,
|
|
140
|
-
/** Told of a failure that is local to this session and did not end it: an identity port that failed
|
|
141
|
-
onLocalFault?: (error: unknown) => void): MeshSession;
|
|
140
|
+
/** Told of a failure that is local to this session and did not end it, while handling a hello from a peer: an identity port that failed to check or sign, an answering hello that could not be sent, a channel that could not be established. The hello is dropped, any half-made handshake for that peer is discarded, and the peer is told nothing, so no failure in handling a hello ends the session. Called once per failing hello; if it throws, or returns a promise that rejects, that is reported once to the console and swallowed. Omit for a caller with no use for it. */
|
|
141
|
+
onLocalFault?: (error: unknown) => void | Promise<void>): MeshSession;
|
|
142
142
|
/** A MeshSession built over a connection that already exists (a Transport's own listen() handed it to onConnection), extended with the one thing a dial-side session can't offer: the device-id of the specific peer at the other end. Unlike createMeshSession, which may end up talking to a relay gossiping about many devices at once, an accepted connection is the agent-comms case -- exactly two peers, directly connected -- so "the peer" is well-defined here in a way it structurally isn't for the dial side. */
|
|
143
143
|
export interface AcceptedMeshSession extends MeshSession {
|
|
144
144
|
/** Resolves with the device-id carried by the first peer-advert this connection's remote sends -- the same self-advertisement mechanism createMeshSession's own directory already relies on for every peer, just narrowed to "the one peer this specific connection is with" rather than accumulated into a directory of possibly many. There is no transport-level authentication behind this yet (see wire-mesh#45's own createTlsTransport item): it is only as trustworthy as the remote's own gossip, exactly the same trust level the dial-side directory already has for every entry in it. */
|
|
@@ -279,73 +279,129 @@ async function establishChannelFrom(identity, { pending, remote, peer, firstSend
|
|
|
279
279
|
function createRelayChannels(identity, sendHello, options = {}) {
|
|
280
280
|
if (options.firstSendCounter !== void 0) assertFirstSendCounter(options.firstSendCounter);
|
|
281
281
|
const channels = /* @__PURE__ */ new Map();
|
|
282
|
-
/**
|
|
283
|
-
function
|
|
282
|
+
/** Tells the owner of a failure that is local to this side. The callback is the owner's code: its own failure, thrown or as a rejected promise it returns, must not end the session or go unobserved, so it is reported once and swallowed. */
|
|
283
|
+
function reportFault(error) {
|
|
284
|
+
const report = (faultError) => {
|
|
285
|
+
console.error("the local fault callback failed", faultError);
|
|
286
|
+
};
|
|
287
|
+
try {
|
|
288
|
+
const result = options.onFault?.(error);
|
|
289
|
+
if (typeof result === "object" && result !== null && "then" in result && typeof result.then === "function") Promise.resolve(result).catch(report);
|
|
290
|
+
} catch (faultError) {
|
|
291
|
+
report(faultError);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
/** Abandons a handshake that failed: the entry goes if it is still the one held for the peer, and whoever awaits it is told. */
|
|
295
|
+
function abandon(entry, error) {
|
|
296
|
+
const failure = error instanceof Error ? error : new Error(String(error));
|
|
297
|
+
if (channels.get(entry.key) === entry) channels.delete(entry.key);
|
|
298
|
+
entry.settle.reject(failure);
|
|
299
|
+
entry.sent.reject(failure);
|
|
300
|
+
}
|
|
301
|
+
/** Starts a handshake with the peer: sends this side's hello, and registers the peer as awaiting at once, so a hello arriving meanwhile completes this handshake instead of starting a second. `answering` is true when a received hello started this, so nobody awaits the outcome and a failure to send is reported to the owner. `track` is false for a handshake answering a hello while a channel with that peer is established: it is not registered, so the established channel stays in place and in use until the new one is up and this side's hello has been sent. A hello that could not be sent leaves no channel behind: the peer would never learn this side's key. */
|
|
302
|
+
function start(peer, { answering, track } = {
|
|
303
|
+
answering: false,
|
|
304
|
+
track: true
|
|
305
|
+
}) {
|
|
284
306
|
const key = deviceIdToHex(peer);
|
|
285
|
-
const settle = Promise.withResolvers();
|
|
286
307
|
const pending = beginHandshake(identity, peer);
|
|
287
308
|
const entry = {
|
|
288
309
|
status: "awaiting",
|
|
289
310
|
pending,
|
|
290
|
-
settle
|
|
311
|
+
settle: Promise.withResolvers(),
|
|
312
|
+
key,
|
|
313
|
+
sent: Promise.withResolvers()
|
|
291
314
|
};
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
315
|
+
entry.settle.promise.catch(() => void 0);
|
|
316
|
+
entry.sent.promise.catch(() => void 0);
|
|
317
|
+
pending.then(async ({ hello }) => {
|
|
318
|
+
try {
|
|
319
|
+
await sendHello(peer, hello);
|
|
320
|
+
} catch (error) {
|
|
321
|
+
abandon(entry, error);
|
|
322
|
+
entry.settle.promise.then((channel) => {
|
|
323
|
+
const held = channels.get(key);
|
|
324
|
+
if (held?.status === "established" && held.channel === channel) channels.delete(key);
|
|
325
|
+
}, () => void 0);
|
|
326
|
+
if (answering && track) reportFault(error);
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
entry.sent.resolve(void 0);
|
|
330
|
+
}, (error) => {
|
|
331
|
+
abandon(entry, error);
|
|
296
332
|
});
|
|
333
|
+
if (track) channels.set(key, entry);
|
|
297
334
|
return entry;
|
|
298
335
|
}
|
|
336
|
+
/** Completes the handshake a verified hello belongs to. Throws on any failure of this side; the caller abandons the handshake. */
|
|
337
|
+
async function complete({ entry, hello, peer, replaces }) {
|
|
338
|
+
const { key } = entry;
|
|
339
|
+
const established = await establishChannelFrom(identity, {
|
|
340
|
+
pending: await entry.pending,
|
|
341
|
+
remote: hello,
|
|
342
|
+
peer,
|
|
343
|
+
...options.firstSendCounter === void 0 ? {} : { firstSendCounter: options.firstSendCounter }
|
|
344
|
+
});
|
|
345
|
+
if (established === void 0) {
|
|
346
|
+
abandon(entry, /* @__PURE__ */ new Error("the peer sent an unusable ephemeral key"));
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
if (replaces !== void 0) await entry.sent.promise;
|
|
350
|
+
if (channels.get(key) !== (replaces ?? entry)) return;
|
|
351
|
+
const channel = {
|
|
352
|
+
...established,
|
|
353
|
+
seal: async (frame) => {
|
|
354
|
+
try {
|
|
355
|
+
return await established.seal(frame);
|
|
356
|
+
} catch (error) {
|
|
357
|
+
const held = channels.get(key);
|
|
358
|
+
if (error instanceof ChannelExhaustedError && held?.status === "established" && held.channel === channel) channels.delete(key);
|
|
359
|
+
throw error;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
channels.set(key, {
|
|
364
|
+
status: "established",
|
|
365
|
+
channel,
|
|
366
|
+
remoteEphemeralKey: hello["ephemeral-key"]
|
|
367
|
+
});
|
|
368
|
+
entry.settle.resolve(channel);
|
|
369
|
+
}
|
|
299
370
|
return {
|
|
300
371
|
ensure: async (peer) => {
|
|
301
|
-
const
|
|
372
|
+
const key = deviceIdToHex(peer);
|
|
373
|
+
const existing = channels.get(key);
|
|
302
374
|
if (existing?.status === "established") return existing.channel;
|
|
303
375
|
return (existing ?? start(peer)).settle.promise;
|
|
304
376
|
},
|
|
305
377
|
applyHello: async (hello) => {
|
|
306
|
-
let
|
|
378
|
+
let entry;
|
|
307
379
|
try {
|
|
308
|
-
verdict = await verifyHello(identity, hello);
|
|
380
|
+
const verdict = await verifyHello(identity, hello);
|
|
381
|
+
if (!verdict.ok) return;
|
|
382
|
+
const key = deviceIdToHex(verdict.peer.deviceId);
|
|
383
|
+
const current = channels.get(key);
|
|
384
|
+
if (current?.status === "established" && bytesEqual(current.remoteEphemeralKey, hello["ephemeral-key"])) return;
|
|
385
|
+
const replaces = current?.status === "established" ? current : void 0;
|
|
386
|
+
entry = current?.status === "awaiting" ? current : start(verdict.peer.deviceId, {
|
|
387
|
+
answering: true,
|
|
388
|
+
track: replaces === void 0
|
|
389
|
+
});
|
|
390
|
+
await complete({
|
|
391
|
+
entry,
|
|
392
|
+
hello,
|
|
393
|
+
peer: verdict.peer,
|
|
394
|
+
replaces
|
|
395
|
+
});
|
|
309
396
|
} catch (error) {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
}
|
|
313
|
-
if (!verdict.ok) return;
|
|
314
|
-
const key = deviceIdToHex(verdict.peer.deviceId);
|
|
315
|
-
const current = channels.get(key);
|
|
316
|
-
const entry = current?.status === "awaiting" ? current : start(verdict.peer.deviceId);
|
|
317
|
-
const established = await establishChannelFrom(identity, {
|
|
318
|
-
pending: await entry.pending,
|
|
319
|
-
remote: hello,
|
|
320
|
-
peer: verdict.peer,
|
|
321
|
-
...options.firstSendCounter === void 0 ? {} : { firstSendCounter: options.firstSendCounter }
|
|
322
|
-
});
|
|
323
|
-
if (established === void 0) {
|
|
324
|
-
entry.settle.reject(/* @__PURE__ */ new Error("the peer sent an unusable ephemeral key"));
|
|
325
|
-
if (channels.get(key) === entry) channels.delete(key);
|
|
326
|
-
return;
|
|
397
|
+
if (entry !== void 0) abandon(entry, error);
|
|
398
|
+
reportFault(error);
|
|
327
399
|
}
|
|
328
|
-
const channel = {
|
|
329
|
-
...established,
|
|
330
|
-
seal: async (frame) => {
|
|
331
|
-
try {
|
|
332
|
-
return await established.seal(frame);
|
|
333
|
-
} catch (error) {
|
|
334
|
-
const held = channels.get(key);
|
|
335
|
-
if (error instanceof ChannelExhaustedError && held?.status === "established" && held.channel === channel) channels.delete(key);
|
|
336
|
-
throw error;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
};
|
|
340
|
-
channels.set(key, {
|
|
341
|
-
status: "established",
|
|
342
|
-
channel
|
|
343
|
-
});
|
|
344
|
-
entry.settle.resolve(channel);
|
|
345
400
|
},
|
|
346
401
|
open: async (data, claimedFrom) => {
|
|
347
402
|
if (claimedFrom === void 0) return;
|
|
348
|
-
const
|
|
403
|
+
const key = deviceIdToHex(claimedFrom);
|
|
404
|
+
const entry = channels.get(key);
|
|
349
405
|
if (entry?.status !== "established") return;
|
|
350
406
|
const frame = await entry.channel.open(data);
|
|
351
407
|
return frame === void 0 ? void 0 : {
|
|
@@ -354,13 +410,13 @@ function createRelayChannels(identity, sendHello, options = {}) {
|
|
|
354
410
|
};
|
|
355
411
|
},
|
|
356
412
|
reset: (reason) => {
|
|
357
|
-
for (const entry of channels.values()) if (entry.status === "awaiting") entry
|
|
413
|
+
for (const entry of [...channels.values()]) if (entry.status === "awaiting") abandon(entry, new Error(reason));
|
|
358
414
|
channels.clear();
|
|
359
415
|
},
|
|
360
416
|
forget: (peer) => {
|
|
361
417
|
const key = deviceIdToHex(peer);
|
|
362
418
|
const entry = channels.get(key);
|
|
363
|
-
if (entry?.status === "awaiting") entry
|
|
419
|
+
if (entry?.status === "awaiting") abandon(entry, /* @__PURE__ */ new Error("the relay pairing was lost"));
|
|
364
420
|
channels.delete(key);
|
|
365
421
|
}
|
|
366
422
|
};
|