@streamr/sdk 103.3.1 → 103.7.0-rc.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.
@@ -41288,6 +41288,22 @@
41288
41288
  cb();
41289
41289
  }, ms);
41290
41290
  };
41291
+ /**
41292
+ * setInterval with AbortSignal support. Aborting will simply clear
41293
+ * the interval silently.
41294
+ */
41295
+ const setAbortableInterval = (cb, ms, abortSignal) => {
41296
+ if (abortSignal.aborted) {
41297
+ return;
41298
+ }
41299
+ const abortListener = () => {
41300
+ clearInterval(timeoutRef);
41301
+ };
41302
+ abortSignal.addEventListener('abort', abortListener, { once: true });
41303
+ const timeoutRef = setInterval(() => {
41304
+ cb();
41305
+ }, ms);
41306
+ };
41291
41307
 
41292
41308
  const noopExecutor = () => { };
41293
41309
  /**
@@ -73321,7 +73337,7 @@
73321
73337
  }
73322
73338
  }
73323
73339
 
73324
- const logger$D = new Logger('LoggingJsonRpcProvider');
73340
+ const logger$E = new Logger('LoggingJsonRpcProvider');
73325
73341
  class LoggingJsonRpcProvider extends JsonRpcProvider {
73326
73342
  urlConfig;
73327
73343
  constructor(urlConfig, network, options) {
@@ -73340,20 +73356,20 @@
73340
73356
  timeout: this.urlConfig.timeout
73341
73357
  }
73342
73358
  };
73343
- logger$D.debug('Send request', logContext);
73359
+ logger$E.debug('Send request', logContext);
73344
73360
  let result;
73345
73361
  try {
73346
73362
  result = await super.send(method, params);
73347
73363
  }
73348
73364
  catch (err) {
73349
- logger$D.debug('Encountered error while requesting', {
73365
+ logger$E.debug('Encountered error while requesting', {
73350
73366
  ...logContext,
73351
73367
  err,
73352
73368
  elapsedTime: Date.now() - startTime
73353
73369
  });
73354
73370
  throw err;
73355
73371
  }
73356
- logger$D.debug('Received response', {
73372
+ logger$E.debug('Received response', {
73357
73373
  ...logContext,
73358
73374
  elapsedTime: Date.now() - startTime
73359
73375
  });
@@ -80246,7 +80262,7 @@
80246
80262
  this.deferredPromises.trailer.resolve({});
80247
80263
  }
80248
80264
  catch (err) {
80249
- logger$y.debug(`Could not parse response, received message is likely `);
80265
+ logger$z.debug(`Could not parse response, received message is likely `);
80250
80266
  const error = new FailedToParse(`Failed to parse received response, network protocol version likely is likely incompatible`, err);
80251
80267
  this.rejectDeferredPromises(error, StatusCode.SERVER_ERROR);
80252
80268
  }
@@ -80267,7 +80283,7 @@
80267
80283
  return this.callContext;
80268
80284
  }
80269
80285
  }
80270
- const logger$y = new Logger('RpcCommunicator');
80286
+ const logger$z = new Logger('RpcCommunicator');
80271
80287
  class RpcCommunicator {
80272
80288
  stopped = false;
80273
80289
  rpcClientTransport;
@@ -80321,7 +80337,7 @@
80321
80337
  if (deferredPromises && (!callContext || !callContext.notification)) {
80322
80338
  this.registerRequest(rpcMessage.requestId, deferredPromises, callContext, requestOptions.timeout);
80323
80339
  }
80324
- logger$y.trace(`onOutGoingMessage, messageId: ${rpcMessage.requestId}`);
80340
+ logger$z.trace(`onOutGoingMessage, messageId: ${rpcMessage.requestId}`);
80325
80341
  if (this.outgoingMessageListener) {
80326
80342
  this.outgoingMessageListener(rpcMessage, rpcMessage.requestId, callContext)
80327
80343
  .catch((clientSideException) => {
@@ -80352,7 +80368,7 @@
80352
80368
  }
80353
80369
  }
80354
80370
  async onIncomingMessage(rpcMessage, callContext) {
80355
- logger$y.trace(`onIncomingMessage, requestId: ${rpcMessage.requestId}`);
80371
+ logger$z.trace(`onIncomingMessage, requestId: ${rpcMessage.requestId}`);
80356
80372
  if (rpcMessage.header.response && this.ongoingRequests.has(rpcMessage.requestId)) {
80357
80373
  if (rpcMessage.errorType !== undefined) {
80358
80374
  this.rejectOngoingRequest(rpcMessage);
@@ -80414,7 +80430,7 @@
80414
80430
  await this.rpcServerRegistry.handleNotification(rpcMessage, callContext);
80415
80431
  }
80416
80432
  catch (err) {
80417
- logger$y.debug('error', { err });
80433
+ logger$z.debug('error', { err });
80418
80434
  }
80419
80435
  }
80420
80436
  registerRequest(requestId, deferredPromises, callContext, timeout = this.rpcRequestTimeout) {
@@ -81601,6 +81617,355 @@
81601
81617
  var ipaddrExports = requireIpaddr();
81602
81618
  var ipaddr = /*@__PURE__*/getDefaultExportFromCjs$1(ipaddrExports);
81603
81619
 
