webpeerjs 0.0.10 → 0.1.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.
@@ -15,6 +15,7 @@
15
15
  const CONFIG_PEER_DISCOVERY_GLOBAL = '_peer-discovery._p2p._pubsub';
16
16
  const CONFIG_PEER_DISCOVERY_WEBPEERJS= prefix$1+'-peer-discovery';
17
17
  const CONFIG_PUBSUB_PEER_DISCOVERY = [CONFIG_PEER_DISCOVERY_GLOBAL, CONFIG_PEER_DISCOVERY_UNIVERSAL_CONNECTIVITY, CONFIG_PEER_DISCOVERY_WEBPEERJS];
18
+ const CONFIG_PUPSUB_PEER_DATA = ['_'+prefix$1+'-peer-data_'];
18
19
  const CONFIG_DELEGATED_API = 'https://delegated-ipfs.dev';
19
20
  const CONFIG_DNS_RESOLVER = 'https://dns.google/resolve';
20
21
  const CONFIG_KNOWN_BOOTSTRAP_DNS = '_dnsaddr.bootstrap.libp2p.io';
@@ -3417,6 +3418,7 @@
3417
3418
  }
3418
3419
  // Error codes
3419
3420
  const ERR_TIMEOUT = 'ERR_TIMEOUT';
3421
+ const ERR_INVALID_MESSAGE = 'ERR_INVALID_MESSAGE';
3420
3422
 
3421
3423
  /** Noop for browser compatibility */
3422
3424
  function setMaxListeners$1() { }
@@ -3572,6 +3574,14 @@
3572
3574
  * value should be a string array of provided capabilities.
3573
3575
  */
3574
3576
  const serviceCapabilities = Symbol.for('@libp2p/service-capabilities');
3577
+ /**
3578
+ * This symbol is used by libp2p services to define the capabilities they
3579
+ * require from other libp2p services.
3580
+ *
3581
+ * The service should define a property with this symbol as the key and the
3582
+ * value should be a string array of required capabilities.
3583
+ */
3584
+ const serviceDependencies = Symbol.for('@libp2p/service-dependencies');
3575
3585
 
3576
3586
  /**
3577
3587
  * Returns true if the two passed Uint8Arrays have the same content
@@ -3946,7 +3956,7 @@
3946
3956
 
3947
3957
  const V = -1;
3948
3958
  const names = {};
3949
- const codes$3 = {};
3959
+ const codes$4 = {};
3950
3960
  const table = [
3951
3961
  [4, 32, 'ip4'],
3952
3962
  [6, 16, 'tcp'],
@@ -3995,7 +4005,7 @@
3995
4005
  // populate tables
3996
4006
  table.forEach(row => {
3997
4007
  const proto = createProtocol(...row);
3998
- codes$3[proto.code] = proto;
4008
+ codes$4[proto.code] = proto;
3999
4009
  names[proto.name] = proto;
4000
4010
  });
4001
4011
  function createProtocol(code, size, name, resolvable, path) {
@@ -4021,8 +4031,8 @@
4021
4031
  */
4022
4032
  function getProtocol(proto) {
4023
4033
  if (typeof proto === 'number') {
4024
- if (codes$3[proto] != null) {
4025
- return codes$3[proto];
4034
+ if (codes$4[proto] != null) {
4035
+ return codes$4[proto];
4026
4036
  }
4027
4037
  throw new Error(`no protocol with code: ${proto}`);
4028
4038
  }
@@ -6719,7 +6729,7 @@
6719
6729
  }
6720
6730
  }
6721
6731
 
6722
- var browser = {exports: {}};
6732
+ var browser$1 = {exports: {}};
6723
6733
 
6724
6734
  /**
6725
6735
  * Helpers.
@@ -7436,9 +7446,9 @@
7436
7446
  return '[UnexpectedJSONParseError]: ' + error.message;
7437
7447
  }
7438
7448
  };
7439
- } (browser, browser.exports));
7449
+ } (browser$1, browser$1.exports));
7440
7450
 
7441
- var browserExports = browser.exports;
7451
+ var browserExports = browser$1.exports;
7442
7452
  var debug = /*@__PURE__*/getDefaultExportFromCjs(browserExports);
7443
7453
 
7444
7454
  /**
@@ -19198,7 +19208,7 @@
19198
19208
  }
19199
19209
 
19200
19210
  const log$1 = logger('delegated-routing-v1-http-api-client');
19201
- const defaultValues$1 = {
19211
+ const defaultValues$2 = {
19202
19212
  concurrentRequests: 4,
19203
19213
  timeout: 30e3
19204
19214
  };
@@ -19218,10 +19228,10 @@
19218
19228
  this.shutDownController = new AbortController();
19219
19229
  setMaxListeners(Infinity, this.shutDownController.signal);
19220
19230
  this.httpQueue = new PQueue({
19221
- concurrency: init.concurrentRequests ?? defaultValues$1.concurrentRequests
19231
+ concurrency: init.concurrentRequests ?? defaultValues$2.concurrentRequests
19222
19232
  });
19223
19233
  this.clientUrl = url instanceof URL ? url : new URL(url);
19224
- this.timeout = init.timeout ?? defaultValues$1.timeout;
19234
+ this.timeout = init.timeout ?? defaultValues$2.timeout;
19225
19235
  this.contentRouting = new DelegatedRoutingV1HttpApiClientContentRouting(this);
19226
19236
  this.peerRouting = new DelegatedRoutingV1HttpApiClientPeerRouting(this);
19227
19237
  }
@@ -19728,7 +19738,7 @@
19728
19738
  return peerIdFromKeys(marshalPublicKey(privateKey.public), marshalPrivateKey(privateKey));
19729
19739
  }
19730
19740
 
19731
- const codes$2 = {
19741
+ const codes$3 = {
19732
19742
  ERR_SIGNATURE_NOT_VALID: 'ERR_SIGNATURE_NOT_VALID'
19733
19743
  };
19734
19744
 
@@ -19849,7 +19859,7 @@
19849
19859
  const envelope = await RecordEnvelope.createFromProtobuf(data);
19850
19860
  const valid = await envelope.validate(domain);
19851
19861
  if (!valid) {
19852
- throw new CodeError$2('envelope signature is not valid for the given domain', codes$2.ERR_SIGNATURE_NOT_VALID);
19862
+ throw new CodeError$2('envelope signature is not valid for the given domain', codes$3.ERR_SIGNATURE_NOT_VALID);
19853
19863
  }
19854
19864
  return envelope;
19855
19865
  };
@@ -20538,7 +20548,7 @@
20538
20548
  return mutexes[opts.name];
20539
20549
  }
20540
20550
 
20541
- const codes$1 = {
20551
+ const codes$2 = {
20542
20552
  ERR_INVALID_PARAMETERS: 'ERR_INVALID_PARAMETERS'
20543
20553
  };
20544
20554
 
@@ -20880,7 +20890,7 @@
20880
20890
  const NAMESPACE_COMMON = '/peers/';
20881
20891
  function peerIdToDatastoreKey(peerId) {
20882
20892
  if (!isPeerId(peerId) || peerId.type == null) {
20883
- throw new CodeError$2('Invalid PeerId', codes$1.ERR_INVALID_PARAMETERS);
20893
+ throw new CodeError$2('Invalid PeerId', codes$2.ERR_INVALID_PARAMETERS);
20884
20894
  }
20885
20895
  const b32key = peerId.toCID().toString();
20886
20896
  return new Key(`${NAMESPACE_COMMON}${b32key}`);
@@ -20896,7 +20906,7 @@
20896
20906
  addr.multiaddr = multiaddr(addr.multiaddr);
20897
20907
  }
20898
20908
  if (!isMultiaddr(addr.multiaddr)) {
20899
- throw new CodeError$2('Multiaddr was invalid', codes$1.ERR_INVALID_PARAMETERS);
20909
+ throw new CodeError$2('Multiaddr was invalid', codes$2.ERR_INVALID_PARAMETERS);
20900
20910
  }
20901
20911
  if (!(await filter(peerId, addr.multiaddr))) {
20902
20912
  continue;
@@ -20926,14 +20936,14 @@
20926
20936
 
20927
20937
  async function toPeerPB(peerId, data, strategy, options) {
20928
20938
  if (data == null) {
20929
- throw new CodeError$2('Invalid PeerData', codes$1.ERR_INVALID_PARAMETERS);
20939
+ throw new CodeError$2('Invalid PeerData', codes$2.ERR_INVALID_PARAMETERS);
20930
20940
  }
20931
20941
  if (data.publicKey != null && peerId.publicKey != null && !equals(data.publicKey, peerId.publicKey)) {
20932
- throw new CodeError$2('publicKey bytes do not match peer id publicKey bytes', codes$1.ERR_INVALID_PARAMETERS);
20942
+ throw new CodeError$2('publicKey bytes do not match peer id publicKey bytes', codes$2.ERR_INVALID_PARAMETERS);
20933
20943
  }
20934
20944
  const existingPeer = options.existingPeer;
20935
20945
  if (existingPeer != null && !peerId.equals(existingPeer.id)) {
20936
- throw new CodeError$2('peer id did not match existing peer id', codes$1.ERR_INVALID_PARAMETERS);
20946
+ throw new CodeError$2('peer id did not match existing peer id', codes$2.ERR_INVALID_PARAMETERS);
20937
20947
  }
20938
20948
  let addresses = existingPeer?.addresses ?? [];
20939
20949
  let protocols = new Set(existingPeer?.protocols ?? []);
@@ -21061,30 +21071,30 @@
21061
21071
  }
21062
21072
  function validateMetadata(key, value) {
21063
21073
  if (typeof key !== 'string') {
21064
- throw new CodeError$2('Metadata key must be a string', codes$1.ERR_INVALID_PARAMETERS);
21074
+ throw new CodeError$2('Metadata key must be a string', codes$2.ERR_INVALID_PARAMETERS);
21065
21075
  }
21066
21076
  if (!(value instanceof Uint8Array)) {
21067
- throw new CodeError$2('Metadata value must be a Uint8Array', codes$1.ERR_INVALID_PARAMETERS);
21077
+ throw new CodeError$2('Metadata value must be a Uint8Array', codes$2.ERR_INVALID_PARAMETERS);
21068
21078
  }
21069
21079
  }
21070
21080
  function validateTag(key, tag) {
21071
21081
  if (typeof key !== 'string') {
21072
- throw new CodeError$2('Tag name must be a string', codes$1.ERR_INVALID_PARAMETERS);
21082
+ throw new CodeError$2('Tag name must be a string', codes$2.ERR_INVALID_PARAMETERS);
21073
21083
  }
21074
21084
  if (tag.value != null) {
21075
21085
  if (parseInt(`${tag.value}`, 10) !== tag.value) {
21076
- throw new CodeError$2('Tag value must be an integer', codes$1.ERR_INVALID_PARAMETERS);
21086
+ throw new CodeError$2('Tag value must be an integer', codes$2.ERR_INVALID_PARAMETERS);
21077
21087
  }
21078
21088
  if (tag.value < 0 || tag.value > 100) {
21079
- throw new CodeError$2('Tag value must be between 0-100', codes$1.ERR_INVALID_PARAMETERS);
21089
+ throw new CodeError$2('Tag value must be between 0-100', codes$2.ERR_INVALID_PARAMETERS);
21080
21090
  }
21081
21091
  }
21082
21092
  if (tag.ttl != null) {
21083
21093
  if (parseInt(`${tag.ttl}`, 10) !== tag.ttl) {
21084
- throw new CodeError$2('Tag ttl must be an integer', codes$1.ERR_INVALID_PARAMETERS);
21094
+ throw new CodeError$2('Tag ttl must be an integer', codes$2.ERR_INVALID_PARAMETERS);
21085
21095
  }
21086
21096
  if (tag.ttl < 0) {
21087
- throw new CodeError$2('Tag ttl must be between greater than 0', codes$1.ERR_INVALID_PARAMETERS);
21097
+ throw new CodeError$2('Tag ttl must be between greater than 0', codes$2.ERR_INVALID_PARAMETERS);
21088
21098
  }
21089
21099
  }
21090
21100
  }
@@ -21148,7 +21158,7 @@
21148
21158
  }
21149
21159
  async delete(peerId) {
21150
21160
  if (this.peerId.equals(peerId)) {
21151
- throw new CodeError$2('Cannot delete self peer', codes$1.ERR_INVALID_PARAMETERS);
21161
+ throw new CodeError$2('Cannot delete self peer', codes$2.ERR_INVALID_PARAMETERS);
21152
21162
  }
21153
21163
  await this.datastore.delete(peerIdToDatastoreKey(peerId));
21154
21164
  }
@@ -22568,7 +22578,7 @@
22568
22578
  * DNS.matches(multiaddr('/dns6/example.org')) // true
22569
22579
  * ```
22570
22580
  */
22571
- fmt(or$1(_DNS, _DNSADDR, _DNS4, _DNS6));
22581
+ const DNS$2 = fmt(or$1(_DNS, _DNSADDR, _DNS4, _DNS6));
22572
22582
  const _IP4 = and$1(literal('ip4'), func(isIPv4));
22573
22583
  const _IP6 = and$1(literal('ip6'), func(isIPv6));
22574
22584
  const _IP = or$1(_IP4, _IP6);
@@ -22588,6 +22598,20 @@
22588
22598
  * ```
22589
22599
  */
22590
22600
  const IP_OR_DOMAIN = fmt(_IP_OR_DOMAIN);
22601
+ /**
22602
+ * Matches ip4 or ip6 addresses.
22603
+ *
22604
+ * @example
22605
+ *
22606
+ * ```ts
22607
+ * import { multiaddr } from '@multiformats/multiaddr'
22608
+ * import { IP } from '@multiformats/multiaddr-matcher'
22609
+ *
22610
+ * IP.matches(multiaddr('/ip4/123.123.123.123')) // true
22611
+ * IP.matches(multiaddr('/ip6/fe80::1cc1:a3b8:322f:cf22')) // true
22612
+ * ```
22613
+ */
22614
+ const IP$1 = fmt(_IP);
22591
22615
  const _TCP = and$1(_IP_OR_DOMAIN, literal('tcp'), number$1());
22592
22616
  const _UDP = and$1(_IP_OR_DOMAIN, literal('udp'), number$1());
22593
22617
  const TCP_OR_UDP = or$1(_TCP, _UDP);
@@ -22627,7 +22651,20 @@
22627
22651
  * ```
22628
22652
  */
22629
22653
  const Circuit$1 = fmt(_Circuit$1);
22630
- or$1(and$1(_P2P$1, literal('p2p-circuit'), literal('webrtc'), peerId()), and$1(_P2P$1, literal('webrtc'), optional(peerId())), literal('webrtc'));
22654
+ const _WebRTC = or$1(and$1(_P2P$1, literal('p2p-circuit'), literal('webrtc'), peerId()), and$1(_P2P$1, literal('webrtc'), optional(peerId())), literal('webrtc'));
22655
+ /**
22656
+ * Matches WebRTC addresses
22657
+ *
22658
+ * @example
22659
+ *
22660
+ * ```ts
22661
+ * import { multiaddr } from '@multiformats/multiaddr'
22662
+ * import { WebRTC } from '@multiformats/multiaddr-matcher'
22663
+ *
22664
+ * WebRTC.matches(multiaddr('/ip4/123.123.123.123/tcp/1234/p2p/QmRelay/p2p-circuit/webrtc/p2p/QmTarget')) // true
22665
+ * ```
22666
+ */
22667
+ const WebRTC = fmt(_WebRTC);
22631
22668
  or$1(and$1(_IP_OR_DOMAIN, literal('tcp'), number$1(), literal('http'), optional(peerId())), and$1(_IP_OR_DOMAIN, literal('http'), optional(peerId())));
22632
22669
  or$1(and$1(_IP_OR_DOMAIN, literal('tcp'), or$1(and$1(literal('443'), literal('http')), and$1(number$1(), literal('https'))), optional(peerId())), and$1(_IP_OR_DOMAIN, literal('tls'), literal('http'), optional(peerId())), and$1(_IP_OR_DOMAIN, literal('https'), optional(peerId())));
22633
22670
 
@@ -23360,7 +23397,7 @@
23360
23397
  messages["ERR_PROTECTOR_REQUIRED"] = "Private network is enforced, but no protector was provided";
23361
23398
  messages["NOT_FOUND"] = "Not found";
23362
23399
  })(messages || (messages = {}));
23363
- var codes;
23400
+ var codes$1;
23364
23401
  (function (codes) {
23365
23402
  codes["ERR_PROTECTOR_REQUIRED"] = "ERR_PROTECTOR_REQUIRED";
23366
23403
  codes["ERR_PEER_DIAL_INTERCEPTED"] = "ERR_PEER_DIAL_INTERCEPTED";
@@ -23421,7 +23458,7 @@
23421
23458
  codes["ERR_TOO_MANY_INBOUND_PROTOCOL_STREAMS"] = "ERR_TOO_MANY_INBOUND_PROTOCOL_STREAMS";
23422
23459
  codes["ERR_CONNECTION_DENIED"] = "ERR_CONNECTION_DENIED";
23423
23460
  codes["ERR_TRANSFER_LIMIT_EXCEEDED"] = "ERR_TRANSFER_LIMIT_EXCEEDED";
23424
- })(codes || (codes = {}));
23461
+ })(codes$1 || (codes$1 = {}));
23425
23462
 
23426
23463
  const DefaultConfig = {
23427
23464
  addresses: {
@@ -23443,10 +23480,10 @@
23443
23480
  async function validateConfig(opts) {
23444
23481
  const resultingOptions = mergeOptions$1(DefaultConfig, opts);
23445
23482
  if (resultingOptions.connectionProtector === null && globalThis.process?.env?.LIBP2P_FORCE_PNET != null) { // eslint-disable-line no-undef
23446
- throw new CodeError$2(messages.ERR_PROTECTOR_REQUIRED, codes.ERR_PROTECTOR_REQUIRED);
23483
+ throw new CodeError$2(messages.ERR_PROTECTOR_REQUIRED, codes$1.ERR_PROTECTOR_REQUIRED);
23447
23484
  }
23448
23485
  if (!(await peerIdFromKeys(resultingOptions.privateKey.public.bytes, resultingOptions.privateKey.bytes)).equals(resultingOptions.peerId)) {
23449
- throw new CodeError$2('Private key doesn\'t match peer id', codes.ERR_INVALID_KEY);
23486
+ throw new CodeError$2('Private key doesn\'t match peer id', codes$1.ERR_INVALID_KEY);
23450
23487
  }
23451
23488
  return resultingOptions;
23452
23489
  }
@@ -23710,18 +23747,18 @@
23710
23747
  // ensure PeerId is either not set or is consistent
23711
23748
  peer.forEach(ma => {
23712
23749
  if (!isMultiaddr(ma)) {
23713
- throw new CodeError$2('Invalid Multiaddr', codes.ERR_INVALID_MULTIADDR);
23750
+ throw new CodeError$2('Invalid Multiaddr', codes$1.ERR_INVALID_MULTIADDR);
23714
23751
  }
23715
23752
  const maPeerIdStr = ma.getPeerId();
23716
23753
  if (maPeerIdStr == null) {
23717
23754
  if (peerId != null) {
23718
- throw new CodeError$2('Multiaddrs must all have the same peer id or have no peer id', codes.ERR_INVALID_PARAMETERS);
23755
+ throw new CodeError$2('Multiaddrs must all have the same peer id or have no peer id', codes$1.ERR_INVALID_PARAMETERS);
23719
23756
  }
23720
23757
  }
23721
23758
  else {
23722
23759
  const maPeerId = peerIdFromString(maPeerIdStr);
23723
23760
  if (peerId == null || !peerId.equals(maPeerId)) {
23724
- throw new CodeError$2('Multiaddrs must all have the same peer id or have no peer id', codes.ERR_INVALID_PARAMETERS);
23761
+ throw new CodeError$2('Multiaddrs must all have the same peer id or have no peer id', codes$1.ERR_INVALID_PARAMETERS);
23725
23762
  }
23726
23763
  }
23727
23764
  });
@@ -24911,7 +24948,7 @@
24911
24948
  for (const address of addrsToDial) {
24912
24949
  if (dialed === this.maxPeerAddrsToDial) {
24913
24950
  this.log('dialed maxPeerAddrsToDial (%d) addresses for %p, not trying any others', dialed, peerId);
24914
- throw new CodeError$2('Peer had more than maxPeerAddrsToDial', codes.ERR_TOO_MANY_ADDRESSES);
24951
+ throw new CodeError$2('Peer had more than maxPeerAddrsToDial', codes$1.ERR_TOO_MANY_ADDRESSES);
24915
24952
  }
24916
24953
  dialed++;
24917
24954
  try {
@@ -24947,7 +24984,7 @@
24947
24984
  if (errors.length === 1) {
24948
24985
  throw errors[0];
24949
24986
  }
24950
- throw new AggregateCodeError(errors, 'All multiaddr dials failed', codes.ERR_TRANSPORT_DIAL_FAILED);
24987
+ throw new AggregateCodeError(errors, 'All multiaddr dials failed', codes$1.ERR_TRANSPORT_DIAL_FAILED);
24951
24988
  }
24952
24989
  finally {
24953
24990
  // clean up abort signals/controllers
@@ -24980,10 +25017,10 @@
24980
25017
  // if a peer id or multiaddr(s) with a peer id, make sure it isn't our peer id and that we are allowed to dial it
24981
25018
  if (peerId != null) {
24982
25019
  if (this.components.peerId.equals(peerId)) {
24983
- throw new CodeError$2('Tried to dial self', codes.ERR_DIALED_SELF);
25020
+ throw new CodeError$2('Tried to dial self', codes$1.ERR_DIALED_SELF);
24984
25021
  }
24985
25022
  if ((await this.components.connectionGater.denyDialPeer?.(peerId)) === true) {
24986
- throw new CodeError$2('The dial request is blocked by gater.allowDialPeer', codes.ERR_PEER_DIAL_INTERCEPTED);
25023
+ throw new CodeError$2('The dial request is blocked by gater.allowDialPeer', codes$1.ERR_PEER_DIAL_INTERCEPTED);
24987
25024
  }
24988
25025
  // if just a peer id was passed, load available multiaddrs for this peer
24989
25026
  // from the peer store
@@ -24995,7 +25032,7 @@
24995
25032
  this.log('loaded multiaddrs for %p', peerId, addrs.map(({ multiaddr }) => multiaddr.toString()));
24996
25033
  }
24997
25034
  catch (err) {
24998
- if (err.code !== codes.ERR_NOT_FOUND) {
25035
+ if (err.code !== codes$1.ERR_NOT_FOUND) {
24999
25036
  throw err;
25000
25037
  }
25001
25038
  }
@@ -25013,7 +25050,7 @@
25013
25050
  })));
25014
25051
  }
