wire-mesh-core 4.1.4 → 4.2.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.
@@ -6,6 +6,7 @@ import { a as OWN_VERSION, i as CORE_VERSION_GOSSIP_KEY, n as TOPOLOGY_PEERS_GOS
6
6
  import { signPeerAdvert, verifyPeerAdvert } from "./peer-advert.mjs";
7
7
  import { createAsyncQueue } from "./async-queue.mjs";
8
8
  import { ChannelExhaustedError } from "./channel-exhausted-error.mjs";
9
+ import { deliverContained } from "./fault-report.mjs";
9
10
  import { localHandshake, negotiate } from "./handshake.mjs";
10
11
  import { isDataFrame } from "./hub-mailbox.mjs";
11
12
  import { createPingRoundTrips } from "./ping-round-trips.mjs";
@@ -279,17 +280,9 @@ async function establishChannelFrom(identity, { pending, remote, peer, firstSend
279
280
  function createRelayChannels(identity, sendHello, options = {}) {
280
281
  if (options.firstSendCounter !== void 0) assertFirstSendCounter(options.firstSendCounter);
281
282
  const channels = /* @__PURE__ */ new Map();
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
+ /** 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 goes through the shared containment in fault-report.ts. */
283
284
  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
- }
285
+ deliverContained(async () => options.onFault?.(error), 0);
293
286
  }
294
287
  /** 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
288
  function abandon(entry, error) {
@@ -502,7 +495,7 @@ function createSessionCore(identity, clock, reconnect, dial, onPeerAdvert, addre
502
495
  }
503
496
  /** The "not connected" guard every send* method on the public session needs, deduplicated into one place rather than repeated inline at each call site: throws unless there is a live connection AND the session's own state machine agrees it is "connected" (transmit's own connection===null check alone doesn't cover the latter). Returns the connection itself, narrowed non-null, for a caller that needs to send directly on it (sendPing/sendPingMeasureRtt); a caller that only needs the throwing side effect (sendManageRequest and friends, which go through transmit instead) calls this and discards the result. */
504
497
  function requireConnectedLink() {
505
- if (connection === null || state.status !== "connected") throw new Error("not connected");
498
+ if (feedCancelled || connection === null || state.status !== "connected") throw new Error("not connected");
506
499
  return connection;
507
500
  }
508
501
  /** Sends a frame. Through a relay it is sealed for toDevice on their secure channel first (spec/secure-channel.cddl) and only then wrapped as relay-data, so the hub carries ciphertext. This is the single choke point every outbound manage-request and manage-response passes through, so a consumer of sendManageRequest or respond never needs its own relay logic. */
@@ -833,7 +826,10 @@ function createSessionCore(identity, clock, reconnect, dial, onPeerAdvert, addre
833
826
  },
834
827
  async sendManageRequest(command, scope, targetDevice, token, timeoutMs) {
835
828
  requireConnectedLink();
836
- if (targetDevice !== void 0) await ensureRelayPairing(targetDevice);
829
+ if (targetDevice !== void 0) {
830
+ await ensureRelayPairing(targetDevice);
831
+ requireConnectedLink();
832
+ }
837
833
  const frame = buildManageRequest(command, scope, token);
838
834
  const requestId = frame["request-id"];
839
835
  const outcome = new Promise((resolve, reject) => {
@@ -843,6 +839,7 @@ function createSessionCore(identity, clock, reconnect, dial, onPeerAdvert, addre
843
839
  expectedFrom: targetDevice
844
840
  });
845
841
  });
842
+ outcome.catch(() => void 0);
846
843
  recordFrame({
847
844
  direction: "sent",
848
845
  frame
@@ -852,7 +849,6 @@ function createSessionCore(identity, clock, reconnect, dial, onPeerAdvert, addre
852
849
  await transmit(frame, targetDevice !== void 0, targetDevice);
853
850
  } catch (error) {
854
851
  pendingManageRequests.delete(requestId);
855
- outcome.catch(() => void 0);
856
852
  throw error;
857
853
  }
858
854
  emit();
@@ -9,8 +9,8 @@ var PingTimeoutError = class extends Error {
9
9
  };
10
10
  function createPingRoundTrips() {
11
11
  const pending = [];
12
- function cancel(sentAt) {
13
- const index = pending.findIndex((entry) => entry.sentAt === sentAt);
12
+ function cancel(registration) {
13
+ const index = pending.indexOf(registration);
14
14
  if (index === -1) return false;
15
15
  pending.splice(index, 1);
16
16
  return true;
@@ -24,20 +24,33 @@ function createPingRoundTrips() {
24
24
  for (const entry of pending.splice(0)) entry.reject(new Error(reason));
25
25
  },
26
26
  async sendAndAwait(sentAt, sendPing, timeoutMs) {
27
+ let registration;
27
28
  const rtt = new Promise((resolve, reject) => {
28
- pending.push({
29
+ registration = {
29
30
  sentAt,
30
31
  resolve,
31
32
  reject
32
- });
33
+ };
34
+ pending.push(registration);
33
35
  });
34
- await sendPing();
36
+ if (registration === void 0) throw new Error("the round trip was not registered");
37
+ const registered = registration;
38
+ rtt.catch(() => void 0);
39
+ try {
40
+ await sendPing();
41
+ } catch (error) {
42
+ cancel(registered);
43
+ throw error;
44
+ }
35
45
  if (timeoutMs === void 0) return rtt;
46
+ let timer;
36
47
  return Promise.race([rtt, new Promise((_resolve, reject) => {
37
- setTimeout(() => {
38
- if (cancel(sentAt)) reject(new PingTimeoutError());
48
+ timer = setTimeout(() => {
49
+ if (cancel(registered)) reject(new PingTimeoutError());
39
50
  }, timeoutMs);
40
- })]);
51
+ })]).finally(() => {
52
+ clearTimeout(timer);
53
+ });
41
54
  }
42
55
  };
43
56
  }
@@ -8,8 +8,8 @@ var PingTimeoutError = class extends Error {
8
8
  };
9
9
  function createPingRoundTrips() {
10
10
  const pending = [];
11
- function cancel(sentAt) {
12
- const index = pending.findIndex((entry) => entry.sentAt === sentAt);
11
+ function cancel(registration) {
12
+ const index = pending.indexOf(registration);
13
13
  if (index === -1) return false;
14
14
  pending.splice(index, 1);
15
15
  return true;
@@ -23,20 +23,33 @@ function createPingRoundTrips() {
23
23
  for (const entry of pending.splice(0)) entry.reject(new Error(reason));
24
24
  },
25
25
  async sendAndAwait(sentAt, sendPing, timeoutMs) {
26
+ let registration;
26
27
  const rtt = new Promise((resolve, reject) => {
27
- pending.push({
28
+ registration = {
28
29
  sentAt,
29
30
  resolve,
30
31
  reject
31
- });
32
+ };
33
+ pending.push(registration);
32
34
  });
33
- await sendPing();
35
+ if (registration === void 0) throw new Error("the round trip was not registered");
36
+ const registered = registration;
37
+ rtt.catch(() => void 0);
38
+ try {
39
+ await sendPing();
40
+ } catch (error) {
41
+ cancel(registered);
42
+ throw error;
43
+ }
34
44
  if (timeoutMs === void 0) return rtt;
45
+ let timer;
35
46
  return Promise.race([rtt, new Promise((_resolve, reject) => {
36
- setTimeout(() => {
37
- if (cancel(sentAt)) reject(new PingTimeoutError());
47
+ timer = setTimeout(() => {
48
+ if (cancel(registered)) reject(new PingTimeoutError());
38
49
  }, timeoutMs);
39
- })]);
50
+ })]).finally(() => {
51
+ clearTimeout(timer);
52
+ });
40
53
  }
41
54
  };
42
55
  }
@@ -0,0 +1,12 @@
1
+ //#region src/domain/timers.ts
2
+ /** Stops a pending timer from keeping the process alive, where the runtime has that notion. Node's `setTimeout` returns an object with `unref()`; a browser's or a Worker's returns a number, which has nothing to unref and must not be called as if it did. */
3
+ function unrefIfSupported(handle) {
4
+ if (typeof handle === "object" && handle !== null && "unref" in handle && typeof handle.unref === "function") Reflect.apply(handle.unref, handle, []);
5
+ }
6
+ //#endregion
7
+ Object.defineProperty(exports, "unrefIfSupported", {
8
+ enumerable: true,
9
+ get: function() {
10
+ return unrefIfSupported;
11
+ }
12
+ });
@@ -0,0 +1,7 @@
1
+ //#region src/domain/timers.ts
2
+ /** Stops a pending timer from keeping the process alive, where the runtime has that notion. Node's `setTimeout` returns an object with `unref()`; a browser's or a Worker's returns a number, which has nothing to unref and must not be called as if it did. */
3
+ function unrefIfSupported(handle) {
4
+ if (typeof handle === "object" && handle !== null && "unref" in handle && typeof handle.unref === "function") Reflect.apply(handle.unref, handle, []);
5
+ }
6
+ //#endregion
7
+ export { unrefIfSupported as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wire-mesh-core",
3
- "version": "4.1.4",
3
+ "version": "4.2.0",
4
4
  "dependencies": {
5
5
  "cbor2": "2.3.0",
6
6
  "cddl.js": "2.0.0",
@@ -90,6 +90,10 @@
90
90
  "import": "./dist/domain/channel-exhausted-error.mjs",
91
91
  "require": "./dist/domain/channel-exhausted-error.cjs"
92
92
  },
93
+ "./domain/consume-incoming": {
94
+ "import": "./dist/domain/consume-incoming.mjs",
95
+ "require": "./dist/domain/consume-incoming.cjs"
96
+ },
93
97
  "./domain/coordinator-election": {
94
98
  "import": "./dist/domain/coordinator-election.mjs",
95
99
  "require": "./dist/domain/coordinator-election.cjs"
@@ -114,6 +118,10 @@
114
118
  "import": "./dist/domain/erasure-coding.mjs",
115
119
  "require": "./dist/domain/erasure-coding.cjs"
116
120
  },
121
+ "./domain/fault-report": {
122
+ "import": "./dist/domain/fault-report.mjs",
123
+ "require": "./dist/domain/fault-report.cjs"
124
+ },
117
125
  "./domain/gossip-expansion": {
118
126
  "import": "./dist/domain/gossip-expansion.mjs",
119
127
  "require": "./dist/domain/gossip-expansion.cjs"