81620
+ /**
81621
+ * @license
81622
+ * Copyright 2019 Google LLC
81623
+ * SPDX-License-Identifier: Apache-2.0
81624
+ */
81625
+ const proxyMarker = Symbol("Comlink.proxy");
81626
+ const createEndpoint = Symbol("Comlink.endpoint");
81627
+ const releaseProxy = Symbol("Comlink.releaseProxy");
81628
+ const finalizer = Symbol("Comlink.finalizer");
81629
+ const throwMarker = Symbol("Comlink.thrown");
81630
+ const isObject$1 = (val) => (typeof val === "object" && val !== null) || typeof val === "function";
81631
+ /**
81632
+ * Internal transfer handle to handle objects marked to proxy.
81633
+ */
81634
+ const proxyTransferHandler = {
81635
+ canHandle: (val) => isObject$1(val) && val[proxyMarker],
81636
+ serialize(obj) {
81637
+ const { port1, port2 } = new MessageChannel();
81638
+ expose(obj, port1);
81639
+ return [port2, [port2]];
81640
+ },
81641
+ deserialize(port) {
81642
+ port.start();
81643
+ return wrap$1(port);
81644
+ },
81645
+ };
81646
+ /**
81647
+ * Internal transfer handler to handle thrown exceptions.
81648
+ */
81649
+ const throwTransferHandler = {
81650
+ canHandle: (value) => isObject$1(value) && throwMarker in value,
81651
+ serialize({ value }) {
81652
+ let serialized;
81653
+ if (value instanceof Error) {
81654
+ serialized = {
81655
+ isError: true,
81656
+ value: {
81657
+ message: value.message,
81658
+ name: value.name,
81659
+ stack: value.stack,
81660
+ },
81661
+ };
81662
+ }
81663
+ else {
81664
+ serialized = { isError: false, value };
81665
+ }
81666
+ return [serialized, []];
81667
+ },
81668
+ deserialize(serialized) {
81669
+ if (serialized.isError) {
81670
+ throw Object.assign(new Error(serialized.value.message), serialized.value);
81671
+ }
81672
+ throw serialized.value;
81673
+ },
81674
+ };
81675
+ /**
81676
+ * Allows customizing the serialization of certain values.
81677
+ */
81678
+ const transferHandlers = new Map([
81679
+ ["proxy", proxyTransferHandler],
81680
+ ["throw", throwTransferHandler],
81681
+ ]);
81682
+ function isAllowedOrigin(allowedOrigins, origin) {
81683
+ for (const allowedOrigin of allowedOrigins) {
81684
+ if (origin === allowedOrigin || allowedOrigin === "*") {
81685
+ return true;
81686
+ }
81687
+ if (allowedOrigin instanceof RegExp && allowedOrigin.test(origin)) {
81688
+ return true;
81689
+ }
81690
+ }
81691
+ return false;
81692
+ }
81693
+ function expose(obj, ep = globalThis, allowedOrigins = ["*"]) {
81694
+ ep.addEventListener("message", function callback(ev) {
81695
+ if (!ev || !ev.data) {
81696
+ return;
81697
+ }
81698
+ if (!isAllowedOrigin(allowedOrigins, ev.origin)) {
81699
+ console.warn(`Invalid origin '${ev.origin}' for comlink proxy`);
81700
+ return;
81701
+ }
81702
+ const { id, type, path } = Object.assign({ path: [] }, ev.data);
81703
+ const argumentList = (ev.data.argumentList || []).map(fromWireValue);
81704
+ let returnValue;
81705
+ try {
81706
+ const parent = path.slice(0, -1).reduce((obj, prop) => obj[prop], obj);
81707
+ const rawValue = path.reduce((obj, prop) => obj[prop], obj);
81708
+ switch (type) {
81709
+ case "GET" /* MessageType.GET */:
81710
+ {
81711
+ returnValue = rawValue;
81712
+ }
81713
+ break;
81714
+ case "SET" /* MessageType.SET */:
81715
+ {
81716
+ parent[path.slice(-1)[0]] = fromWireValue(ev.data.value);
81717
+ returnValue = true;
81718
+ }
81719
+ break;
81720
+ case "APPLY" /* MessageType.APPLY */:
81721
+ {
81722
+ returnValue = rawValue.apply(parent, argumentList);
81723
+ }
81724
+ break;
81725
+ case "CONSTRUCT" /* MessageType.CONSTRUCT */:
81726
+ {
81727
+ const value = new rawValue(...argumentList);
81728
+ returnValue = proxy(value);
81729
+ }
81730
+ break;
81731
+ case "ENDPOINT" /* MessageType.ENDPOINT */:
81732
+ {
81733
+ const { port1, port2 } = new MessageChannel();
81734
+ expose(obj, port2);
81735
+ returnValue = transfer(port1, [port1]);
81736
+ }
81737
+ break;
81738
+ case "RELEASE" /* MessageType.RELEASE */:
81739
+ {
81740
+ returnValue = undefined;
81741
+ }
81742
+ break;
81743
+ default:
81744
+ return;
81745
+ }
81746
+ }
81747
+ catch (value) {
81748
+ returnValue = { value, [throwMarker]: 0 };
81749
+ }
81750
+ Promise.resolve(returnValue)
81751
+ .catch((value) => {
81752
+ return { value, [throwMarker]: 0 };
81753
+ })
81754
+ .then((returnValue) => {
81755
+ const [wireValue, transferables] = toWireValue(returnValue);
81756
+ ep.postMessage(Object.assign(Object.assign({}, wireValue), { id }), transferables);
81757
+ if (type === "RELEASE" /* MessageType.RELEASE */) {
81758
+ // detach and deactive after sending release response above.
81759
+ ep.removeEventListener("message", callback);
81760
+ closeEndPoint(ep);
81761
+ if (finalizer in obj && typeof obj[finalizer] === "function") {
81762
+ obj[finalizer]();
81763
+ }
81764
+ }
81765
+ })
81766
+ .catch((error) => {
81767
+ // Send Serialization Error To Caller
81768
+ const [wireValue, transferables] = toWireValue({
81769
+ value: new TypeError("Unserializable return value"),
81770
+ [throwMarker]: 0,
81771
+ });
81772
+ ep.postMessage(Object.assign(Object.assign({}, wireValue), { id }), transferables);
81773
+ });
81774
+ });
81775
+ if (ep.start) {
81776
+ ep.start();
81777
+ }
81778
+ }
81779
+ function isMessagePort(endpoint) {
81780
+ return endpoint.constructor.name === "MessagePort";
81781
+ }
81782
+ function closeEndPoint(endpoint) {
81783
+ if (isMessagePort(endpoint))
81784
+ endpoint.close();
81785
+ }
81786
+ function wrap$1(ep, target) {
81787
+ const pendingListeners = new Map();
81788
+ ep.addEventListener("message", function handleMessage(ev) {
81789
+ const { data } = ev;
81790
+ if (!data || !data.id) {
81791
+ return;
81792
+ }
81793
+ const resolver = pendingListeners.get(data.id);
81794
+ if (!resolver) {
81795
+ return;
81796
+ }
81797
+ try {
81798
+ resolver(data);
81799
+ }
81800
+ finally {
81801
+ pendingListeners.delete(data.id);
81802
+ }
81803
+ });
81804
+ return createProxy(ep, pendingListeners, [], target);
81805
+ }
81806
+ function throwIfProxyReleased(isReleased) {
81807
+ if (isReleased) {
81808
+ throw new Error("Proxy has been released and is not useable");
81809
+ }
81810
+ }
81811
+ function releaseEndpoint(ep) {
81812
+ return requestResponseMessage(ep, new Map(), {
81813
+ type: "RELEASE" /* MessageType.RELEASE */,
81814
+ }).then(() => {
81815
+ closeEndPoint(ep);
81816
+ });
81817
+ }
81818
+ const proxyCounter = new WeakMap();
81819
+ const proxyFinalizers = "FinalizationRegistry" in globalThis &&
81820
+ new FinalizationRegistry((ep) => {
81821
+ const newCount = (proxyCounter.get(ep) || 0) - 1;
81822
+ proxyCounter.set(ep, newCount);
81823
+ if (newCount === 0) {
81824
+ releaseEndpoint(ep);
81825
+ }
81826
+ });
81827
+ function registerProxy(proxy, ep) {
81828
+ const newCount = (proxyCounter.get(ep) || 0) + 1;
81829
+ proxyCounter.set(ep, newCount);
81830
+ if (proxyFinalizers) {
81831
+ proxyFinalizers.register(proxy, ep, proxy);
81832
+ }
81833
+ }
81834
+ function unregisterProxy(proxy) {
81835
+ if (proxyFinalizers) {
81836
+ proxyFinalizers.unregister(proxy);
81837
+ }
81838
+ }
81839
+ function createProxy(ep, pendingListeners, path = [], target = function () { }) {
81840
+ let isProxyReleased = false;
81841
+ const proxy = new Proxy(target, {
81842
+ get(_target, prop) {
81843
+ throwIfProxyReleased(isProxyReleased);
81844
+ if (prop === releaseProxy) {
81845
+ return () => {
81846
+ unregisterProxy(proxy);
81847
+ releaseEndpoint(ep);
81848
+ pendingListeners.clear();
81849
+ isProxyReleased = true;
81850
+ };
81851
+ }
81852
+ if (prop === "then") {
81853
+ if (path.length === 0) {
81854
+ return { then: () => proxy };
81855
+ }
81856
+ const r = requestResponseMessage(ep, pendingListeners, {
81857
+ type: "GET" /* MessageType.GET */,
81858
+ path: path.map((p) => p.toString()),
81859
+ }).then(fromWireValue);
81860
+ return r.then.bind(r);
81861
+ }
81862
+ return createProxy(ep, pendingListeners, [...path, prop]);
81863
+ },
81864
+ set(_target, prop, rawValue) {
81865
+ throwIfProxyReleased(isProxyReleased);
81866
+ // FIXME: ES6 Proxy Handler `set` methods are supposed to return a
81867
+ // boolean. To show good will, we return true asynchronously ¯\_(ツ)_/¯
81868
+ const [value, transferables] = toWireValue(rawValue);
81869
+ return requestResponseMessage(ep, pendingListeners, {
81870
+ type: "SET" /* MessageType.SET */,
81871
+ path: [...path, prop].map((p) => p.toString()),
81872
+ value,
81873
+ }, transferables).then(fromWireValue);
81874
+ },
81875
+ apply(_target, _thisArg, rawArgumentList) {
81876
+ throwIfProxyReleased(isProxyReleased);
81877
+ const last = path[path.length - 1];
81878
+ if (last === createEndpoint) {
81879
+ return requestResponseMessage(ep, pendingListeners, {
81880
+ type: "ENDPOINT" /* MessageType.ENDPOINT */,
81881
+ }).then(fromWireValue);
81882
+ }
81883
+ // We just pretend that `bind()` didn’t happen.
81884
+ if (last === "bind") {
81885
+ return createProxy(ep, pendingListeners, path.slice(0, -1));
81886
+ }
81887
+ const [argumentList, transferables] = processArguments(rawArgumentList);
81888
+ return requestResponseMessage(ep, pendingListeners, {
81889
+ type: "APPLY" /* MessageType.APPLY */,
81890
+ path: path.map((p) => p.toString()),
81891
+ argumentList,
81892
+ }, transferables).then(fromWireValue);
81893
+ },
81894
+ construct(_target, rawArgumentList) {
81895
+ throwIfProxyReleased(isProxyReleased);
81896
+ const [argumentList, transferables] = processArguments(rawArgumentList);
81897
+ return requestResponseMessage(ep, pendingListeners, {
81898
+ type: "CONSTRUCT" /* MessageType.CONSTRUCT */,
81899
+ path: path.map((p) => p.toString()),
81900
+ argumentList,
81901
+ }, transferables).then(fromWireValue);
81902
+ },
81903
+ });
81904
+ registerProxy(proxy, ep);
81905
+ return proxy;
81906
+ }
81907
+ function myFlat(arr) {
81908
+ return Array.prototype.concat.apply([], arr);
81909
+ }
81910
+ function processArguments(argumentList) {
81911
+ const processed = argumentList.map(toWireValue);
81912
+ return [processed.map((v) => v[0]), myFlat(processed.map((v) => v[1]))];
81913
+ }
81914
+ const transferCache = new WeakMap();
81915
+ function transfer(obj, transfers) {
81916
+ transferCache.set(obj, transfers);
81917
+ return obj;
81918
+ }
81919
+ function proxy(obj) {
81920
+ return Object.assign(obj, { [proxyMarker]: true });
81921
+ }
81922
+ function toWireValue(value) {
81923
+ for (const [name, handler] of transferHandlers) {
81924
+ if (handler.canHandle(value)) {
81925
+ const [serializedValue, transferables] = handler.serialize(value);
81926
+ return [
81927
+ {
81928
+ type: "HANDLER" /* WireValueType.HANDLER */,
81929
+ name,
81930
+ value: serializedValue,
81931
+ },
81932
+ transferables,
81933
+ ];
81934
+ }
81935
+ }
81936
+ return [
81937
+ {
81938
+ type: "RAW" /* WireValueType.RAW */,
81939
+ value,
81940
+ },
81941
+ transferCache.get(value) || [],
81942
+ ];
81943
+ }
81944
+ function fromWireValue(value) {
81945
+ switch (value.type) {
81946
+ case "HANDLER" /* WireValueType.HANDLER */:
81947
+ return transferHandlers.get(value.name).deserialize(value.value);
81948
+ case "RAW" /* WireValueType.RAW */:
81949
+ return value.value;
81950
+ }
81951
+ }
81952
+ function requestResponseMessage(ep, pendingListeners, msg, transfers) {
81953
+ return new Promise((resolve) => {
81954
+ const id = generateUUID();
81955
+ pendingListeners.set(id, resolve);
81956
+ if (ep.start) {
81957
+ ep.start();
81958
+ }
81959
+ ep.postMessage(Object.assign({ id }, msg), transfers);
81960
+ });
81961
+ }
81962
+ function generateUUID() {
81963
+ return new Array(4)
81964
+ .fill(0)
81965
+ .map(() => Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(16))
81966
+ .join("-");
81967
+ }
81968
+
81604
81969
  var global$1;
81605
81970
  var hasRequiredGlobal;
81606
81971
 
@@ -85073,7 +85438,7 @@
85073
85438
  };
85074
85439
 
85075
85440
  const DEFAULT_MAX_CACHE_AGE = 1000 * 60 * 60; // 1 hour
85076
- const logger$v = new Logger('getLocalRegion');
85441
+ const logger$w = new Logger('getLocalRegion');
85077
85442
  let cachedLocalRegion = undefined;
85078
85443
  let cachedLocalRegionFetchTime = undefined;
85079
85444
  const getLocalAirportCode = async () => {
@@ -85093,7 +85458,7 @@
85093
85458
  // indicate that the region is random by adding 99, the convention is
85094
85459
  // that random region numbers end with 99
85095
85460
  const randomRegion = airportCodeToRegion[randomAirportCode][0] + 99;
85096
- logger$v.warn(`Could not get airport code, using random region: ${randomRegion}`);
85461
+ logger$w.warn(`Could not get airport code, using random region: ${randomRegion}`);
85097
85462
  return randomRegion;
85098
85463
  };