25015
25052
  catch (err) {
25016
- if (err.code !== codes.ERR_NO_ROUTERS_AVAILABLE) {
25053
+ if (err.code !== codes$1.ERR_NO_ROUTERS_AVAILABLE) {
25017
25054
  this.log.error('looking up multiaddrs for %p in the peer routing failed', peerId, err);
25018
25055
  }
25019
25056
  }
@@ -25083,7 +25120,7 @@
25083
25120
  const dedupedMultiaddrs = [...dedupedAddrs.values()];
25084
25121
  // make sure we actually have some addresses to dial
25085
25122
  if (dedupedMultiaddrs.length === 0) {
25086
- throw new CodeError$2('The dial request has no valid addresses', codes.ERR_NO_VALID_ADDRESSES);
25123
+ throw new CodeError$2('The dial request has no valid addresses', codes$1.ERR_NO_VALID_ADDRESSES);
25087
25124
  }
25088
25125
  const gatedAdrs = [];
25089
25126
  for (const addr of dedupedMultiaddrs) {
@@ -25095,7 +25132,7 @@
25095
25132
  const sortedGatedAddrs = gatedAdrs.sort(this.addressSorter);
25096
25133
  // make sure we actually have some addresses to dial
25097
25134
  if (sortedGatedAddrs.length === 0) {
25098
- throw new CodeError$2('The connection gater denied all addresses in the dial request', codes.ERR_NO_VALID_ADDRESSES);
25135
+ throw new CodeError$2('The connection gater denied all addresses in the dial request', codes$1.ERR_NO_VALID_ADDRESSES);
25099
25136
  }
25100
25137
  this.log.trace('addresses for %p before filtering', peerId ?? 'unknown peer', resolvedAddresses.map(({ multiaddr }) => multiaddr.toString()));
25101
25138
  this.log.trace('addresses for %p after filtering', peerId ?? 'unknown peer', sortedGatedAddrs.map(({ multiaddr }) => multiaddr.toString()));
@@ -25157,7 +25194,7 @@
25157
25194
  this.maxConnections = init.maxConnections ?? defaultOptions.maxConnections;
25158
25195
  const minConnections = init.minConnections ?? defaultOptions.minConnections;
25159
25196
  if (this.maxConnections < minConnections) {
25160
- throw new CodeError$2('Connection Manager maxConnections must be greater than minConnections', codes.ERR_INVALID_PARAMETERS);
25197
+ throw new CodeError$2('Connection Manager maxConnections must be greater than minConnections', codes$1.ERR_INVALID_PARAMETERS);
25161
25198
  }
25162
25199
  /**
25163
25200
  * Map of connections per peer
@@ -25410,7 +25447,7 @@
25410
25447
  }
25411
25448
  async openConnection(peerIdOrMultiaddr, options = {}) {
25412
25449
  if (!this.isStarted()) {
25413
- throw new CodeError$2('Not started', codes.ERR_NODE_NOT_STARTED);
25450
+ throw new CodeError$2('Not started', codes$1.ERR_NODE_NOT_STARTED);
25414
25451
  }
25415
25452
  options.signal?.throwIfAborted();
25416
25453
  const { peerId } = getPeerAddress(peerIdOrMultiaddr);
@@ -25543,7 +25580,7 @@
25543
25580
  */
25544
25581
  async *findProviders(key, options = {}) {
25545
25582
  if (this.routers.length === 0) {
25546
- throw new CodeError$2('No content routers available', codes.ERR_NO_ROUTERS_AVAILABLE);
25583
+ throw new CodeError$2('No content routers available', codes$1.ERR_NO_ROUTERS_AVAILABLE);
25547
25584
  }
25548
25585
  const self = this;
25549
25586
  const seen = new PeerSet();
@@ -25573,7 +25610,7 @@
25573
25610
  */
25574
25611
  async provide(key, options = {}) {
25575
25612
  if (this.routers.length === 0) {
25576
- throw new CodeError$2('No content routers available', codes.ERR_NO_ROUTERS_AVAILABLE);
25613
+ throw new CodeError$2('No content routers available', codes$1.ERR_NO_ROUTERS_AVAILABLE);
25577
25614
  }
25578
25615
  await Promise.all(this.routers.map(async (router) => {
25579
25616
  await router.provide(key, options);
@@ -25584,7 +25621,7 @@
25584
25621
  */
25585
25622
  async put(key, value, options) {
25586
25623
  if (!this.isStarted()) {
25587
- throw new CodeError$2(messages.NOT_STARTED_YET, codes.ERR_NODE_NOT_STARTED);
25624
+ throw new CodeError$2(messages.NOT_STARTED_YET, codes$1.ERR_NODE_NOT_STARTED);
25588
25625
  }
25589
25626
  await Promise.all(this.routers.map(async (router) => {
25590
25627
  await router.put(key, value, options);
@@ -25596,7 +25633,7 @@
25596
25633
  */
25597
25634
  async get(key, options) {
25598
25635
  if (!this.isStarted()) {
25599
- throw new CodeError$2(messages.NOT_STARTED_YET, codes.ERR_NODE_NOT_STARTED);
25636
+ throw new CodeError$2(messages.NOT_STARTED_YET, codes$1.ERR_NODE_NOT_STARTED);
25600
25637
  }
25601
25638
  return Promise.any(this.routers.map(async (router) => {
25602
25639
  return router.get(key, options);
@@ -25813,10 +25850,10 @@
25813
25850
  */
25814
25851
  async findPeer(id, options) {
25815
25852
  if (this.routers.length === 0) {
25816
- throw new CodeError$2('No peer routers available', codes.ERR_NO_ROUTERS_AVAILABLE);
25853
+ throw new CodeError$2('No peer routers available', codes$1.ERR_NO_ROUTERS_AVAILABLE);
25817
25854
  }
25818
25855
  if (id.toString() === this.peerId.toString()) {
25819
- throw new CodeError$2('Should not try to find self', codes.ERR_FIND_SELF);
25856
+ throw new CodeError$2('Should not try to find self', codes$1.ERR_FIND_SELF);
25820
25857
  }
25821
25858
  const self = this;
25822
25859
  const source = merge$1(...this.routers.map(router => (async function* () {
@@ -25839,14 +25876,14 @@
25839
25876
  }
25840
25877
  return peer;
25841
25878
  }
25842
- throw new CodeError$2(messages.NOT_FOUND, codes.ERR_NOT_FOUND);
25879
+ throw new CodeError$2(messages.NOT_FOUND, codes$1.ERR_NOT_FOUND);
25843
25880
  }
25844
25881
  /**
25845
25882
  * Attempt to find the closest peers on the network to the given key
25846
25883
  */
25847
25884
  async *getClosestPeers(key, options = {}) {
25848
25885
  if (this.routers.length === 0) {
25849
- throw new CodeError$2('No peer routers available', codes.ERR_NO_ROUTERS_AVAILABLE);
25886
+ throw new CodeError$2('No peer routers available', codes$1.ERR_NO_ROUTERS_AVAILABLE);
25850
25887
  }
25851
25888
  const self = this;
25852
25889
  const seen = new PeerSet();
@@ -26022,7 +26059,7 @@
26022
26059
  getHandler(protocol) {
26023
26060
  const handler = this.handlers.get(protocol);
26024
26061
  if (handler == null) {
26025
- throw new CodeError$2(`No handler registered for protocol ${protocol}`, codes.ERR_NO_HANDLER_FOR_PROTOCOL);
26062
+ throw new CodeError$2(`No handler registered for protocol ${protocol}`, codes$1.ERR_NO_HANDLER_FOR_PROTOCOL);
26026
26063
  }
26027
26064
  return handler;
26028
26065
  }
@@ -26040,7 +26077,7 @@
26040
26077
  */
26041
26078
  async handle(protocol, handler, opts) {
26042
26079
  if (this.handlers.has(protocol)) {
26043
- throw new CodeError$2(`Handler already registered for protocol ${protocol}`, codes.ERR_PROTOCOL_HANDLER_ALREADY_REGISTERED);
26080
+ throw new CodeError$2(`Handler already registered for protocol ${protocol}`, codes$1.ERR_PROTOCOL_HANDLER_ALREADY_REGISTERED);
26044
26081
  }
26045
26082
  const options = mergeOptions$1.bind({ ignoreUndefined: true })({
26046
26083
  maxInboundStreams: DEFAULT_MAX_INBOUND_STREAMS$1,
@@ -26074,7 +26111,7 @@
26074
26111
  */
26075
26112
  async register(protocol, topology) {
26076
26113
  if (topology == null) {
26077
- throw new CodeError$2('invalid topology', codes.ERR_INVALID_PARAMETERS);
26114
+ throw new CodeError$2('invalid topology', codes$1.ERR_INVALID_PARAMETERS);
26078
26115
  }
26079
26116
  // Create topology
26080
26117
  const id = `${(Math.random() * 1e9).toString(36)}${Date.now()}`;
@@ -26122,7 +26159,7 @@
26122
26159
  }
26123
26160
  })
26124
26161
  .catch(err => {
26125
- if (err.code === codes.ERR_NOT_FOUND) {
26162
+ if (err.code === codes$1.ERR_NOT_FOUND) {
26126
26163
  // peer has not completed identify so they are not in the peer store
26127
26164
  return;
26128
26165
  }
@@ -26241,10 +26278,10 @@
26241
26278
  add(transport) {
26242
26279
  const tag = transport[Symbol.toStringTag];
26243
26280
  if (tag == null) {
26244
- throw new CodeError$2('Transport must have a valid tag', codes.ERR_INVALID_KEY);
26281
+ throw new CodeError$2('Transport must have a valid tag', codes$1.ERR_INVALID_KEY);
26245
26282
  }
26246
26283
  if (this.transports.has(tag)) {
26247
- throw new CodeError$2(`There is already a transport with the tag ${tag}`, codes.ERR_DUPLICATE_TRANSPORT);
26284
+ throw new CodeError$2(`There is already a transport with the tag ${tag}`, codes$1.ERR_DUPLICATE_TRANSPORT);
26248
26285
  }
26249
26286
  this.log('adding transport %s', tag);
26250
26287
  this.transports.set(tag, transport);
@@ -26291,7 +26328,7 @@
26291
26328
  async dial(ma, options) {
26292
26329
  const transport = this.dialTransportForMultiaddr(ma);
26293
26330
  if (transport == null) {
26294
- throw new CodeError$2(`No transport available for address ${String(ma)}`, codes.ERR_TRANSPORT_UNAVAILABLE);
26331
+ throw new CodeError$2(`No transport available for address ${String(ma)}`, codes$1.ERR_TRANSPORT_UNAVAILABLE);
26295
26332
  }
26296
26333
  try {
26297
26334
  return await transport.dial(ma, {
@@ -26301,7 +26338,7 @@
26301
26338
  }
26302
26339
  catch (err) {
26303
26340
  if (err.code == null) {
26304
- err.code = codes.ERR_TRANSPORT_DIAL_FAILED;
26341
+ err.code = codes$1.ERR_TRANSPORT_DIAL_FAILED;
26305
26342
  }
26306
26343
  throw err;
26307
26344
  }
@@ -26357,7 +26394,7 @@
26357
26394
  */
26358
26395
  async listen(addrs) {
26359
26396
  if (!this.isStarted()) {
26360
- throw new CodeError$2('Not started', codes.ERR_NODE_NOT_STARTED);
26397
+ throw new CodeError$2('Not started', codes$1.ERR_NODE_NOT_STARTED);
26361
26398
  }
26362
26399
  if (addrs == null || addrs.length === 0) {
26363
26400
  this.log('no addresses were provided for listening, this node is dial only');
@@ -26408,7 +26445,7 @@
26408
26445
  // just wait for any (`p-any`) listener to succeed on each transport before returning
26409
26446
  const isListening = results.find(r => r.status === 'fulfilled');
26410
26447
  if ((isListening == null) && this.faultTolerance !== FaultTolerance.NO_FATAL) {
26411
- throw new CodeError$2(`Transport (${key}) could not listen on any available address`, codes.ERR_NO_VALID_ADDRESSES);
26448
+ throw new CodeError$2(`Transport (${key}) could not listen on any available address`, codes$1.ERR_NO_VALID_ADDRESSES);
26412
26449
  }
26413
26450
  }
26414
26451
  // If no transports were able to listen, throw an error. This likely
@@ -26416,7 +26453,7 @@
26416
26453
  if (couldNotListen.length === this.transports.size) {
26417
26454
  const message = `no valid addresses were provided for transports [${couldNotListen.join(', ')}]`;
26418
26455
  if (this.faultTolerance === FaultTolerance.FATAL_ALL) {
26419
- throw new CodeError$2(message, codes.ERR_NO_VALID_ADDRESSES);
26456
+ throw new CodeError$2(message, codes$1.ERR_NO_VALID_ADDRESSES);
26420
26457
  }
26421
26458
  this.log(`libp2p in dial mode only: ${message}`);
26422
26459
  }
@@ -27016,7 +27053,7 @@
27016
27053
  return options.maxInboundStreams;
27017
27054
  }
27018
27055
  catch (err) {
27019
- if (err.code !== codes.ERR_NO_HANDLER_FOR_PROTOCOL) {
27056
+ if (err.code !== codes$1.ERR_NO_HANDLER_FOR_PROTOCOL) {
27020
27057
  throw err;
27021
27058
  }
27022
27059
  }
@@ -27030,7 +27067,7 @@
27030
27067
  }
27031
27068
  }
27032
27069
  catch (err) {
27033
- if (err.code !== codes.ERR_NO_HANDLER_FOR_PROTOCOL) {
27070
+ if (err.code !== codes$1.ERR_NO_HANDLER_FOR_PROTOCOL) {
27034
27071
  throw err;
27035
27072
  }
27036
27073
  }
@@ -27068,7 +27105,7 @@
27068
27105
  const connectionGater = this.components.connectionGater[connectionType];
27069
27106
  if (connectionGater !== undefined) {
27070
27107
  if (await connectionGater(remotePeer, maConn)) {
27071
- throw new CodeError$2(`The multiaddr connection is blocked by gater.${connectionType}`, codes.ERR_CONNECTION_INTERCEPTED);
27108
+ throw new CodeError$2(`The multiaddr connection is blocked by gater.${connectionType}`, codes$1.ERR_CONNECTION_INTERCEPTED);
27072
27109
  }
27073
27110
  }
27074
27111
  }
@@ -27078,7 +27115,7 @@
27078
27115
  async upgradeInbound(maConn, opts) {
27079
27116
  const accept = await this.components.connectionManager.acceptIncomingConnection(maConn);
27080
27117
  if (!accept) {
27081
- throw new CodeError$2('connection denied', codes.ERR_CONNECTION_DENIED);
27118
+ throw new CodeError$2('connection denied', codes$1.ERR_CONNECTION_DENIED);
27082
27119
  }
27083
27120
  let encryptedConn;
27084
27121
  let remotePeer;
@@ -27093,7 +27130,7 @@
27093
27130
  setMaxListeners(Infinity, signal);
27094
27131
  try {
27095
27132
  if ((await this.components.connectionGater.denyInboundConnection?.(maConn)) === true) {
27096
- throw new CodeError$2('The multiaddr connection is blocked by gater.acceptConnection', codes.ERR_CONNECTION_INTERCEPTED);
27133
+ throw new CodeError$2('The multiaddr connection is blocked by gater.acceptConnection', codes$1.ERR_CONNECTION_INTERCEPTED);
27097
27134
  }
27098
27135
  this.components.metrics?.trackMultiaddrConnection(maConn);
27099
27136
  maConn.log('starting the inbound connection upgrade');
@@ -27124,7 +27161,7 @@
27124
27161
  else {
27125
27162
  const idStr = maConn.remoteAddr.getPeerId();
27126
27163
  if (idStr == null) {
27127
- throw new CodeError$2('inbound connection that skipped encryption must have a peer id', codes.ERR_INVALID_MULTIADDR);
27164
+ throw new CodeError$2('inbound connection that skipped encryption must have a peer id', codes$1.ERR_INVALID_MULTIADDR);
27128
27165
  }
27129
27166
  const remotePeerId = peerIdFromString(idStr);
27130
27167
  cryptoProtocol = 'native';
@@ -27209,7 +27246,7 @@
27209
27246
  }
27210
27247
  else {
27211
27248
  if (remotePeerId == null) {
27212
- throw new CodeError$2('Encryption was skipped but no peer id was passed', codes.ERR_INVALID_PEER);
27249
+ throw new CodeError$2('Encryption was skipped but no peer id was passed', codes$1.ERR_INVALID_PEER);
27213
27250
  }
27214
27251
  cryptoProtocol = 'native';
27215
27252
  remotePeer = remotePeerId;
@@ -27276,7 +27313,7 @@
27276
27313
  const incomingLimit = findIncomingStreamLimit(protocol, this.components.registrar);
27277
27314
  const streamCount = countStreams(protocol, 'inbound', connection);
27278
27315
  if (streamCount === incomingLimit) {
27279
- const err = new CodeError$2(`Too many inbound protocol streams for protocol "${protocol}" - limit ${incomingLimit}`, codes.ERR_TOO_MANY_INBOUND_PROTOCOL_STREAMS);
27316
+ const err = new CodeError$2(`Too many inbound protocol streams for protocol "${protocol}" - limit ${incomingLimit}`, codes$1.ERR_TOO_MANY_INBOUND_PROTOCOL_STREAMS);
27280
27317
  muxedStream.abort(err);
27281
27318
  throw err;
27282
27319
  }
@@ -27315,7 +27352,7 @@
27315
27352
  });
27316
27353
  newStream = async (protocols, options = {}) => {
27317
27354
  if (muxer == null) {
27318
- throw new CodeError$2('Stream is not multiplexed', codes.ERR_MUXER_UNAVAILABLE);
27355
+ throw new CodeError$2('Stream is not multiplexed', codes$1.ERR_MUXER_UNAVAILABLE);
27319
27356
  }
27320
27357
  connection.log('starting new stream for protocols %s', protocols);
27321
27358
  const muxedStream = await muxer.newStream();
@@ -27340,7 +27377,7 @@
27340
27377
  const outgoingLimit = findOutgoingStreamLimit(protocol, this.components.registrar, options);
27341
27378
  const streamCount = countStreams(protocol, 'outbound', connection);
27342
27379
  if (streamCount >= outgoingLimit) {
27343
- const err = new CodeError$2(`Too many outbound protocol streams for protocol "${protocol}" - ${streamCount}/${outgoingLimit}`, codes.ERR_TOO_MANY_OUTBOUND_PROTOCOL_STREAMS);
27380
+ const err = new CodeError$2(`Too many outbound protocol streams for protocol "${protocol}" - ${streamCount}/${outgoingLimit}`, codes$1.ERR_TOO_MANY_OUTBOUND_PROTOCOL_STREAMS);
27344
27381
  muxedStream.abort(err);
27345
27382
  throw err;
27346
27383
  }
@@ -27377,7 +27414,7 @@
27377
27414
  if (err.code != null) {
27378
27415
  throw err;
27379
27416
  }
27380
- throw new CodeError$2(String(err), codes.ERR_UNSUPPORTED_PROTOCOL);
27417
+ throw new CodeError$2(String(err), codes$1.ERR_UNSUPPORTED_PROTOCOL);
27381
27418
  }
27382
27419
  };
27383
27420
  // Pipe all data through the muxer
@@ -27416,7 +27453,7 @@
27416
27453
  });
27417
27454
  maConn.timeline.upgraded = Date.now();
27418
27455
  const errConnectionNotMultiplexed = () => {
27419
- throw new CodeError$2('connection is not multiplexed', codes.ERR_CONNECTION_NOT_MULTIPLEXED);
27456
+ throw new CodeError$2('connection is not multiplexed', codes$1.ERR_CONNECTION_NOT_MULTIPLEXED);
27420
27457
  };
27421
27458
  // Create the connection
27422
27459
  connection = createConnection({
@@ -27493,7 +27530,7 @@
27493
27530
  }
27494
27531
  catch (err) {
27495
27532
  connection.log.error('encrypting inbound connection failed', err);
27496
- throw new CodeError$2(err.message, codes.ERR_ENCRYPTION_FAILED);
27533
+ throw new CodeError$2(err.message, codes$1.ERR_ENCRYPTION_FAILED);
27497
27534
  }
27498
27535
  }
27499
27536
  /**
@@ -27521,7 +27558,7 @@
27521
27558
  }
27522
27559
  catch (err) {
27523
27560
  connection.log.error('encrypting outbound connection to %p failed', remotePeerId, err);
27524
- throw new CodeError$2(err.message, codes.ERR_ENCRYPTION_FAILED);
27561
+ throw new CodeError$2(err.message, codes$1.ERR_ENCRYPTION_FAILED);
27525
27562
  }
27526
27563
  }
27527
27564
  /**
@@ -27543,7 +27580,7 @@
27543
27580
  }
27544
27581
  catch (err) {
27545
27582
  connection.log.error('error multiplexing outbound connection', err);
27546
- throw new CodeError$2(String(err), codes.ERR_MUXER_UNAVAILABLE);
27583
+ throw new CodeError$2(String(err), codes$1.ERR_MUXER_UNAVAILABLE);
27547
27584
  }
27548
27585
  }
27549
27586
  /**
@@ -27562,7 +27599,7 @@
27562
27599
  }
27563
27600
  catch (err) {
27564
27601
  connection.log.error('error multiplexing inbound connection', err);
27565
- throw new CodeError$2(String(err), codes.ERR_MUXER_UNAVAILABLE);
27602
+ throw new CodeError$2(String(err), codes$1.ERR_MUXER_UNAVAILABLE);
27566
27603
  }
27567
27604
  }
27568
27605
  }
@@ -27769,11 +27806,11 @@
27769
27806
  }
27770
27807
  async dialProtocol(peer, protocols, options = {}) {
27771
27808
  if (protocols == null) {
27772
- throw new CodeError$2('no protocols were provided to open a stream', codes.ERR_INVALID_PROTOCOLS_FOR_STREAM);
27809
+ throw new CodeError$2('no protocols were provided to open a stream', codes$1.ERR_INVALID_PROTOCOLS_FOR_STREAM);
27773
27810
  }
27774
27811
  protocols = Array.isArray(protocols) ? protocols : [protocols];
27775
27812
  if (protocols.length === 0) {
27776
- throw new CodeError$2('no protocols were provided to open a stream', codes.ERR_INVALID_PROTOCOLS_FOR_STREAM);
27813
+ throw new CodeError$2('no protocols were provided to open a stream', codes$1.ERR_INVALID_PROTOCOLS_FOR_STREAM);
27777
27814
  }
27778
27815
  const connection = await this.dial(peer, options);
27779
27816
  return connection.newStream(protocols, options);
@@ -27805,7 +27842,7 @@
27805
27842
  }
27806
27843
  }
27807
27844
  catch (err) {
27808
- if (err.code !== codes.ERR_NOT_FOUND) {
27845
+ if (err.code !== codes$1.ERR_NOT_FOUND) {
27809
27846
  throw err;
27810
27847
  }
27811
27848
  }
@@ -27854,7 +27891,7 @@
27854
27891
  #onDiscoveryPeer(evt) {
27855
27892
  const { detail: peer } = evt;
27856
27893
  if (peer.id.toString() === this.peerId.toString()) {
27857
- this.log.error(new Error(codes.ERR_DISCOVERED_SELF));
27894
+ this.log.error(new Error(codes$1.ERR_DISCOVERED_SELF));
27858
27895
  return;
27859
27896
  }
27860
27897
  void this.components.peerStore.merge(peer.id, {
@@ -32067,484 +32104,2755 @@
32067
32104
  };
32068
32105
  }
32069
32106
 
32070
- // Protocol violation errors
32071
- const ERR_INVALID_FRAME = 'ERR_INVALID_FRAME';
32072
- const ERR_UNREQUESTED_PING = 'ERR_UNREQUESTED_PING';
32073
- const ERR_NOT_MATCHING_PING = 'ERR_NOT_MATCHING_PING';
32074
- const ERR_STREAM_ALREADY_EXISTS = 'ERR_STREAM_ALREADY_EXISTS';
32075
- const ERR_DECODE_INVALID_VERSION = 'ERR_DECODE_INVALID_VERSION';
32076
- const ERR_BOTH_CLIENTS = 'ERR_BOTH_CLIENTS';
32077
- const ERR_RECV_WINDOW_EXCEEDED = 'ERR_RECV_WINDOW_EXCEEDED';
32078
- const PROTOCOL_ERRORS = new Set([
32079
- ERR_INVALID_FRAME,
32080
- ERR_UNREQUESTED_PING,
32081
- ERR_NOT_MATCHING_PING,
32082
- ERR_STREAM_ALREADY_EXISTS,
32083
- ERR_DECODE_INVALID_VERSION,
32084
- ERR_BOTH_CLIENTS,
32085
- ERR_RECV_WINDOW_EXCEEDED
32086
- ]);
32087
- // local errors
32088
- const ERR_INVALID_CONFIG = 'ERR_INVALID_CONFIG';
32089
- const ERR_MUXER_LOCAL_CLOSED = 'ERR_MUXER_LOCAL_CLOSED';
32090
- const ERR_MUXER_REMOTE_CLOSED = 'ERR_MUXER_REMOTE_CLOSED';
32091
- const ERR_STREAM_ABORT = 'ERR_STREAM_ABORT';
32092
- const ERR_MAX_OUTBOUND_STREAMS_EXCEEDED = 'ERROR_MAX_OUTBOUND_STREAMS_EXCEEDED';
32093
- const ERR_DECODE_IN_PROGRESS = 'ERR_DECODE_IN_PROGRESS';
32094
- /**
32095
- * INITIAL_STREAM_WINDOW is the initial stream window size.
32096
- *
32097
- * Not an implementation choice, this is defined in the specification
32098
- */
32099
- const INITIAL_STREAM_WINDOW = 256 * 1024;
32100
- /**
32101
- * Default max stream window
32102
- */
32103
- const MAX_STREAM_WINDOW = 16 * 1024 * 1024;
32107
+ var codes;
32108
+ (function (codes) {
32109
+ codes["ERR_ALREADY_ABORTED"] = "ERR_ALREADY_ABORTED";
32110
+ codes["ERR_DATA_CHANNEL"] = "ERR_DATA_CHANNEL";
32111
+ codes["ERR_CONNECTION_CLOSED"] = "ERR_CONNECTION_CLOSED";
32112
+ codes["ERR_HASH_NOT_SUPPORTED"] = "ERR_HASH_NOT_SUPPORTED";
32113
+ codes["ERR_INVALID_MULTIADDR"] = "ERR_INVALID_MULTIADDR";
32114
+ codes["ERR_INVALID_FINGERPRINT"] = "ERR_INVALID_FINGERPRINT";
32115
+ codes["ERR_INVALID_PARAMETERS"] = "ERR_INVALID_PARAMETERS";
32116
+ codes["ERR_NOT_IMPLEMENTED"] = "ERR_NOT_IMPLEMENTED";
32117
+ codes["ERR_TOO_MANY_INBOUND_PROTOCOL_STREAMS"] = "ERR_TOO_MANY_INBOUND_PROTOCOL_STREAMS";
32118
+ codes["ERR_TOO_MANY_OUTBOUND_PROTOCOL_STREAMS"] = "ERR_TOO_MANY_OUTBOUND_PROTOCOL_STREAMS";
32119
+ })(codes || (codes = {}));
32104
32120
 
32105
- const defaultConfig = {
32106
- enableKeepAlive: true,
32107
- keepAliveInterval: 30000,
32108
- maxInboundStreams: 1000,
32109
- maxOutboundStreams: 1000,
32110
- initialStreamWindowSize: INITIAL_STREAM_WINDOW,
32111
- maxStreamWindowSize: MAX_STREAM_WINDOW,
32112
- maxMessageSize: 64 * 1024
32113
- };
32114
- function verifyConfig(config) {
32115
- if (config.keepAliveInterval <= 0) {
32116
- throw new CodeError$2('keep-alive interval must be positive', ERR_INVALID_CONFIG);
32117
- }
32118
- if (config.maxInboundStreams < 0) {
32119
- throw new CodeError$2('max inbound streams must be larger or equal 0', ERR_INVALID_CONFIG);
32121
+ var __spreadArray = (undefined && undefined.__spreadArray) || function (to, from, pack) {
32122
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
32123
+ if (ar || !(i in from)) {
32124
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
32125
+ ar[i] = from[i];
32126
+ }
32120
32127
  }
32121
- if (config.maxOutboundStreams < 0) {
32122
- throw new CodeError$2('max outbound streams must be larger or equal 0', ERR_INVALID_CONFIG);
32128
+ return to.concat(ar || Array.prototype.slice.call(from));
32129
+ };
32130
+ var BrowserInfo = /** @class */ (function () {
32131
+ function BrowserInfo(name, version, os) {
32132
+ this.name = name;
32133
+ this.version = version;
32134
+ this.os = os;
32135
+ this.type = 'browser';
32123
32136
  }
32124
- if (config.initialStreamWindowSize < INITIAL_STREAM_WINDOW) {
32125
- throw new CodeError$2('InitialStreamWindowSize must be larger or equal 256 kB', ERR_INVALID_CONFIG);
32137
+ return BrowserInfo;
32138
+ }());
32139
+ var NodeInfo = /** @class */ (function () {
32140
+ function NodeInfo(version) {
32141
+ this.version = version;
32142
+ this.type = 'node';
32143
+ this.name = 'node';
32144
+ this.os = process.platform;
32145
+ }
32146
+ return NodeInfo;
32147
+ }());
32148
+ var SearchBotDeviceInfo = /** @class */ (function () {
32149
+ function SearchBotDeviceInfo(name, version, os, bot) {
32150
+ this.name = name;
32151
+ this.version = version;
32152
+ this.os = os;
32153
+ this.bot = bot;
32154
+ this.type = 'bot-device';
32155
+ }
32156
+ return SearchBotDeviceInfo;
32157
+ }());
32158
+ var BotInfo = /** @class */ (function () {
32159
+ function BotInfo() {
32160
+ this.type = 'bot';
32161
+ this.bot = true; // NOTE: deprecated test name instead
32162
+ this.name = 'bot';
32163
+ this.version = null;
32164
+ this.os = null;
32165
+ }
32166
+ return BotInfo;
32167
+ }());
32168
+ var ReactNativeInfo = /** @class */ (function () {
32169
+ function ReactNativeInfo() {
32170
+ this.type = 'react-native';
32171
+ this.name = 'react-native';
32172
+ this.version = null;
32173
+ this.os = null;
32174
+ }
32175
+ return ReactNativeInfo;
32176
+ }());
32177
+ // tslint:disable-next-line:max-line-length
32178
+ var SEARCHBOX_UA_REGEX = /alexa|bot|crawl(er|ing)|facebookexternalhit|feedburner|google web preview|nagios|postrank|pingdom|slurp|spider|yahoo!|yandex/;
32179
+ var SEARCHBOT_OS_REGEX = /(nuhk|curl|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask\ Jeeves\/Teoma|ia_archiver)/;
32180
+ var REQUIRED_VERSION_PARTS = 3;
32181
+ var userAgentRules = [
32182
+ ['aol', /AOLShield\/([0-9\._]+)/],
32183
+ ['edge', /Edge\/([0-9\._]+)/],
32184
+ ['edge-ios', /EdgiOS\/([0-9\._]+)/],
32185
+ ['yandexbrowser', /YaBrowser\/([0-9\._]+)/],
32186
+ ['kakaotalk', /KAKAOTALK\s([0-9\.]+)/],
32187
+ ['samsung', /SamsungBrowser\/([0-9\.]+)/],
32188
+ ['silk', /\bSilk\/([0-9._-]+)\b/],
32189
+ ['miui', /MiuiBrowser\/([0-9\.]+)$/],
32190
+ ['beaker', /BeakerBrowser\/([0-9\.]+)/],
32191
+ ['edge-chromium', /EdgA?\/([0-9\.]+)/],
32192
+ [
32193
+ 'chromium-webview',
32194
+ /(?!Chrom.*OPR)wv\).*Chrom(?:e|ium)\/([0-9\.]+)(:?\s|$)/,
32195
+ ],
32196
+ ['chrome', /(?!Chrom.*OPR)Chrom(?:e|ium)\/([0-9\.]+)(:?\s|$)/],
32197
+ ['phantomjs', /PhantomJS\/([0-9\.]+)(:?\s|$)/],
32198
+ ['crios', /CriOS\/([0-9\.]+)(:?\s|$)/],
32199
+ ['firefox', /Firefox\/([0-9\.]+)(?:\s|$)/],
32200
+ ['fxios', /FxiOS\/([0-9\.]+)/],
32201
+ ['opera-mini', /Opera Mini.*Version\/([0-9\.]+)/],
32202
+ ['opera', /Opera\/([0-9\.]+)(?:\s|$)/],
32203
+ ['opera', /OPR\/([0-9\.]+)(:?\s|$)/],
32204
+ ['pie', /^Microsoft Pocket Internet Explorer\/(\d+\.\d+)$/],
32205
+ ['pie', /^Mozilla\/\d\.\d+\s\(compatible;\s(?:MSP?IE|MSInternet Explorer) (\d+\.\d+);.*Windows CE.*\)$/],
32206
+ ['netfront', /^Mozilla\/\d\.\d+.*NetFront\/(\d.\d)/],
32207
+ ['ie', /Trident\/7\.0.*rv\:([0-9\.]+).*\).*Gecko$/],
32208
+ ['ie', /MSIE\s([0-9\.]+);.*Trident\/[4-7].0/],
32209
+ ['ie', /MSIE\s(7\.0)/],
32210
+ ['bb10', /BB10;\sTouch.*Version\/([0-9\.]+)/],
32211
+ ['android', /Android\s([0-9\.]+)/],
32212
+ ['ios', /Version\/([0-9\._]+).*Mobile.*Safari.*/],
32213
+ ['safari', /Version\/([0-9\._]+).*Safari/],
32214
+ ['facebook', /FB[AS]V\/([0-9\.]+)/],
32215
+ ['instagram', /Instagram\s([0-9\.]+)/],
32216
+ ['ios-webview', /AppleWebKit\/([0-9\.]+).*Mobile/],
32217
+ ['ios-webview', /AppleWebKit\/([0-9\.]+).*Gecko\)$/],
32218
+ ['curl', /^curl\/([0-9\.]+)$/],
32219
+ ['searchbot', SEARCHBOX_UA_REGEX],
32220
+ ];
32221
+ var operatingSystemRules = [
32222
+ ['iOS', /iP(hone|od|ad)/],
32223
+ ['Android OS', /Android/],
32224
+ ['BlackBerry OS', /BlackBerry|BB10/],
32225
+ ['Windows Mobile', /IEMobile/],
32226
+ ['Amazon OS', /Kindle/],
32227
+ ['Windows 3.11', /Win16/],
32228
+ ['Windows 95', /(Windows 95)|(Win95)|(Windows_95)/],
32229
+ ['Windows 98', /(Windows 98)|(Win98)/],
32230
+ ['Windows 2000', /(Windows NT 5.0)|(Windows 2000)/],
32231
+ ['Windows XP', /(Windows NT 5.1)|(Windows XP)/],
32232
+ ['Windows Server 2003', /(Windows NT 5.2)/],
32233
+ ['Windows Vista', /(Windows NT 6.0)/],
32234
+ ['Windows 7', /(Windows NT 6.1)/],
32235
+ ['Windows 8', /(Windows NT 6.2)/],
32236
+ ['Windows 8.1', /(Windows NT 6.3)/],
32237
+ ['Windows 10', /(Windows NT 10.0)/],
32238
+ ['Windows ME', /Windows ME/],
32239
+ ['Windows CE', /Windows CE|WinCE|Microsoft Pocket Internet Explorer/],
32240
+ ['Open BSD', /OpenBSD/],
32241
+ ['Sun OS', /SunOS/],
32242
+ ['Chrome OS', /CrOS/],
32243
+ ['Linux', /(Linux)|(X11)/],
32244
+ ['Mac OS', /(Mac_PowerPC)|(Macintosh)/],
32245
+ ['QNX', /QNX/],
32246
+ ['BeOS', /BeOS/],
32247
+ ['OS/2', /OS\/2/],
32248
+ ];
32249
+ function detect(userAgent) {
32250
+ if (typeof document === 'undefined' &&
32251
+ typeof navigator !== 'undefined' &&
32252
+ navigator.product === 'ReactNative') {
32253
+ return new ReactNativeInfo();
32254
+ }
32255
+ if (typeof navigator !== 'undefined') {
32256
+ return parseUserAgent(navigator.userAgent);
32257
+ }
32258
+ return getNodeVersion();
32259
+ }
32260
+ function matchUserAgent(ua) {
32261
+ // opted for using reduce here rather than Array#first with a regex.test call
32262
+ // this is primarily because using the reduce we only perform the regex
32263
+ // execution once rather than once for the test and for the exec again below
32264
+ // probably something that needs to be benchmarked though
32265
+ return (ua !== '' &&
32266
+ userAgentRules.reduce(function (matched, _a) {
32267
+ var browser = _a[0], regex = _a[1];
32268
+ if (matched) {
32269
+ return matched;
32270
+ }
32271
+ var uaMatch = regex.exec(ua);
32272
+ return !!uaMatch && [browser, uaMatch];
32273
+ }, false));
32274
+ }
32275
+ function parseUserAgent(ua) {
32276
+ var matchedRule = matchUserAgent(ua);
32277
+ if (!matchedRule) {
32278
+ return null;
32126
32279
  }
32127
- if (config.maxStreamWindowSize < config.initialStreamWindowSize) {
32128
- throw new CodeError$2('MaxStreamWindowSize must be larger than the InitialStreamWindowSize', ERR_INVALID_CONFIG);
32280
+ var name = matchedRule[0], match = matchedRule[1];
32281
+ if (name === 'searchbot') {
32282
+ return new BotInfo();
32129
32283
  }
32130
- if (config.maxStreamWindowSize > 2 ** 32 - 1) {
32131
- throw new CodeError$2('MaxStreamWindowSize must be less than equal MAX_UINT32', ERR_INVALID_CONFIG);
32284
+ // Do not use RegExp for split operation as some browser do not support it (See: http://blog.stevenlevithan.com/archives/cross-browser-split)
32285
+ var versionParts = match[1] && match[1].split('.').join('_').split('_').slice(0, 3);
32286
+ if (versionParts) {
32287
+ if (versionParts.length < REQUIRED_VERSION_PARTS) {
32288
+ versionParts = __spreadArray(__spreadArray([], versionParts, true), createVersionParts(REQUIRED_VERSION_PARTS - versionParts.length), true);
32289
+ }
32132
32290
  }
32133
- if (config.maxMessageSize < 1024) {
32134
- throw new CodeError$2('MaxMessageSize must be greater than a kilobyte', ERR_INVALID_CONFIG);
32291
+ else {
32292
+ versionParts = [];
32135
32293
  }
32136
- }
32137
-
32138
- var FrameType;
32139
- (function (FrameType) {
32140
- /** Used to transmit data. May transmit zero length payloads depending on the flags. */
32141
- FrameType[FrameType["Data"] = 0] = "Data";
32142
- /** Used to updated the senders receive window size. This is used to implement per-session flow control. */
32143
- FrameType[FrameType["WindowUpdate"] = 1] = "WindowUpdate";
32144
- /** Used to measure RTT. It can also be used to heart-beat and do keep-alives over TCP. */
32145
- FrameType[FrameType["Ping"] = 2] = "Ping";
32146
- /** Used to close a session. */
32147
- FrameType[FrameType["GoAway"] = 3] = "GoAway";
32148
- })(FrameType || (FrameType = {}));
32149
- var Flag;
32150
- (function (Flag) {
32151
- /** Signals the start of a new stream. May be sent with a data or window update message. Also sent with a ping to indicate outbound. */
32152
- Flag[Flag["SYN"] = 1] = "SYN";
32153
- /** Acknowledges the start of a new stream. May be sent with a data or window update message. Also sent with a ping to indicate response. */
32154
- Flag[Flag["ACK"] = 2] = "ACK";
32155
- /** Performs a half-close of a stream. May be sent with a data message or window update. */
32156
- Flag[Flag["FIN"] = 4] = "FIN";
32157
- /** Reset a stream immediately. May be sent with a data or window update message. */
32158
- Flag[Flag["RST"] = 8] = "RST";
32159
- })(Flag || (Flag = {}));
32160
- Object.values(Flag).filter((x) => typeof x !== 'string');
32161
- const YAMUX_VERSION = 0;
32162
- var GoAwayCode;
32163
- (function (GoAwayCode) {
32164
- GoAwayCode[GoAwayCode["NormalTermination"] = 0] = "NormalTermination";
32165
- GoAwayCode[GoAwayCode["ProtocolError"] = 1] = "ProtocolError";
32166
- GoAwayCode[GoAwayCode["InternalError"] = 2] = "InternalError";
32167
- })(GoAwayCode || (GoAwayCode = {}));
32168
- const HEADER_LENGTH = 12;
32169
-
32170
- // used to bitshift in decoding
32171
- // native bitshift can overflow into a negative number, so we bitshift by multiplying by a power of 2
32172
- const twoPow24 = 2 ** 24;
32173
- /**
32174
- * Decode a header from the front of a buffer
32175
- *
32176
- * @param data - Assumed to have enough bytes for a header
32177
- */
32178
- function decodeHeader(data) {
32179
- if (data[0] !== YAMUX_VERSION) {
32180
- throw new CodeError$2('Invalid frame version', ERR_DECODE_INVALID_VERSION);
32294
+ var version = versionParts.join('.');
32295
+ var os = detectOS(ua);
32296
+ var searchBotMatch = SEARCHBOT_OS_REGEX.exec(ua);
32297
+ if (searchBotMatch && searchBotMatch[1]) {
32298
+ return new SearchBotDeviceInfo(name, version, os, searchBotMatch[1]);
32181
32299
  }
32182
- return {
32183
- type: data[1],
32184
- flag: (data[2] << 8) + data[3],
32185
- streamID: (data[4] * twoPow24) + (data[5] << 16) + (data[6] << 8) + data[7],
32186
- length: (data[8] * twoPow24) + (data[9] << 16) + (data[10] << 8) + data[11]
32187
- };
32300
+ return new BrowserInfo(name, version, os);
32188
32301
  }
32189
- /**
32190
- * Decodes yamux frames from a source
32191
- */
32192
- class Decoder {
32193
- source;
32194
- /** Buffer for in-progress frames */
32195
- buffer;
32196
- /** Used to sanity check against decoding while in an inconsistent state */
32197
- frameInProgress;
32198
- constructor(source) {
32199
- // Normally, when entering a for-await loop with an iterable/async iterable, the only ways to exit the loop are:
32200
- // 1. exhaust the iterable
32201
- // 2. throw an error - slow, undesirable if there's not actually an error
32202
- // 3. break or return - calls the iterable's `return` method, finalizing the iterable, no more iteration possible
32203
- //
32204
- // In this case, we want to enter (and exit) a for-await loop per chunked data frame and continue processing the iterable.
32205
- // To do this, we strip the `return` method from the iterator and can now `break` early and continue iterating.
32206
- // Exiting the main for-await is still possible via 1. and 2.
32207
- this.source = returnlessSource(source);
32208
- this.buffer = new Uint8ArrayList();
32209
- this.frameInProgress = false;
32210
- }
32211
- /**
32212
- * Emits frames from the decoder source.
32213
- *
32214
- * Note: If `readData` is emitted, it _must_ be called before the next iteration
32215
- * Otherwise an error is thrown
32216
- */
32217
- async *emitFrames() {
32218
- for await (const chunk of this.source) {
32219
- this.buffer.append(chunk);
32220
- // Loop to consume as many bytes from the buffer as possible
32221
- // Eg: when a single chunk contains several frames
32222
- while (true) {
32223
- const header = this.readHeader();
32224
- if (header === undefined) {
32225
- break;
32226
- }
32227
- const { type, length } = header;
32228
- if (type === FrameType.Data) {
32229
- // This is a data frame, the frame body must still be read
32230
- // `readData` must be called before the next iteration here
32231
- this.frameInProgress = true;
32232
- yield {
32233
- header,
32234
- readData: this.readBytes.bind(this, length)
32235
- };
32236
- }
32237
- else {
32238
- yield { header };
32239
- }
32240
- }
32241
- }
32242
- }
32243
- readHeader() {
32244
- // Sanity check to ensure a header isn't read when another frame is partially decoded
32245
- // In practice this shouldn't happen
32246
- if (this.frameInProgress) {
32247
- throw new CodeError$2('decoding frame already in progress', ERR_DECODE_IN_PROGRESS);
32248
- }
32249
- if (this.buffer.length < HEADER_LENGTH) {
32250
- // not enough data yet
32251
- return;
32252
- }
32253
- const header = decodeHeader(this.buffer.subarray(0, HEADER_LENGTH));
32254
- this.buffer.consume(HEADER_LENGTH);
32255
- return header;
32256
- }
32257
- async readBytes(length) {
32258
- if (this.buffer.length < length) {
32259
- for await (const chunk of this.source) {
32260
- this.buffer.append(chunk);
32261
- if (this.buffer.length >= length) {
32262
- // see note above, the iterator is not `return`ed here
32263
- break;
32264
- }
32265
- }
32302
+ function detectOS(ua) {
32303
+ for (var ii = 0, count = operatingSystemRules.length; ii < count; ii++) {
32304
+ var _a = operatingSystemRules[ii], os = _a[0], regex = _a[1];
32305
+ var match = regex.exec(ua);
32306
+ if (match) {
32307
+ return os;
32266
32308
  }
32267
- const out = this.buffer.sublist(0, length);
32268
- this.buffer.consume(length);
32269
- // The next frame can now be decoded
32270
- this.frameInProgress = false;
32271
- return out;
32272
32309
  }
32310
+ return null;
32273
32311
  }
32274
- /**
32275
- * Strip the `return` method from a `Source`
32276
- */
32277
- function returnlessSource(source) {
32278
- if (source[Symbol.iterator] !== undefined) {
32279
- const iterator = source[Symbol.iterator]();
32280
- iterator.return = undefined;
32281
- return {
32282
- [Symbol.iterator]() { return iterator; }
32283
- };
32284
- }
32285
- else if (source[Symbol.asyncIterator] !== undefined) {
32286
- const iterator = source[Symbol.asyncIterator]();
32287
- iterator.return = undefined;
32288
- return {
32289
- [Symbol.asyncIterator]() { return iterator; }
32290
- };
32291
- }
32292
- else {
32293
- throw new Error('a source must be either an iterable or an async iterable');
32294
- }
32312
+ function getNodeVersion() {
32313
+ var isNode = typeof process !== 'undefined' && process.version;
32314
+ return isNode ? new NodeInfo(process.version.slice(1)) : null;
32295
32315
  }
32296
-
32297
- function encodeHeader(header) {
32298
- const frame = new Uint8Array(HEADER_LENGTH);
32299
- // always assume version 0
32300
- // frameView.setUint8(0, header.version)
32301
- frame[1] = header.type;
32302
- frame[2] = header.flag >>> 8;
32303
- frame[3] = header.flag;
32304
- frame[4] = header.streamID >>> 24;
32305
- frame[5] = header.streamID >>> 16;
32306
- frame[6] = header.streamID >>> 8;
32307
- frame[7] = header.streamID;
32308
- frame[8] = header.length >>> 24;
32309
- frame[9] = header.length >>> 16;
32310
- frame[10] = header.length >>> 8;
32311
- frame[11] = header.length;
32312
- return frame;
32316
+ function createVersionParts(count) {
32317
+ var output = [];
32318
+ for (var ii = 0; ii < count; ii++) {
32319
+ output.push('0');
32320
+ }
32321
+ return output;
32313
32322
  }
32314
32323
 
32315
- /**
32316
- * @packageDocumentation
32317
- *
32318
- * Calls a function for each value in an (async)iterable.
32319
- *
32320
- * The function can be sync or async.
32321
- *
32322
- * Async functions can be awaited on so may slow down processing of the (async)iterable.
32323
- *
32324
- * @example
32325
- *
32326
- * ```javascript
32327
- * import each from 'it-foreach'
32328
- * import drain from 'it-drain'
32329
- *
32330
- * // This can also be an iterator, generator, etc
32331
- * const values = [0, 1, 2, 3, 4]
32332
- *
32333
- * // prints [0, 0], [1, 1], [2, 2], [3, 3], [4, 4]
32334
- * const arr = drain(
32335
- * each(values, console.info)
32336
- * )
32337
- * ```
32338
- *
32339
- * Async sources and callbacks must be awaited:
32340
- *
32341
- * ```javascript
32342
- * import each from 'it-foreach'
32343
- * import drain from 'it-drain'
32344
- *
32345
- * const values = async function * () {
32346
- * yield * [0, 1, 2, 3, 4]
32347
- * }
32348
- *
32349
- * // prints [0, 0], [1, 1], [2, 2], [3, 3], [4, 4]
32350
- * const arr = await drain(
32351
- * each(values(), console.info)
32352
- * )
32353
- * ```
32354
- */
32355
- function isAsyncIterable$1(thing) {
32356
- return thing[Symbol.asyncIterator] != null;
32357
- }
32358
- function isPromise$1(thing) {
32359
- return thing?.then != null;
32360
- }
32361
- function forEach(source, fn) {
32362
- let index = 0;
32363
- if (isAsyncIterable$1(source)) {
32364
- return (async function* () {
32365
- for await (const val of source) {
32366
- const res = fn(val, index++);
32367
- if (isPromise$1(res)) {
32368
- await res;
32369
- }
32370
- yield val;
32371
- }
32372
- })();
32373
- }
32374
- // if fn function returns a promise we have to return an async generator
32375
- const peekable$1 = peekable(source);
32376
- const { value, done } = peekable$1.next();
32377
- if (done === true) {
32378
- return (function* () { }());
32324
+ const browser = detect();
32325
+ const isFirefox = ((browser != null) && browser.name === 'firefox');
32326
+ const nopSource = async function* nop() { };
32327
+ const nopSink = async (_) => { };
32328
+ const DATA_CHANNEL_DRAIN_TIMEOUT = 30 * 1000;
32329
+ function drainAndClose(channel, direction, drainTimeout = DATA_CHANNEL_DRAIN_TIMEOUT, options) {
32330
+ if (channel.readyState !== 'open') {
32331
+ return;
32379
32332
  }
32380
- const res = fn(value, index++);
32381
- if (typeof res?.then === 'function') {
32382
- return (async function* () {
32383
- yield value;
32384
- for await (const val of peekable$1) {
32385
- const res = fn(val, index++);
32386
- if (isPromise$1(res)) {
32387
- await res;
32333
+ void Promise.resolve()
32334
+ .then(async () => {
32335
+ // wait for bufferedAmount to become zero
32336
+ if (channel.bufferedAmount > 0) {
32337
+ options.log('%s drain channel with %d buffered bytes', direction, channel.bufferedAmount);
32338
+ const deferred = pDefer();
32339
+ let drained = false;
32340
+ channel.bufferedAmountLowThreshold = 0;
32341
+ const closeListener = () => {
32342
+ if (!drained) {
32343
+ options.log('%s drain channel closed before drain', direction);
32344
+ deferred.resolve();
32388
32345
  }
32389
- yield val;
32390
- }
32391
- })();
32392
- }
32393
- const func = fn;
32394
- return (function* () {
32395
- yield value;
32396
- for (const val of peekable$1) {
32397
- func(val, index++);
32398
- yield val;
32346
+ };
32347
+ channel.addEventListener('close', closeListener, {
32348
+ once: true
32349
+ });
32350
+ channel.addEventListener('bufferedamountlow', () => {
32351
+ drained = true;
32352
+ channel.removeEventListener('close', closeListener);
32353
+ deferred.resolve();
32354
+ });
32355
+ await pTimeout(deferred.promise, {
32356
+ milliseconds: drainTimeout
32357
+ });
32399
32358
  }
32400
- })();
32359
+ })
32360
+ .then(async () => {
32361
+ // only close if the channel is still open
32362
+ if (channel.readyState === 'open') {
32363
+ channel.close();
32364
+ }
32365
+ })
32366
+ .catch(err => {
32367
+ options.log.error('error closing outbound stream', err);
32368
+ });
32401
32369
  }
32402
32370
 
32403
- var StreamState;
32404
- (function (StreamState) {
32405
- StreamState[StreamState["Init"] = 0] = "Init";
32406
- StreamState[StreamState["SYNSent"] = 1] = "SYNSent";
32407
- StreamState[StreamState["SYNReceived"] = 2] = "SYNReceived";
32408
- StreamState[StreamState["Established"] = 3] = "Established";
32409
- StreamState[StreamState["Finished"] = 4] = "Finished";
32410
- })(StreamState || (StreamState = {}));
32411
- /** YamuxStream is used to represent a logical stream within a session */
32412
- class YamuxStream extends AbstractStream {
32413
- name;
32414
- state;
32415
- config;
32416
- _id;
32417
- /** The number of available bytes to send */
32418
- sendWindowCapacity;
32419
- /** Callback to notify that the sendWindowCapacity has been updated */
32420
- sendWindowCapacityUpdate;
32421
- /** The number of bytes available to receive in a full window */
32422
- recvWindow;
32423
- /** The number of available bytes to receive */
32424
- recvWindowCapacity;
32425
- /**
32426
- * An 'epoch' is the time it takes to process and read data
32427
- *
32428
- * Used in conjunction with RTT to determine whether to increase the recvWindow
32429
- */
32430
- epochStart;
32431
- getRTT;
32432
- sendFrame;
32433
- constructor(init) {
32434
- super({
32435
- ...init,
32436
- onEnd: (err) => {
32437
- this.state = StreamState.Finished;
32438
- init.onEnd?.(err);
32439
- }
32440
- });
32441
- this.config = init.config;
32442
- this._id = parseInt(init.id, 10);
32443
- this.name = init.name;
32444
- this.state = init.state;
32445
- this.sendWindowCapacity = INITIAL_STREAM_WINDOW;
32446
- this.recvWindow = this.config.initialStreamWindowSize;
32447
- this.recvWindowCapacity = this.recvWindow;
32448
- this.epochStart = Date.now();
32449
- this.getRTT = init.getRTT;
32450
- this.sendFrame = init.sendFrame;
32451
- this.source = forEach(this.source, () => {
32452
- this.sendWindowUpdate();
32453
- });
32454
- }
32371
+ class WebRTCMultiaddrConnection {
32372
+ log;
32455
32373
  /**
32456
- * Send a message to the remote muxer informing them a new stream is being
32457
- * opened.
32458
- *
32459
- * This is a noop for Yamux because the first window update is sent when
32460
- * .newStream is called on the muxer which opens the stream on the remote.
32374
+ * WebRTC Peer Connection
32461
32375
  */
32462
- async sendNewStream() {
32463
- }
32376
+ peerConnection;
32464
32377
  /**
32465
- * Send a data message to the remote muxer
32378
+ * The multiaddr address used to communicate with the remote peer
32466
32379
  */
32467
- async sendData(buf, options = {}) {
32468
- buf = buf.sublist();
32469
- // send in chunks, waiting for window updates
32470
- while (buf.byteLength !== 0) {
32471
- // wait for the send window to refill
32472
- if (this.sendWindowCapacity === 0) {
32473
- this.log?.trace('wait for send window capacity, status %s', this.status);
32474
- await this.waitForSendWindowCapacity(options);
32475
- // check we didn't close while waiting for send window capacity
32476
- if (this.status === 'closed' || this.status === 'aborted' || this.status === 'reset') {
32477
- this.log?.trace('%s while waiting for send window capacity', this.status);
32478
- return;
32479
- }
32480
- }
32481
- // send as much as we can
32482
- const toSend = Math.min(this.sendWindowCapacity, this.config.maxMessageSize - HEADER_LENGTH, buf.length);
32483
- const flags = this.getSendFlags();
32484
- this.sendFrame({
32485
- type: FrameType.Data,
32486
- flag: flags,
32487
- streamID: this._id,
32488
- length: toSend
32489
- }, buf.sublist(0, toSend));
32490
- this.sendWindowCapacity -= toSend;
32491
- buf.consume(toSend);
32492
- }
32493
- }
32380
+ remoteAddr;
32494
32381
  /**
32495
- * Send a reset message to the remote muxer
32382
+ * Holds the lifecycle times of the connection
32496
32383
  */
32497
- async sendReset() {
32498
- this.sendFrame({
32499
- type: FrameType.WindowUpdate,
32500
- flag: Flag.RST,
32501
- streamID: this._id,
32502
- length: 0
32503
- });
32504
- }
32384
+ timeline;
32505
32385
  /**
32506
- * Send a message to the remote muxer, informing them no more data messages
32507
- * will be sent by this end of the stream
32386
+ * Optional metrics counter group for this connection
32508
32387
  */
32509
- async sendCloseWrite() {
32510
- const flags = this.getSendFlags() | Flag.FIN;
32511
- this.sendFrame({
32512
- type: FrameType.WindowUpdate,
32513
- flag: flags,
32514
- streamID: this._id,
32515
- length: 0
32516
- });
32517
- }
32388
+ metrics;
32518
32389
  /**
32519
- * Send a message to the remote muxer, informing them no more data messages
32520
- * will be read by this end of the stream
32390
+ * The stream source, a no-op as the transport natively supports multiplexing
32521
32391
  */
32522
- async sendCloseRead() {
32523
- }
32392
+ source = nopSource();
32524
32393
  /**
32525
- * Wait for the send window to be non-zero
32526
- *
32527
- * Will throw with ERR_STREAM_ABORT if the stream gets aborted
32394
+ * The stream destination, a no-op as the transport natively supports multiplexing
32528
32395
  */
32529
- async waitForSendWindowCapacity(options = {}) {
32530
- if (this.sendWindowCapacity > 0) {
32531
- return;
32532
- }
32533
- let resolve;
32534
- let reject;
32535
- const abort = () => {
32536
- if (this.status === 'open' || this.status === 'closing') {
32537
- reject(new CodeError$2('stream aborted', ERR_STREAM_ABORT));
32538
- }
32539
- else {
32540
- // the stream was closed already, ignore the failure to send
32541
- resolve();
32396
+ sink = nopSink;
32397
+ constructor(components, init) {
32398
+ this.log = components.logger.forComponent('libp2p:webrtc:maconn');
32399
+ this.remoteAddr = init.remoteAddr;
32400
+ this.timeline = init.timeline;
32401
+ this.peerConnection = init.peerConnection;
32402
+ const initialState = this.peerConnection.connectionState;
32403
+ this.peerConnection.onconnectionstatechange = () => {
32404
+ this.log.trace('peer connection state change', this.peerConnection.connectionState, 'initial state', initialState);
32405
+ if (this.peerConnection.connectionState === 'disconnected' || this.peerConnection.connectionState === 'failed' || this.peerConnection.connectionState === 'closed') {
32406
+ // nothing else to do but close the connection
32407
+ this.timeline.close = Date.now();
32542
32408
  }
32543
32409
  };
32544
- options.signal?.addEventListener('abort', abort);
32545
- try {
32546
- await new Promise((_resolve, _reject) => {
32547
- this.sendWindowCapacityUpdate = () => {
32410
+ }
32411
+ async close(options) {
32412
+ this.log.trace('closing connection');
32413
+ this.peerConnection.close();
32414
+ this.timeline.close = Date.now();
32415
+ this.metrics?.increment({ close: true });
32416
+ }
32417
+ abort(err) {
32418
+ this.log.error('closing connection due to error', err);
32419
+ this.peerConnection.close();
32420
+ this.timeline.close = Date.now();
32421
+ this.metrics?.increment({ abort: true });
32422
+ }
32423
+ }
32424
+
32425
+ const normalizeEmitter = emitter => {
32426
+ const addListener = emitter.addEventListener || emitter.on || emitter.addListener;
32427
+ const removeListener = emitter.removeEventListener || emitter.off || emitter.removeListener;
32428
+
32429
+ if (!addListener || !removeListener) {
32430
+ throw new TypeError('Emitter is not compatible');
32431
+ }
32432
+
32433
+ return {
32434
+ addListener: addListener.bind(emitter),
32435
+ removeListener: removeListener.bind(emitter),
32436
+ };
32437
+ };
32438
+
32439
+ function pEventMultiple(emitter, event, options) {
32440
+ let cancel;
32441
+ const returnValue = new Promise((resolve, reject) => {
32442
+ options = {
32443
+ rejectionEvents: ['error'],
32444
+ multiArgs: false,
32445
+ resolveImmediately: false,
32446
+ ...options,
32447
+ };
32448
+
32449
+ if (!(options.count >= 0 && (options.count === Number.POSITIVE_INFINITY || Number.isInteger(options.count)))) {
32450
+ throw new TypeError('The `count` option should be at least 0 or more');
32451
+ }
32452
+
32453
+ options.signal?.throwIfAborted();
32454
+
32455
+ // Allow multiple events
32456
+ const events = [event].flat();
32457
+
32458
+ const items = [];
32459
+ const {addListener, removeListener} = normalizeEmitter(emitter);
32460
+
32461
+ const onItem = (...arguments_) => {
32462
+ const value = options.multiArgs ? arguments_ : arguments_[0];
32463
+
32464
+ // eslint-disable-next-line unicorn/no-array-callback-reference
32465
+ if (options.filter && !options.filter(value)) {
32466
+ return;
32467
+ }
32468
+
32469
+ items.push(value);
32470
+
32471
+ if (options.count === items.length) {
32472
+ cancel();
32473
+ resolve(items);
32474
+ }
32475
+ };
32476
+
32477
+ const rejectHandler = error => {
32478
+ cancel();
32479
+ reject(error);
32480
+ };
32481
+
32482
+ cancel = () => {
32483
+ for (const event of events) {
32484
+ removeListener(event, onItem);
32485
+ }
32486
+
32487
+ for (const rejectionEvent of options.rejectionEvents) {
32488
+ removeListener(rejectionEvent, rejectHandler);
32489
+ }
32490
+ };
32491
+
32492
+ for (const event of events) {
32493
+ addListener(event, onItem);
32494
+ }
32495
+
32496
+ for (const rejectionEvent of options.rejectionEvents) {
32497
+ addListener(rejectionEvent, rejectHandler);
32498
+ }
32499
+
32500
+ if (options.signal) {
32501
+ options.signal.addEventListener('abort', () => {
32502
+ rejectHandler(options.signal.reason);
32503
+ }, {once: true});
32504
+ }
32505
+
32506
+ if (options.resolveImmediately) {
32507
+ resolve(items);
32508
+ }
32509
+ });
32510
+
32511
+ returnValue.cancel = cancel;
32512
+
32513
+ if (typeof options.timeout === 'number') {
32514
+ const timeout = pTimeout(returnValue, {milliseconds: options.timeout});
32515
+ timeout.cancel = cancel;
32516
+ return timeout;
32517
+ }
32518
+
32519
+ return returnValue;
32520
+ }
32521
+
32522
+ function pEvent(emitter, event, options) {
32523
+ if (typeof options === 'function') {
32524
+ options = {filter: options};
32525
+ }
32526
+
32527
+ options = {
32528
+ ...options,
32529
+ count: 1,
32530
+ resolveImmediately: false,
32531
+ };
32532
+
32533
+ const arrayPromise = pEventMultiple(emitter, event, options);
32534
+ const promise = arrayPromise.then(array => array[0]);
32535
+ promise.cancel = arrayPromise.cancel;
32536
+
32537
+ return promise;
32538
+ }
32539
+
32540
+ /* eslint-disable import/export */
32541
+ /* eslint-disable complexity */
32542
+ /* eslint-disable @typescript-eslint/no-namespace */
32543
+ /* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */
32544
+ /* eslint-disable @typescript-eslint/no-empty-interface */
32545
+ var Message$2;
32546
+ (function (Message) {
32547
+ (function (Flag) {
32548
+ Flag["FIN"] = "FIN";
32549
+ Flag["STOP_SENDING"] = "STOP_SENDING";
32550
+ Flag["RESET"] = "RESET";
32551
+ Flag["FIN_ACK"] = "FIN_ACK";
32552
+ })(Message.Flag || (Message.Flag = {}));
32553
+ let __FlagValues;
32554
+ (function (__FlagValues) {
32555
+ __FlagValues[__FlagValues["FIN"] = 0] = "FIN";
32556
+ __FlagValues[__FlagValues["STOP_SENDING"] = 1] = "STOP_SENDING";
32557
+ __FlagValues[__FlagValues["RESET"] = 2] = "RESET";
32558
+ __FlagValues[__FlagValues["FIN_ACK"] = 3] = "FIN_ACK";
32559
+ })(__FlagValues || (__FlagValues = {}));
32560
+ (function (Flag) {
32561
+ Flag.codec = () => {
32562
+ return enumeration(__FlagValues);
32563
+ };
32564
+ })(Message.Flag || (Message.Flag = {}));
32565
+ let _codec;
32566
+ Message.codec = () => {
32567
+ if (_codec == null) {
32568
+ _codec = message((obj, w, opts = {}) => {
32569
+ if (opts.lengthDelimited !== false) {
32570
+ w.fork();
32571
+ }
32572
+ if (obj.flag != null) {
32573
+ w.uint32(8);
32574
+ Message.Flag.codec().encode(obj.flag, w);
32575
+ }
32576
+ if (obj.message != null) {
32577
+ w.uint32(18);
32578
+ w.bytes(obj.message);
32579
+ }
32580
+ if (opts.lengthDelimited !== false) {
32581
+ w.ldelim();
32582
+ }
32583
+ }, (reader, length) => {
32584
+ const obj = {};
32585
+ const end = length == null ? reader.len : reader.pos + length;
32586
+ while (reader.pos < end) {
32587
+ const tag = reader.uint32();
32588
+ switch (tag >>> 3) {
32589
+ case 1:
32590
+ obj.flag = Message.Flag.codec().decode(reader);
32591
+ break;
32592
+ case 2:
32593
+ obj.message = reader.bytes();
32594
+ break;
32595
+ default:
32596
+ reader.skipType(tag & 7);
32597
+ break;
32598
+ }
32599
+ }
32600
+ return obj;
32601
+ });
32602
+ }
32603
+ return _codec;
32604
+ };
32605
+ Message.encode = (obj) => {
32606
+ return encodeMessage(obj, Message.codec());
32607
+ };
32608
+ Message.decode = (buf) => {
32609
+ return decodeMessage(buf, Message.codec());
32610
+ };
32611
+ })(Message$2 || (Message$2 = {}));
32612
+
32613
+ /**
32614
+ * How much can be buffered to the DataChannel at once
32615
+ */
32616
+ const MAX_BUFFERED_AMOUNT = 16 * 1024 * 1024;
32617
+ /**
32618
+ * How long time we wait for the 'bufferedamountlow' event to be emitted
32619
+ */
32620
+ const BUFFERED_AMOUNT_LOW_TIMEOUT = 30 * 1000;
32621
+ /**
32622
+ * protobuf field definition overhead
32623
+ */
32624
+ const PROTOBUF_OVERHEAD = 5;
32625
+ /**
32626
+ * Length of varint, in bytes
32627
+ */
32628
+ const VARINT_LENGTH = 2;
32629
+ /**
32630
+ * Max message size that can be sent to the DataChannel
32631
+ */
32632
+ const MAX_MESSAGE_SIZE = 16 * 1024;
32633
+ /**
32634
+ * When closing streams we send a FIN then wait for the remote to
32635
+ * reply with a FIN_ACK. If that does not happen within this timeout
32636
+ * we close the stream anyway.
32637
+ */
32638
+ const FIN_ACK_TIMEOUT = 5000;
32639
+ /**
32640
+ * When sending data messages, if the channel is not in the "open" state, wait
32641
+ * this long for the "open" event to fire.
32642
+ */
32643
+ const OPEN_TIMEOUT = 5000;
32644
+ class WebRTCStream extends AbstractStream {
32645
+ /**
32646
+ * The data channel used to send and receive data
32647
+ */
32648
+ channel;
32649
+ /**
32650
+ * push data from the underlying datachannel to the length prefix decoder
32651
+ * and then the protobuf decoder.
32652
+ */
32653
+ incomingData;
32654
+ maxBufferedAmount;
32655
+ bufferedAmountLowEventTimeout;
32656
+ /**
32657
+ * The maximum size of a message in bytes
32658
+ */
32659
+ maxMessageSize;
32660
+ /**
32661
+ * When this promise is resolved, the remote has sent us a FIN flag
32662
+ */
32663
+ receiveFinAck;
32664
+ finAckTimeout;
32665
+ openTimeout;
32666
+ constructor(init) {
32667
+ // override onEnd to send/receive FIN_ACK before closing the stream
32668
+ const originalOnEnd = init.onEnd;
32669
+ init.onEnd = (err) => {
32670
+ this.log.trace('readable and writeable ends closed', this.status);
32671
+ void Promise.resolve(async () => {
32672
+ if (this.timeline.abort != null || this.timeline.reset !== null) {
32673
+ return;
32674
+ }
32675
+ // wait for FIN_ACK if we haven't received it already
32676
+ try {
32677
+ await pTimeout(this.receiveFinAck.promise, {
32678
+ milliseconds: this.finAckTimeout
32679
+ });
32680
+ }
32681
+ catch (err) {
32682
+ this.log.error('error receiving FIN_ACK', err);
32683
+ }
32684
+ })
32685
+ .then(() => {
32686
+ // stop processing incoming messages
32687
+ this.incomingData.end();
32688
+ // final cleanup
32689
+ originalOnEnd?.(err);
32690
+ })
32691
+ .catch(err => {
32692
+ this.log.error('error ending stream', err);
32693
+ });
32694
+ };
32695
+ super(init);
32696
+ this.channel = init.channel;
32697
+ this.channel.binaryType = 'arraybuffer';
32698
+ this.incomingData = pushable$1();
32699
+ this.bufferedAmountLowEventTimeout = init.bufferedAmountLowEventTimeout ?? BUFFERED_AMOUNT_LOW_TIMEOUT;
32700
+ this.maxBufferedAmount = init.maxBufferedAmount ?? MAX_BUFFERED_AMOUNT;
32701
+ this.maxMessageSize = (init.maxMessageSize ?? MAX_MESSAGE_SIZE) - PROTOBUF_OVERHEAD - VARINT_LENGTH;
32702
+ this.receiveFinAck = pDefer();
32703
+ this.finAckTimeout = init.closeTimeout ?? FIN_ACK_TIMEOUT;
32704
+ this.openTimeout = init.openTimeout ?? OPEN_TIMEOUT;
32705
+ // set up initial state
32706
+ switch (this.channel.readyState) {
32707
+ case 'open':
32708
+ this.timeline.open = new Date().getTime();
32709
+ break;
32710
+ case 'closed':
32711
+ case 'closing':
32712
+ if (this.timeline.close === undefined || this.timeline.close === 0) {
32713
+ this.timeline.close = Date.now();
32714
+ }
32715
+ break;
32716
+ case 'connecting':
32717
+ // noop
32718
+ break;
32719
+ default:
32720
+ this.log.error('unknown datachannel state %s', this.channel.readyState);
32721
+ throw new CodeError$2('Unknown datachannel state', 'ERR_INVALID_STATE');
32722
+ }
32723
+ // handle RTCDataChannel events
32724
+ this.channel.onopen = (_evt) => {
32725
+ this.timeline.open = new Date().getTime();
32726
+ };
32727
+ this.channel.onclose = (_evt) => {
32728
+ // if the channel has closed we'll never receive a FIN_ACK so resolve the
32729
+ // promise so we don't try to wait later
32730
+ this.receiveFinAck.resolve();
32731
+ void this.close().catch(err => {
32732
+ this.log.error('error closing stream after channel closed', err);
32733
+ });
32734
+ };
32735
+ this.channel.onerror = (evt) => {
32736
+ const err = evt.error;
32737
+ this.abort(err);
32738
+ };
32739
+ this.channel.onmessage = async (event) => {
32740
+ const { data } = event;
32741
+ if (data === null || data.byteLength === 0) {
32742
+ return;
32743
+ }
32744
+ this.incomingData.push(new Uint8Array(data, 0, data.byteLength));
32745
+ };
32746
+ const self = this;
32747
+ // pipe framed protobuf messages through a length prefixed decoder, and
32748
+ // surface data from the `Message.message` field through a source.
32749
+ Promise.resolve().then(async () => {
32750
+ for await (const buf of decode$1(this.incomingData)) {
32751
+ const message = self.processIncomingProtobuf(buf);
32752
+ if (message != null) {
32753
+ self.sourcePush(new Uint8ArrayList(message));
32754
+ }
32755
+ }
32756
+ })
32757
+ .catch(err => {
32758
+ this.log.error('error processing incoming data channel messages', err);
32759
+ });
32760
+ }
32761
+ sendNewStream() {
32762
+ // opening new streams is handled by WebRTC so this is a noop
32763
+ }
32764
+ async _sendMessage(data, checkBuffer = true) {
32765
+ if (checkBuffer && this.channel.bufferedAmount > this.maxBufferedAmount) {
32766
+ try {
32767
+ this.log('channel buffer is %d, wait for "bufferedamountlow" event', this.channel.bufferedAmount);
32768
+ await pEvent(this.channel, 'bufferedamountlow', { timeout: this.bufferedAmountLowEventTimeout });
32769
+ }
32770
+ catch (err) {
32771
+ if (err instanceof TimeoutError) {
32772
+ throw new CodeError$2(`Timed out waiting for DataChannel buffer to clear after ${this.bufferedAmountLowEventTimeout}ms`, 'ERR_BUFFER_CLEAR_TIMEOUT');
32773
+ }
32774
+ throw err;
32775
+ }
32776
+ }
32777
+ if (this.channel.readyState === 'closed' || this.channel.readyState === 'closing') {
32778
+ throw new CodeError$2(`Invalid datachannel state - ${this.channel.readyState}`, 'ERR_INVALID_STATE');
32779
+ }
32780
+ if (this.channel.readyState !== 'open') {
32781
+ this.log('channel state is "%s" and not "open", waiting for "open" event before sending data', this.channel.readyState);
32782
+ await pEvent(this.channel, 'open', { timeout: this.openTimeout });
32783
+ this.log('channel state is now "%s", sending data', this.channel.readyState);
32784
+ }
32785
+ // send message without copying data
32786
+ this.channel.send(data.subarray());
32787
+ }
32788
+ async sendData(data) {
32789
+ // sending messages is an async operation so use a copy of the list as it
32790
+ // may be changed beneath us
32791
+ data = data.sublist();
32792
+ while (data.byteLength > 0) {
32793
+ const toSend = Math.min(data.byteLength, this.maxMessageSize);
32794
+ const buf = data.subarray(0, toSend);
32795
+ const msgbuf = Message$2.encode({ message: buf });
32796
+ const sendbuf = encode.single(msgbuf);
32797
+ await this._sendMessage(sendbuf);
32798
+ data.consume(toSend);
32799
+ }
32800
+ }
32801
+ async sendReset() {
32802
+ await this._sendFlag(Message$2.Flag.RESET);
32803
+ }
32804
+ async sendCloseWrite(options) {
32805
+ const sent = await this._sendFlag(Message$2.Flag.FIN);
32806
+ if (sent) {
32807
+ this.log.trace('awaiting FIN_ACK');
32808
+ try {
32809
+ await raceSignal(this.receiveFinAck.promise, options?.signal, {
32810
+ errorMessage: 'sending close-write was aborted before FIN_ACK was received',
32811
+ errorCode: 'ERR_FIN_ACK_NOT_RECEIVED'
32812
+ });
32813
+ }
32814
+ catch (err) {
32815
+ this.log.error('failed to await FIN_ACK', err);
32816
+ }
32817
+ }
32818
+ else {
32819
+ this.log.trace('sending FIN failed, not awaiting FIN_ACK');
32820
+ }
32821
+ // if we've attempted to receive a FIN_ACK, do not try again
32822
+ this.receiveFinAck.resolve();
32823
+ }
32824
+ async sendCloseRead() {
32825
+ await this._sendFlag(Message$2.Flag.STOP_SENDING);
32826
+ }
32827
+ /**
32828
+ * Handle incoming
32829
+ */
32830
+ processIncomingProtobuf(buffer) {
32831
+ const message = Message$2.decode(buffer);
32832
+ if (message.flag !== undefined) {
32833
+ this.log.trace('incoming flag %s, write status "%s", read status "%s"', message.flag, this.writeStatus, this.readStatus);
32834
+ if (message.flag === Message$2.Flag.FIN) {
32835
+ // We should expect no more data from the remote, stop reading
32836
+ this.remoteCloseWrite();
32837
+ this.log.trace('sending FIN_ACK');
32838
+ void this._sendFlag(Message$2.Flag.FIN_ACK)
32839
+ .catch(err => {
32840
+ this.log.error('error sending FIN_ACK immediately', err);
32841
+ });
32842
+ }
32843
+ if (message.flag === Message$2.Flag.RESET) {
32844
+ // Stop reading and writing to the stream immediately
32845
+ this.reset();
32846
+ }
32847
+ if (message.flag === Message$2.Flag.STOP_SENDING) {
32848
+ // The remote has stopped reading
32849
+ this.remoteCloseRead();
32850
+ }
32851
+ if (message.flag === Message$2.Flag.FIN_ACK) {
32852
+ this.log.trace('received FIN_ACK');
32853
+ this.receiveFinAck.resolve();
32854
+ }
32855
+ }
32856
+ // ignore data messages if we've closed the readable end already
32857
+ if (this.readStatus === 'ready') {
32858
+ return message.message;
32859
+ }
32860
+ }
32861
+ async _sendFlag(flag) {
32862
+ if (this.channel.readyState !== 'open') {
32863
+ // flags can be sent while we or the remote are closing the datachannel so
32864
+ // if the channel isn't open, don't try to send it but return false to let
32865
+ // the caller know and act if they need to
32866
+ this.log.trace('not sending flag %s because channel is "%s" and not "open"', this.channel.readyState, flag.toString());
32867
+ return false;
32868
+ }
32869
+ this.log.trace('sending flag %s', flag.toString());
32870
+ const msgbuf = Message$2.encode({ flag });
32871
+ const prefixedBuf = encode.single(msgbuf);
32872
+ try {
32873
+ await this._sendMessage(prefixedBuf, false);
32874
+ return true;
32875
+ }
32876
+ catch (err) {
32877
+ this.log.error('could not send flag %s', flag.toString(), err);
32878
+ }
32879
+ return false;
32880
+ }
32881
+ }
32882
+ function createStream(options) {
32883
+ const { channel, direction } = options;
32884
+ return new WebRTCStream({
32885
+ id: direction === 'inbound' ? (`i${channel.id}`) : `r${channel.id}`,
32886
+ log: options.logger.forComponent(`libp2p:webrtc:stream:${direction}:${channel.id}`),
32887
+ ...options
32888
+ });
32889
+ }
32890
+
32891
+ const PROTOCOL$1 = '/webrtc';
32892
+ class DataChannelMuxerFactory {
32893
+ protocol;
32894
+ /**
32895
+ * WebRTC Peer Connection
32896
+ */
32897
+ peerConnection;
32898
+ bufferedStreams = [];
32899
+ metrics;
32900
+ dataChannelOptions;
32901
+ components;
32902
+ log;
32903
+ constructor(components, init) {
32904
+ this.components = components;
32905
+ this.peerConnection = init.peerConnection;
32906
+ this.metrics = init.metrics;
32907
+ this.protocol = init.protocol ?? PROTOCOL$1;
32908
+ this.dataChannelOptions = init.dataChannelOptions ?? {};
32909
+ this.log = components.logger.forComponent('libp2p:webrtc:datachannelmuxerfactory');
32910
+ // store any datachannels opened before upgrade has been completed
32911
+ this.peerConnection.ondatachannel = ({ channel }) => {
32912
+ this.log.trace('incoming early datachannel with channel id %d and label "%s"', channel.id);
32913
+ // 'init' channel is only used during connection establishment
32914
+ if (channel.label === 'init') {
32915
+ this.log.trace('closing early init channel');
32916
+ channel.close();
32917
+ return;
32918
+ }
32919
+ // @ts-expect-error fields are set below
32920
+ const bufferedStream = {};
32921
+ const stream = createStream({
32922
+ channel,
32923
+ direction: 'inbound',
32924
+ onEnd: (err) => {
32925
+ bufferedStream.onEnd(err);
32926
+ },
32927
+ logger: components.logger,
32928
+ ...this.dataChannelOptions
32929
+ });
32930
+ bufferedStream.stream = stream;
32931
+ bufferedStream.channel = channel;
32932
+ bufferedStream.onEnd = () => {
32933
+ this.bufferedStreams = this.bufferedStreams.filter(s => s.stream.id !== stream.id);
32934
+ };
32935
+ this.bufferedStreams.push(bufferedStream);
32936
+ };
32937
+ }
32938
+ createStreamMuxer(init) {
32939
+ return new DataChannelMuxer(this.components, {
32940
+ ...init,
32941
+ peerConnection: this.peerConnection,
32942
+ dataChannelOptions: this.dataChannelOptions,
32943
+ metrics: this.metrics,
32944
+ streams: this.bufferedStreams,
32945
+ protocol: this.protocol
32946
+ });
32947
+ }
32948
+ }
32949
+ /**
32950
+ * A libp2p data channel stream muxer
32951
+ */
32952
+ class DataChannelMuxer {
32953
+ init;
32954
+ /**
32955
+ * Array of streams in the data channel
32956
+ */
32957
+ streams;
32958
+ protocol;
32959
+ log;
32960
+ peerConnection;
32961
+ dataChannelOptions;
32962
+ metrics;
32963
+ logger;
32964
+ constructor(components, init) {
32965
+ this.init = init;
32966
+ this.log = components.logger.forComponent('libp2p:webrtc:muxer');
32967
+ this.logger = components.logger;
32968
+ this.streams = init.streams.map(s => s.stream);
32969
+ this.peerConnection = init.peerConnection;
32970
+ this.protocol = init.protocol ?? PROTOCOL$1;
32971
+ this.metrics = init.metrics;
32972
+ this.dataChannelOptions = init.dataChannelOptions ?? {};
32973
+ /**
32974
+ * Fired when a data channel has been added to the connection has been
32975
+ * added by the remote peer.
32976
+ *
32977
+ * {@link https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/datachannel_event}
32978
+ */
32979
+ this.peerConnection.ondatachannel = ({ channel }) => {
32980
+ this.log.trace('incoming datachannel with channel id %d', channel.id);
32981
+ // 'init' channel is only used during connection establishment
32982
+ if (channel.label === 'init') {
32983
+ this.log.trace('closing init channel');
32984
+ channel.close();
32985
+ return;
32986
+ }
32987
+ const stream = createStream({
32988
+ channel,
32989
+ direction: 'inbound',
32990
+ onEnd: () => {
32991
+ this.log('incoming channel %s ended with state %s', channel.id, channel.readyState);
32992
+ this.#onStreamEnd(stream, channel);
32993
+ },
32994
+ logger: this.logger,
32995
+ ...this.dataChannelOptions
32996
+ });
32997
+ this.streams.push(stream);
32998
+ this.metrics?.increment({ incoming_stream: true });
32999
+ init?.onIncomingStream?.(stream);
33000
+ };
33001
+ // the DataChannelMuxer constructor is called during set up of the
33002
+ // connection by the upgrader.
33003
+ //
33004
+ // If we invoke `init.onIncomingStream` immediately, the connection object
33005
+ // will not be set up yet so add a tiny delay before letting the
33006
+ // connection know about early streams
33007
+ if (this.init.streams.length > 0) {
33008
+ queueMicrotask(() => {
33009
+ this.init.streams.forEach(bufferedStream => {
33010
+ bufferedStream.onEnd = () => {
33011
+ this.log('incoming early channel %s ended with state %s', bufferedStream.channel.id, bufferedStream.channel.readyState);
33012
+ this.#onStreamEnd(bufferedStream.stream, bufferedStream.channel);
33013
+ };
33014
+ this.metrics?.increment({ incoming_stream: true });
33015
+ this.init?.onIncomingStream?.(bufferedStream.stream);
33016
+ });
33017
+ });
33018
+ }
33019
+ }
33020
+ #onStreamEnd(stream, channel) {
33021
+ this.log.trace('stream %s %s %s onEnd', stream.direction, stream.id, stream.protocol);
33022
+ drainAndClose(channel, `${stream.direction} ${stream.id} ${stream.protocol}`, this.dataChannelOptions.drainTimeout, {
33023
+ log: this.log
33024
+ });
33025
+ this.streams = this.streams.filter(s => s.id !== stream.id);
33026
+ this.metrics?.increment({ stream_end: true });
33027
+ this.init?.onStreamEnd?.(stream);
33028
+ }
33029
+ /**
33030
+ * Gracefully close all tracked streams and stop the muxer
33031
+ */
33032
+ async close(options) {
33033
+ try {
33034
+ await Promise.all(this.streams.map(async (stream) => stream.close(options)));
33035
+ }
33036
+ catch (err) {
33037
+ this.abort(err);
33038
+ }
33039
+ }
33040
+ /**
33041
+ * Abort all tracked streams and stop the muxer
33042
+ */
33043
+ abort(err) {
33044
+ for (const stream of this.streams) {
33045
+ stream.abort(err);
33046
+ }
33047
+ }
33048
+ /**
33049
+ * The stream source, a no-op as the transport natively supports multiplexing
33050
+ */
33051
+ source = nopSource();
33052
+ /**
33053
+ * The stream destination, a no-op as the transport natively supports multiplexing
33054
+ */
33055
+ sink = nopSink;
33056
+ newStream() {
33057
+ // The spec says the label SHOULD be an empty string: https://github.com/libp2p/specs/blob/master/webrtc/README.md#rtcdatachannel-label
33058
+ const channel = this.peerConnection.createDataChannel('');
33059
+ this.log.trace('opened outgoing datachannel with channel id %s', channel.id);
33060
+ const stream = createStream({
33061
+ channel,
33062
+ direction: 'outbound',
33063
+ onEnd: () => {
33064
+ this.log('outgoing channel %s ended with state %s', channel.id, channel.readyState);
33065
+ this.#onStreamEnd(stream, channel);
33066
+ },
33067
+ logger: this.logger,
33068
+ ...this.dataChannelOptions
33069
+ });
33070
+ this.streams.push(stream);
33071
+ this.metrics?.increment({ outgoing_stream: true });
33072
+ return stream;
33073
+ }
33074
+ }
33075
+
33076
+ const RTCPeerConnection = globalThis.RTCPeerConnection;
33077
+ const RTCSessionDescription = globalThis.RTCSessionDescription;
33078
+ const RTCIceCandidate = globalThis.RTCIceCandidate;
33079
+
33080
+ /**
33081
+ * @packageDocumentation
33082
+ *
33083
+ * This module makes it easy to send and receive length-prefixed Protobuf encoded
33084
+ * messages over streams.
33085
+ *
33086
+ * @example
33087
+ *
33088
+ * ```typescript
33089
+ * import { pbStream } from 'it-protobuf-stream'
33090
+ * import { MessageType } from './src/my-message-type.js'
33091
+ *
33092
+ * // RequestType and ResponseType have been generate from `.proto` files and have
33093
+ * // `.encode` and `.decode` methods for serialization/deserialization
33094
+ *
33095
+ * const stream = pbStream(duplex)
33096
+ *
33097
+ * // write a message to the stream
33098
+ * stream.write({
33099
+ * foo: 'bar'
33100
+ * }, MessageType)
33101
+ *
33102
+ * // read a message from the stream
33103
+ * const res = await stream.read(MessageType)
33104
+ * ```
33105
+ */
33106
+ function pbStream(duplex, opts) {
33107
+ const lp = lpStream(duplex, opts);
33108
+ const W = {
33109
+ read: async (proto, options) => {
33110
+ // readLP, decode
33111
+ const value = await lp.read(options);
33112
+ return proto.decode(value);
33113
+ },
33114
+ write: async (message, proto, options) => {
33115
+ // encode, writeLP
33116
+ await lp.write(proto.encode(message), options);
33117
+ },
33118
+ writeV: async (messages, proto, options) => {
33119
+ // encode, writeLP
33120
+ await lp.writeV(messages.map(message => proto.encode(message)), options);
33121
+ },
33122
+ pb: (proto) => {
33123
+ return {
33124
+ read: async (options) => W.read(proto, options),
33125
+ write: async (d, options) => W.write(d, proto, options),
33126
+ writeV: async (d, options) => W.writeV(d, proto, options),
33127
+ unwrap: () => W
33128
+ };
33129
+ },
33130
+ unwrap: () => {
33131
+ return lp.unwrap();
33132
+ }
33133
+ };
33134
+ return W;
33135
+ }
33136
+
33137
+ /* eslint-disable import/export */
33138
+ /* eslint-disable complexity */
33139
+ /* eslint-disable @typescript-eslint/no-namespace */
33140
+ /* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */
33141
+ /* eslint-disable @typescript-eslint/no-empty-interface */
33142
+ var Message$1;
33143
+ (function (Message) {
33144
+ (function (Type) {
33145
+ Type["SDP_OFFER"] = "SDP_OFFER";
33146
+ Type["SDP_ANSWER"] = "SDP_ANSWER";
33147
+ Type["ICE_CANDIDATE"] = "ICE_CANDIDATE";
33148
+ })(Message.Type || (Message.Type = {}));
33149
+ let __TypeValues;
33150
+ (function (__TypeValues) {
33151
+ __TypeValues[__TypeValues["SDP_OFFER"] = 0] = "SDP_OFFER";
33152
+ __TypeValues[__TypeValues["SDP_ANSWER"] = 1] = "SDP_ANSWER";
33153
+ __TypeValues[__TypeValues["ICE_CANDIDATE"] = 2] = "ICE_CANDIDATE";
33154
+ })(__TypeValues || (__TypeValues = {}));
33155
+ (function (Type) {
33156
+ Type.codec = () => {
33157
+ return enumeration(__TypeValues);
33158
+ };
33159
+ })(Message.Type || (Message.Type = {}));
33160
+ let _codec;
33161
+ Message.codec = () => {
33162
+ if (_codec == null) {
33163
+ _codec = message((obj, w, opts = {}) => {
33164
+ if (opts.lengthDelimited !== false) {
33165
+ w.fork();
33166
+ }
33167
+ if (obj.type != null) {
33168
+ w.uint32(8);
33169
+ Message.Type.codec().encode(obj.type, w);
33170
+ }
33171
+ if (obj.data != null) {
33172
+ w.uint32(18);
33173
+ w.string(obj.data);
33174
+ }
33175
+ if (opts.lengthDelimited !== false) {
33176
+ w.ldelim();
33177
+ }
33178
+ }, (reader, length) => {
33179
+ const obj = {};
33180
+ const end = length == null ? reader.len : reader.pos + length;
33181
+ while (reader.pos < end) {
33182
+ const tag = reader.uint32();
33183
+ switch (tag >>> 3) {
33184
+ case 1:
33185
+ obj.type = Message.Type.codec().decode(reader);
33186
+ break;
33187
+ case 2:
33188
+ obj.data = reader.string();
33189
+ break;
33190
+ default:
33191
+ reader.skipType(tag & 7);
33192
+ break;
33193
+ }
33194
+ }
33195
+ return obj;
33196
+ });
33197
+ }
33198
+ return _codec;
33199
+ };
33200
+ Message.encode = (obj) => {
33201
+ return encodeMessage(obj, Message.codec());
33202
+ };
33203
+ Message.decode = (buf) => {
33204
+ return decodeMessage(buf, Message.codec());
33205
+ };
33206
+ })(Message$1 || (Message$1 = {}));
33207
+
33208
+ const readCandidatesUntilConnected = async (pc, stream, options) => {
33209
+ try {
33210
+ const connectedPromise = pDefer();
33211
+ resolveOnConnected(pc, connectedPromise);
33212
+ // read candidates until we are connected or we reach the end of the stream
33213
+ while (true) {
33214
+ // if we connect, stop trying to read from the stream
33215
+ const message = await Promise.race([
33216
+ connectedPromise.promise,
33217
+ stream.read({
33218
+ signal: options.signal
33219
+ }).catch(() => { })
33220
+ ]);
33221
+ // stream ended or we became connected
33222
+ if (message == null) {
33223
+ // throw if we timed out
33224
+ options.signal?.throwIfAborted();
33225
+ break;
33226
+ }
33227
+ if (message.type !== Message$1.Type.ICE_CANDIDATE) {
33228
+ throw new CodeError$2('ICE candidate message expected', 'ERR_NOT_ICE_CANDIDATE');
33229
+ }
33230
+ const candidateInit = JSON.parse(message.data ?? 'null');
33231
+ // an empty string means this generation of candidates is complete, a null
33232
+ // candidate means candidate gathering has finished
33233
+ // see - https://www.w3.org/TR/webrtc/#rtcpeerconnectioniceevent
33234
+ if (candidateInit === '' || candidateInit === null) {
33235
+ options.log.trace('end-of-candidates received');
33236
+ continue;
33237
+ }
33238
+ const candidate = new RTCIceCandidate(candidateInit);
33239
+ options.log.trace('%s received new ICE candidate %o', options.direction, candidateInit);
33240
+ try {
33241
+ await pc.addIceCandidate(candidate);
33242
+ }
33243
+ catch (err) {
33244
+ options.log.error('%s bad candidate received', options.direction, candidateInit, err);
33245
+ }
33246
+ }
33247
+ }
33248
+ catch (err) {
33249
+ options.log.error('%s error parsing ICE candidate', options.direction, err);
33250
+ if (options.signal?.aborted === true) {
33251
+ throw err;
33252
+ }
33253
+ }
33254
+ };
33255
+ function getConnectionState(pc) {
33256
+ return isFirefox ? pc.iceConnectionState : pc.connectionState;
33257
+ }
33258
+ function resolveOnConnected(pc, promise) {
33259
+ pc[isFirefox ? 'oniceconnectionstatechange' : 'onconnectionstatechange'] = (_) => {
33260
+ switch (getConnectionState(pc)) {
33261
+ case 'connected':
33262
+ promise.resolve();
33263
+ break;
33264
+ case 'failed':
33265
+ case 'disconnected':
33266
+ case 'closed':
33267
+ promise.reject(new CodeError$2('RTCPeerConnection was closed', 'ERR_CONNECTION_CLOSED_BEFORE_CONNECTED'));
33268
+ break;
33269
+ }
33270
+ };
33271
+ }
33272
+
33273
+ async function initiateConnection({ rtcConfiguration, dataChannel, signal, metrics, multiaddr: ma, connectionManager, transportManager, log, logger }) {
33274
+ const { baseAddr } = splitAddr(ma);
33275
+ metrics?.dialerEvents.increment({ open: true });
33276
+ log.trace('dialing base address: %a', baseAddr);
33277
+ const relayPeer = baseAddr.getPeerId();
33278
+ if (relayPeer == null) {
33279
+ throw new CodeError$2('Relay peer was missing', 'ERR_INVALID_ADDRESS');
33280
+ }
33281
+ const connections = connectionManager.getConnections(peerIdFromString(relayPeer));
33282
+ let connection;
33283
+ let shouldCloseConnection = false;
33284
+ if (connections.length === 0) {
33285
+ // use the transport manager to open a connection. Initiating a WebRTC
33286
+ // connection takes place in the context of a dial - if we use the
33287
+ // connection manager instead we can end up joining our own dial context
33288
+ connection = await transportManager.dial(baseAddr, {
33289
+ signal
33290
+ });
33291
+ // this connection is unmanaged by the connection manager so we should
33292
+ // close it when we are done
33293
+ shouldCloseConnection = true;
33294
+ }
33295
+ else {
33296
+ connection = connections[0];
33297
+ }
33298
+ try {
33299
+ const stream = await connection.newStream(SIGNALING_PROTO_ID, {
33300
+ signal,
33301
+ runOnTransientConnection: true
33302
+ });
33303
+ const messageStream = pbStream(stream).pb(Message$1);
33304
+ const peerConnection = new RTCPeerConnection(rtcConfiguration);
33305
+ const muxerFactory = new DataChannelMuxerFactory({
33306
+ logger
33307
+ }, {
33308
+ peerConnection,
33309
+ dataChannelOptions: dataChannel
33310
+ });
33311
+ try {
33312
+ // we create the channel so that the RTCPeerConnection has a component for
33313
+ // which to collect candidates. The label is not relevant to connection
33314
+ // initiation but can be useful for debugging
33315
+ const channel = peerConnection.createDataChannel('init');
33316
+ // setup callback to write ICE candidates to the remote peer
33317
+ peerConnection.onicecandidate = ({ candidate }) => {
33318
+ // a null candidate means end-of-candidates, an empty string candidate
33319
+ // means end-of-candidates for this generation, otherwise this should
33320
+ // be a valid candidate object
33321
+ // see - https://www.w3.org/TR/webrtc/#rtcpeerconnectioniceevent
33322
+ const data = JSON.stringify(candidate?.toJSON() ?? null);
33323
+ log.trace('initiator sending ICE candidate %o', candidate);
33324
+ void messageStream.write({
33325
+ type: Message$1.Type.ICE_CANDIDATE,
33326
+ data
33327
+ }, {
33328
+ signal
33329
+ })
33330
+ .catch(err => {
33331
+ log.error('error sending ICE candidate', err);
33332
+ });
33333
+ };
33334
+ peerConnection.onicecandidateerror = (event) => {
33335
+ log.error('initiator ICE candidate error', event);
33336
+ };
33337
+ // create an offer
33338
+ const offerSdp = await peerConnection.createOffer().catch(err => {
33339
+ log.error('could not execute createOffer', err);
33340
+ throw new CodeError$2('Failed to set createOffer', 'ERR_SDP_HANDSHAKE_FAILED');
33341
+ });
33342
+ log.trace('initiator send SDP offer %s', offerSdp.sdp);
33343
+ // write the offer to the stream
33344
+ await messageStream.write({ type: Message$1.Type.SDP_OFFER, data: offerSdp.sdp }, {
33345
+ signal
33346
+ });
33347
+ // set offer as local description
33348
+ await peerConnection.setLocalDescription(offerSdp).catch(err => {
33349
+ log.error('could not execute setLocalDescription', err);
33350
+ throw new CodeError$2('Failed to set localDescription', 'ERR_SDP_HANDSHAKE_FAILED');
33351
+ });
33352
+ // read answer
33353
+ const answerMessage = await messageStream.read({
33354
+ signal
33355
+ });
33356
+ if (answerMessage.type !== Message$1.Type.SDP_ANSWER) {
33357
+ throw new CodeError$2('Remote should send an SDP answer', 'ERR_SDP_HANDSHAKE_FAILED');
33358
+ }
33359
+ log.trace('initiator receive SDP answer %s', answerMessage.data);
33360
+ const answerSdp = new RTCSessionDescription({ type: 'answer', sdp: answerMessage.data });
33361
+ await peerConnection.setRemoteDescription(answerSdp).catch(err => {
33362
+ log.error('could not execute setRemoteDescription', err);
33363
+ throw new CodeError$2('Failed to set remoteDescription', 'ERR_SDP_HANDSHAKE_FAILED');
33364
+ });
33365
+ log.trace('initiator read candidates until connected');
33366
+ await readCandidatesUntilConnected(peerConnection, messageStream, {
33367
+ direction: 'initiator',
33368
+ signal,
33369
+ log
33370
+ });
33371
+ log.trace('initiator connected, closing init channel');
33372
+ channel.close();
33373
+ log.trace('closing signalling channel');
33374
+ await stream.close({
33375
+ signal
33376
+ });
33377
+ log.trace('initiator connected to remote address %s', ma);
33378
+ return {
33379
+ remoteAddress: ma,
33380
+ peerConnection,
33381
+ muxerFactory
33382
+ };
33383
+ }
33384
+ catch (err) {
33385
+ log.error('outgoing signalling error', err);
33386
+ peerConnection.close();
33387
+ stream.abort(err);
33388
+ throw err;
33389
+ }
33390
+ finally {
33391
+ peerConnection.onicecandidate = null;
33392
+ peerConnection.onicecandidateerror = null;
33393
+ }
33394
+ }
33395
+ finally {
33396
+ // if we had to open a connection to perform the SDP handshake
33397
+ // close it because it's not tracked by the connection manager
33398
+ if (shouldCloseConnection) {
33399
+ try {
33400
+ await connection.close({
33401
+ signal
33402
+ });
33403
+ }
33404
+ catch (err) {
33405
+ connection.abort(err);
33406
+ }
33407
+ }
33408
+ }
33409
+ }
33410
+
33411
+ class WebRTCPeerListener extends TypedEventEmitter {
33412
+ peerId;
33413
+ transportManager;
33414
+ shutdownController;
33415
+ constructor(components, init) {
33416
+ super();
33417
+ this.peerId = components.peerId;
33418
+ this.transportManager = components.transportManager;
33419
+ this.shutdownController = init.shutdownController;
33420
+ }
33421
+ async listen() {
33422
+ this.safeDispatchEvent('listening', {});
33423
+ }
33424
+ getAddrs() {
33425
+ return this.transportManager
33426
+ .getListeners()
33427
+ .filter(l => l !== this)
33428
+ .map(l => l.getAddrs()
33429
+ .filter(ma => Circuit.matches(ma))
33430
+ .map(ma => {
33431
+ return ma.encapsulate(`/webrtc/p2p/${this.peerId}`);
33432
+ }))
33433
+ .flat();
33434
+ }
33435
+ async close() {
33436
+ this.shutdownController.abort();
33437
+ this.safeDispatchEvent('close', {});
33438
+ }
33439
+ }
33440
+
33441
+ async function handleIncomingStream({ peerConnection, stream, signal, connection, log }) {
33442
+ log.trace('new inbound signaling stream');
33443
+ const messageStream = pbStream(stream).pb(Message$1);
33444
+ try {
33445
+ // candidate callbacks
33446
+ peerConnection.onicecandidate = ({ candidate }) => {
33447
+ // a null candidate means end-of-candidates, an empty string candidate
33448
+ // means end-of-candidates for this generation, otherwise this should
33449
+ // be a valid candidate object
33450
+ // see - https://www.w3.org/TR/webrtc/#rtcpeerconnectioniceevent
33451
+ const data = JSON.stringify(candidate?.toJSON() ?? null);
33452
+ log.trace('recipient sending ICE candidate %s', data);
33453
+ messageStream.write({
33454
+ type: Message$1.Type.ICE_CANDIDATE,
33455
+ data
33456
+ }, {
33457
+ signal
33458
+ })
33459
+ .catch(err => {
33460
+ log.error('error sending ICE candidate', err);
33461
+ });
33462
+ };
33463
+ // read an SDP offer
33464
+ const pbOffer = await messageStream.read({
33465
+ signal
33466
+ });
33467
+ if (pbOffer.type !== Message$1.Type.SDP_OFFER) {
33468
+ throw new CodeError$2(`expected message type SDP_OFFER, received: ${pbOffer.type ?? 'undefined'} `, 'ERR_SDP_HANDSHAKE_FAILED');
33469
+ }
33470
+ log.trace('recipient receive SDP offer %s', pbOffer.data);
33471
+ const offer = new RTCSessionDescription({
33472
+ type: 'offer',
33473
+ sdp: pbOffer.data
33474
+ });
33475
+ await peerConnection.setRemoteDescription(offer).catch(err => {
33476
+ log.error('could not execute setRemoteDescription', err);
33477
+ throw new CodeError$2('Failed to set remoteDescription', 'ERR_SDP_HANDSHAKE_FAILED');
33478
+ });
33479
+ // create and write an SDP answer
33480
+ const answer = await peerConnection.createAnswer().catch(err => {
33481
+ log.error('could not execute createAnswer', err);
33482
+ throw new CodeError$2('Failed to create answer', 'ERR_SDP_HANDSHAKE_FAILED');
33483
+ });
33484
+ log.trace('recipient send SDP answer %s', answer.sdp);
33485
+ // write the answer to the remote
33486
+ await messageStream.write({ type: Message$1.Type.SDP_ANSWER, data: answer.sdp }, {
33487
+ signal
33488
+ });
33489
+ await peerConnection.setLocalDescription(answer).catch(err => {
33490
+ log.error('could not execute setLocalDescription', err);
33491
+ throw new CodeError$2('Failed to set localDescription', 'ERR_SDP_HANDSHAKE_FAILED');
33492
+ });
33493
+ log.trace('recipient read candidates until connected');
33494
+ // wait until candidates are connected
33495
+ await readCandidatesUntilConnected(peerConnection, messageStream, {
33496
+ direction: 'recipient',
33497
+ signal,
33498
+ log
33499
+ });
33500
+ }
33501
+ catch (err) {
33502
+ if (peerConnection.connectionState !== 'connected') {
33503
+ log.error('error while handling signaling stream from peer %a', connection.remoteAddr, err);
33504
+ peerConnection.close();
33505
+ throw err;
33506
+ }
33507
+ else {
33508
+ log('error while handling signaling stream from peer %a, ignoring as the RTCPeerConnection is already connected', connection.remoteAddr, err);
33509
+ }
33510
+ }
33511
+ const remoteAddress = multiaddr(`/webrtc/p2p/${connection.remoteAddr.getPeerId()}`);
33512
+ log.trace('recipient connected to remote address %s', remoteAddress);
33513
+ return { remoteAddress };
33514
+ }
33515
+
33516
+ const WEBRTC_TRANSPORT = '/webrtc';
33517
+ const CIRCUIT_RELAY_TRANSPORT = '/p2p-circuit';
33518
+ const SIGNALING_PROTO_ID = '/webrtc-signaling/0.0.1';
33519
+ const INBOUND_CONNECTION_TIMEOUT = 30 * 1000;
33520
+ class WebRTCTransport {
33521
+ components;
33522
+ init;
33523
+ log;
33524
+ _started = false;
33525
+ metrics;
33526
+ shutdownController;
33527
+ constructor(components, init = {}) {
33528
+ this.components = components;
33529
+ this.init = init;
33530
+ this.log = components.logger.forComponent('libp2p:webrtc');
33531
+ this.shutdownController = new AbortController();
33532
+ setMaxListeners(Infinity, this.shutdownController.signal);
33533
+ if (components.metrics != null) {
33534
+ this.metrics = {
33535
+ dialerEvents: components.metrics.registerCounterGroup('libp2p_webrtc_dialer_events_total', {
33536
+ label: 'event',
33537
+ help: 'Total count of WebRTC dialer events by type'
33538
+ }),
33539
+ listenerEvents: components.metrics.registerCounterGroup('libp2p_webrtc_listener_events_total', {
33540
+ label: 'event',
33541
+ help: 'Total count of WebRTC listener events by type'
33542
+ })
33543
+ };
33544
+ }
33545
+ }
33546
+ [transportSymbol] = true;
33547
+ [Symbol.toStringTag] = '@libp2p/webrtc';
33548
+ [serviceCapabilities] = [
33549
+ '@libp2p/transport'
33550
+ ];
33551
+ [serviceDependencies] = [
33552
+ '@libp2p/identify',
33553
+ '@libp2p/circuit-relay-v2-transport'
33554
+ ];
33555
+ isStarted() {
33556
+ return this._started;
33557
+ }
33558
+ async start() {
33559
+ await this.components.registrar.handle(SIGNALING_PROTO_ID, (data) => {
33560
+ this._onProtocol(data).catch(err => { this.log.error('failed to handle incoming connect from %p', data.connection.remotePeer, err); });
33561
+ }, {
33562
+ runOnTransientConnection: true
33563
+ });
33564
+ this._started = true;
33565
+ }
33566
+ async stop() {
33567
+ await this.components.registrar.unhandle(SIGNALING_PROTO_ID);
33568
+ this._started = false;
33569
+ }
33570
+ createListener(options) {
33571
+ return new WebRTCPeerListener(this.components, {
33572
+ shutdownController: this.shutdownController
33573
+ });
33574
+ }
33575
+ /**
33576
+ * Filter check for all Multiaddrs that this transport can listen on
33577
+ */
33578
+ listenFilter(multiaddrs) {
33579
+ return multiaddrs.filter(WebRTC.exactMatch);
33580
+ }
33581
+ /**
33582
+ * Filter check for all Multiaddrs that this transport can dial
33583
+ */
33584
+ dialFilter(multiaddrs) {
33585
+ return this.listenFilter(multiaddrs);
33586
+ }
33587
+ /*
33588
+ * dial connects to a remote via the circuit relay or any other protocol
33589
+ * and proceeds to upgrade to a webrtc connection.
33590
+ * multiaddr of the form: <multiaddr>/webrtc/p2p/<destination-peer>
33591
+ * For a circuit relay, this will be of the form
33592
+ * <relay address>/p2p/<relay-peer>/p2p-circuit/webrtc/p2p/<destination-peer>
33593
+ */
33594
+ async dial(ma, options) {
33595
+ this.log.trace('dialing address: %a', ma);
33596
+ const { remoteAddress, peerConnection, muxerFactory } = await initiateConnection({
33597
+ rtcConfiguration: typeof this.init.rtcConfiguration === 'function' ? await this.init.rtcConfiguration() : this.init.rtcConfiguration,
33598
+ dataChannel: this.init.dataChannel,
33599
+ multiaddr: ma,
33600
+ dataChannelOptions: this.init.dataChannel,
33601
+ signal: options.signal,
33602
+ connectionManager: this.components.connectionManager,
33603
+ transportManager: this.components.transportManager,
33604
+ log: this.log,
33605
+ logger: this.components.logger
33606
+ });
33607
+ const webRTCConn = new WebRTCMultiaddrConnection(this.components, {
33608
+ peerConnection,
33609
+ timeline: { open: Date.now() },
33610
+ remoteAddr: remoteAddress,
33611
+ metrics: this.metrics?.dialerEvents
33612
+ });
33613
+ const connection = await options.upgrader.upgradeOutbound(webRTCConn, {
33614
+ skipProtection: true,
33615
+ skipEncryption: true,
33616
+ muxerFactory
33617
+ });
33618
+ // close the connection on shut down
33619
+ this._closeOnShutdown(peerConnection, webRTCConn);
33620
+ return connection;
33621
+ }
33622
+ async _onProtocol({ connection, stream }) {
33623
+ const signal = AbortSignal.timeout(this.init.inboundConnectionTimeout ?? INBOUND_CONNECTION_TIMEOUT);
33624
+ const peerConnection = new RTCPeerConnection(typeof this.init.rtcConfiguration === 'function' ? await this.init.rtcConfiguration() : this.init.rtcConfiguration);
33625
+ const muxerFactory = new DataChannelMuxerFactory(this.components, {
33626
+ peerConnection,
33627
+ dataChannelOptions: this.init.dataChannel
33628
+ });
33629
+ try {
33630
+ const { remoteAddress } = await handleIncomingStream({
33631
+ peerConnection,
33632
+ connection,
33633
+ stream,
33634
+ signal,
33635
+ log: this.log
33636
+ });
33637
+ // close the stream if SDP messages have been exchanged successfully
33638
+ await stream.close({
33639
+ signal
33640
+ });
33641
+ const webRTCConn = new WebRTCMultiaddrConnection(this.components, {
33642
+ peerConnection,
33643
+ timeline: { open: (new Date()).getTime() },
33644
+ remoteAddr: remoteAddress,
33645
+ metrics: this.metrics?.listenerEvents
33646
+ });
33647
+ await this.components.upgrader.upgradeInbound(webRTCConn, {
33648
+ skipEncryption: true,
33649
+ skipProtection: true,
33650
+ muxerFactory
33651
+ });
33652
+ // close the connection on shut down
33653
+ this._closeOnShutdown(peerConnection, webRTCConn);
33654
+ }
33655
+ catch (err) {
33656
+ this.log.error('incoming signalling error', err);
33657
+ peerConnection.close();
33658
+ stream.abort(err);
33659
+ throw err;
33660
+ }
33661
+ }
33662
+ _closeOnShutdown(pc, webRTCConn) {
33663
+ // close the connection on shut down
33664
+ const shutDownListener = () => {
33665
+ webRTCConn.close()
33666
+ .catch(err => {
33667
+ this.log.error('could not close WebRTCMultiaddrConnection', err);
33668
+ });
33669
+ };
33670
+ this.shutdownController.signal.addEventListener('abort', shutDownListener);
33671
+ pc.addEventListener('close', () => {
33672
+ this.shutdownController.signal.removeEventListener('abort', shutDownListener);
33673
+ });
33674
+ }
33675
+ }
33676
+ function splitAddr(ma) {
33677
+ const addrs = ma.toString().split(WEBRTC_TRANSPORT + '/');
33678
+ if (addrs.length !== 2) {
33679
+ throw new CodeError$2('webrtc protocol was not present in multiaddr', codes.ERR_INVALID_MULTIADDR);
33680
+ }
33681
+ if (!addrs[0].includes(CIRCUIT_RELAY_TRANSPORT)) {
33682
+ throw new CodeError$2('p2p-circuit protocol was not present in multiaddr', codes.ERR_INVALID_MULTIADDR);
33683
+ }
33684
+ // look for remote peerId
33685
+ let remoteAddr = multiaddr(addrs[0]);
33686
+ const destination = multiaddr('/' + addrs[1]);
33687
+ const destinationIdString = destination.getPeerId();
33688
+ if (destinationIdString == null) {
33689
+ throw new CodeError$2('destination peer id was missing', codes.ERR_INVALID_MULTIADDR);
33690
+ }
33691
+ const lastProtoInRemote = remoteAddr.protos().pop();
33692
+ if (lastProtoInRemote === undefined) {
33693
+ throw new CodeError$2('invalid multiaddr', codes.ERR_INVALID_MULTIADDR);
33694
+ }
33695
+ if (lastProtoInRemote.name !== 'p2p') {
33696
+ remoteAddr = remoteAddr.encapsulate(`/p2p/${destinationIdString}`);
33697
+ }
33698
+ return { baseAddr: remoteAddr, peerId: peerIdFromString(destinationIdString) };
33699
+ }
33700
+
33701
+ /**
33702
+ * @packageDocumentation
33703
+ *
33704
+ * A [libp2p transport](https://docs.libp2p.io/concepts/transports/overview/) based on [WebRTC datachannels](https://webrtc.org/).
33705
+ *
33706
+ * [WebRTC](https://www.w3.org/TR/webrtc/) is a specification that allows real-time communication between nodes - it's commonly used in browser video conferencing applications but it also provides a reliable data transport mechanism called [data channels](https://www.w3.org/TR/webrtc/#peer-to-peer-data-api) which libp2p uses to facilitate [protocol streams](https://docs.libp2p.io/concepts/multiplex/overview/) between peers.
33707
+ *
33708
+ * There are two transports exposed by this module, [webRTC](https://github.com/libp2p/specs/blob/master/webrtc/webrtc.md) and [webRTCDirect](https://github.com/libp2p/specs/blob/master/webrtc/webrtc-direct.md).
33709
+ *
33710
+ * ## WebRTC vs WebRTC Direct
33711
+ *
33712
+ * The connection establishment phase of WebRTC involves a handshake using [SDP](https://en.wikipedia.org/wiki/Session_Description_Protocol) during which two peers will exchange information such as open ports, network addresses and required capabilities.
33713
+ *
33714
+ * A third party is usually necessary to carry out this handshake, forwarding messages between the two peers until they can make a direct connection between themselves.
33715
+ *
33716
+ * The WebRTC transport uses libp2p [Circuit Relay](https://docs.libp2p.io/concepts/nat/circuit-relay/)s to forward SDP messages. Once a direct connection is formed the relay plays no further part in the exchange.
33717
+ *
33718
+ * WebRTC Direct uses a technique known as [SDP munging](https://webrtchacks.com/not-a-guide-to-sdp-munging/) to skip the handshake step, instead encoding enough information in the connection request that the responder can derive what would have been in the handshake messages and so requires no third parties to establish a connection.
33719
+ *
33720
+ * A WebRTC Direct multiaddr also includes a certhash of the target peer - this is used to allow opening a connection to the remote, which would otherwise be denied due to use of a self-signed certificate.
33721
+ *
33722
+ * In both cases, once the connection is established a [Noise handshake](https://noiseprotocol.org/noise.html) is carried out to ensure that the remote peer has the private key that corresponds to the public key that makes up their PeerId, giving you both encryption and authentication.
33723
+ *
33724
+ * ## Support
33725
+ *
33726
+ * WebRTC is supported in both Node.js and browsers.
33727
+ *
33728
+ * At the time of writing, WebRTC Direct is dial-only in browsers and not supported in Node.js at all.
33729
+ *
33730
+ * Support in Node.js is possible but PRs will need to be opened to [libdatachannel](https://github.com/paullouisageneau/libdatachannel) and the appropriate APIs exposed in [node-datachannel](https://github.com/murat-dogan/node-datachannel).
33731
+ *
33732
+ * WebRTC Direct support is available in rust-libp2p and arriving soon in go-libp2p.
33733
+ *
33734
+ * See the WebRTC section of https://connectivity.libp2p.io for more information.
33735
+ *
33736
+ * @example WebRTC
33737
+ *
33738
+ * WebRTC requires use of a relay to connect two nodes. The listener first discovers a relay server and makes a reservation, then the dialer can connect via the relayed address.
33739
+ *
33740
+ * ```TypeScript
33741
+ * import { noise } from '@chainsafe/libp2p-noise'
33742
+ * import { yamux } from '@chainsafe/libp2p-yamux'
33743
+ * import { echo } from '@libp2p/echo'
33744
+ * import { circuitRelayTransport, circuitRelayServer } from '@libp2p/circuit-relay-v2'
33745
+ * import { identify } from '@libp2p/identify'
33746
+ * import { webRTC } from '@libp2p/webrtc'
33747
+ * import { webSockets } from '@libp2p/websockets'
33748
+ * import * as filters from '@libp2p/websockets/filters'
33749
+ * import { WebRTC } from '@multiformats/multiaddr-matcher'
33750
+ * import delay from 'delay'
33751
+ * import { pipe } from 'it-pipe'
33752
+ * import { createLibp2p } from 'libp2p'
33753
+ * import type { Multiaddr } from '@multiformats/multiaddr'
33754
+ *
33755
+ * // the relay server listens on a transport dialable by the listener and the
33756
+ * // dialer, and has a relay service configured
33757
+ * const relay = await createLibp2p({
33758
+ * addresses: {
33759
+ * listen: ['/ip4/127.0.0.1/tcp/0/ws']
33760
+ * },
33761
+ * transports: [
33762
+ * webSockets({filter: filters.all})
33763
+ * ],
33764
+ * connectionEncryption: [noise()],
33765
+ * streamMuxers: [yamux()],
33766
+ * services: {
33767
+ * identify: identify(),
33768
+ * relay: circuitRelayServer()
33769
+ * }
33770
+ * })
33771
+ *
33772
+ * // the listener has a WebSocket transport to dial the relay, a Circuit Relay
33773
+ * // transport to make a reservation, and a WebRTC transport to accept incoming
33774
+ * // WebRTC connections
33775
+ * const listener = await createLibp2p({
33776
+ * addresses: {
33777
+ * listen: ['/webrtc']
33778
+ * },
33779
+ * transports: [
33780
+ * webSockets({filter: filters.all}),
33781
+ * webRTC(),
33782
+ * circuitRelayTransport({
33783
+ * discoverRelays: 1
33784
+ * })
33785
+ * ],
33786
+ * connectionEncryption: [noise()],
33787
+ * streamMuxers: [yamux()],
33788
+ * services: {
33789
+ * identify: identify(),
33790
+ * echo: echo()
33791
+ * }
33792
+ * })
33793
+ *
33794
+ * // the listener dials the relay (or discovers a public relay via some other
33795
+ * // method)
33796
+ * await listener.dial(relay.getMultiaddrs(), {
33797
+ * signal: AbortSignal.timeout(5000)
33798
+ * })
33799
+ *
33800
+ * let webRTCMultiaddr: Multiaddr | undefined
33801
+ *
33802
+ * // wait for the listener to make a reservation on the relay
33803
+ * while (true) {
33804
+ * webRTCMultiaddr = listener.getMultiaddrs().find(ma => WebRTC.matches(ma))
33805
+ *
33806
+ * if (webRTCMultiaddr != null) {
33807
+ * break
33808
+ * }
33809
+ *
33810
+ * // try again later
33811
+ * await delay(1000)
33812
+ * }
33813
+ *
33814
+ * // the dialer has Circuit Relay, WebSocket and WebRTC transports to dial
33815
+ * // the listener via the relay, complete the SDP handshake and establish a
33816
+ * // direct WebRTC connection
33817
+ * const dialer = await createLibp2p({
33818
+ * transports: [
33819
+ * webSockets({filter: filters.all}),
33820
+ * webRTC(),
33821
+ * circuitRelayTransport()
33822
+ * ],
33823
+ * connectionEncryption: [noise()],
33824
+ * streamMuxers: [yamux()],
33825
+ * services: {
33826
+ * identify: identify(),
33827
+ * echo: echo()
33828
+ * }
33829
+ * })
33830
+ *
33831
+ * // dial the listener and open an echo protocol stream
33832
+ * const stream = await dialer.dialProtocol(webRTCMultiaddr, dialer.services.echo.protocol, {
33833
+ * signal: AbortSignal.timeout(5000)
33834
+ * })
33835
+ *
33836
+ * // we can now stop the relay
33837
+ * await relay.stop()
33838
+ *
33839
+ * // send/receive some data from the remote peer via a direct connection
33840
+ * await pipe(
33841
+ * [new TextEncoder().encode('hello world')],
33842
+ * stream,
33843
+ * async source => {
33844
+ * for await (const buf of source) {
33845
+ * console.info(new TextDecoder().decode(buf.subarray()))
33846
+ * }
33847
+ * }
33848
+ * )
33849
+ * ```
33850
+ *
33851
+ * @example WebRTC Direct
33852
+ *
33853
+ * At the time of writing WebRTC Direct is dial-only in browsers and unsupported in Node.js.
33854
+ *
33855
+ * The only implementation that supports a WebRTC Direct listener is go-libp2p and it's not yet enabled by default.
33856
+ *
33857
+ * ```TypeScript
33858
+ * import { createLibp2p } from 'libp2p'
33859
+ * import { noise } from '@chainsafe/libp2p-noise'
33860
+ * import { multiaddr } from '@multiformats/multiaddr'
33861
+ * import { pipe } from 'it-pipe'
33862
+ * import { fromString, toString } from 'uint8arrays'
33863
+ * import { webRTCDirect } from '@libp2p/webrtc'
33864
+ *
33865
+ * const node = await createLibp2p({
33866
+ * transports: [
33867
+ * webRTCDirect()
33868
+ * ],
33869
+ * connectionEncryption: [
33870
+ * noise()
33871
+ * ]
33872
+ * })
33873
+ *
33874
+ * await node.start()
33875
+ *
33876
+ * // this multiaddr corresponds to a remote node running a WebRTC Direct listener
33877
+ * const ma = multiaddr('/ip4/0.0.0.0/udp/56093/webrtc-direct/certhash/uEiByaEfNSLBexWBNFZy_QB1vAKEj7JAXDizRs4_SnTflsQ')
33878
+ * const stream = await node.dialProtocol(ma, '/my-protocol/1.0.0', {
33879
+ * signal: AbortSignal.timeout(10_000)
33880
+ * })
33881
+ *
33882
+ * await pipe(
33883
+ * [fromString(`Hello js-libp2p-webrtc\n`)],
33884
+ * stream,
33885
+ * async function (source) {
33886
+ * for await (const buf of source) {
33887
+ * console.info(toString(buf.subarray()))
33888
+ * }
33889
+ * }
33890
+ * )
33891
+ * ```
33892
+ */
33893
+ /**
33894
+ * @param {WebRTCTransportInit} init - WebRTC transport configuration
33895
+ * @param {RTCConfiguration} init.rtcConfiguration - RTCConfiguration
33896
+ * @param init.dataChannel - DataChannel configurations
33897
+ * @param {number} init.dataChannel.maxMessageSize - Max message size that can be sent through the DataChannel. Larger messages will be chunked into smaller messages below this size (default 16kb)
33898
+ * @param {number} init.dataChannel.maxBufferedAmount - Max buffered amount a DataChannel can have (default 16mb)
33899
+ * @param {number} init.dataChannel.bufferedAmountLowEventTimeout - If max buffered amount is reached, this is the max time that is waited before the buffer is cleared (default 30 seconds)
33900
+ * @returns
33901
+ */
33902
+ function webRTC(init) {
33903
+ return (components) => new WebRTCTransport(components, init);
33904
+ }
33905
+
33906
+ /* eslint-disable import/export */
33907
+ /* eslint-disable complexity */
33908
+ /* eslint-disable @typescript-eslint/no-namespace */
33909
+ /* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */
33910
+ /* eslint-disable @typescript-eslint/no-empty-interface */
33911
+ var HolePunch;
33912
+ (function (HolePunch) {
33913
+ (function (Type) {
33914
+ Type["UNUSED"] = "UNUSED";
33915
+ Type["CONNECT"] = "CONNECT";
33916
+ Type["SYNC"] = "SYNC";
33917
+ })(HolePunch.Type || (HolePunch.Type = {}));
33918
+ let __TypeValues;
33919
+ (function (__TypeValues) {
33920
+ __TypeValues[__TypeValues["UNUSED"] = 0] = "UNUSED";
33921
+ __TypeValues[__TypeValues["CONNECT"] = 100] = "CONNECT";
33922
+ __TypeValues[__TypeValues["SYNC"] = 300] = "SYNC";
33923
+ })(__TypeValues || (__TypeValues = {}));
33924
+ (function (Type) {
33925
+ Type.codec = () => {
33926
+ return enumeration(__TypeValues);
33927
+ };
33928
+ })(HolePunch.Type || (HolePunch.Type = {}));
33929
+ let _codec;
33930
+ HolePunch.codec = () => {
33931
+ if (_codec == null) {
33932
+ _codec = message((obj, w, opts = {}) => {
33933
+ if (opts.lengthDelimited !== false) {
33934
+ w.fork();
33935
+ }
33936
+ if (obj.type != null) {
33937
+ w.uint32(8);
33938
+ HolePunch.Type.codec().encode(obj.type, w);
33939
+ }
33940
+ if (obj.observedAddresses != null) {
33941
+ for (const value of obj.observedAddresses) {
33942
+ w.uint32(18);
33943
+ w.bytes(value);
33944
+ }
33945
+ }
33946
+ if (opts.lengthDelimited !== false) {
33947
+ w.ldelim();
33948
+ }
33949
+ }, (reader, length) => {
33950
+ const obj = {
33951
+ observedAddresses: []
33952
+ };
33953
+ const end = length == null ? reader.len : reader.pos + length;
33954
+ while (reader.pos < end) {
33955
+ const tag = reader.uint32();
33956
+ switch (tag >>> 3) {
33957
+ case 1:
33958
+ obj.type = HolePunch.Type.codec().decode(reader);
33959
+ break;
33960
+ case 2:
33961
+ obj.observedAddresses.push(reader.bytes());
33962
+ break;
33963
+ default:
33964
+ reader.skipType(tag & 7);
33965
+ break;
33966
+ }
33967
+ }
33968
+ return obj;
33969
+ });
33970
+ }
33971
+ return _codec;
33972
+ };
33973
+ HolePunch.encode = (obj) => {
33974
+ return encodeMessage(obj, HolePunch.codec());
33975
+ };
33976
+ HolePunch.decode = (buf) => {
33977
+ return decodeMessage(buf, HolePunch.codec());
33978
+ };
33979
+ })(HolePunch || (HolePunch = {}));
33980
+
33981
+ /**
33982
+ * Returns true if the passed multiaddr is public, not relayed and we have a
33983
+ * transport that can dial it
33984
+ */
33985
+ function isPublicAndDialable(ma, transportManager) {
33986
+ // ignore circuit relay
33987
+ if (Circuit$1.matches(ma)) {
33988
+ return false;
33989
+ }
33990
+ const transport = transportManager.dialTransportForMultiaddr(ma);
33991
+ if (transport == null) {
33992
+ return false;
33993
+ }
33994
+ // dns addresses are probably public?
33995
+ if (DNS$2.matches(ma)) {
33996
+ return true;
33997
+ }
33998
+ // ensure we have only IPv4/IPv6 addresses
33999
+ if (!IP$1.matches(ma)) {
34000
+ return false;
34001
+ }
34002
+ return isPrivateIp(ma.toOptions().host) === false;
34003
+ }
34004
+
34005
+ // https://github.com/libp2p/specs/blob/master/relay/DCUtR.md#rpc-messages
34006
+ const MAX_DCUTR_MESSAGE_SIZE = 1024 * 4;
34007
+ // ensure the dial has a high priority to jump to the head of the dial queue
34008
+ const DCUTR_DIAL_PRIORITY = 100;
34009
+ const defaultValues$1 = {
34010
+ // https://github.com/libp2p/go-libp2p/blob/8d2e54e1637041d5cf4fac1e531287560bd1f4ac/p2p/protocol/holepunch/holepuncher.go#L27
34011
+ timeout: 5000,
34012
+ // https://github.com/libp2p/go-libp2p/blob/8d2e54e1637041d5cf4fac1e531287560bd1f4ac/p2p/protocol/holepunch/holepuncher.go#L28
34013
+ retries: 3,
34014
+ maxInboundStreams: 1,
34015
+ maxOutboundStreams: 1
34016
+ };
34017
+ class DefaultDCUtRService {
34018
+ started;
34019
+ timeout;
34020
+ retries;
34021
+ maxInboundStreams;
34022
+ maxOutboundStreams;
34023
+ peerStore;
34024
+ registrar;
34025
+ connectionManager;
34026
+ addressManager;
34027
+ transportManager;
34028
+ topologyId;
34029
+ log;
34030
+ constructor(components, init) {
34031
+ this.log = components.logger.forComponent('libp2p:dcutr');
34032
+ this.started = false;
34033
+ this.peerStore = components.peerStore;
34034
+ this.registrar = components.registrar;
34035
+ this.addressManager = components.addressManager;
34036
+ this.connectionManager = components.connectionManager;
34037
+ this.transportManager = components.transportManager;
34038
+ this.timeout = init.timeout ?? defaultValues$1.timeout;
34039
+ this.retries = init.retries ?? defaultValues$1.retries;
34040
+ this.maxInboundStreams = init.maxInboundStreams ?? defaultValues$1.maxInboundStreams;
34041
+ this.maxOutboundStreams = init.maxOutboundStreams ?? defaultValues$1.maxOutboundStreams;
34042
+ }
34043
+ [Symbol.toStringTag] = '@libp2p/dcutr';
34044
+ [serviceDependencies] = [
34045
+ '@libp2p/identify'
34046
+ ];
34047
+ isStarted() {
34048
+ return this.started;
34049
+ }
34050
+ async start() {
34051
+ if (this.started) {
34052
+ return;
34053
+ }
34054
+ // register for notifications of when peers that support DCUtR connect
34055
+ // nb. requires the identify service to be enabled
34056
+ this.topologyId = await this.registrar.register(multicodec, {
34057
+ notifyOnTransient: true,
34058
+ onConnect: (peerId, connection) => {
34059
+ if (!connection.transient) {
34060
+ // the connection is already direct, no upgrade is required
34061
+ return;
34062
+ }
34063
+ // the inbound peer starts the connection upgrade
34064
+ if (connection.direction !== 'inbound') {
34065
+ return;
34066
+ }
34067
+ this.upgradeInbound(connection)
34068
+ .catch(err => {
34069
+ this.log.error('error during outgoing DCUtR attempt', err);
34070
+ });
34071
+ }
34072
+ });
34073
+ await this.registrar.handle(multicodec, (data) => {
34074
+ void this.handleIncomingUpgrade(data.stream, data.connection).catch(err => {
34075
+ this.log.error('error during incoming DCUtR attempt', err);
34076
+ data.stream.abort(err);
34077
+ });
34078
+ }, {
34079
+ maxInboundStreams: this.maxInboundStreams,
34080
+ maxOutboundStreams: this.maxOutboundStreams,
34081
+ runOnTransientConnection: true
34082
+ });
34083
+ this.started = true;
34084
+ }
34085
+ async stop() {
34086
+ await this.registrar.unhandle(multicodec);
34087
+ if (this.topologyId != null) {
34088
+ this.registrar.unregister(this.topologyId);
34089
+ }
34090
+ this.started = false;
34091
+ }
34092
+ /**
34093
+ * Perform the inbound connection upgrade as B
34094
+ *
34095
+ * @see https://github.com/libp2p/specs/blob/master/relay/DCUtR.md#the-protocol
34096
+ */
34097
+ async upgradeInbound(relayedConnection) {
34098
+ // Upon observing the new connection, the inbound peer (here B) checks the
34099
+ // addresses advertised by A via identify.
34100
+ //
34101
+ // If that set includes public addresses, then A may be reachable by a direct
34102
+ // connection, in which case B attempts a unilateral connection upgrade by
34103
+ // initiating a direct connection to A.
34104
+ if (await this.attemptUnilateralConnectionUpgrade(relayedConnection)) {
34105
+ return;
34106
+ }
34107
+ let stream;
34108
+ for (let i = 0; i < this.retries; i++) {
34109
+ const options = {
34110
+ signal: AbortSignal.timeout(this.timeout)
34111
+ };
34112
+ try {
34113
+ // 1. B opens a stream to A using the /libp2p/dcutr protocol.
34114
+ stream = await relayedConnection.newStream([multicodec], {
34115
+ signal: options.signal,
34116
+ runOnTransientConnection: true
34117
+ });
34118
+ const pb = pbStream(stream, {
34119
+ maxDataLength: MAX_DCUTR_MESSAGE_SIZE
34120
+ }).pb(HolePunch);
34121
+ // 2. B sends to A a Connect message containing its observed (and
34122
+ // possibly predicted) addresses from identify and starts a timer
34123
+ // to measure RTT of the relay connection.
34124
+ this.log('B sending connect to %p', relayedConnection.remotePeer);
34125
+ const connectTimer = Date.now();
34126
+ await pb.write({
34127
+ type: HolePunch.Type.CONNECT,
34128
+ observedAddresses: this.addressManager.getAddresses().map(ma => ma.bytes)
34129
+ }, options);
34130
+ this.log('B receiving connect from %p', relayedConnection.remotePeer);
34131
+ // 4. Upon receiving the Connect, B sends a Sync message
34132
+ const connect = await pb.read(options);
34133
+ if (connect.type !== HolePunch.Type.CONNECT) {
34134
+ this.log('A sent wrong message type');
34135
+ throw new CodeError$2('DCUtR message type was incorrect', ERR_INVALID_MESSAGE);
34136
+ }
34137
+ const multiaddrs = this.getDialableMultiaddrs(connect.observedAddresses);
34138
+ if (multiaddrs.length === 0) {
34139
+ this.log('A did not have any dialable multiaddrs');
34140
+ throw new CodeError$2('DCUtR connect message had no multiaddrs', ERR_INVALID_MESSAGE);
34141
+ }
34142
+ const rtt = Date.now() - connectTimer;
34143
+ this.log('A sending sync, rtt %dms', rtt);
34144
+ await pb.write({
34145
+ type: HolePunch.Type.SYNC,
34146
+ observedAddresses: []
34147
+ }, options);
34148
+ this.log('A waiting for half RTT');
34149
+ // ..and starts a timer for half the RTT measured from the time between
34150
+ // sending the initial Connect and receiving the response
34151
+ await delay(rtt / 2);
34152
+ // TODO: when we have a QUIC transport, the dial step is different - for
34153
+ // now we only have tcp support
34154
+ // https://github.com/libp2p/specs/blob/master/relay/DCUtR.md#the-protocol
34155
+ this.log('B dialing', multiaddrs);
34156
+ // Upon expiry of the timer, B dials the address to A.
34157
+ const conn = await this.connectionManager.openConnection(multiaddrs, {
34158
+ signal: options.signal,
34159
+ priority: DCUTR_DIAL_PRIORITY
34160
+ });
34161
+ this.log('DCUtR to %p succeeded to address %a, closing relayed connection', relayedConnection.remotePeer, conn.remoteAddr);
34162
+ await relayedConnection.close(options);
34163
+ break;
34164
+ }
34165
+ catch (err) {
34166
+ this.log.error('error while attempting DCUtR on attempt %d of %d', i + 1, this.retries, err);
34167
+ stream?.abort(err);
34168
+ if (i === this.retries) {
34169
+ throw err;
34170
+ }
34171
+ }
34172
+ finally {
34173
+ if (stream != null) {
34174
+ await stream.close(options);
34175
+ }
34176
+ }
34177
+ }
34178
+ }
34179
+ /**
34180
+ * This is performed when A has dialed B via a relay but A also has a public
34181
+ * address that B can dial directly
34182
+ */
34183
+ async attemptUnilateralConnectionUpgrade(relayedConnection) {
34184
+ // Upon observing the new connection, the inbound peer (here B) checks the
34185
+ // addresses advertised by A via identify.
34186
+ const peerInfo = await this.peerStore.get(relayedConnection.remotePeer);
34187
+ // If that set includes public addresses, then A may be reachable by a direct
34188
+ // connection, in which case B attempts a unilateral connection upgrade by
34189
+ // initiating a direct connection to A.
34190
+ const publicAddresses = peerInfo.addresses
34191
+ .map(address => {
34192
+ const ma = address.multiaddr;
34193
+ // ensure all multiaddrs have the peer id
34194
+ if (ma.getPeerId() == null) {
34195
+ return ma.encapsulate(`/p2p/${relayedConnection.remotePeer}`);
34196
+ }
34197
+ return ma;
34198
+ })
34199
+ .filter(ma => {
34200
+ return isPublicAndDialable(ma, this.transportManager);
34201
+ });
34202
+ if (publicAddresses.length > 0) {
34203
+ const signal = AbortSignal.timeout(this.timeout);
34204
+ try {
34205
+ this.log('attempting unilateral connection upgrade to %a', publicAddresses);
34206
+ // force-dial the multiaddr(s), otherwise `connectionManager.openConnection`
34207
+ // will return the existing relayed connection
34208
+ const connection = await this.connectionManager.openConnection(publicAddresses, {
34209
+ signal,
34210
+ force: true
34211
+ });
34212
+ if (connection.transient) {
34213
+ throw new Error('Could not open a new, non-transient, connection');
34214
+ }
34215
+ this.log('unilateral connection upgrade to %p succeeded via %a, closing relayed connection', relayedConnection.remotePeer, connection.remoteAddr);
34216
+ await relayedConnection.close({
34217
+ signal
34218
+ });
34219
+ return true;
34220
+ }
34221
+ catch (err) {
34222
+ this.log.error('unilateral connection upgrade to %p on addresses %a failed', relayedConnection.remotePeer, publicAddresses, err);
34223
+ }
34224
+ }
34225
+ else {
34226
+ this.log('peer %p has no public addresses, not attempting unilateral connection upgrade', relayedConnection.remotePeer);
34227
+ }
34228
+ // no public addresses or failed to dial public addresses
34229
+ return false;
34230
+ }
34231
+ /**
34232
+ * Perform the connection upgrade as A
34233
+ *
34234
+ * @see https://github.com/libp2p/specs/blob/master/relay/DCUtR.md#the-protocol
34235
+ */
34236
+ async handleIncomingUpgrade(stream, relayedConnection) {
34237
+ const options = {
34238
+ signal: AbortSignal.timeout(this.timeout)
34239
+ };
34240
+ try {
34241
+ const pb = pbStream(stream, {
34242
+ maxDataLength: MAX_DCUTR_MESSAGE_SIZE
34243
+ }).pb(HolePunch);
34244
+ this.log('A receiving connect');
34245
+ // 3. Upon receiving the Connect, A responds back with a Connect message
34246
+ // containing its observed (and possibly predicted) addresses.
34247
+ const connect = await pb.read(options);
34248
+ if (connect.type !== HolePunch.Type.CONNECT) {
34249
+ this.log('B sent wrong message type');
34250
+ throw new CodeError$2('DCUtR message type was incorrect', ERR_INVALID_MESSAGE);
34251
+ }
34252
+ if (connect.observedAddresses.length === 0) {
34253
+ this.log('B sent no multiaddrs');
34254
+ throw new CodeError$2('DCUtR connect message had no multiaddrs', ERR_INVALID_MESSAGE);
34255
+ }
34256
+ const multiaddrs = this.getDialableMultiaddrs(connect.observedAddresses);
34257
+ if (multiaddrs.length === 0) {
34258
+ this.log('B had no dialable multiaddrs');
34259
+ throw new CodeError$2('DCUtR connect message had no dialable multiaddrs', ERR_INVALID_MESSAGE);
34260
+ }
34261
+ this.log('A sending connect');
34262
+ await pb.write({
34263
+ type: HolePunch.Type.CONNECT,
34264
+ observedAddresses: this.addressManager.getAddresses().map(ma => ma.bytes)
34265
+ });
34266
+ this.log('A receiving sync');
34267
+ const sync = await pb.read(options);
34268
+ if (sync.type !== HolePunch.Type.SYNC) {
34269
+ throw new CodeError$2('DCUtR message type was incorrect', ERR_INVALID_MESSAGE);
34270
+ }
34271
+ // TODO: when we have a QUIC transport, the dial step is different - for
34272
+ // now we only have tcp support
34273
+ // https://github.com/libp2p/specs/blob/master/relay/DCUtR.md#the-protocol
34274
+ // Upon receiving the Sync, A immediately dials the address to B
34275
+ this.log('A dialing', multiaddrs);
34276
+ const connection = await this.connectionManager.openConnection(multiaddrs, {
34277
+ signal: options.signal,
34278
+ priority: DCUTR_DIAL_PRIORITY,
34279
+ force: true
34280
+ });
34281
+ this.log('DCUtR to %p succeeded via %a, closing relayed connection', relayedConnection.remotePeer, connection.remoteAddr);
34282
+ await relayedConnection.close(options);
34283
+ }
34284
+ catch (err) {
34285
+ this.log.error('incoming DCUtR from %p failed', relayedConnection.remotePeer, err);
34286
+ stream.abort(err);
34287
+ }
34288
+ finally {
34289
+ await stream.close(options);
34290
+ }
34291
+ }
34292
+ /**
34293
+ * Takes the `addr` and converts it to a Multiaddr if possible
34294
+ */
34295
+ getDialableMultiaddrs(addrs) {
34296
+ const output = [];
34297
+ for (const addr of addrs) {
34298
+ if (addr == null || addr.length === 0) {
34299
+ continue;
34300
+ }
34301
+ try {
34302
+ const ma = multiaddr(addr);
34303
+ if (!isPublicAndDialable(ma, this.transportManager)) {
34304
+ continue;
34305
+ }
34306
+ output.push(ma);
34307
+ }
34308
+ catch { }
34309
+ }
34310
+ return output;
34311
+ }
34312
+ }
34313
+
34314
+ /**
34315
+ * @packageDocumentation
34316
+ *
34317
+ * Direct Connection Upgrade through Relay (DCUtR) is a protocol that allows two
34318
+ * nodes to connect to each other who would otherwise be prevented doing so due
34319
+ * to being behind NATed connections or firewalls.
34320
+ *
34321
+ * The protocol involves making a relayed connection between the two peers and
34322
+ * using the relay to synchronise connection timings so that they dial each other
34323
+ * at precisely the same moment.
34324
+ *
34325
+ * @example
34326
+ *
34327
+ * ```TypeScript
34328
+ * import { createLibp2p } from 'libp2p'
34329
+ * import { circuitRelayTransport } from '@libp2p/circuit-relay-v2'
34330
+ * import { tcp } from '@libp2p/tcp'
34331
+ * import { identify } from '@libp2p/identify'
34332
+ * import { dcutr } from '@libp2p/dcutr'
34333
+ * import { multiaddr } from '@multiformats/multiaddr'
34334
+ *
34335
+ * const node = await createLibp2p({
34336
+ * transports: [
34337
+ * circuitRelayTransport(),
34338
+ * tcp()
34339
+ * ],
34340
+ * services: {
34341
+ * identify: identify(),
34342
+ * dcutr: dcutr()
34343
+ * }
34344
+ * })
34345
+ *
34346
+ * // QmTarget is a peer that is behind a NAT, supports TCP and has a relay
34347
+ * // reservation
34348
+ * const ma = multiaddr('/ip4/.../p2p/QmRelay/p2p-circuit/p2p/QmTarget')
34349
+ * await node.dial(ma)
34350
+ *
34351
+ * // after a while the connection should automatically get upgraded to a
34352
+ * // direct connection (e.g. non-transient)
34353
+ * while (true) {
34354
+ * const connections = node.getConnections()
34355
+ *
34356
+ * if (connections.find(conn => conn.transient === false)) {
34357
+ * console.info('have direct connection')
34358
+ * break
34359
+ * } else {
34360
+ * console.info('have relayed connection')
34361
+ *
34362
+ * // wait a few seconds to see if it's succeeded yet
34363
+ * await new Promise<void>((resolve) => {
34364
+ * setTimeout(() => resolve(), 5000)
34365
+ * })
34366
+ * }
34367
+ * }
34368
+ * ```
34369
+ */
34370
+ /**
34371
+ * The DCUtR protocol
34372
+ */
34373
+ const multicodec = '/libp2p/dcutr';
34374
+ function dcutr(init = {}) {
34375
+ return (components) => new DefaultDCUtRService(components, init);
34376
+ }
34377
+
34378
+ // Protocol violation errors
34379
+ const ERR_INVALID_FRAME = 'ERR_INVALID_FRAME';
34380
+ const ERR_UNREQUESTED_PING = 'ERR_UNREQUESTED_PING';
34381
+ const ERR_NOT_MATCHING_PING = 'ERR_NOT_MATCHING_PING';
34382
+ const ERR_STREAM_ALREADY_EXISTS = 'ERR_STREAM_ALREADY_EXISTS';
34383
+ const ERR_DECODE_INVALID_VERSION = 'ERR_DECODE_INVALID_VERSION';
34384
+ const ERR_BOTH_CLIENTS = 'ERR_BOTH_CLIENTS';
34385
+ const ERR_RECV_WINDOW_EXCEEDED = 'ERR_RECV_WINDOW_EXCEEDED';
34386
+ const PROTOCOL_ERRORS = new Set([
34387
+ ERR_INVALID_FRAME,
34388
+ ERR_UNREQUESTED_PING,
34389
+ ERR_NOT_MATCHING_PING,
34390
+ ERR_STREAM_ALREADY_EXISTS,
34391
+ ERR_DECODE_INVALID_VERSION,
34392
+ ERR_BOTH_CLIENTS,
34393
+ ERR_RECV_WINDOW_EXCEEDED
34394
+ ]);
34395
+ // local errors
34396
+ const ERR_INVALID_CONFIG = 'ERR_INVALID_CONFIG';
34397
+ const ERR_MUXER_LOCAL_CLOSED = 'ERR_MUXER_LOCAL_CLOSED';
34398
+ const ERR_MUXER_REMOTE_CLOSED = 'ERR_MUXER_REMOTE_CLOSED';
34399
+ const ERR_STREAM_ABORT = 'ERR_STREAM_ABORT';
34400
+ const ERR_MAX_OUTBOUND_STREAMS_EXCEEDED = 'ERROR_MAX_OUTBOUND_STREAMS_EXCEEDED';
34401
+ const ERR_DECODE_IN_PROGRESS = 'ERR_DECODE_IN_PROGRESS';
34402
+ /**
34403
+ * INITIAL_STREAM_WINDOW is the initial stream window size.
34404
+ *
34405
+ * Not an implementation choice, this is defined in the specification
34406
+ */
34407
+ const INITIAL_STREAM_WINDOW = 256 * 1024;
34408
+ /**
34409
+ * Default max stream window
34410
+ */
34411
+ const MAX_STREAM_WINDOW = 16 * 1024 * 1024;
34412
+
34413
+ const defaultConfig = {
34414
+ enableKeepAlive: true,
34415
+ keepAliveInterval: 30000,
34416
+ maxInboundStreams: 1000,
34417
+ maxOutboundStreams: 1000,
34418
+ initialStreamWindowSize: INITIAL_STREAM_WINDOW,
34419
+ maxStreamWindowSize: MAX_STREAM_WINDOW,
34420
+ maxMessageSize: 64 * 1024
34421
+ };
34422
+ function verifyConfig(config) {
34423
+ if (config.keepAliveInterval <= 0) {
34424
+ throw new CodeError$2('keep-alive interval must be positive', ERR_INVALID_CONFIG);
34425
+ }
34426
+ if (config.maxInboundStreams < 0) {
34427
+ throw new CodeError$2('max inbound streams must be larger or equal 0', ERR_INVALID_CONFIG);
34428
+ }
34429
+ if (config.maxOutboundStreams < 0) {
34430
+ throw new CodeError$2('max outbound streams must be larger or equal 0', ERR_INVALID_CONFIG);
34431
+ }
34432
+ if (config.initialStreamWindowSize < INITIAL_STREAM_WINDOW) {
34433
+ throw new CodeError$2('InitialStreamWindowSize must be larger or equal 256 kB', ERR_INVALID_CONFIG);
34434
+ }
34435
+ if (config.maxStreamWindowSize < config.initialStreamWindowSize) {
34436
+ throw new CodeError$2('MaxStreamWindowSize must be larger than the InitialStreamWindowSize', ERR_INVALID_CONFIG);
34437
+ }
34438
+ if (config.maxStreamWindowSize > 2 ** 32 - 1) {
34439
+ throw new CodeError$2('MaxStreamWindowSize must be less than equal MAX_UINT32', ERR_INVALID_CONFIG);
34440
+ }
34441
+ if (config.maxMessageSize < 1024) {
34442
+ throw new CodeError$2('MaxMessageSize must be greater than a kilobyte', ERR_INVALID_CONFIG);
34443
+ }
34444
+ }
34445
+
34446
+ var FrameType;
34447
+ (function (FrameType) {
34448
+ /** Used to transmit data. May transmit zero length payloads depending on the flags. */
34449
+ FrameType[FrameType["Data"] = 0] = "Data";
34450
+ /** Used to updated the senders receive window size. This is used to implement per-session flow control. */
34451
+ FrameType[FrameType["WindowUpdate"] = 1] = "WindowUpdate";
34452
+ /** Used to measure RTT. It can also be used to heart-beat and do keep-alives over TCP. */
34453
+ FrameType[FrameType["Ping"] = 2] = "Ping";
34454
+ /** Used to close a session. */
34455
+ FrameType[FrameType["GoAway"] = 3] = "GoAway";
34456
+ })(FrameType || (FrameType = {}));
34457
+ var Flag;
34458
+ (function (Flag) {
34459
+ /** Signals the start of a new stream. May be sent with a data or window update message. Also sent with a ping to indicate outbound. */
34460
+ Flag[Flag["SYN"] = 1] = "SYN";
34461
+ /** Acknowledges the start of a new stream. May be sent with a data or window update message. Also sent with a ping to indicate response. */
34462
+ Flag[Flag["ACK"] = 2] = "ACK";
34463
+ /** Performs a half-close of a stream. May be sent with a data message or window update. */
34464
+ Flag[Flag["FIN"] = 4] = "FIN";
34465
+ /** Reset a stream immediately. May be sent with a data or window update message. */
34466
+ Flag[Flag["RST"] = 8] = "RST";
34467
+ })(Flag || (Flag = {}));
34468
+ Object.values(Flag).filter((x) => typeof x !== 'string');
34469
+ const YAMUX_VERSION = 0;
34470
+ var GoAwayCode;
34471
+ (function (GoAwayCode) {
34472
+ GoAwayCode[GoAwayCode["NormalTermination"] = 0] = "NormalTermination";
34473
+ GoAwayCode[GoAwayCode["ProtocolError"] = 1] = "ProtocolError";
34474
+ GoAwayCode[GoAwayCode["InternalError"] = 2] = "InternalError";
34475
+ })(GoAwayCode || (GoAwayCode = {}));
34476
+ const HEADER_LENGTH = 12;
34477
+
34478
+ // used to bitshift in decoding
34479
+ // native bitshift can overflow into a negative number, so we bitshift by multiplying by a power of 2
34480
+ const twoPow24 = 2 ** 24;
34481
+ /**
34482
+ * Decode a header from the front of a buffer
34483
+ *
34484
+ * @param data - Assumed to have enough bytes for a header
34485
+ */
34486
+ function decodeHeader(data) {
34487
+ if (data[0] !== YAMUX_VERSION) {
34488
+ throw new CodeError$2('Invalid frame version', ERR_DECODE_INVALID_VERSION);
34489
+ }
34490
+ return {
34491
+ type: data[1],
34492
+ flag: (data[2] << 8) + data[3],
34493
+ streamID: (data[4] * twoPow24) + (data[5] << 16) + (data[6] << 8) + data[7],
34494
+ length: (data[8] * twoPow24) + (data[9] << 16) + (data[10] << 8) + data[11]
34495
+ };
34496
+ }
34497
+ /**
34498
+ * Decodes yamux frames from a source
34499
+ */
34500
+ class Decoder {
34501
+ source;
34502
+ /** Buffer for in-progress frames */
34503
+ buffer;
34504
+ /** Used to sanity check against decoding while in an inconsistent state */
34505
+ frameInProgress;
34506
+ constructor(source) {
34507
+ // Normally, when entering a for-await loop with an iterable/async iterable, the only ways to exit the loop are:
34508
+ // 1. exhaust the iterable
34509
+ // 2. throw an error - slow, undesirable if there's not actually an error
34510
+ // 3. break or return - calls the iterable's `return` method, finalizing the iterable, no more iteration possible
34511
+ //
34512
+ // In this case, we want to enter (and exit) a for-await loop per chunked data frame and continue processing the iterable.
34513
+ // To do this, we strip the `return` method from the iterator and can now `break` early and continue iterating.
34514
+ // Exiting the main for-await is still possible via 1. and 2.
34515
+ this.source = returnlessSource(source);
34516
+ this.buffer = new Uint8ArrayList();
34517
+ this.frameInProgress = false;
34518
+ }
34519
+ /**
34520
+ * Emits frames from the decoder source.
34521
+ *
34522
+ * Note: If `readData` is emitted, it _must_ be called before the next iteration
34523
+ * Otherwise an error is thrown
34524
+ */
34525
+ async *emitFrames() {
34526
+ for await (const chunk of this.source) {
34527
+ this.buffer.append(chunk);
34528
+ // Loop to consume as many bytes from the buffer as possible
34529
+ // Eg: when a single chunk contains several frames
34530
+ while (true) {
34531
+ const header = this.readHeader();
34532
+ if (header === undefined) {
34533
+ break;
34534
+ }
34535
+ const { type, length } = header;
34536
+ if (type === FrameType.Data) {
34537
+ // This is a data frame, the frame body must still be read
34538
+ // `readData` must be called before the next iteration here
34539
+ this.frameInProgress = true;
34540
+ yield {
34541
+ header,
34542
+ readData: this.readBytes.bind(this, length)
34543
+ };
34544
+ }
34545
+ else {
34546
+ yield { header };
34547
+ }
34548
+ }
34549
+ }
34550
+ }
34551
+ readHeader() {
34552
+ // Sanity check to ensure a header isn't read when another frame is partially decoded
34553
+ // In practice this shouldn't happen
34554
+ if (this.frameInProgress) {
34555
+ throw new CodeError$2('decoding frame already in progress', ERR_DECODE_IN_PROGRESS);
34556
+ }
34557
+ if (this.buffer.length < HEADER_LENGTH) {
34558
+ // not enough data yet
34559
+ return;
34560
+ }
34561
+ const header = decodeHeader(this.buffer.subarray(0, HEADER_LENGTH));
34562
+ this.buffer.consume(HEADER_LENGTH);
34563
+ return header;
34564
+ }
34565
+ async readBytes(length) {
34566
+ if (this.buffer.length < length) {
34567
+ for await (const chunk of this.source) {
34568
+ this.buffer.append(chunk);
34569
+ if (this.buffer.length >= length) {
34570
+ // see note above, the iterator is not `return`ed here
34571
+ break;
34572
+ }
34573
+ }
34574
+ }
34575
+ const out = this.buffer.sublist(0, length);
34576
+ this.buffer.consume(length);
34577
+ // The next frame can now be decoded
34578
+ this.frameInProgress = false;
34579
+ return out;
34580
+ }
34581
+ }
34582
+ /**
34583
+ * Strip the `return` method from a `Source`
34584
+ */
34585
+ function returnlessSource(source) {
34586
+ if (source[Symbol.iterator] !== undefined) {
34587
+ const iterator = source[Symbol.iterator]();
34588
+ iterator.return = undefined;
34589
+ return {
34590
+ [Symbol.iterator]() { return iterator; }
34591
+ };
34592
+ }
34593
+ else if (source[Symbol.asyncIterator] !== undefined) {
34594
+ const iterator = source[Symbol.asyncIterator]();
34595
+ iterator.return = undefined;
34596
+ return {
34597
+ [Symbol.asyncIterator]() { return iterator; }
34598
+ };
34599
+ }
34600
+ else {
34601
+ throw new Error('a source must be either an iterable or an async iterable');
34602
+ }
34603
+ }
34604
+
34605
+ function encodeHeader(header) {
34606
+ const frame = new Uint8Array(HEADER_LENGTH);
34607
+ // always assume version 0
34608
+ // frameView.setUint8(0, header.version)
34609
+ frame[1] = header.type;
34610
+ frame[2] = header.flag >>> 8;
34611
+ frame[3] = header.flag;
34612
+ frame[4] = header.streamID >>> 24;
34613
+ frame[5] = header.streamID >>> 16;
34614
+ frame[6] = header.streamID >>> 8;
34615
+ frame[7] = header.streamID;
34616
+ frame[8] = header.length >>> 24;
34617
+ frame[9] = header.length >>> 16;
34618
+ frame[10] = header.length >>> 8;
34619
+ frame[11] = header.length;
34620
+ return frame;
34621
+ }
34622
+
34623
+ /**
34624
+ * @packageDocumentation
34625
+ *
34626
+ * Calls a function for each value in an (async)iterable.
34627
+ *
34628
+ * The function can be sync or async.
34629
+ *
34630
+ * Async functions can be awaited on so may slow down processing of the (async)iterable.
34631
+ *
34632
+ * @example
34633
+ *
34634
+ * ```javascript
34635
+ * import each from 'it-foreach'
34636
+ * import drain from 'it-drain'
34637
+ *
34638
+ * // This can also be an iterator, generator, etc
34639
+ * const values = [0, 1, 2, 3, 4]
34640
+ *
34641
+ * // prints [0, 0], [1, 1], [2, 2], [3, 3], [4, 4]
34642
+ * const arr = drain(
34643
+ * each(values, console.info)
34644
+ * )
34645
+ * ```
34646
+ *
34647
+ * Async sources and callbacks must be awaited:
34648
+ *
34649
+ * ```javascript
34650
+ * import each from 'it-foreach'
34651
+ * import drain from 'it-drain'
34652
+ *
34653
+ * const values = async function * () {
34654
+ * yield * [0, 1, 2, 3, 4]
34655
+ * }
34656
+ *
34657
+ * // prints [0, 0], [1, 1], [2, 2], [3, 3], [4, 4]
34658
+ * const arr = await drain(
34659
+ * each(values(), console.info)
34660
+ * )
34661
+ * ```
34662
+ */
34663
+ function isAsyncIterable$1(thing) {
34664
+ return thing[Symbol.asyncIterator] != null;
34665
+ }
34666
+ function isPromise$1(thing) {
34667
+ return thing?.then != null;
34668
+ }
34669
+ function forEach(source, fn) {
34670
+ let index = 0;
34671
+ if (isAsyncIterable$1(source)) {
34672
+ return (async function* () {
34673
+ for await (const val of source) {
34674
+ const res = fn(val, index++);
34675
+ if (isPromise$1(res)) {
34676
+ await res;
34677
+ }
34678
+ yield val;
34679
+ }
34680
+ })();
34681
+ }
34682
+ // if fn function returns a promise we have to return an async generator
34683
+ const peekable$1 = peekable(source);
34684
+ const { value, done } = peekable$1.next();
34685
+ if (done === true) {
34686
+ return (function* () { }());
34687
+ }
34688
+ const res = fn(value, index++);
34689
+ if (typeof res?.then === 'function') {
34690
+ return (async function* () {
34691
+ yield value;
34692
+ for await (const val of peekable$1) {
34693
+ const res = fn(val, index++);
34694
+ if (isPromise$1(res)) {
34695
+ await res;
34696
+ }
34697
+ yield val;
34698
+ }
34699
+ })();
34700
+ }
34701
+ const func = fn;
34702
+ return (function* () {
34703
+ yield value;
34704
+ for (const val of peekable$1) {
34705
+ func(val, index++);
34706
+ yield val;
34707
+ }
34708
+ })();
34709
+ }
34710
+
34711
+ var StreamState;
34712
+ (function (StreamState) {
34713
+ StreamState[StreamState["Init"] = 0] = "Init";
34714
+ StreamState[StreamState["SYNSent"] = 1] = "SYNSent";
34715
+ StreamState[StreamState["SYNReceived"] = 2] = "SYNReceived";
34716
+ StreamState[StreamState["Established"] = 3] = "Established";
34717
+ StreamState[StreamState["Finished"] = 4] = "Finished";
34718
+ })(StreamState || (StreamState = {}));
34719
+ /** YamuxStream is used to represent a logical stream within a session */
34720
+ class YamuxStream extends AbstractStream {
34721
+ name;
34722
+ state;
34723
+ config;
34724
+ _id;
34725
+ /** The number of available bytes to send */
34726
+ sendWindowCapacity;
34727
+ /** Callback to notify that the sendWindowCapacity has been updated */
34728
+ sendWindowCapacityUpdate;
34729
+ /** The number of bytes available to receive in a full window */
34730
+ recvWindow;
34731
+ /** The number of available bytes to receive */
34732
+ recvWindowCapacity;
34733
+ /**
34734
+ * An 'epoch' is the time it takes to process and read data
34735
+ *
34736
+ * Used in conjunction with RTT to determine whether to increase the recvWindow
34737
+ */
34738
+ epochStart;
34739
+ getRTT;
34740
+ sendFrame;
34741
+ constructor(init) {
34742
+ super({
34743
+ ...init,
34744
+ onEnd: (err) => {
34745
+ this.state = StreamState.Finished;
34746
+ init.onEnd?.(err);
34747
+ }
34748
+ });
34749
+ this.config = init.config;
34750
+ this._id = parseInt(init.id, 10);
34751
+ this.name = init.name;
34752
+ this.state = init.state;
34753
+ this.sendWindowCapacity = INITIAL_STREAM_WINDOW;
34754
+ this.recvWindow = this.config.initialStreamWindowSize;
34755
+ this.recvWindowCapacity = this.recvWindow;
34756
+ this.epochStart = Date.now();
34757
+ this.getRTT = init.getRTT;
34758
+ this.sendFrame = init.sendFrame;
34759
+ this.source = forEach(this.source, () => {
34760
+ this.sendWindowUpdate();
34761
+ });
34762
+ }
34763
+ /**
34764
+ * Send a message to the remote muxer informing them a new stream is being
34765
+ * opened.
34766
+ *
34767
+ * This is a noop for Yamux because the first window update is sent when
34768
+ * .newStream is called on the muxer which opens the stream on the remote.
34769
+ */
34770
+ async sendNewStream() {
34771
+ }
34772
+ /**
34773
+ * Send a data message to the remote muxer
34774
+ */
34775
+ async sendData(buf, options = {}) {
34776
+ buf = buf.sublist();
34777
+ // send in chunks, waiting for window updates
34778
+ while (buf.byteLength !== 0) {
34779
+ // wait for the send window to refill
34780
+ if (this.sendWindowCapacity === 0) {
34781
+ this.log?.trace('wait for send window capacity, status %s', this.status);
34782
+ await this.waitForSendWindowCapacity(options);
34783
+ // check we didn't close while waiting for send window capacity
34784
+ if (this.status === 'closed' || this.status === 'aborted' || this.status === 'reset') {
34785
+ this.log?.trace('%s while waiting for send window capacity', this.status);
34786
+ return;
34787
+ }
34788
+ }
34789
+ // send as much as we can
34790
+ const toSend = Math.min(this.sendWindowCapacity, this.config.maxMessageSize - HEADER_LENGTH, buf.length);
34791
+ const flags = this.getSendFlags();
34792
+ this.sendFrame({
34793
+ type: FrameType.Data,
34794
+ flag: flags,
34795
+ streamID: this._id,
34796
+ length: toSend
34797
+ }, buf.sublist(0, toSend));
34798
+ this.sendWindowCapacity -= toSend;
34799
+ buf.consume(toSend);
34800
+ }
34801
+ }
34802
+ /**
34803
+ * Send a reset message to the remote muxer
34804
+ */
34805
+ async sendReset() {
34806
+ this.sendFrame({
34807
+ type: FrameType.WindowUpdate,
34808
+ flag: Flag.RST,
34809
+ streamID: this._id,
34810
+ length: 0
34811
+ });
34812
+ }
34813
+ /**
34814
+ * Send a message to the remote muxer, informing them no more data messages
34815
+ * will be sent by this end of the stream
34816
+ */
34817
+ async sendCloseWrite() {
34818
+ const flags = this.getSendFlags() | Flag.FIN;
34819
+ this.sendFrame({
34820
+ type: FrameType.WindowUpdate,
34821
+ flag: flags,
34822
+ streamID: this._id,
34823
+ length: 0
34824
+ });
34825
+ }
34826
+ /**
34827
+ * Send a message to the remote muxer, informing them no more data messages
34828
+ * will be read by this end of the stream
34829
+ */
34830
+ async sendCloseRead() {
34831
+ }
34832
+ /**
34833
+ * Wait for the send window to be non-zero
34834
+ *
34835
+ * Will throw with ERR_STREAM_ABORT if the stream gets aborted
34836
+ */
34837
+ async waitForSendWindowCapacity(options = {}) {
34838
+ if (this.sendWindowCapacity > 0) {
34839
+ return;
34840
+ }
34841
+ let resolve;
34842
+ let reject;
34843
+ const abort = () => {
34844
+ if (this.status === 'open' || this.status === 'closing') {
34845
+ reject(new CodeError$2('stream aborted', ERR_STREAM_ABORT));
34846
+ }
34847
+ else {
34848
+ // the stream was closed already, ignore the failure to send
34849
+ resolve();
34850
+ }
34851
+ };
34852
+ options.signal?.addEventListener('abort', abort);
34853
+ try {
34854
+ await new Promise((_resolve, _reject) => {
34855
+ this.sendWindowCapacityUpdate = () => {
32548
34856
  _resolve();
32549
34857
  };
32550
34858
  reject = _reject;
@@ -33558,63 +35866,6 @@
33558
35866
  return (components) => new PubSubPeerDiscovery(components, init);
33559
35867
  }
33560
35868
 
33561
- /**
33562
- * @packageDocumentation
33563
- *
33564
- * This module makes it easy to send and receive length-prefixed Protobuf encoded
33565
- * messages over streams.
33566
- *
33567
- * @example
33568
- *
33569
- * ```typescript
33570
- * import { pbStream } from 'it-protobuf-stream'
33571
- * import { MessageType } from './src/my-message-type.js'
33572
- *
33573
- * // RequestType and ResponseType have been generate from `.proto` files and have
33574
- * // `.encode` and `.decode` methods for serialization/deserialization
33575
- *
33576
- * const stream = pbStream(duplex)
33577
- *
33578
- * // write a message to the stream
33579
- * stream.write({
33580
- * foo: 'bar'
33581
- * }, MessageType)
33582
- *
33583
- * // read a message from the stream
33584
- * const res = await stream.read(MessageType)
33585
- * ```
33586
- */
33587
- function pbStream(duplex, opts) {
33588
- const lp = lpStream(duplex, opts);
33589
- const W = {
33590
- read: async (proto, options) => {
33591
- // readLP, decode
33592
- const value = await lp.read(options);
33593
- return proto.decode(value);
33594
- },
33595
- write: async (message, proto, options) => {
33596
- // encode, writeLP
33597
- await lp.write(proto.encode(message), options);
33598
- },
33599
- writeV: async (messages, proto, options) => {
33600
- // encode, writeLP
33601
- await lp.writeV(messages.map(message => proto.encode(message)), options);
33602
- },
33603
- pb: (proto) => {
33604
- return {
33605
- read: async (options) => W.read(proto, options),
33606
- write: async (d, options) => W.write(d, proto, options),
33607
- writeV: async (d, options) => W.writeV(d, proto, options),
33608
- unwrap: () => W
33609
- };
33610
- },
33611
- unwrap: () => {
33612
- return lp.unwrap();
33613
- }
33614
- };
33615
- return W;
33616
- }
33617
-
33618
35869
  /**
33619
35870
  * Multicodec code
33620
35871
  */
@@ -43902,121 +46153,6 @@
43902
46153
  }
43903
46154
  }
43904
46155
 
43905
- const normalizeEmitter = emitter => {
43906
- const addListener = emitter.addEventListener || emitter.on || emitter.addListener;
43907
- const removeListener = emitter.removeEventListener || emitter.off || emitter.removeListener;
43908
-
43909
- if (!addListener || !removeListener) {
43910
- throw new TypeError('Emitter is not compatible');
43911
- }
43912
-
43913
- return {
43914
- addListener: addListener.bind(emitter),
43915
- removeListener: removeListener.bind(emitter),
43916
- };
43917
- };
43918
-
43919
- function pEventMultiple(emitter, event, options) {
43920
- let cancel;
43921
- const returnValue = new Promise((resolve, reject) => {
43922
- options = {
43923
- rejectionEvents: ['error'],
43924
- multiArgs: false,
43925
- resolveImmediately: false,
43926
- ...options,
43927
- };
43928
-
43929
- if (!(options.count >= 0 && (options.count === Number.POSITIVE_INFINITY || Number.isInteger(options.count)))) {
43930
- throw new TypeError('The `count` option should be at least 0 or more');
43931
- }
43932
-
43933
- options.signal?.throwIfAborted();
43934
-
43935
- // Allow multiple events
43936
- const events = [event].flat();
43937
-
43938
- const items = [];
43939
- const {addListener, removeListener} = normalizeEmitter(emitter);
43940
-
43941
- const onItem = (...arguments_) => {
43942
- const value = options.multiArgs ? arguments_ : arguments_[0];
43943
-
43944
- // eslint-disable-next-line unicorn/no-array-callback-reference
43945
- if (options.filter && !options.filter(value)) {
43946
- return;
43947
- }
43948
-
43949
- items.push(value);
43950
-
43951
- if (options.count === items.length) {
43952
- cancel();
43953
- resolve(items);
43954
- }
43955
- };
43956
-
43957
- const rejectHandler = error => {
43958
- cancel();
43959
- reject(error);
43960
- };
43961
-
43962
- cancel = () => {
43963
- for (const event of events) {
43964
- removeListener(event, onItem);
43965
- }
43966
-
43967
- for (const rejectionEvent of options.rejectionEvents) {
43968
- removeListener(rejectionEvent, rejectHandler);
43969
- }
43970
- };
43971
-
43972
- for (const event of events) {
43973
- addListener(event, onItem);
43974
- }
43975
-
43976
- for (const rejectionEvent of options.rejectionEvents) {
43977
- addListener(rejectionEvent, rejectHandler);
43978
- }
43979
-
43980
- if (options.signal) {
43981
- options.signal.addEventListener('abort', () => {
43982
- rejectHandler(options.signal.reason);
43983
- }, {once: true});
43984
- }
43985
-
43986
- if (options.resolveImmediately) {
43987
- resolve(items);
43988
- }
43989
- });
43990
-
43991
- returnValue.cancel = cancel;
43992
-
43993
- if (typeof options.timeout === 'number') {
43994
- const timeout = pTimeout(returnValue, {milliseconds: options.timeout});
43995
- timeout.cancel = cancel;
43996
- return timeout;
43997
- }
43998
-
43999
- return returnValue;
44000
- }
44001
-
44002
- function pEvent(emitter, event, options) {
44003
- if (typeof options === 'function') {
44004
- options = {filter: options};
44005
- }
44006
-
44007
- options = {
44008
- ...options,
44009
- count: 1,
44010
- resolveImmediately: false,
44011
- };
44012
-
44013
- const arrayPromise = pEventMultiple(emitter, event, options);
44014
- const promise = arrayPromise.then(array => array[0]);
44015
- promise.cancel = arrayPromise.cancel;
44016
-
44017
- return promise;
44018
- }
44019
-
44020
46156
  /**
44021
46157
  * Receives notifications of new peers joining the network that support the DHT protocol
44022
46158
  */
@@ -47108,6 +49244,10 @@
47108
49244
 
47109
49245
  this.id = this.#libp2p.peerId.toString();
47110
49246
 
49247
+ for(const topic of CONFIG_PUPSUB_PEER_DATA){
49248
+ this.#libp2p.services.pubsub.subscribe(topic);
49249
+ }
49250
+
47111
49251
 
47112
49252
  //listen to peer connect event
47113
49253
  this.#libp2p.addEventListener("peer:connect",async (evt) => {
@@ -47174,9 +49314,9 @@
47174
49314
  return
47175
49315
  }
47176
49316
  {
47177
- const topic = event.detail.topic;
49317
+ event.detail.topic;
47178
49318
  const senderPeerId = event.detail.from.toString();
47179
- if(CONFIG_PUBSUB_PEER_DISCOVERY.includes(topic)){
49319
+
47180
49320
  try{
47181
49321
 
47182
49322
  //if it is webpeer
@@ -47212,15 +49352,17 @@
47212
49352
  const addrs = this.#discoveredPeers.get(senderPeerId);
47213
49353
  let mddrs = [];
47214
49354
  for(const addr of addrs){
49355
+ if(!addr.includes('webrtc'))continue
47215
49356
  const mddr = multiaddr(addr);
47216
49357
  mddrs.push(mddr);
47217
49358
  }
47218
49359
  this.#dialMultiaddress(mddrs);
47219
49360
  }
47220
- else {
49361
+ else if(this.#connectedPeers.has(senderPeerId)){
47221
49362
  const addrs = this.#connectedPeers.get(senderPeerId).addrs;
47222
49363
  let mddrs = [];
47223
49364
  for(const addr of addrs){
49365
+ if(!addr.includes('webrtc'))continue
47224
49366
  const mddr = multiaddr(addr);
47225
49367
  mddrs.push(mddr);
47226
49368
  }
@@ -47269,8 +49411,10 @@
47269
49411
 
47270
49412
  //update room members
47271
49413
  if(!this.#rooms[room].members.includes(id)){
47272
- this.#rooms[room].members.push(id);
47273
- this.#rooms[room].onMembers(this.#rooms[room].members);
49414
+ if(this.#connectedPeers.has(id)){
49415
+ this.#rooms[room].members.push(id);
49416
+ this.#rooms[room].onMembers(this.#rooms[room].members);
49417
+ }
47274
49418
  }
47275
49419
 
47276
49420
  //inbound message
@@ -47289,8 +49433,10 @@
47289
49433
  for(const room of Object.keys(this.#rooms)){
47290
49434
  //update room members
47291
49435
  if(!this.#rooms[room].members.includes(id)){
47292
- this.#rooms[room].members.push(id);
47293
- this.#rooms[room].onMembers(this.#rooms[room].members);
49436
+ if(this.#connectedPeers.has(id)){
49437
+ this.#rooms[room].members.push(id);
49438
+ this.#rooms[room].onMembers(this.#rooms[room].members);
49439
+ }
47294
49440
  }
47295
49441
  }
47296
49442
  }
@@ -47299,7 +49445,7 @@
47299
49445
 
47300
49446
  //repply announce with ping
47301
49447
  if(signal == 'announce'){
47302
- setTimeout(()=>{this.#ping('yes');},1000);
49448
+ setTimeout(()=>{this.#ping('');},1000);
47303
49449
  //console.log('rooms',rooms)
47304
49450
  }
47305
49451
 
@@ -47312,12 +49458,7 @@
47312
49458
 
47313
49459
  }catch(err){
47314
49460
  }
47315
- }else {
47316
- const json = JSON.parse(topic);
47317
- const room = json.room;
47318
- const message = new TextDecoder().decode(event.detail.data);
47319
- this.#rooms[room].onMessage(message);
47320
- }
49461
+
47321
49462
  }
47322
49463
 
47323
49464
  });
@@ -47358,6 +49499,7 @@
47358
49499
  if(!this.#connections.has(id)){
47359
49500
  let mddrs = [];
47360
49501
  for(const addr of addrs){
49502
+ if(!addr.includes('webrtc'))continue
47361
49503
  const mddr = multiaddr(addr);
47362
49504
  mddrs.push(mddr);
47363
49505
  }
@@ -47445,7 +49587,7 @@
47445
49587
  const mddrs = [];
47446
49588
  peer.addresses.forEach((addr)=>{
47447
49589
  const maddr = addr.multiaddr.toString()+'/p2p/'+id;
47448
- if(maddr.includes('webtransport') && maddr.includes('certhash')){
49590
+ if(maddr.includes('webtransport') && maddr.includes('certhash') && maddr.includes('webrtc')){
47449
49591
  mddrs.push(maddr);
47450
49592
  }
47451
49593
  });
@@ -47607,7 +49749,7 @@
47607
49749
  //join room version 1 user pupsub via pupsub peer discovery
47608
49750
  {
47609
49751
 
47610
- const topics = CONFIG_PUBSUB_PEER_DISCOVERY;
49752
+ const topics = CONFIG_PUPSUB_PEER_DATA;
47611
49753
 
47612
49754
  this.#rooms[room] = {
47613
49755
  onMessage : () => {},
@@ -47661,6 +49803,7 @@
47661
49803
  else {
47662
49804
  this.status = 'unconnected';
47663
49805
  }
49806
+ this.#ping();
47664
49807
  }
47665
49808
 
47666
49809
  async #registerProtocol(){
@@ -47753,7 +49896,7 @@
47753
49896
  await this.#libp2p.handle(CONFIG_PROTOCOL, handler, {
47754
49897
  maxInboundStreams: 50,
47755
49898
  maxOutboundStreams: 50,
47756
- runOnTransientConnection:true
49899
+ runOnTransientConnection:false
47757
49900
  });
47758
49901
 
47759
49902
  await this.#libp2p.register(CONFIG_PROTOCOL, {
@@ -47800,7 +49943,7 @@
47800
49943
 
47801
49944
  try{
47802
49945
 
47803
- const stream = await this.#libp2p.dialProtocol(mddr, CONFIG_PROTOCOL,{runOnTransientConnection:true});
49946
+ const stream = await this.#libp2p.dialProtocol(mddr, CONFIG_PROTOCOL,{runOnTransientConnection:false});
47804
49947
 
47805
49948
  const output = await pipe(
47806
49949
  message,
@@ -47817,7 +49960,7 @@
47817
49960
  return string
47818
49961
  }
47819
49962
  );
47820
-
49963
+ //console.log(output)
47821
49964
  const json = JSON.parse(output);
47822
49965
  if(json.protocol == CONFIG_PROTOCOL){
47823
49966
  const address = [addr];
@@ -48021,7 +50164,7 @@
48021
50164
 
48022
50165
  //announce and ping via pupsub peer discovery
48023
50166
  async #announce(){
48024
- const topics = CONFIG_PUBSUB_PEER_DISCOVERY;
50167
+ const topics = CONFIG_PUPSUB_PEER_DATA;
48025
50168
  const data = JSON.stringify({prefix:CONFIG_PREFIX,signal:'announce',id:this.#libp2p.peerId.toString(),address:this.address,rooms:this.#rooms});
48026
50169
  const peer = {
48027
50170
  publicKey: this.#libp2p.peerId.publicKey,
@@ -48033,7 +50176,7 @@
48033
50176
  }
48034
50177
  }
48035
50178
  async #ping(){
48036
- const topics = CONFIG_PUBSUB_PEER_DISCOVERY;
50179
+ const topics = CONFIG_PUPSUB_PEER_DATA;
48037
50180
  const data = JSON.stringify({prefix:CONFIG_PREFIX,signal:'ping',id:this.#libp2p.peerId.toString(),address:this.address,rooms:this.#rooms});
48038
50181
  const peer = {
48039
50182
  publicKey: this.#libp2p.peerId.publicKey,
@@ -48573,11 +50716,13 @@
48573
50716
  const libp2p = await createLibp2p({
48574
50717
  addresses: {
48575
50718
  listen: [
50719
+ '/webrtc'
48576
50720
  ],
48577
50721
  },
48578
50722
  transports:[
48579
50723
  webTransport(),
48580
50724
  webSockets(),
50725
+ webRTC(),
48581
50726
  circuitRelayTransport({
48582
50727
  discoverRelays: CONFIG_DISCOVER_RELAYS,
48583
50728
  reservationConcurrency: 1,
@@ -48642,7 +50787,7 @@
48642
50787
  allowPublishToZeroTopicPeers: true,
48643
50788
  msgIdFn: msgIdFnStrictNoSign$1,
48644
50789
  ignoreDuplicatePublishError: true,
48645
- runOnTransientConnection:true,
50790
+ runOnTransientConnection:false,
48646
50791
  }),
48647
50792
  identify: identify(),
48648
50793
  identifyPush: identifyPush(),
@@ -48651,7 +50796,7 @@
48651
50796
  peerInfoMapper: removePrivateAddressesMapper,
48652
50797
  clientMode: false
48653
50798
  }),
48654
-
50799
+ dcutr: dcutr()
48655
50800
  },
48656
50801
  peerStore: {
48657
50802
  persistence: true,