85099
85464
  const getLocalRegionWithCache = async (maxCacheAge = DEFAULT_MAX_CACHE_AGE) => {
@@ -85745,6 +86110,9 @@
85745
86110
  class SendFailed extends Err {
85746
86111
  constructor(message, originalError) { super(ErrorCode.SEND_FAILED, message, originalError); }
85747
86112
  }
86113
+ function logGapDiagnosticSampled(layer, opts = {}) {
86114
+ return;
86115
+ }
85748
86116
 
85749
86117
  // @generated message type with reflection information, may provide speed optimized methods
85750
86118
  let Empty$Type$2 = class Empty$Type extends MessageType {
@@ -87288,7 +87656,7 @@
87288
87656
  return v4();
87289
87657
  };
87290
87658
 
87291
- const logger$C = new Logger('ManagedConnection');
87659
+ const logger$D = new Logger('ManagedConnection');
87292
87660
  // ManagedConnection is a component used as a wrapper for IConnection after they have been successfully handshaked.
87293
87661
  // Should only be used in the ConnectionManager.
87294
87662
  class ManagedConnection extends EventEmitter {
@@ -87318,7 +87686,7 @@
87318
87686
  this.remotePeerDescriptor = peerDescriptor;
87319
87687
  }
87320
87688
  onDisconnected(gracefulLeave) {
87321
- logger$C.trace(getNodeIdOrUnknownFromPeerDescriptor(this.remotePeerDescriptor) + ' onDisconnected() ' + gracefulLeave);
87689
+ logger$D.trace(getNodeIdOrUnknownFromPeerDescriptor(this.remotePeerDescriptor) + ' onDisconnected() ' + gracefulLeave);
87322
87690
  if (!this.replacedAsDuplicate) {
87323
87691
  this.emit('disconnected', gracefulLeave);
87324
87692
  }
@@ -87327,7 +87695,7 @@
87327
87695
  // TODO: Can this be removed if ManagedConnections can never be duplicates?
87328
87696
  // Handle duplicates in the ConncetorFacade and no longer have PendingConnections in ConnectionManager
87329
87697
  replaceAsDuplicate() {
87330
- logger$C.trace(getNodeIdOrUnknownFromPeerDescriptor(this.remotePeerDescriptor) + ' replaceAsDuplicate');
87698
+ logger$D.trace(getNodeIdOrUnknownFromPeerDescriptor(this.remotePeerDescriptor) + ' replaceAsDuplicate');
87331
87699
  this.replacedAsDuplicate = true;
87332
87700
  }
87333
87701
  send(data) {
@@ -87474,10 +87842,10 @@
87474
87842
  }
87475
87843
  }
87476
87844
 
87477
- const logger$B = new Logger('ConnectionLockRpcRemote');
87845
+ const logger$C = new Logger('ConnectionLockRpcRemote');
87478
87846
  class ConnectionLockRpcRemote extends RpcRemote {
87479
87847
  async lockRequest(lockId) {
87480
- logger$B.trace(`Requesting locked connection to ${toNodeId(this.getPeerDescriptor())}`);
87848
+ logger$C.trace(`Requesting locked connection to ${toNodeId(this.getPeerDescriptor())}`);
87481
87849
  const request = {
87482
87850
  lockId
87483
87851
  };
@@ -87487,12 +87855,12 @@
87487
87855
  return res.accepted;
87488
87856
  }
87489
87857
  catch (err) {
87490
- logger$B.debug('Connection lock rejected', { err });
87858
+ logger$C.debug('Connection lock rejected', { err });
87491
87859
  return false;
87492
87860
  }
87493
87861
  }
87494
87862
  unlockRequest(lockId) {
87495
- logger$B.trace(`Requesting connection to be unlocked from ${toNodeId(this.getPeerDescriptor())}`);
87863
+ logger$C.trace(`Requesting connection to be unlocked from ${toNodeId(this.getPeerDescriptor())}`);
87496
87864
  const request = {
87497
87865
  lockId
87498
87866
  };
@@ -87500,11 +87868,11 @@
87500
87868
  notification: true
87501
87869
  });
87502
87870
  this.getClient().unlockRequest(request, options).catch((_e) => {
87503
- logger$B.trace('failed to send unlockRequest');
87871
+ logger$C.trace('failed to send unlockRequest');
87504
87872
  });
87505
87873
  }
87506
87874
  async gracefulDisconnect(disconnectMode) {
87507
- logger$B.trace(`Notifying a graceful disconnect to ${toNodeId(this.getPeerDescriptor())}`);
87875
+ logger$C.trace(`Notifying a graceful disconnect to ${toNodeId(this.getPeerDescriptor())}`);
87508
87876
  const request = {
87509
87877
  disconnectMode
87510
87878
  };
@@ -87516,7 +87884,7 @@
87516
87884
  await this.getClient().gracefulDisconnect(request, options);
87517
87885
  }
87518
87886
  async setPrivate(isPrivate) {
87519
- logger$B.trace(`Setting isPrivate: ${isPrivate} for ${toNodeId(this.getPeerDescriptor())}`);
87887
+ logger$C.trace(`Setting isPrivate: ${isPrivate} for ${toNodeId(this.getPeerDescriptor())}`);
87520
87888
  const request = {
87521
87889
  isPrivate
87522
87890
  };
@@ -87528,7 +87896,7 @@
87528
87896
  }
87529
87897
  }
87530
87898
 
87531
- const logger$A = new Logger('ConnectionLockRpcLocal');
87899
+ const logger$B = new Logger('ConnectionLockRpcLocal');
87532
87900
  class ConnectionLockRpcLocal {
87533
87901
  options;
87534
87902
  constructor(options) {
@@ -87557,7 +87925,7 @@
87557
87925
  }
87558
87926
  async gracefulDisconnect(disconnectNotice, context) {
87559
87927
  const senderPeerDescriptor = context.incomingSourceDescriptor;
87560
- logger$A.trace(getNodeIdOrUnknownFromPeerDescriptor(senderPeerDescriptor) + ' received gracefulDisconnect notice');
87928
+ logger$B.trace(getNodeIdOrUnknownFromPeerDescriptor(senderPeerDescriptor) + ' received gracefulDisconnect notice');
87561
87929
  if (disconnectNotice.disconnectMode === DisconnectMode$2.LEAVING) {
87562
87930
  await this.options.closeConnection(senderPeerDescriptor, true, 'graceful leave notified');
87563
87931
  }
@@ -87608,7 +87976,7 @@
87608
87976
  NatType["OPEN_INTERNET"] = "open_internet";
87609
87977
  NatType["UNKNOWN"] = "unknown";
87610
87978
  })(NatType || (NatType = {}));
87611
- const logger$z = new Logger('ConnectionManager');
87979
+ const logger$A = new Logger('ConnectionManager');
87612
87980
  var ConnectionManagerState;
87613
87981
  (function (ConnectionManagerState) {
87614
87982
  ConnectionManagerState["IDLE"] = "idle";
@@ -87658,7 +88026,7 @@
87658
88026
  getLocalPeerDescriptor: () => this.getLocalPeerDescriptor(),
87659
88027
  setPrivate: (id, isPrivate) => {
87660
88028
  if (!this.options.allowIncomingPrivateConnections) {
87661
- logger$z.debug(`node ${id} attemted to set a connection as private, but it is not allowed`);
88029
+ logger$A.debug(`node ${id} attemted to set a connection as private, but it is not allowed`);
87662
88030
  return;
87663
88031
  }
87664
88032
  if (isPrivate) {
@@ -87692,7 +88060,7 @@
87692
88060
  const connection = endpoint.connection;
87693
88061
  const nodeId = connection.getNodeId();
87694
88062
  if (!this.locks.isLocked(nodeId) && !this.locks.isPrivate(nodeId) && Date.now() - connection.getLastUsedTimestamp() > maxIdleTime) {
87695
- logger$z.trace('disconnecting in timeout interval: ' + getNodeIdOrUnknownFromPeerDescriptor(connection.getPeerDescriptor()));
88063
+ logger$A.trace('disconnecting in timeout interval: ' + getNodeIdOrUnknownFromPeerDescriptor(connection.getPeerDescriptor()));
87696
88064
  disconnectionCandidates.addContact(connection);
87697
88065
  }
87698
88066
  }
@@ -87700,7 +88068,7 @@
87700
88068
  const disconnectables = disconnectionCandidates.getFurthestContacts(this.endpoints.size - maxConnections);
87701
88069
  for (const disconnectable of disconnectables) {
87702
88070
  const peerDescriptor = disconnectable.getPeerDescriptor();
87703
- logger$z.trace('garbageCollecting ' + toNodeId(peerDescriptor));
88071
+ logger$A.trace('garbageCollecting ' + toNodeId(peerDescriptor));
87704
88072
  this.gracefullyDisconnectAsync(peerDescriptor, DisconnectMode$2.NORMAL).catch((_e) => { });
87705
88073
  }
87706
88074
  }
@@ -87709,11 +88077,11 @@
87709
88077
  throw new CouldNotStart(`Cannot start already ${this.state} module`);
87710
88078
  }
87711
88079
  this.state = ConnectionManagerState.RUNNING;
87712
- logger$z.trace(`Starting ConnectionManager...`);
88080
+ logger$A.trace(`Starting ConnectionManager...`);
87713
88081
  await this.connectorFacade.start((connection) => this.onNewConnection(connection), (nodeId) => this.hasConnection(nodeId), this);
87714
88082
  // Garbage collection of connections
87715
88083
  this.disconnectorIntervalRef = setInterval(() => {
87716
- logger$z.trace('disconnectorInterval');
88084
+ logger$A.trace('disconnectorInterval');
87717
88085
  const LAST_USED_LIMIT = 20000;
87718
88086
  this.garbageCollectConnections(this.options.maxConnections ?? 80, LAST_USED_LIMIT);
87719
88087
  }, 5000); // TODO use options option or named constant?
@@ -87723,7 +88091,7 @@
87723
88091
  return;
87724
88092
  }
87725
88093
  this.state = ConnectionManagerState.STOPPING;
87726
- logger$z.trace(`Stopping ConnectionManager`);
88094
+ logger$A.trace(`Stopping ConnectionManager`);
87727
88095
  if (this.disconnectorIntervalRef) {
87728
88096
  clearInterval(this.disconnectorIntervalRef);
87729
88097
  }
@@ -87733,23 +88101,23 @@
87733
88101
  await this.gracefullyDisconnectAsync(endpoint.connection.getPeerDescriptor(), DisconnectMode$2.LEAVING);
87734
88102
  }
87735
88103
  catch (e) {
87736
- logger$z.error(e);
88104
+ logger$A.error(e);
87737
88105
  }
87738
88106
  }
87739
88107
  else {
87740
88108
  const connection = endpoint.connection;
87741
- logger$z.trace('handshake of connection not completed, force-closing');
88109
+ logger$A.trace('handshake of connection not completed, force-closing');
87742
88110
  // TODO use options option or named constant?
87743
88111
  const eventReceived = waitForEvent(connection, 'disconnected', 2000);
87744
88112
  // TODO should we have some handling for this floating promise?
87745
88113
  connection.close(true);
87746
88114
  try {
87747
88115
  await eventReceived;
87748
- logger$z.trace('resolving after receiving disconnected event from non-handshaked connection');
88116
+ logger$A.trace('resolving after receiving disconnected event from non-handshaked connection');
87749
88117
  }
87750
88118
  catch (e) {
87751
88119
  endpoint.buffer.reject();
87752
- logger$z.trace('force-closing non-handshaked connection timed out ' + e);
88120
+ logger$A.trace('force-closing non-handshaked connection timed out ' + e);
87753
88121
  }
87754
88122
  }
87755
88123
  }));
@@ -87778,7 +88146,7 @@
87778
88146
  throw new CannotConnectToSelf('Cannot send to self');
87779
88147
  }
87780
88148
  const nodeId = toNodeId(peerDescriptor);
87781
- logger$z.trace(`Sending message to: ${nodeId}`);
88149
+ logger$A.trace(`Sending message to: ${nodeId}`);
87782
88150
  message = {
87783
88151
  ...message,
87784
88152
  sourceDescriptor: this.getLocalPeerDescriptor()
@@ -87833,13 +88201,13 @@
87833
88201
  }
87834
88202
  handleMessage(message) {
87835
88203
  const messageType = message.body.oneofKind;
87836
- logger$z.trace('Received message of type ' + messageType);
88204
+ logger$A.trace('Received message of type ' + messageType);
87837
88205
  if (messageType !== 'rpcMessage') {
87838
- logger$z.trace('Filtered out non-RPC message of type ' + messageType);
88206
+ logger$A.trace('Filtered out non-RPC message of type ' + messageType);
87839
88207
  return;
87840
88208
  }
87841
88209
  if (this.duplicateMessageDetector.isMostLikelyDuplicate(message.messageId)) {
87842
- logger$z.trace('handleMessage filtered duplicate ' + toNodeId(message.sourceDescriptor)
88210
+ logger$A.trace('handleMessage filtered duplicate ' + toNodeId(message.sourceDescriptor)
87843
88211
  + ' ' + message.serviceId + ' ' + message.messageId);
87844
88212
  return;
87845
88213
  }
@@ -87848,7 +88216,7 @@
87848
88216
  this.rpcCommunicator?.handleMessageFromPeer(message);
87849
88217
  }
87850
88218
  else {
87851
- logger$z.trace('emit "message" ' + toNodeId(message.sourceDescriptor)
88219
+ logger$A.trace('emit "message" ' + toNodeId(message.sourceDescriptor)
87852
88220
  + ' ' + message.serviceId + ' ' + message.messageId);
87853
88221
  this.emit('message', message);
87854
88222
  }
@@ -87865,7 +88233,7 @@
87865
88233
  }
87866
88234
  catch (e) {
87867
88235
  // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
87868
- logger$z.debug(`Parsing incoming data into Message failed: ${e}`);
88236
+ logger$A.debug(`Parsing incoming data into Message failed: ${e}`);
87869
88237
  return;
87870
88238
  }
87871
88239
  message.sourceDescriptor = peerDescriptor;
@@ -87874,7 +88242,7 @@
87874
88242
  }
87875
88243
  catch (e) {
87876
88244
  // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
87877
- logger$z.debug(`Handling incoming data failed: ${e}`);
88245
+ logger$A.debug(`Handling incoming data failed: ${e}`);
87878
88246
  }
87879
88247
  }
87880
88248
  onConnected(peerDescriptor, connection) {
@@ -87887,7 +88255,7 @@
87887
88255
  const pendingConnection = endpoint.connection;
87888
88256
  const buffer = outputBuffer.getBuffer();
87889
88257
  while (buffer.length > 0) {
87890
- logger$z.trace('emptying buffer');
88258
+ logger$A.trace('emptying buffer');
87891
88259
  managedConnection.send(buffer.shift());
87892
88260
  }
87893
88261
  outputBuffer.resolve();
@@ -87904,7 +88272,7 @@
87904
88272
  }
87905
88273
  onDisconnected(peerDescriptor, gracefulLeave) {
87906
88274
  const nodeId = toNodeId(peerDescriptor);
87907
- logger$z.trace(nodeId + ' onDisconnected() gracefulLeave: ' + gracefulLeave);
88275
+ logger$A.trace(nodeId + ' onDisconnected() gracefulLeave: ' + gracefulLeave);
87908
88276
  const endpoint = this.endpoints.get(nodeId);
87909
88277
  if (endpoint) {
87910
88278
  this.locks.clearAllLocks(nodeId);
@@ -87912,7 +88280,7 @@
87912
88280
  endpoint.buffer.reject();
87913
88281
  }
87914
88282
  this.endpoints.delete(nodeId);
87915
- logger$z.trace(nodeId + ' deleted connection in onDisconnected() gracefulLeave: ' + gracefulLeave);
88283
+ logger$A.trace(nodeId + ' deleted connection in onDisconnected() gracefulLeave: ' + gracefulLeave);
87916
88284
  this.emit('disconnected', peerDescriptor, gracefulLeave);
87917
88285
  this.onConnectionCountChange();
87918
88286
  }
@@ -87921,7 +88289,7 @@
87921
88289
  if (this.state === ConnectionManagerState.STOPPED) {
87922
88290
  return false;
87923
88291
  }
87924
- logger$z.trace('onNewConnection()');
88292
+ logger$A.trace('onNewConnection()');
87925
88293
  if (!this.acceptNewConnection(connection)) {
87926
88294
  return false;
87927
88295
  }
@@ -87931,7 +88299,7 @@
87931
88299
  }
87932
88300
  acceptNewConnection(newConnection) {
87933
88301
  const nodeId = toNodeId(newConnection.getPeerDescriptor());
87934
- logger$z.trace(nodeId + ' acceptNewConnection()');
88302
+ logger$A.trace(nodeId + ' acceptNewConnection()');
87935
88303
  if (this.endpoints.has(nodeId)) {
87936
88304
  if (getOfferer(toNodeId(this.getLocalPeerDescriptor()), nodeId) === 'remote') {
87937
88305
  let buffer;
@@ -87940,14 +88308,14 @@
87940
88308
  // Could be related to WS client connections not realizing that they have been disconnected.
87941
88309
  // Makes refactoring duplicate connection handling to the connectors very difficult.
87942
88310
  if (this.endpoints.get(nodeId).connected) {
87943
- logger$z.debug('replacing connected connection', { nodeId });
88311
+ logger$A.debug('replacing connected connection', { nodeId });
87944
88312
  buffer = new OutputBuffer();
87945
88313
  }
87946
88314
  else {
87947
88315
  buffer = endpoint.buffer;
87948
88316
  }
87949
88317
  const oldConnection = endpoint.connection;
87950
- logger$z.trace('replaced: ' + nodeId);
88318
+ logger$A.trace('replaced: ' + nodeId);
87951
88319
  oldConnection.replaceAsDuplicate();
87952
88320
  this.endpoints.set(nodeId, { connected: false, connection: newConnection, buffer: buffer });
87953
88321
  return true;
@@ -87956,7 +88324,7 @@
87956
88324
  return false;
87957
88325
  }
87958
88326
  }
87959
- logger$z.trace(nodeId + ' added to connections at acceptNewConnection');
88327
+ logger$A.trace(nodeId + ' added to connections at acceptNewConnection');
87960
88328
  this.endpoints.set(nodeId, {
87961
88329
  connected: false,
87962
88330
  buffer: new OutputBuffer(),
@@ -87966,14 +88334,14 @@
87966
88334
  }
87967
88335
  async closeConnection(peerDescriptor, gracefulLeave, reason) {
87968
88336
  const nodeId = toNodeId(peerDescriptor);
87969
- logger$z.trace(nodeId + ' ' + 'closeConnection() ' + reason);
88337
+ logger$A.trace(nodeId + ' ' + 'closeConnection() ' + reason);
87970
88338
  this.locks.clearAllLocks(nodeId);
87971
88339
  if (this.endpoints.has(nodeId)) {
87972
88340
  const connectionToClose = this.endpoints.get(nodeId).connection;
87973
88341
  await connectionToClose.close(gracefulLeave);
87974
88342
  }
87975
88343
  else {
87976
- logger$z.trace(nodeId + ' ' + 'closeConnection() this.endpoints did not have the id');
88344
+ logger$A.trace(nodeId + ' ' + 'closeConnection() this.endpoints did not have the id');
87977
88345
  this.emit('disconnected', peerDescriptor, false);
87978
88346
  }
87979
88347
  }
@@ -87985,8 +88353,8 @@
87985
88353
  const rpcRemote = new ConnectionLockRpcRemote(this.getLocalPeerDescriptor(), targetDescriptor, this.rpcCommunicator, ConnectionLockRpcClient);
87986
88354
  this.locks.addLocalLocked(nodeId, lockId);
87987
88355
  rpcRemote.lockRequest(lockId)
87988
- .then((_accepted) => logger$z.trace('LockRequest successful'))
87989
- .catch((err) => { logger$z.debug(err); });
88356
+ .then((_accepted) => logger$A.trace('LockRequest successful'))
88357
+ .catch((err) => { logger$A.debug(err); });
87990
88358
  }
87991
88359
  unlockConnection(targetDescriptor, lockId) {
87992
88360
  if (this.state === ConnectionManagerState.STOPPED || areEqualPeerDescriptors(targetDescriptor, this.getLocalPeerDescriptor())) {
@@ -88038,7 +88406,7 @@
88038
88406
  async gracefullyDisconnectAsync(targetDescriptor, disconnectMode) {
88039
88407
  const endpoint = this.endpoints.get(toNodeId(targetDescriptor));
88040
88408
  if (!endpoint) {
88041
- logger$z.debug('gracefullyDisconnectedAsync() tried on a non-existing connection');
88409
+ logger$A.debug('gracefullyDisconnectedAsync() tried on a non-existing connection');
88042
88410
  return;
88043
88411
  }
88044
88412
  if (endpoint.connected) {
@@ -88047,15 +88415,15 @@
88047
88415
  // TODO use options option or named constant?
88048
88416
  // eslint-disable-next-line promise/catch-or-return
88049
88417
  waitForEvent(connection, 'disconnected', 2000).then(() => {
88050
- logger$z.trace('disconnected event received in gracefullyDisconnectAsync()');
88418
+ logger$A.trace('disconnected event received in gracefullyDisconnectAsync()');
88051
88419
  })
88052
88420
  .catch((e) => {
88053
- logger$z.trace('force-closing connection after timeout ' + e);
88421
+ logger$A.trace('force-closing connection after timeout ' + e);
88054
88422
  // TODO should we have some handling for this floating promise?
88055
88423
  connection.close(true);
88056
88424
  })
88057
88425
  .finally(() => {
88058
- logger$z.trace('resolving after receiving disconnected event');
88426
+ logger$A.trace('resolving after receiving disconnected event');
88059
88427
  resolve();
88060
88428
  });
88061
88429
  });
@@ -88070,13 +88438,13 @@
88070
88438
  }
88071
88439
  async doGracefullyDisconnectAsync(targetDescriptor, disconnectMode) {
88072
88440
  const nodeId = toNodeId(targetDescriptor);
88073
- logger$z.trace(nodeId + ' gracefullyDisconnectAsync()');
88441
+ logger$A.trace(nodeId + ' gracefullyDisconnectAsync()');
88074
88442
  const rpcRemote = new ConnectionLockRpcRemote(this.getLocalPeerDescriptor(), targetDescriptor, this.rpcCommunicator, ConnectionLockRpcClient);
88075
88443
  try {
88076
88444
  await rpcRemote.gracefulDisconnect(disconnectMode);
88077
88445
  }
88078
88446
  catch (ex) {
88079
- logger$z.trace(nodeId + ' remote.gracefulDisconnect() failed' + ex);
88447
+ logger$A.trace(nodeId + ' remote.gracefulDisconnect() failed' + ex);
88080
88448
  }
88081
88449
  }
88082
88450
  getConnections() {
@@ -88136,9 +88504,9 @@
88136
88504
  }
88137
88505
  };
88138
88506
 
88139
- var version$2 = "103.3.1";
88507
+ var version$2 = "103.7.0-rc.2";
88140
88508
 
88141
- const logger$x = new Logger('Handshaker');
88509
+ const logger$y = new Logger('Handshaker');
88142
88510
  // Optimally the Outgoing and Incoming Handshakers could be their own separate classes
88143
88511
  // However, in cases where the PeerDescriptor of the other end of the connection can be known
88144
88512
  // only after a HandshakeRequest a base Handshaker class is needed as the IncomingHandshaker currently
@@ -88160,7 +88528,7 @@
88160
88528
  }
88161
88529
  };
88162
88530
  const handshakeCompletedListener = (peerDescriptor) => {
88163
- logger$x.trace('handshake completed for outgoing connection, ' + toNodeId(peerDescriptor));
88531
+ logger$y.trace('handshake completed for outgoing connection, ' + toNodeId(peerDescriptor));
88164
88532
  pendingConnection.onHandshakeCompleted(connection);
88165
88533
  stopHandshaker();
88166
88534
  };
@@ -88260,12 +88628,12 @@
88260
88628
  try {
88261
88629
  const message = Message$2.fromBinary(data);
88262
88630
  if (message.body.oneofKind === 'handshakeRequest') {
88263
- logger$x.trace('handshake request received');
88631
+ logger$y.trace('handshake request received');
88264
88632
  const handshake = message.body.handshakeRequest;
88265
88633
  this.emit('handshakeRequest', handshake.sourcePeerDescriptor, handshake.protocolVersion, handshake.targetPeerDescriptor);
88266
88634
  }
88267
88635
  if (message.body.oneofKind === 'handshakeResponse') {
88268
- logger$x.trace('handshake response received');
88636
+ logger$y.trace('handshake response received');
88269
88637
  const handshake = message.body.handshakeResponse;
88270
88638
  const error = !isMaybeSupportedProtocolVersion(handshake.protocolVersion)
88271
88639
  ? HandshakeError$2.UNSUPPORTED_PROTOCOL_VERSION : handshake.error;
@@ -88278,18 +88646,18 @@
88278
88646
  }
88279
88647
  }
88280
88648
  catch (err) {
88281
- logger$x.debug('error while parsing handshake message', err);
88649
+ logger$y.debug('error while parsing handshake message', err);
88282
88650
  }
88283
88651
  }
88284
88652
  sendHandshakeRequest(remotePeerDescriptor) {
88285
88653
  const msg = createHandshakeRequest(this.localPeerDescriptor, remotePeerDescriptor);
88286
88654
  this.connection.send(Message$2.toBinary(msg));
88287
- logger$x.trace('handshake request sent');
88655
+ logger$y.trace('handshake request sent');
88288
88656
  }
88289
88657
  sendHandshakeResponse(error) {
88290
88658
  const msg = createHandshakeResponse(this.localPeerDescriptor, error);
88291
88659
  this.connection.send(Message$2.toBinary(msg));
88292
- logger$x.trace('handshake response sent');
88660
+ logger$y.trace('handshake response sent');
88293
88661
  }
88294
88662
  stop() {
88295
88663
  this.connection.off('data', this.onDataListener);
@@ -88297,7 +88665,7 @@
88297
88665
  }
88298
88666
  };
88299
88667
 
88300
- const logger$w = new Logger('PendingConnection');
88668
+ const logger$x = new Logger('PendingConnection');
88301
88669
  // PendingConnection is used as a reference to a connection that should be opened and handshaked to a given PeerDescriptor
88302
88670
  // It does not hold a connection internally. The public method onHandshakedCompleted should be called once a connection for the
88303
88671
  // remotePeerDescriptor is opened and handshaked successfully.
@@ -88315,7 +88683,7 @@
88315
88683
  }, timeout, this.connectingAbortController.signal);
88316
88684
  }
88317
88685
  replaceAsDuplicate() {
88318
- logger$w.trace(getNodeIdOrUnknownFromPeerDescriptor(this.remotePeerDescriptor) + ' replaceAsDuplicate');
88686
+ logger$x.trace(getNodeIdOrUnknownFromPeerDescriptor(this.remotePeerDescriptor) + ' replaceAsDuplicate');
88319
88687
  this.replacedAsDuplicate = true;
88320
88688
  }
88321
88689
  onHandshakeCompleted(connection) {
@@ -88372,6 +88740,8 @@
88372
88740
  }
88373
88741
  }
88374
88742
 
88743
+ const isWorkerEnvironment = typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope;
88744
+
88375
88745
  var RtcDescription;
88376
88746
  (function (RtcDescription) {
88377
88747
  RtcDescription["OFFER"] = "offer";
@@ -88386,8 +88756,8 @@
88386
88756
  DisconnectedRtcPeerConnectionStateEnum["FAILED"] = "failed";
88387
88757
  DisconnectedRtcPeerConnectionStateEnum["CLOSED"] = "closed";
88388
88758
  })(DisconnectedRtcPeerConnectionStateEnum || (DisconnectedRtcPeerConnectionStateEnum = {}));
88389
- const logger$u = new Logger('WebrtcConnection (browser)');
88390
- class WebrtcConnection extends EventEmitter {
88759
+ const logger$v = new Logger('DirectWebrtcConnection (browser)');
88760
+ class DirectWebrtcConnection extends EventEmitter {
88391
88761
  connectionId;
88392
88762
  connectionType = ConnectionType.WEBRTC;
88393
88763
  // We need to keep track of connection state ourselves because
@@ -88425,7 +88795,7 @@
88425
88795
  }
88426
88796
  };
88427
88797
  this.peerConnection.onicegatheringstatechange = () => {
88428
- logger$u.trace(`conn.onGatheringStateChange: ${this.peerConnection?.iceGatheringState}`);
88798
+ logger$v.trace(`conn.onGatheringStateChange: ${this.peerConnection?.iceGatheringState}`);
88429
88799
  };
88430
88800
  this.peerConnection.onconnectionstatechange = () => this.onStateChange();
88431
88801
  if (isOffering) {
@@ -88436,7 +88806,7 @@
88436
88806
  await this.peerConnection.setLocalDescription();
88437
88807
  }
88438
88808
  catch (err) {
88439
- logger$u.warn('Failed to set local description', { err });
88809
+ logger$v.warn('Failed to set local description', { err });
88440
88810
  }
88441
88811
  if (this.peerConnection.localDescription !== null) {
88442
88812
  this.emit('localDescription', this.peerConnection.localDescription?.sdp, this.peerConnection.localDescription?.type);
@@ -88464,14 +88834,14 @@
88464
88834
  clearTimeout(this.earlyTimeout);
88465
88835
  }
88466
88836
  catch (err) {
88467
- logger$u.warn('Failed to set remote description', { err });
88837
+ logger$v.warn('Failed to set remote description', { err });
88468
88838
  }
88469
88839
  if ((type.toLowerCase() === RtcDescription.OFFER) && (this.peerConnection !== undefined)) {
88470
88840
  try {
88471
88841
  await this.peerConnection.setLocalDescription();
88472
88842
  }
88473
88843
  catch (err) {
88474
- logger$u.warn('Failed to set local description', { err });
88844
+ logger$v.warn('Failed to set local description', { err });
88475
88845
  }
88476
88846
  if (this.peerConnection.localDescription !== null) {
88477
88847
  this.emit('localDescription', this.peerConnection.localDescription.sdp, this.peerConnection.localDescription.type);
@@ -88481,7 +88851,7 @@
88481
88851
  addRemoteCandidate(candidate, mid) {
88482
88852
  this.peerConnection?.addIceCandidate({ candidate: candidate, sdpMid: mid })
88483
88853
  .catch((err) => {
88484
- logger$u.warn('Failed to add ICE candidate', { err });
88854
+ logger$v.warn('Failed to add ICE candidate', { err });
88485
88855
  });
88486
88856
  }
88487
88857
  isOpen() {
@@ -88504,7 +88874,7 @@
88504
88874
  this.dataChannel.close();
88505
88875
  }
88506
88876
  catch (err) {
88507
- logger$u.warn('Failed to close data channel', { err });
88877
+ logger$v.warn('Failed to close data channel', { err });
88508
88878
  }
88509
88879
  }
88510
88880
  this.dataChannel = undefined;
@@ -88513,7 +88883,7 @@
88513
88883
  this.peerConnection.close();
88514
88884
  }
88515
88885
  catch (err) {
88516
- logger$u.warn('Failed to close connection', { err });
88886
+ logger$v.warn('Failed to close connection', { err });
88517
88887
  }
88518
88888
  }
88519
88889
  this.peerConnection = undefined;
@@ -88525,6 +88895,9 @@
88525
88895
  }
88526
88896
  send(data) {
88527
88897
  if (this.lastState === 'connected') {
88898
+ logGapDiagnosticSampled('dht.dc.send', {
88899
+ detail: { bufferedAmount: this.dataChannel.bufferedAmount, queueLen: this.messageQueue.length }
88900
+ });
88528
88901
  if (this.dataChannel.bufferedAmount > this.bufferThresholdHigh) {
88529
88902
  this.messageQueue.push(data);
88530
88903
  }
@@ -88533,7 +88906,7 @@
88533
88906
  }
88534
88907
  }
88535
88908
  else {
88536
- logger$u.warn('Tried to send on a connection with last state ' + this.lastState);
88909
+ logger$v.warn('Tried to send on a connection with last state ' + this.lastState);
88537
88910
  }
88538
88911
  }
88539
88912
  setupDataChannel(dataChannel) {
@@ -88541,22 +88914,22 @@
88541
88914
  this.dataChannel.binaryType = 'arraybuffer';
88542
88915
  this.dataChannel.bufferedAmountLowThreshold = this.bufferThresholdLow;
88543
88916
  dataChannel.onopen = () => {
88544
- logger$u.trace('dc.onOpen');
88917
+ logger$v.trace('dc.onOpen');
88545
88918
  this.onDataChannelOpen();
88546
88919
  };
88547
88920
  dataChannel.onclose = () => {
88548
- logger$u.trace('dc.onClosed');
88921
+ logger$v.trace('dc.onClosed');
88549
88922
  this.doClose(false);
88550
88923
  };
88551
88924
  dataChannel.onerror = (err) => {
88552
- logger$u.warn('Data channel error', { err });
88925
+ logger$v.warn('Data channel error', { err });
88553
88926
  };
88554
88927
  dataChannel.onmessage = (msg) => {
88555
- logger$u.trace('dc.onmessage');
88928
+ logger$v.trace('dc.onmessage');
88556
88929
  this.emit('data', new Uint8Array(msg.data));
88557
88930
  };
88558
88931
  dataChannel.onbufferedamountlow = () => {
88559
- logger$u.trace('dc.onBufferedAmountLow');
88932
+ logger$v.trace('dc.onBufferedAmountLow');
88560
88933
  while (this.messageQueue.length > 0 && this.dataChannel.bufferedAmount < this.bufferThresholdHigh) {
88561
88934
  const data = this.messageQueue.shift();
88562
88935
  this.dataChannel.send(data);
@@ -88595,6 +88968,292 @@
88595
88968
  }
88596
88969
  }
88597
88970
 
88971
+ /**
88972
+ * Call this function on the **main thread** before the worker starts using
88973
+ * WebRTC connections. It creates a dedicated `MessageChannel`, exposes a
88974
+ * {@link WebrtcBridge} instance on one port, and sends the other port to
88975
+ * the worker so that `WorkerWebrtcConnection` can reach the bridge.
88976
+ *
88977
+ * @example
88978
+ * ```ts
88979
+ * import { installWebrtcBridge } from '@streamr/dht'
88980
+ *
88981
+ * const worker = new Worker('./my-worker.ts', { type: 'module' })
88982
+ * installWebrtcBridge(worker)
88983
+ * ```
88984
+ */
88985
+ const WEBRTC_BRIDGE_PORT_MESSAGE_TYPE = 'streamr-webrtc-bridge-port';
88986
+
88987
+ /**
88988
+ * WorkerWebrtcConnection — runs inside a **Web Worker**.
88989
+ *
88990
+ * Implements the same IWebrtcConnection + IConnection interfaces as
88991
+ * DirectWebrtcConnection, but delegates RTCPeerConnection management to
88992
+ * the main-thread {@link WebrtcBridge} via Comlink.
88993
+ *
88994
+ * The RTCDataChannel is **transferred** from the main thread and lives
88995
+ * entirely in the worker — all data events (onmessage, onopen, onclose,
88996
+ * onbufferedamountlow) fire in the worker's event loop. The main thread
88997
+ * is never involved in the data path.
88998
+ */
88999
+ // ── Module-level bridge client (initialized once per worker) ────────
89000
+ let resolveBridgeProxy;
89001
+ const bridgeProxyPromise = new Promise((resolve) => {
89002
+ resolveBridgeProxy = resolve;
89003
+ });
89004
+ // Listen for the bridge port message from the main thread.
89005
+ // This is guarded so it only runs inside a worker context.
89006
+ if (isWorkerEnvironment) {
89007
+ const handler = (e) => {
89008
+ if (e.data?.type === WEBRTC_BRIDGE_PORT_MESSAGE_TYPE && e.data.port) {
89009
+ const proxy = wrap$1(e.data.port);
89010
+ resolveBridgeProxy(proxy);
89011
+ self.removeEventListener('message', handler);
89012
+ }
89013
+ };
89014
+ self.addEventListener('message', handler);
89015
+ }
89016
+ function getBridgeProxy() {
89017
+ return bridgeProxyPromise;
89018
+ }
89019
+ // ── Disconnection states ────────────────────────────────────────────
89020
+ var DisconnectedState;
89021
+ (function (DisconnectedState) {
89022
+ DisconnectedState["DISCONNECTED"] = "disconnected";
89023
+ DisconnectedState["FAILED"] = "failed";
89024
+ DisconnectedState["CLOSED"] = "closed";
89025
+ })(DisconnectedState || (DisconnectedState = {}));
89026
+ const logger$u = new Logger('WorkerWebrtcConnection');
89027
+ // ── WorkerWebrtcConnection ──────────────────────────────────────────
89028
+ class WorkerWebrtcConnection extends EventEmitter {
89029
+ connectionId;
89030
+ connectionType = ConnectionType.WEBRTC;
89031
+ iceServers;
89032
+ bufferThresholdHigh;
89033
+ bufferThresholdLow;
89034
+ dataChannel;
89035
+ bridge;
89036
+ closed = false;
89037
+ connected = false;
89038
+ earlyTimeout;
89039
+ messageQueue = [];
89040
+ startPromise;
89041
+ constructor(params) {
89042
+ super();
89043
+ this.connectionId = createRandomConnectionId();
89044
+ this.iceServers = params.iceServers ?? [];
89045
+ this.bufferThresholdHigh = params.bufferThresholdHigh ?? 2 ** 17;
89046
+ this.bufferThresholdLow = params.bufferThresholdLow ?? 2 ** 15;
89047
+ this.earlyTimeout = setTimeout(() => {
89048
+ this.doClose(false, 'timed out due to remote descriptor not being set');
89049
+ }, EARLY_TIMEOUT);
89050
+ }
89051
+ // ── IWebrtcConnection ───────────────────────────────────────
89052
+ start(isOffering) {
89053
+ this.startPromise = this.doStart(isOffering);
89054
+ this.startPromise.catch((err) => {
89055
+ logger$u.warn('Failed to start worker WebRTC connection', { err });
89056
+ this.doClose(false, 'Failed to start');
89057
+ });
89058
+ }
89059
+ async doStart(isOffering) {
89060
+ this.bridge = await getBridgeProxy();
89061
+ const iceServers = this.iceServers.map(({ url, port, username, password }) => ({
89062
+ urls: `${url}:${port}`,
89063
+ username,
89064
+ credential: password,
89065
+ }));
89066
+ await this.bridge.start(this.connectionId, iceServers, isOffering, proxy({
89067
+ onLocalCandidate: (candidate, mid) => {
89068
+ if (!this.closed) {
89069
+ this.emit('localCandidate', candidate, mid);
89070
+ }
89071
+ },
89072
+ onLocalDescription: (description, type) => {
89073
+ if (!this.closed) {
89074
+ this.emit('localDescription', description, type);
89075
+ }
89076
+ },
89077
+ onConnectionStateChange: (state) => {
89078
+ if (state === DisconnectedState.CLOSED ||
89079
+ state === DisconnectedState.DISCONNECTED ||
89080
+ state === DisconnectedState.FAILED) {
89081
+ this.doClose(false, `pcState=${state}`);
89082
+ }
89083
+ },
89084
+ onDataChannel: (channel) => {
89085
+ if (!this.closed) {
89086
+ this.setupDataChannel(channel);
89087
+ // If the channel was already open at transfer time
89088
+ if (channel.readyState === 'open') {
89089
+ this.onDataChannelOpen();
89090
+ }
89091
+ }
89092
+ },
89093
+ }));
89094
+ }
89095
+ async setRemoteDescription(description, type) {
89096
+ if (this.startPromise) {
89097
+ await this.startPromise;
89098
+ }
89099
+ if (!this.bridge || this.closed) {
89100
+ return;
89101
+ }
89102
+ const wasSet = await this.bridge.setRemoteDescription(this.connectionId, description, type);
89103
+ if (wasSet) {
89104
+ clearTimeout(this.earlyTimeout);
89105
+ }
89106
+ }
89107
+ addRemoteCandidate(candidate, mid) {
89108
+ this.doAddRemoteCandidate(candidate, mid).catch((err) => {
89109
+ logger$u.warn('Failed to add remote candidate via bridge', { err });
89110
+ });
89111
+ }
89112
+ async doAddRemoteCandidate(candidate, mid) {
89113
+ if (this.startPromise) {
89114
+ await this.startPromise;
89115
+ }
89116
+ if (!this.bridge || this.closed) {
89117
+ return;
89118
+ }
89119
+ await this.bridge.addRemoteCandidate(this.connectionId, candidate, mid);
89120
+ }
89121
+ isOpen() {
89122
+ return this.connected;
89123
+ }
89124
+ // ── IConnection ─────────────────────────────────────────────
89125
+ async close(gracefulLeave, reason) {
89126
+ this.doClose(gracefulLeave, reason);
89127
+ }
89128
+ destroy() {
89129
+ this.removeAllListeners();
89130
+ this.doClose(false);
89131
+ }
89132
+ send(data) {
89133
+ if (this.connected && this.dataChannel) {
89134
+ logGapDiagnosticSampled('dht.dc.send', {
89135
+ detail: { bufferedAmount: this.dataChannel.bufferedAmount, queueLen: this.messageQueue.length }
89136
+ });
89137
+ if (this.dataChannel.bufferedAmount > this.bufferThresholdHigh) {
89138
+ this.messageQueue.push(data);
89139
+ }
89140
+ else {
89141
+ this.dataChannel.send(data);
89142
+ }
89143
+ }
89144
+ else if (!this.closed) {
89145
+ this.messageQueue.push(data);
89146
+ }
89147
+ }
89148
+ setConnectionId(connectionId) {
89149
+ const oldId = this.connectionId;
89150
+ this.connectionId = connectionId;
89151
+ if (this.bridge && oldId !== connectionId) {
89152
+ this.bridge.renameConnection(oldId, connectionId).catch(() => { });
89153
+ }
89154
+ }
89155
+ // ── DataChannel handling (runs entirely in the worker) ──────
89156
+ setupDataChannel(dataChannel) {
89157
+ this.dataChannel = dataChannel;
89158
+ this.dataChannel.binaryType = 'arraybuffer';
89159
+ this.dataChannel.bufferedAmountLowThreshold = this.bufferThresholdLow;
89160
+ dataChannel.onopen = () => {
89161
+ logger$u.trace('dc.onOpen (worker)');
89162
+ this.onDataChannelOpen();
89163
+ };
89164
+ dataChannel.onclose = () => {
89165
+ logger$u.trace('dc.onClosed (worker)');
89166
+ this.doClose(false, 'dataChannel.onclose');
89167
+ };
89168
+ dataChannel.onerror = (err) => {
89169
+ logger$u.warn('Data channel error (worker)', { err });
89170
+ };
89171
+ dataChannel.onmessage = (msg) => {
89172
+ logger$u.trace('dc.onmessage (worker)');
89173
+ this.emit('data', new Uint8Array(msg.data));
89174
+ };
89175
+ dataChannel.onbufferedamountlow = () => {
89176
+ logger$u.trace('dc.onBufferedAmountLow (worker)');
89177
+ while (this.messageQueue.length > 0 &&
89178
+ this.dataChannel.bufferedAmount < this.bufferThresholdHigh) {
89179
+ const data = this.messageQueue.shift();
89180
+ this.dataChannel.send(data);
89181
+ }
89182
+ };
89183
+ }
89184
+ onDataChannelOpen() {
89185
+ this.connected = true;
89186
+ this.flushMessageQueue();
89187
+ this.emit('connected');
89188
+ }
89189
+ flushMessageQueue() {
89190
+ while (this.messageQueue.length > 0 &&
89191
+ this.dataChannel &&
89192
+ this.dataChannel.bufferedAmount < this.bufferThresholdHigh) {
89193
+ const data = this.messageQueue.shift();
89194
+ this.dataChannel.send(data);
89195
+ }
89196
+ }
89197
+ // ── Teardown ────────────────────────────────────────────────
89198
+ doClose(gracefulLeave, reason) {
89199
+ if (!this.closed) {
89200
+ this.closed = true;
89201
+ this.connected = false;
89202
+ this.messageQueue.length = 0;
89203
+ clearTimeout(this.earlyTimeout);
89204
+ this.stopListening();
89205
+ this.emit('disconnected', gracefulLeave, undefined, reason);
89206
+ this.removeAllListeners();
89207
+ if (this.dataChannel !== undefined) {
89208
+ try {
89209
+ this.dataChannel.close();
89210
+ }
89211
+ catch (err) {
89212
+ logger$u.warn('Failed to close data channel (worker)', { err });
89213
+ }
89214
+ }
89215
+ this.dataChannel = undefined;
89216
+ // Tell the main-thread bridge to tear down the RTCPeerConnection.
89217
+ // Fire-and-forget — we don't block on this.
89218
+ this.bridge
89219
+ ?.close(this.connectionId)
89220
+ .catch(() => {
89221
+ // intentionally swallowed
89222
+ });
89223
+ }
89224
+ }
89225
+ stopListening() {
89226
+ if (this.dataChannel !== undefined) {
89227
+ this.dataChannel.onopen = null;
89228
+ this.dataChannel.onclose = null;
89229
+ this.dataChannel.onerror = null;
89230
+ this.dataChannel.onbufferedamountlow = null;
89231
+ this.dataChannel.onmessage = null;
89232
+ }
89233
+ }
89234
+ }
89235
+
89236
+ /**
89237
+ * Conditional re-export of the browser WebrtcConnection.
89238
+ *
89239
+ * At module-load time we detect whether we are running inside a Web Worker.
89240
+ * - **Main thread** → use {@link DirectWebrtcConnection} which owns the
89241
+ * `RTCPeerConnection` and `RTCDataChannel` directly.
89242
+ * - **Worker thread** → use {@link WorkerWebrtcConnection} which delegates
89243
+ * `RTCPeerConnection` signaling to the main thread via a Comlink bridge
89244
+ * and receives a transferred `RTCDataChannel` that lives entirely in the
89245
+ * worker.
89246
+ *
89247
+ * Both classes implement `IWebrtcConnection & IConnection` and expose the
89248
+ * same public API, so upstream code (WebrtcConnector, etc.) is unaffected.
89249
+ */
89250
+ // The constructor — points to the right class based on the runtime
89251
+ // environment. The type assertion is safe because both implementations
89252
+ // share the same public interface surface.
89253
+ const WebrtcConnection = (isWorkerEnvironment
89254
+ ? WorkerWebrtcConnection
89255
+ : DirectWebrtcConnection);
89256
+
88598
89257
  const logger$t = new Logger('WebrtcConnectorRpcRemote');
88599
89258
  class WebrtcConnectorRpcRemote extends RpcRemote {
88600
89259
  requestConnection() {
@@ -90757,7 +91416,7 @@
90757
91416
  logger$d$1.debug(`Ring join on ${this.options.serviceId} timed out`);
90758
91417
  }
90759
91418
  finally {
90760
- sessions.forEach((session) => this.ongoingDiscoverySessions.delete(session.id));
91419
+ sessions.forEach((session) => this.ongoingRingDiscoverySessions.delete(session.id));
90761
91420
  }
90762
91421
  }
90763
91422
  async rejoinDht(entryPoint, contactedPeers = new Set(), distantJoinContactPeers = new Set()) {
@@ -93073,7 +93732,7 @@
93073
93732
  }
93074
93733
  }
93075
93734
 
93076
- var version$1 = "103.3.1";
93735
+ var version$1 = "103.7.0-rc.2";
93077
93736
 
93078
93737
  // @generated message type with reflection information, may provide speed optimized methods
93079
93738
  let Any$Type$1 = class Any$Type extends MessageType {
@@ -94720,6 +95379,18 @@
94720
95379
  */
94721
95380
  const PauseNeighborRequest = new PauseNeighborRequest$Type();
94722
95381
  // @generated message type with reflection information, may provide speed optimized methods
95382
+ class PauseNeighborResponse$Type extends MessageType {
95383
+ constructor() {
95384
+ super("PauseNeighborResponse", [
95385
+ { no: 1, name: "accepted", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }
95386
+ ]);
95387
+ }
95388
+ }
95389
+ /**
95390
+ * @generated MessageType for protobuf message PauseNeighborResponse
95391
+ */
95392
+ const PauseNeighborResponse = new PauseNeighborResponse$Type();
95393
+ // @generated message type with reflection information, may provide speed optimized methods
94723
95394
  class ResumeNeighborRequest$Type extends MessageType {
94724
95395
  constructor() {
94725
95396
  super("ResumeNeighborRequest", [
@@ -94775,7 +95446,7 @@
94775
95446
  * @generated ServiceType for protobuf service PlumtreeRpc
94776
95447
  */
94777
95448
  const PlumtreeRpc = new ServiceType("PlumtreeRpc", [
94778
- { name: "pauseNeighbor", options: {}, I: PauseNeighborRequest, O: Empty$1 },
95449
+ { name: "pauseNeighbor", options: {}, I: PauseNeighborRequest, O: PauseNeighborResponse },
94779
95450
  { name: "resumeNeighbor", options: {}, I: ResumeNeighborRequest, O: Empty$1 },
94780
95451
  { name: "sendMetadata", options: {}, I: MessageID$1, O: Empty$1 }
94781
95452
  ]);
@@ -95802,6 +96473,7 @@
95802
96473
  this.options.neighborFinder.stop();
95803
96474
  this.options.neighborUpdateManager.stop();
95804
96475
  this.options.inspector.stop();
96476
+ this.duplicateDetectors.clear();
95805
96477
  }
95806
96478
  broadcast(msg, previousNode) {
95807
96479
  if (!previousNode) {
@@ -96377,14 +97049,17 @@
96377
97049
  async pauseNeighbor(request, context) {
96378
97050
  const sender = toNodeId(context.incomingSourceDescriptor);
96379
97051
  if (this.neighbors.has(sender)) {
96380
- this.pausedNodes.add(sender, request.messageChainId);
97052
+ const accepted = this.pausedNodes.add(sender, request.messageChainId);
97053
+ return { accepted };
96381
97054
  }
96382
- return Empty$1;
97055
+ return { accepted: false };
96383
97056
  }
96384
97057
  async resumeNeighbor(request, context) {
96385
97058
  const sender = context.incomingSourceDescriptor;
96386
- this.pausedNodes.delete(toNodeId(sender), request.messageChainId);
96387
- await this.sendBuffer(request.fromTimestamp, request.messageChainId, sender);
97059
+ if (this.neighbors.has(toNodeId(sender))) {
97060
+ this.pausedNodes.delete(toNodeId(sender), request.messageChainId);
97061
+ await this.sendBuffer(request.fromTimestamp, request.messageChainId, sender);
97062
+ }
96388
97063
  return Empty$1;
96389
97064
  }
96390
97065
  }
@@ -96397,10 +97072,9 @@
96397
97072
  await this.getClient().sendMetadata(msg, options);
96398
97073
  }
96399
97074
  async pauseNeighbor(messageChainId) {
96400
- const options = this.formDhtRpcOptions({
96401
- notification: true
96402
- });
96403
- await this.getClient().pauseNeighbor({ messageChainId }, options);
97075
+ const options = this.formDhtRpcOptions();
97076
+ const response = await this.getClient().pauseNeighbor({ messageChainId }, options);
97077
+ return response.accepted;
96404
97078
  }
96405
97079
  async resumeNeighbor(fromTimestamp, messageChainId) {
96406
97080
  const options = this.formDhtRpcOptions({
@@ -96422,9 +97096,10 @@
96422
97096
  this.pausedNeighbors.set(msgChainId, new Set());
96423
97097
  }
96424
97098
  if (this.pausedNeighbors.get(msgChainId).size >= this.limit) {
96425
- return;
97099
+ return false;
96426
97100
  }
96427
97101
  this.pausedNeighbors.get(msgChainId).add(node);
97102
+ return true;
96428
97103
  }
96429
97104
  delete(node, msgChainId) {
96430
97105
  this.pausedNeighbors.get(msgChainId)?.delete(node);
@@ -96457,19 +97132,24 @@
96457
97132
  }
96458
97133
 
96459
97134
  const MAX_PAUSED_NEIGHBORS_DEFAULT = 3;
97135
+ const DEFAULT_RECOVERY_TIMEOUT = 500;
97136
+ const DEFAULT_RECOVERY_CHECK_INTERVAL = 200;
97137
+ const DEFAULT_RECOVERY_COOLDOWN = 2500;
96460
97138
  const logger$4$1 = new Logger('PlumtreeManager');
96461
97139
  class PlumtreeManager extends EventEmitter {
96462
97140
  neighbors;
96463
97141
  localPeerDescriptor;
96464
- // We have paused sending real data to these neighbrs and only send metadata
96465
97142
  localPausedNeighbors;
96466
- // We have asked these nodes to pause sending real data to us, used to limit sending of pausing and resuming requests
96467
97143
  remotePausedNeighbors;
96468
97144
  rpcLocal;
96469
97145
  latestMessages = new Map();
96470
97146
  rpcCommunicator;
96471
- metadataTimestampsAheadOfRealData = new Map();
96472
97147
  maxPausedNeighbors;
97148
+ recoveryState = new Map();
97149
+ recoveryCooldownUntil = new Map();
97150
+ recoveryTimeout;
97151
+ recoveryCooldown;
97152
+ abortController = new AbortController();
96473
97153
  constructor(options) {
96474
97154
  super();
96475
97155
  this.neighbors = options.neighbors;
@@ -96477,12 +97157,22 @@
96477
97157
  this.localPeerDescriptor = options.localPeerDescriptor;
96478
97158
  this.localPausedNeighbors = new PausedNeighbors(options.maxPausedNeighbors ?? MAX_PAUSED_NEIGHBORS_DEFAULT);
96479
97159
  this.remotePausedNeighbors = new PausedNeighbors(options.maxPausedNeighbors ?? MAX_PAUSED_NEIGHBORS_DEFAULT);
97160
+ this.recoveryTimeout = options.recoveryTimeout ?? DEFAULT_RECOVERY_TIMEOUT;
97161
+ this.recoveryCooldown = options.recoveryCooldown ?? DEFAULT_RECOVERY_COOLDOWN;
96480
97162
  this.rpcLocal = new PlumtreeRpcLocal(this.neighbors, this.localPausedNeighbors, (metadata, previousNode) => this.onMetadata(metadata, previousNode), (fromTimestamp, msgChainId, remotePeerDescriptor) => this.sendBuffer(fromTimestamp, msgChainId, remotePeerDescriptor));
96481
- this.neighbors.on('nodeRemoved', (nodeId) => this.onNeighborRemoved(nodeId));
97163
+ this.neighbors.on('nodeRemoved', this.onNeighborRemoved);
96482
97164
  this.rpcCommunicator = options.rpcCommunicator;
96483
97165
  this.rpcCommunicator.registerRpcNotification(MessageID$1, 'sendMetadata', (msg, context) => this.rpcLocal.sendMetadata(msg, context));
96484
- this.rpcCommunicator.registerRpcNotification(PauseNeighborRequest, 'pauseNeighbor', (msg, context) => this.rpcLocal.pauseNeighbor(msg, context));
97166
+ this.rpcCommunicator.registerRpcMethod(PauseNeighborRequest, PauseNeighborResponse, 'pauseNeighbor', (msg, context) => this.rpcLocal.pauseNeighbor(msg, context));
96485
97167
  this.rpcCommunicator.registerRpcNotification(ResumeNeighborRequest, 'resumeNeighbor', (msg, context) => this.rpcLocal.resumeNeighbor(msg, context));
97168
+ setAbortableInterval(() => {
97169
+ const now = performance.now();
97170
+ for (const [chainId, state] of this.recoveryState) {
97171
+ if (now - state.metadataAheadSince >= this.recoveryTimeout && !state.resumeInProgress) {
97172
+ this.attemptRecovery(chainId, state, this.getLatestMessageTimestamp(chainId));
97173
+ }
97174
+ }
97175
+ }, options.recoveryCheckInterval ?? DEFAULT_RECOVERY_CHECK_INTERVAL, this.abortController.signal);
96486
97176
  }
96487
97177
  async pauseNeighbor(node, msgChainId) {
96488
97178
  if (this.neighbors.has(toNodeId(node))
@@ -96490,8 +97180,16 @@
96490
97180
  && this.remotePausedNeighbors.size(msgChainId) < this.maxPausedNeighbors) {
96491
97181
  logger$4$1.debug(`Pausing neighbor ${toNodeId(node)}`);
96492
97182
  this.remotePausedNeighbors.add(toNodeId(node), msgChainId);
96493
- const remote = this.createRemote(node);
96494
- await remote.pauseNeighbor(msgChainId);
97183
+ try {
97184
+ const remote = this.createRemote(node);
97185
+ const accepted = await remote.pauseNeighbor(msgChainId);
97186
+ if (!accepted) {
97187
+ this.remotePausedNeighbors.delete(toNodeId(node), msgChainId);
97188
+ }
97189
+ }
97190
+ catch (_e) {
97191
+ this.remotePausedNeighbors.delete(toNodeId(node), msgChainId);
97192
+ }
96495
97193
  }
96496
97194
  }
96497
97195
  async resumeNeighbor(node, msgChainId, fromTimestamp) {
@@ -96502,9 +97200,15 @@
96502
97200
  await remote.resumeNeighbor(fromTimestamp, msgChainId);
96503
97201
  }
96504
97202
  }
96505
- onNeighborRemoved(nodeId) {
97203
+ onNeighborRemoved = (nodeId) => {
96506
97204
  this.localPausedNeighbors.deleteAll(nodeId);
96507
97205
  this.remotePausedNeighbors.deleteAll(nodeId);
97206
+ for (const [_chainId, state] of this.recoveryState) {
97207
+ state.candidates = state.candidates.filter((c) => toNodeId(c) !== nodeId);
97208
+ if (state.lastAttemptedNode !== null && toNodeId(state.lastAttemptedNode) === nodeId) {
97209
+ state.lastAttemptedNode = null;
97210
+ }
97211
+ }
96508
97212
  if (this.neighbors.size() > 0) {
96509
97213
  this.remotePausedNeighbors.forEach((pausedNeighbors, msgChainId) => {
96510
97214
  if (pausedNeighbors.size >= this.neighbors.size()) {
@@ -96514,7 +97218,7 @@
96514
97218
  }
96515
97219
  });
96516
97220
  }
96517
- }
97221
+ };
96518
97222
  getLatestMessageTimestamp(msgChainId) {
96519
97223
  if (!this.latestMessages.has(msgChainId) || this.latestMessages.get(msgChainId).length === 0) {
96520
97224
  return 0;
@@ -96524,22 +97228,61 @@
96524
97228
  async sendBuffer(fromTimestamp, msgChainId, neighbor) {
96525
97229
  const remote = new ContentDeliveryRpcRemote(this.localPeerDescriptor, neighbor, this.rpcCommunicator, ContentDeliveryRpcClient);
96526
97230
  const messages = this.latestMessages.get(msgChainId)?.filter((msg) => msg.messageId.timestamp > fromTimestamp) ?? [];
96527
- await Promise.all(messages.map((msg) => remote.sendStreamMessage(msg)));
97231
+ for (const msg of messages) {
97232
+ await remote.sendStreamMessage(msg);
97233
+ }
96528
97234
  }
96529
97235
  async onMetadata(msg, previousNode) {
96530
- // If we receive newer metadata than messages in the buffer, resume the sending neighbor
96531
- const latestMessageTimestamp = this.getLatestMessageTimestamp(msg.messageChainId);
96532
- if (latestMessageTimestamp < msg.timestamp) {
96533
- if (!this.metadataTimestampsAheadOfRealData.has(msg.messageChainId)) {
96534
- this.metadataTimestampsAheadOfRealData.set(msg.messageChainId, new Set());
96535
- }
96536
- this.metadataTimestampsAheadOfRealData.get(msg.messageChainId).add(msg.timestamp);
96537
- if (this.metadataTimestampsAheadOfRealData.get(msg.messageChainId).size > 1) {
96538
- await this.resumeNeighbor(previousNode, msg.messageChainId, this.getLatestMessageTimestamp(msg.messageChainId));
96539
- this.metadataTimestampsAheadOfRealData.get(msg.messageChainId).forEach((timestamp) => {
96540
- this.metadataTimestampsAheadOfRealData.get(msg.messageChainId).delete(timestamp);
96541
- });
96542
- }
97236
+ const latestTs = this.getLatestMessageTimestamp(msg.messageChainId);
97237
+ if (latestTs >= msg.timestamp) {
97238
+ return;
97239
+ }
97240
+ const chainId = msg.messageChainId;
97241
+ const cooldownUntil = this.recoveryCooldownUntil.get(chainId);
97242
+ if (cooldownUntil !== undefined && performance.now() < cooldownUntil) {
97243
+ return;
97244
+ }
97245
+ let state = this.recoveryState.get(chainId);
97246
+ if (!state) {
97247
+ state = {
97248
+ timestampsAhead: new Set(),
97249
+ metadataAheadSince: performance.now(),
97250
+ candidates: [],
97251
+ lastAttemptedNode: null,
97252
+ resumeInProgress: false
97253
+ };
97254
+ this.recoveryState.set(chainId, state);
97255
+ }
97256
+ state.timestampsAhead.add(msg.timestamp);
97257
+ const nodeId = toNodeId(previousNode);
97258
+ const isLastAttempted = state.lastAttemptedNode !== null && toNodeId(state.lastAttemptedNode) === nodeId;
97259
+ if (!isLastAttempted && !state.candidates.some((c) => toNodeId(c) === nodeId)) {
97260
+ state.candidates.push(previousNode);
97261
+ }
97262
+ if (state.timestampsAhead.size > 1 && !state.resumeInProgress) {
97263
+ await this.attemptRecovery(chainId, state, latestTs);
97264
+ }
97265
+ }
97266
+ async attemptRecovery(chainId, state, latestTs) {
97267
+ const candidate = state.candidates.shift();
97268
+ if (!candidate) {
97269
+ state.metadataAheadSince = performance.now();
97270
+ return;
97271
+ }
97272
+ state.resumeInProgress = true;
97273
+ state.lastAttemptedNode = candidate;
97274
+ state.candidates = [];
97275
+ state.timestampsAhead.clear();
97276
+ state.metadataAheadSince = performance.now();
97277
+ try {
97278
+ const remote = this.createRemote(candidate);
97279
+ await remote.resumeNeighbor(latestTs, chainId);
97280
+ }
97281
+ catch (_e) {
97282
+ logger$4$1.debug('Recovery resume failed, will retry with next candidate');
97283
+ }
97284
+ finally {
97285
+ state.resumeInProgress = false;
96543
97286
  }
96544
97287
  }
96545
97288
  createRemote(neighbor) {
@@ -96557,8 +97300,13 @@
96557
97300
  this.latestMessages.get(messageChainId).shift();
96558
97301
  this.latestMessages.get(messageChainId).push(msg);
96559
97302
  }
96560
- if (this.metadataTimestampsAheadOfRealData.has(msg.messageId.messageChainId)) {
96561
- this.metadataTimestampsAheadOfRealData.get(msg.messageId.messageChainId).delete(msg.messageId.timestamp);
97303
+ const state = this.recoveryState.get(messageChainId);
97304
+ if (state) {
97305
+ if (state.lastAttemptedNode) {
97306
+ this.remotePausedNeighbors.delete(toNodeId(state.lastAttemptedNode), messageChainId);
97307
+ }
97308
+ this.recoveryState.delete(messageChainId);
97309
+ this.recoveryCooldownUntil.set(messageChainId, performance.now() + this.recoveryCooldown);
96562
97310
  }
96563
97311
  this.emit('message', msg);
96564
97312
  const neighbors = this.neighbors.getAll().filter((neighbor) => toNodeId(neighbor.getPeerDescriptor()) !== previousNode);
@@ -96576,8 +97324,18 @@
96576
97324
  return this.localPausedNeighbors.isPaused(toNodeId(node), msgChainId)
96577
97325
  || this.remotePausedNeighbors.isPaused(toNodeId(node), msgChainId);
96578
97326
  }
97327
+ getLocalPausedNeighbors() {
97328
+ return this.localPausedNeighbors;
97329
+ }
97330
+ getRemotePausedNeighbors() {
97331
+ return this.remotePausedNeighbors;
97332
+ }
96579
97333
  stop() {
97334
+ this.abortController.abort();
96580
97335
  this.neighbors.off('nodeRemoved', this.onNeighborRemoved);
97336
+ this.latestMessages.clear();
97337
+ this.recoveryState.clear();
97338
+ this.recoveryCooldownUntil.clear();
96581
97339
  }
96582
97340
  }
96583
97341