@waku/core 0.0.18 → 0.0.20

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.
Files changed (37) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/bundle/index.js +548 -109
  3. package/bundle/lib/message/version_0.js +1 -1
  4. package/bundle/{version_0-c6b47311.js → version_0-9c941081.js} +402 -23
  5. package/dist/index.d.ts +4 -2
  6. package/dist/index.js +4 -2
  7. package/dist/index.js.map +1 -1
  8. package/dist/lib/connection_manager.d.ts +8 -0
  9. package/dist/lib/connection_manager.js +74 -6
  10. package/dist/lib/connection_manager.js.map +1 -1
  11. package/dist/lib/filter/{filter_rpc.d.ts → v1/filter_rpc.d.ts} +1 -3
  12. package/dist/lib/filter/v1/filter_rpc.js.map +1 -0
  13. package/dist/lib/filter/{index.d.ts → v1/index.d.ts} +0 -2
  14. package/dist/lib/filter/{index.js → v1/index.js} +3 -3
  15. package/dist/lib/filter/v1/index.js.map +1 -0
  16. package/dist/lib/filter/v2/filter_rpc.d.ts +40 -0
  17. package/dist/lib/filter/v2/filter_rpc.js +109 -0
  18. package/dist/lib/filter/v2/filter_rpc.js.map +1 -0
  19. package/dist/lib/filter/v2/index.d.ts +3 -0
  20. package/dist/lib/filter/v2/index.js +235 -0
  21. package/dist/lib/filter/v2/index.js.map +1 -0
  22. package/dist/lib/wait_for_remote_peer.d.ts +1 -1
  23. package/dist/lib/wait_for_remote_peer.js +1 -1
  24. package/dist/lib/waku.d.ts +5 -5
  25. package/dist/lib/waku.js.map +1 -1
  26. package/package.json +9 -10
  27. package/src/index.ts +5 -2
  28. package/src/lib/connection_manager.ts +96 -8
  29. package/src/lib/filter/{filter_rpc.ts → v1/filter_rpc.ts} +1 -4
  30. package/src/lib/filter/{index.ts → v1/index.ts} +5 -6
  31. package/src/lib/filter/v2/filter_rpc.ts +131 -0
  32. package/src/lib/filter/v2/index.ts +413 -0
  33. package/src/lib/wait_for_remote_peer.ts +1 -1
  34. package/src/lib/waku.ts +5 -7
  35. package/dist/lib/filter/filter_rpc.js.map +0 -1
  36. package/dist/lib/filter/index.js.map +0 -1
  37. /package/dist/lib/filter/{filter_rpc.js → v1/filter_rpc.js} +0 -0
package/bundle/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { g as getDefaultExportFromCjs, d as debug } from './browser-bde977a3.js';
2
- import { c as createEncoder, v as version_0, F as FilterRpc$1, P as PushRpc$1, a as PushResponse, H as HistoryRpc$1, b as PagingInfo, d as HistoryResponse } from './version_0-c6b47311.js';
3
- export { e as createDecoder } from './version_0-c6b47311.js';
2
+ import { c as createEncoder, v as version_0, F as FilterRpc$1, M as MessagePush, a as FilterSubscribeRequest, b as FilterSubscribeResponse$1, P as PushRpc$1, d as PushResponse, H as HistoryRpc$1, e as PagingInfo, f as HistoryResponse } from './version_0-9c941081.js';
3
+ export { g as createDecoder } from './version_0-9c941081.js';
4
4
  import { BaseProtocol } from './lib/base_protocol.js';
5
5
 
6
6
  const symbol$2 = Symbol.for('@libp2p/peer-id');
@@ -2975,7 +2975,7 @@ var Tags;
2975
2975
  })(Tags || (Tags = {}));
2976
2976
 
2977
2977
  const RelayPingContentTopic = "/relay-ping/1/ping/null";
2978
- const log$6 = debug("waku:keep-alive");
2978
+ const log$7 = debug("waku:keep-alive");
2979
2979
  class KeepAliveManager {
2980
2980
  pingKeepAliveTimers;
2981
2981
  relayKeepAliveTimers;
@@ -2995,7 +2995,7 @@ class KeepAliveManager {
2995
2995
  if (pingPeriodSecs !== 0) {
2996
2996
  const interval = setInterval(() => {
2997
2997
  libp2pPing(peerId).catch((e) => {
2998
- log$6(`Ping failed (${peerIdStr})`, e);
2998
+ log$7(`Ping failed (${peerIdStr})`, e);
2999
2999
  });
3000
3000
  }, pingPeriodSecs * 1000);
3001
3001
  this.pingKeepAliveTimers.set(peerIdStr, interval);
@@ -3007,10 +3007,10 @@ class KeepAliveManager {
3007
3007
  ephemeral: true,
3008
3008
  });
3009
3009
  const interval = setInterval(() => {
3010
- log$6("Sending Waku Relay ping message");
3010
+ log$7("Sending Waku Relay ping message");
3011
3011
  relay
3012
3012
  .send(encoder, { payload: new Uint8Array([1]) })
3013
- .catch((e) => log$6("Failed to send relay ping", e));
3013
+ .catch((e) => log$7("Failed to send relay ping", e));
3014
3014
  }, relayPeriodSecs * 1000);
3015
3015
  this.relayKeepAliveTimers.set(peerId, interval);
3016
3016
  }
@@ -3038,9 +3038,10 @@ class KeepAliveManager {
3038
3038
  }
3039
3039
  }
3040
3040
 
3041
- const log$5 = debug("waku:connection-manager");
3041
+ const log$6 = debug("waku:connection-manager");
3042
3042
  const DEFAULT_MAX_BOOTSTRAP_PEERS_ALLOWED = 1;
3043
3043
  const DEFAULT_MAX_DIAL_ATTEMPTS_FOR_PEER = 3;
3044
+ const DEFAULT_MAX_PARALLEL_DIALS = 3;
3044
3045
  class ConnectionManager {
3045
3046
  static instances = new Map();
3046
3047
  keepAliveManager;
@@ -3048,6 +3049,8 @@ class ConnectionManager {
3048
3049
  libp2pComponents;
3049
3050
  dialAttemptsForPeer = new Map();
3050
3051
  dialErrorsForPeer = new Map();
3052
+ currentActiveDialCount = 0;
3053
+ pendingPeerDialQueue = [];
3051
3054
  static create(peerId, libp2p, keepAliveOptions, relay, options) {
3052
3055
  let instance = ConnectionManager.instances.get(peerId);
3053
3056
  if (!instance) {
@@ -3061,12 +3064,34 @@ class ConnectionManager {
3061
3064
  this.options = {
3062
3065
  maxDialAttemptsForPeer: DEFAULT_MAX_DIAL_ATTEMPTS_FOR_PEER,
3063
3066
  maxBootstrapPeersAllowed: DEFAULT_MAX_BOOTSTRAP_PEERS_ALLOWED,
3067
+ maxParallelDials: DEFAULT_MAX_PARALLEL_DIALS,
3064
3068
  ...options,
3065
3069
  };
3066
3070
  this.keepAliveManager = new KeepAliveManager(keepAliveOptions, relay);
3067
3071
  this.run()
3068
- .then(() => log$5(`Connection Manager is now running`))
3069
- .catch((error) => log$5(`Unexpected error while running service`, error));
3072
+ .then(() => log$6(`Connection Manager is now running`))
3073
+ .catch((error) => log$6(`Unexpected error while running service`, error));
3074
+ // libp2p emits `peer:discovery` events during its initialization
3075
+ // which means that before the ConnectionManager is initialized, some peers may have been discovered
3076
+ // we will dial the peers in peerStore ONCE before we start to listen to the `peer:discovery` events within the ConnectionManager
3077
+ this.dialPeerStorePeers();
3078
+ }
3079
+ async dialPeerStorePeers() {
3080
+ const peerInfos = await this.libp2pComponents.peerStore.all();
3081
+ const dialPromises = [];
3082
+ for (const peerInfo of peerInfos) {
3083
+ if (this.libp2pComponents
3084
+ .getConnections()
3085
+ .find((c) => c.remotePeer === peerInfo.id))
3086
+ continue;
3087
+ dialPromises.push(this.attemptDial(peerInfo.id));
3088
+ }
3089
+ try {
3090
+ await Promise.all(dialPromises);
3091
+ }
3092
+ catch (error) {
3093
+ log$6(`Unexpected error while dialing peer store peers`, error);
3094
+ }
3070
3095
  }
3071
3096
  async run() {
3072
3097
  // start event listeners
@@ -3081,10 +3106,11 @@ class ConnectionManager {
3081
3106
  this.libp2pComponents.removeEventListener("peer:discovery", this.onEventHandlers["peer:discovery"]);
3082
3107
  }
3083
3108
  async dialPeer(peerId) {
3109
+ this.currentActiveDialCount += 1;
3084
3110
  let dialAttempt = 0;
3085
3111
  while (dialAttempt <= this.options.maxDialAttemptsForPeer) {
3086
3112
  try {
3087
- log$5(`Dialing peer ${peerId.toString()}`);
3113
+ log$6(`Dialing peer ${peerId.toString()}`);
3088
3114
  await this.libp2pComponents.dial(peerId);
3089
3115
  const tags = await this.getTagNamesForPeer(peerId);
3090
3116
  // add tag to connection describing discovery mechanism
@@ -3098,16 +3124,16 @@ class ConnectionManager {
3098
3124
  catch (e) {
3099
3125
  const error = e;
3100
3126
  this.dialErrorsForPeer.set(peerId.toString(), error);
3101
- log$5(`Error dialing peer ${peerId.toString()} - ${error.errors}`);
3127
+ log$6(`Error dialing peer ${peerId.toString()} - ${error.errors}`);
3102
3128
  dialAttempt = this.dialAttemptsForPeer.get(peerId.toString()) ?? 1;
3103
3129
  this.dialAttemptsForPeer.set(peerId.toString(), dialAttempt + 1);
3104
3130
  if (dialAttempt <= this.options.maxDialAttemptsForPeer) {
3105
- log$5(`Reattempting dial (${dialAttempt})`);
3131
+ log$6(`Reattempting dial (${dialAttempt})`);
3106
3132
  }
3107
3133
  }
3108
3134
  }
3109
3135
  try {
3110
- log$5(`Deleting undialable peer ${peerId.toString()} from peer store. Error: ${JSON.stringify(this.dialErrorsForPeer.get(peerId.toString()).errors[0])}
3136
+ log$6(`Deleting undialable peer ${peerId.toString()} from peer store. Error: ${JSON.stringify(this.dialErrorsForPeer.get(peerId.toString()).errors[0])}
3111
3137
  }`);
3112
3138
  this.dialErrorsForPeer.delete(peerId.toString());
3113
3139
  return await this.libp2pComponents.peerStore.delete(peerId);
@@ -3115,6 +3141,30 @@ class ConnectionManager {
3115
3141
  catch (error) {
3116
3142
  throw `Error deleting undialable peer ${peerId.toString()} from peer store - ${error}`;
3117
3143
  }
3144
+ finally {
3145
+ this.currentActiveDialCount -= 1;
3146
+ this.processDialQueue();
3147
+ }
3148
+ }
3149
+ async dropConnection(peerId) {
3150
+ try {
3151
+ await this.libp2pComponents.hangUp(peerId);
3152
+ log$6(`Dropped connection with peer ${peerId.toString()}`);
3153
+ }
3154
+ catch (error) {
3155
+ log$6(`Error dropping connection with peer ${peerId.toString()} - ${error}`);
3156
+ }
3157
+ }
3158
+ async processDialQueue() {
3159
+ if (this.pendingPeerDialQueue.length > 0 &&
3160
+ this.currentActiveDialCount < this.options.maxParallelDials) {
3161
+ const peerId = this.pendingPeerDialQueue.shift();
3162
+ if (!peerId)
3163
+ return;
3164
+ this.attemptDial(peerId).catch((error) => {
3165
+ log$6(error);
3166
+ });
3167
+ }
3118
3168
  }
3119
3169
  startPeerDiscoveryListener() {
3120
3170
  this.libp2pComponents.peerStore.addEventListener("peer", this.onEventHandlers["peer:discovery"]);
@@ -3137,16 +3187,34 @@ class ConnectionManager {
3137
3187
  */
3138
3188
  this.libp2pComponents.addEventListener("peer:disconnect", this.onEventHandlers["peer:disconnect"]);
3139
3189
  }
3190
+ async attemptDial(peerId) {
3191
+ if (this.currentActiveDialCount >= this.options.maxParallelDials) {
3192
+ this.pendingPeerDialQueue.push(peerId);
3193
+ return;
3194
+ }
3195
+ if (!(await this.shouldDialPeer(peerId)))
3196
+ return;
3197
+ this.dialPeer(peerId).catch((err) => {
3198
+ throw `Error dialing peer ${peerId.toString()} : ${err}`;
3199
+ });
3200
+ }
3140
3201
  onEventHandlers = {
3141
3202
  "peer:discovery": async (evt) => {
3142
3203
  const { id: peerId } = evt.detail;
3143
- if (!(await this.shouldDialPeer(peerId)))
3144
- return;
3145
- this.dialPeer(peerId).catch((err) => log$5(`Error dialing peer ${peerId.toString()} : ${err}`));
3204
+ this.attemptDial(peerId).catch((err) => log$6(`Error dialing peer ${peerId.toString()} : ${err}`));
3146
3205
  },
3147
- "peer:connect": (evt) => {
3148
- {
3149
- this.keepAliveManager.start(evt.detail.remotePeer, this.libp2pComponents.ping.bind(this));
3206
+ "peer:connect": async (evt) => {
3207
+ const { remotePeer: peerId } = evt.detail;
3208
+ this.keepAliveManager.start(peerId, this.libp2pComponents.ping.bind(this));
3209
+ const isBootstrap = (await this.getTagNamesForPeer(peerId)).includes(Tags.BOOTSTRAP);
3210
+ if (isBootstrap) {
3211
+ const bootstrapConnections = this.libp2pComponents
3212
+ .getConnections()
3213
+ .filter((conn) => conn.tags.includes(Tags.BOOTSTRAP));
3214
+ // If we have too many bootstrap connections, drop one
3215
+ if (bootstrapConnections.length > this.options.maxBootstrapPeersAllowed) {
3216
+ await this.dropConnection(peerId);
3217
+ }
3150
3218
  }
3151
3219
  },
3152
3220
  "peer:disconnect": () => {
@@ -3191,7 +3259,7 @@ class ConnectionManager {
3191
3259
  const DefaultPingKeepAliveValueSecs = 0;
3192
3260
  const DefaultRelayKeepAliveValueSecs = 5 * 60;
3193
3261
  const DefaultUserAgent = "js-waku";
3194
- const log$4 = debug("waku:waku");
3262
+ const log$5 = debug("waku:waku");
3195
3263
  class WakuNode {
3196
3264
  libp2p;
3197
3265
  relay;
@@ -3219,7 +3287,7 @@ class WakuNode {
3219
3287
  : 0;
3220
3288
  const peerId = this.libp2p.peerId.toString();
3221
3289
  this.connectionManager = ConnectionManager.create(peerId, libp2p, { pingKeepAlive, relayKeepAlive }, this.relay);
3222
- log$4("Waku node created", peerId, `relay: ${!!this.relay}, store: ${!!this.store}, light push: ${!!this
3290
+ log$5("Waku node created", peerId, `relay: ${!!this.relay}, store: ${!!this.store}, light push: ${!!this
3223
3291
  .lightPush}, filter: ${!!this.filter}`);
3224
3292
  }
3225
3293
  /**
@@ -3243,7 +3311,7 @@ class WakuNode {
3243
3311
  this.relay.gossipSub.multicodecs.forEach((codec) => codecs.push(codec));
3244
3312
  }
3245
3313
  else {
3246
- log$4("Relay codec not included in dial codec: protocol not mounted locally");
3314
+ log$5("Relay codec not included in dial codec: protocol not mounted locally");
3247
3315
  }
3248
3316
  }
3249
3317
  if (_protocols.includes(Protocols.Store)) {
@@ -3251,7 +3319,7 @@ class WakuNode {
3251
3319
  codecs.push(this.store.multicodec);
3252
3320
  }
3253
3321
  else {
3254
- log$4("Store codec not included in dial codec: protocol not mounted locally");
3322
+ log$5("Store codec not included in dial codec: protocol not mounted locally");
3255
3323
  }
3256
3324
  }
3257
3325
  if (_protocols.includes(Protocols.LightPush)) {
@@ -3259,7 +3327,7 @@ class WakuNode {
3259
3327
  codecs.push(this.lightPush.multicodec);
3260
3328
  }
3261
3329
  else {
3262
- log$4("Light Push codec not included in dial codec: protocol not mounted locally");
3330
+ log$5("Light Push codec not included in dial codec: protocol not mounted locally");
3263
3331
  }
3264
3332
  }
3265
3333
  if (_protocols.includes(Protocols.Filter)) {
@@ -3267,10 +3335,10 @@ class WakuNode {
3267
3335
  codecs.push(this.filter.multicodec);
3268
3336
  }
3269
3337
  else {
3270
- log$4("Filter codec not included in dial codec: protocol not mounted locally");
3338
+ log$5("Filter codec not included in dial codec: protocol not mounted locally");
3271
3339
  }
3272
3340
  }
3273
- log$4(`Dialing to ${peerId.toString()} with protocols ${_protocols}`);
3341
+ log$5(`Dialing to ${peerId.toString()} with protocols ${_protocols}`);
3274
3342
  return this.libp2p.dialProtocol(peerId, codecs);
3275
3343
  }
3276
3344
  async start() {
@@ -3315,7 +3383,7 @@ var waku = /*#__PURE__*/Object.freeze({
3315
3383
  */
3316
3384
  const DefaultPubSubTopic = "/waku/2/default-waku/proto";
3317
3385
 
3318
- var index$3 = /*#__PURE__*/Object.freeze({
3386
+ var index$4 = /*#__PURE__*/Object.freeze({
3319
3387
  __proto__: null,
3320
3388
  version_0: version_0
3321
3389
  });
@@ -3397,11 +3465,11 @@ const isSizeValid = (payload) => {
3397
3465
  return true;
3398
3466
  };
3399
3467
 
3400
- function isAsyncIterable$1(thing) {
3468
+ function isAsyncIterable$3(thing) {
3401
3469
  return thing[Symbol.asyncIterator] != null;
3402
3470
  }
3403
3471
  function all(source) {
3404
- if (isAsyncIterable$1(source)) {
3472
+ if (isAsyncIterable$3(source)) {
3405
3473
  return (async () => {
3406
3474
  const arr = [];
3407
3475
  for await (const entry of source) {
@@ -4158,7 +4226,7 @@ const unsigned = {
4158
4226
  }
4159
4227
  };
4160
4228
 
4161
- function isAsyncIterable(thing) {
4229
+ function isAsyncIterable$2(thing) {
4162
4230
  return thing[Symbol.asyncIterator] != null;
4163
4231
  }
4164
4232
 
@@ -4191,7 +4259,7 @@ function encode(source, options) {
4191
4259
  yield* chunk;
4192
4260
  }
4193
4261
  }
4194
- if (isAsyncIterable(source)) {
4262
+ if (isAsyncIterable$2(source)) {
4195
4263
  return (async function* () {
4196
4264
  for await (const chunk of source) {
4197
4265
  yield* maybeYield(chunk);
@@ -4347,7 +4415,7 @@ function decode(source, options) {
4347
4415
  }
4348
4416
  }
4349
4417
  }
4350
- if (isAsyncIterable(source)) {
4418
+ if (isAsyncIterable$2(source)) {
4351
4419
  return (async function* () {
4352
4420
  for await (const buf of source) {
4353
4421
  buffer.append(buf);
@@ -4662,77 +4730,56 @@ function _pushable(getNext, options) {
4662
4730
  return pushable;
4663
4731
  }
4664
4732
 
4665
- /**
4666
- * Treat one or more iterables as a single iterable.
4667
- *
4668
- * Nb. sources are iterated over in parallel so the
4669
- * order of emitted items is not guaranteed.
4670
- */
4671
- async function* merge(...sources) {
4672
- const output = pushable({
4673
- objectMode: true
4674
- });
4675
- void Promise.resolve().then(async () => {
4676
- try {
4677
- await Promise.all(sources.map(async (source) => {
4678
- for await (const item of source) {
4679
- output.push(item);
4680
- }
4681
- }));
4682
- output.end();
4683
- }
4684
- catch (err) {
4685
- output.end(err);
4733
+ function isAsyncIterable$1(thing) {
4734
+ return thing[Symbol.asyncIterator] != null;
4735
+ }
4736
+ function merge(...sources) {
4737
+ const syncSources = [];
4738
+ for (const source of sources) {
4739
+ if (!isAsyncIterable$1(source)) {
4740
+ syncSources.push(source);
4686
4741
  }
4687
- });
4688
- yield* output;
4742
+ }
4743
+ if (syncSources.length === sources.length) {
4744
+ // all sources are synchronous
4745
+ return (function* () {
4746
+ for (const source of syncSources) {
4747
+ yield* source;
4748
+ }
4749
+ })();
4750
+ }
4751
+ return (async function* () {
4752
+ const output = pushable({
4753
+ objectMode: true
4754
+ });
4755
+ void Promise.resolve().then(async () => {
4756
+ try {
4757
+ await Promise.all(sources.map(async (source) => {
4758
+ for await (const item of source) {
4759
+ output.push(item);
4760
+ }
4761
+ }));
4762
+ output.end();
4763
+ }
4764
+ catch (err) {
4765
+ output.end(err);
4766
+ }
4767
+ });
4768
+ yield* output;
4769
+ })();
4689
4770
  }
4690
4771
 
4691
- const rawPipe = (...fns) => {
4692
- let res;
4693
- while (fns.length > 0) {
4694
- res = fns.shift()(res);
4695
- }
4696
- return res;
4697
- };
4698
- const isIterable = (obj) => {
4699
- return obj != null && (typeof obj[Symbol.asyncIterator] === 'function' ||
4700
- typeof obj[Symbol.iterator] === 'function' ||
4701
- typeof obj.next === 'function' // Probably, right?
4702
- );
4703
- };
4704
- const isDuplex = (obj) => {
4705
- return obj != null && typeof obj.sink === 'function' && isIterable(obj.source);
4706
- };
4707
- const duplexPipelineFn = (duplex) => {
4708
- return (source) => {
4709
- const p = duplex.sink(source);
4710
- if (p.then != null) {
4711
- const stream = pushable({
4712
- objectMode: true
4713
- });
4714
- p.then(() => {
4715
- stream.end();
4716
- }, (err) => {
4717
- stream.end(err);
4718
- });
4719
- const sourceWrap = async function* () {
4720
- yield* duplex.source;
4721
- stream.end();
4722
- };
4723
- return merge(stream, sourceWrap());
4724
- }
4725
- return duplex.source;
4726
- };
4727
- };
4728
4772
  function pipe(first, ...rest) {
4773
+ if (first == null) {
4774
+ throw new Error('Empty pipeline');
4775
+ }
4729
4776
  // Duplex at start: wrap in function and return duplex source
4730
4777
  if (isDuplex(first)) {
4731
4778
  const duplex = first;
4732
4779
  first = () => duplex.source;
4733
4780
  // Iterable at start: wrap in function
4734
4781
  }
4735
- else if (isIterable(first)) {
4782
+ else if (isIterable(first) || isAsyncIterable(first)) {
4736
4783
  const source = first;
4737
4784
  first = () => source;
4738
4785
  }
@@ -4753,6 +4800,59 @@ function pipe(first, ...rest) {
4753
4800
  }
4754
4801
  return rawPipe(...fns);
4755
4802
  }
4803
+ const rawPipe = (...fns) => {
4804
+ let res;
4805
+ while (fns.length > 0) {
4806
+ res = fns.shift()(res);
4807
+ }
4808
+ return res;
4809
+ };
4810
+ const isAsyncIterable = (obj) => {
4811
+ return obj?.[Symbol.asyncIterator] != null;
4812
+ };
4813
+ const isIterable = (obj) => {
4814
+ return obj?.[Symbol.iterator] != null;
4815
+ };
4816
+ const isDuplex = (obj) => {
4817
+ if (obj == null) {
4818
+ return false;
4819
+ }
4820
+ return obj.sink != null && obj.source != null;
4821
+ };
4822
+ const duplexPipelineFn = (duplex) => {
4823
+ return (source) => {
4824
+ const p = duplex.sink(source);
4825
+ if (p?.then != null) {
4826
+ const stream = pushable({
4827
+ objectMode: true
4828
+ });
4829
+ p.then(() => {
4830
+ stream.end();
4831
+ }, (err) => {
4832
+ stream.end(err);
4833
+ });
4834
+ let sourceWrap;
4835
+ const source = duplex.source;
4836
+ if (isAsyncIterable(source)) {
4837
+ sourceWrap = async function* () {
4838
+ yield* source;
4839
+ stream.end();
4840
+ };
4841
+ }
4842
+ else if (isIterable(source)) {
4843
+ sourceWrap = function* () {
4844
+ yield* source;
4845
+ stream.end();
4846
+ };
4847
+ }
4848
+ else {
4849
+ throw new Error('Unknown duplex source type - must be Iterable or AsyncIterable');
4850
+ }
4851
+ return merge(stream, sourceWrap());
4852
+ }
4853
+ return duplex.source;
4854
+ };
4855
+ };
4756
4856
 
4757
4857
  const EmptyMessage = {
4758
4858
  payload: new Uint8Array(),
@@ -4876,7 +4976,7 @@ class FilterRpc {
4876
4976
  }
4877
4977
 
4878
4978
  const FilterCodec = "/vac/waku/filter/2.0.0-beta1";
4879
- const log$3 = debug("waku:filter");
4979
+ const log$4 = debug("waku:filter");
4880
4980
  /**
4881
4981
  * Implements client side of the [Waku v2 Filter protocol](https://rfc.vac.dev/spec/12/).
4882
4982
  *
@@ -4895,7 +4995,7 @@ class Filter extends BaseProtocol {
4895
4995
  this.subscriptions = new Map();
4896
4996
  this.libp2p
4897
4997
  .handle(this.multicodec, this.onRequest.bind(this))
4898
- .catch((e) => log$3("Failed to register filter protocol", e));
4998
+ .catch((e) => log$4("Failed to register filter protocol", e));
4899
4999
  }
4900
5000
  /**
4901
5001
  * @param decoders Decoder or array of Decoders to use to decode messages, it also specifies the content topics.
@@ -4916,10 +5016,10 @@ class Filter extends BaseProtocol {
4916
5016
  const stream = await this.newStream(peer);
4917
5017
  try {
4918
5018
  const res = await pipe([request.encode()], encode, stream, decode, async (source) => await all(source));
4919
- log$3("response", res);
5019
+ log$4("response", res);
4920
5020
  }
4921
5021
  catch (e) {
4922
- log$3("Error subscribing to peer ", peer.id.toString(), "for content topics", contentTopics, ": ", e);
5022
+ log$4("Error subscribing to peer ", peer.id.toString(), "for content topics", contentTopics, ": ", e);
4923
5023
  throw e;
4924
5024
  }
4925
5025
  const subscription = {
@@ -4947,7 +5047,7 @@ class Filter extends BaseProtocol {
4947
5047
  return map;
4948
5048
  }
4949
5049
  onRequest(streamData) {
4950
- log$3("Receiving message push");
5050
+ log$4("Receiving message push");
4951
5051
  try {
4952
5052
  pipe(streamData.stream, decode, async (source) => {
4953
5053
  for await (const bytes of source) {
@@ -4957,30 +5057,30 @@ class Filter extends BaseProtocol {
4957
5057
  }
4958
5058
  }
4959
5059
  }).then(() => {
4960
- log$3("Receiving pipe closed.");
5060
+ log$4("Receiving pipe closed.");
4961
5061
  }, (e) => {
4962
- log$3("Error with receiving pipe", e);
5062
+ log$4("Error with receiving pipe", e);
4963
5063
  });
4964
5064
  }
4965
5065
  catch (e) {
4966
- log$3("Error decoding message", e);
5066
+ log$4("Error decoding message", e);
4967
5067
  }
4968
5068
  }
4969
5069
  async pushMessages(requestId, messages) {
4970
5070
  const subscription = this.subscriptions.get(requestId);
4971
5071
  if (!subscription) {
4972
- log$3(`No subscription locally registered for request ID ${requestId}`);
5072
+ log$4(`No subscription locally registered for request ID ${requestId}`);
4973
5073
  return;
4974
5074
  }
4975
5075
  const { decoders, callback, pubSubTopic } = subscription;
4976
5076
  if (!decoders || !decoders.length) {
4977
- log$3(`No decoder registered for request ID ${requestId}`);
5077
+ log$4(`No decoder registered for request ID ${requestId}`);
4978
5078
  return;
4979
5079
  }
4980
5080
  for (const protoMessage of messages) {
4981
5081
  const contentTopic = protoMessage.contentTopic;
4982
5082
  if (!contentTopic) {
4983
- log$3("Message has no content topic, skipping");
5083
+ log$4("Message has no content topic, skipping");
4984
5084
  return;
4985
5085
  }
4986
5086
  let didDecodeMsg = false;
@@ -4992,7 +5092,7 @@ class Filter extends BaseProtocol {
4992
5092
  return;
4993
5093
  const decoded = await dec.fromProtoObj(pubSubTopic, toProtoMessage(protoMessage));
4994
5094
  if (!decoded) {
4995
- log$3("Not able to decode message");
5095
+ log$4("Not able to decode message");
4996
5096
  return;
4997
5097
  }
4998
5098
  // This is just to prevent more decoding attempt
@@ -5009,7 +5109,7 @@ class Filter extends BaseProtocol {
5009
5109
  await pipe([unsubscribeRequest.encode()], encode, stream.sink);
5010
5110
  }
5011
5111
  catch (e) {
5012
- log$3("Error unsubscribing", e);
5112
+ log$4("Error unsubscribing", e);
5013
5113
  throw e;
5014
5114
  }
5015
5115
  }
@@ -5018,12 +5118,351 @@ function wakuFilter(init = {}) {
5018
5118
  return (libp2p) => new Filter(libp2p, init);
5019
5119
  }
5020
5120
 
5021
- var index$2 = /*#__PURE__*/Object.freeze({
5121
+ var index$3 = /*#__PURE__*/Object.freeze({
5022
5122
  __proto__: null,
5023
5123
  FilterCodec: FilterCodec,
5024
5124
  wakuFilter: wakuFilter
5025
5125
  });
5026
5126
 
5127
+ /**
5128
+ * FilterPushRPC represents a message conforming to the Waku FilterPush protocol.
5129
+ * Protocol documentation: https://rfc.vac.dev/spec/12/
5130
+ */
5131
+ class FilterPushRpc {
5132
+ proto;
5133
+ constructor(proto) {
5134
+ this.proto = proto;
5135
+ }
5136
+ static decode(bytes) {
5137
+ const res = MessagePush.decode(bytes);
5138
+ return new FilterPushRpc(res);
5139
+ }
5140
+ encode() {
5141
+ return MessagePush.encode(this.proto);
5142
+ }
5143
+ get wakuMessage() {
5144
+ return this.proto.wakuMessage;
5145
+ }
5146
+ /**
5147
+ * Get the pubsub topic from the FilterPushRpc object.
5148
+ * @returns string
5149
+ */
5150
+ get pubsubTopic() {
5151
+ return this.proto.pubsubTopic;
5152
+ }
5153
+ }
5154
+ class FilterSubscribeRpc {
5155
+ proto;
5156
+ constructor(proto) {
5157
+ this.proto = proto;
5158
+ }
5159
+ static createSubscribeRequest(pubsubTopic, contentTopics) {
5160
+ return new FilterSubscribeRpc({
5161
+ requestId: v4(),
5162
+ filterSubscribeType: FilterSubscribeRequest.FilterSubscribeType.SUBSCRIBE,
5163
+ pubsubTopic,
5164
+ contentTopics,
5165
+ });
5166
+ }
5167
+ static createUnsubscribeRequest(pubsubTopic, contentTopics) {
5168
+ return new FilterSubscribeRpc({
5169
+ requestId: v4(),
5170
+ filterSubscribeType: FilterSubscribeRequest.FilterSubscribeType.UNSUBSCRIBE,
5171
+ pubsubTopic,
5172
+ contentTopics,
5173
+ });
5174
+ }
5175
+ static createUnsubscribeAllRequest(pubsubTopic) {
5176
+ return new FilterSubscribeRpc({
5177
+ requestId: v4(),
5178
+ filterSubscribeType: FilterSubscribeRequest.FilterSubscribeType.UNSUBSCRIBE_ALL,
5179
+ pubsubTopic,
5180
+ contentTopics: [],
5181
+ });
5182
+ }
5183
+ static createSubscriberPingRequest() {
5184
+ return new FilterSubscribeRpc({
5185
+ requestId: v4(),
5186
+ filterSubscribeType: FilterSubscribeRequest.FilterSubscribeType.SUBSCRIBER_PING,
5187
+ pubsubTopic: "",
5188
+ contentTopics: [],
5189
+ });
5190
+ }
5191
+ static decode(bytes) {
5192
+ const res = FilterSubscribeRequest.decode(bytes);
5193
+ return new FilterSubscribeRpc(res);
5194
+ }
5195
+ encode() {
5196
+ return FilterSubscribeRequest.encode(this.proto);
5197
+ }
5198
+ get filterSubscribeType() {
5199
+ return this.proto.filterSubscribeType;
5200
+ }
5201
+ get requestId() {
5202
+ return this.proto.requestId;
5203
+ }
5204
+ get pubsubTopic() {
5205
+ return this.proto.pubsubTopic;
5206
+ }
5207
+ get contentTopics() {
5208
+ return this.proto.contentTopics;
5209
+ }
5210
+ }
5211
+ class FilterSubscribeResponse {
5212
+ proto;
5213
+ constructor(proto) {
5214
+ this.proto = proto;
5215
+ }
5216
+ static decode(bytes) {
5217
+ const res = FilterSubscribeResponse$1.decode(bytes);
5218
+ return new FilterSubscribeResponse(res);
5219
+ }
5220
+ encode() {
5221
+ return FilterSubscribeResponse$1.encode(this.proto);
5222
+ }
5223
+ get statusCode() {
5224
+ return this.proto.statusCode;
5225
+ }
5226
+ get statusDesc() {
5227
+ return this.proto.statusDesc;
5228
+ }
5229
+ get requestId() {
5230
+ return this.proto.requestId;
5231
+ }
5232
+ }
5233
+
5234
+ const log$3 = debug("waku:filter:v2");
5235
+ const FilterV2Codecs = {
5236
+ SUBSCRIBE: "/vac/waku/filter-subscribe/2.0.0-beta1",
5237
+ PUSH: "/vac/waku/filter-push/2.0.0-beta1",
5238
+ };
5239
+ class Subscription {
5240
+ peer;
5241
+ pubSubTopic;
5242
+ newStream;
5243
+ subscriptionCallbacks;
5244
+ constructor(pubSubTopic, remotePeer, newStream) {
5245
+ this.peer = remotePeer;
5246
+ this.pubSubTopic = pubSubTopic;
5247
+ this.newStream = newStream;
5248
+ this.subscriptionCallbacks = new Map();
5249
+ }
5250
+ async subscribe(decoders, callback) {
5251
+ const decodersArray = Array.isArray(decoders) ? decoders : [decoders];
5252
+ const decodersGroupedByCT = groupByContentTopic(decodersArray);
5253
+ const contentTopics = Array.from(decodersGroupedByCT.keys());
5254
+ const stream = await this.newStream(this.peer);
5255
+ const request = FilterSubscribeRpc.createSubscribeRequest(this.pubSubTopic, contentTopics);
5256
+ try {
5257
+ const res = await pipe([request.encode()], encode, stream, decode, async (source) => await all(source));
5258
+ const { statusCode, requestId, statusDesc } = FilterSubscribeResponse.decode(res[0].slice());
5259
+ if (statusCode < 200 || statusCode >= 300) {
5260
+ throw new Error(`Filter subscribe request ${requestId} failed with status code ${statusCode}: ${statusDesc}`);
5261
+ }
5262
+ log$3("Subscribed to peer ", this.peer.id.toString(), "for content topics", contentTopics);
5263
+ }
5264
+ catch (e) {
5265
+ throw new Error("Error subscribing to peer: " +
5266
+ this.peer.id.toString() +
5267
+ " for content topics: " +
5268
+ contentTopics +
5269
+ ": " +
5270
+ e);
5271
+ }
5272
+ // Save the callback functions by content topics so they
5273
+ // can easily be removed (reciprocally replaced) if `unsubscribe` (reciprocally `subscribe`)
5274
+ // is called for those content topics
5275
+ decodersGroupedByCT.forEach((decoders, contentTopic) => {
5276
+ // Cast the type because a given `subscriptionCallbacks` map may hold
5277
+ // Decoder that decode to different implementations of `IDecodedMessage`
5278
+ const subscriptionCallback = {
5279
+ decoders,
5280
+ callback,
5281
+ };
5282
+ // The callback and decoder may override previous values, this is on
5283
+ // purpose as the user may call `subscribe` to refresh the subscription
5284
+ this.subscriptionCallbacks.set(contentTopic, subscriptionCallback);
5285
+ });
5286
+ }
5287
+ async unsubscribe(contentTopics) {
5288
+ const stream = await this.newStream(this.peer);
5289
+ const unsubscribeRequest = FilterSubscribeRpc.createUnsubscribeRequest(this.pubSubTopic, contentTopics);
5290
+ try {
5291
+ await pipe([unsubscribeRequest.encode()], encode, stream.sink);
5292
+ }
5293
+ catch (error) {
5294
+ throw new Error("Error subscribing: " + error);
5295
+ }
5296
+ contentTopics.forEach((contentTopic) => {
5297
+ this.subscriptionCallbacks.delete(contentTopic);
5298
+ });
5299
+ }
5300
+ async ping() {
5301
+ const stream = await this.newStream(this.peer);
5302
+ const request = FilterSubscribeRpc.createSubscriberPingRequest();
5303
+ try {
5304
+ const res = await pipe([request.encode()], encode, stream, decode, async (source) => await all(source));
5305
+ const { statusCode, requestId, statusDesc } = FilterSubscribeResponse.decode(res[0].slice());
5306
+ if (statusCode < 200 || statusCode >= 300) {
5307
+ throw new Error(`Filter ping request ${requestId} failed with status code ${statusCode}: ${statusDesc}`);
5308
+ }
5309
+ log$3("Ping successful");
5310
+ }
5311
+ catch (error) {
5312
+ log$3("Error pinging: ", error);
5313
+ throw new Error("Error pinging: " + error);
5314
+ }
5315
+ }
5316
+ async unsubscribeAll() {
5317
+ const stream = await this.newStream(this.peer);
5318
+ const request = FilterSubscribeRpc.createUnsubscribeAllRequest(this.pubSubTopic);
5319
+ try {
5320
+ const res = await pipe([request.encode()], encode, stream, decode, async (source) => await all(source));
5321
+ const { statusCode, requestId, statusDesc } = FilterSubscribeResponse.decode(res[0].slice());
5322
+ if (statusCode < 200 || statusCode >= 300) {
5323
+ throw new Error(`Filter unsubscribe all request ${requestId} failed with status code ${statusCode}: ${statusDesc}`);
5324
+ }
5325
+ this.subscriptionCallbacks.clear();
5326
+ log$3("Unsubscribed from all content topics");
5327
+ }
5328
+ catch (error) {
5329
+ throw new Error("Error unsubscribing from all content topics: " + error);
5330
+ }
5331
+ }
5332
+ async processMessage(message) {
5333
+ const contentTopic = message.contentTopic;
5334
+ const subscriptionCallback = this.subscriptionCallbacks.get(contentTopic);
5335
+ if (!subscriptionCallback) {
5336
+ log$3("No subscription callback available for ", contentTopic);
5337
+ return;
5338
+ }
5339
+ await pushMessage(subscriptionCallback, this.pubSubTopic, message);
5340
+ }
5341
+ }
5342
+ class FilterV2 extends BaseProtocol {
5343
+ libp2p;
5344
+ options;
5345
+ activeSubscriptions = new Map();
5346
+ getActiveSubscription(pubSubTopic, peerIdStr) {
5347
+ return this.activeSubscriptions.get(`${pubSubTopic}_${peerIdStr}`);
5348
+ }
5349
+ setActiveSubscription(pubSubTopic, peerIdStr, subscription) {
5350
+ this.activeSubscriptions.set(`${pubSubTopic}_${peerIdStr}`, subscription);
5351
+ return subscription;
5352
+ }
5353
+ constructor(libp2p, options) {
5354
+ super(FilterV2Codecs.SUBSCRIBE, libp2p.peerStore, libp2p.getConnections.bind(libp2p));
5355
+ this.libp2p = libp2p;
5356
+ this.libp2p
5357
+ .handle(FilterV2Codecs.PUSH, this.onRequest.bind(this))
5358
+ .catch((e) => {
5359
+ log$3("Failed to register ", FilterV2Codecs.PUSH, e);
5360
+ });
5361
+ this.activeSubscriptions = new Map();
5362
+ this.options = options ?? {};
5363
+ }
5364
+ async createSubscription(pubSubTopic, peerId) {
5365
+ const _pubSubTopic = pubSubTopic ?? this.options.pubSubTopic ?? DefaultPubSubTopic;
5366
+ const peer = await this.getPeer(peerId);
5367
+ const subscription = this.getActiveSubscription(_pubSubTopic, peer.id.toString()) ??
5368
+ this.setActiveSubscription(_pubSubTopic, peer.id.toString(), new Subscription(_pubSubTopic, peer, this.newStream.bind(this, peer)));
5369
+ return subscription;
5370
+ }
5371
+ toSubscriptionIterator(decoders, opts) {
5372
+ return toAsyncIterator(this, decoders, opts);
5373
+ }
5374
+ /**
5375
+ * This method is used to satisfy the `IReceiver` interface.
5376
+ *
5377
+ * @hidden
5378
+ *
5379
+ * @param decoders The decoders to use for the subscription.
5380
+ * @param callback The callback function to use for the subscription.
5381
+ * @param opts Optional protocol options for the subscription.
5382
+ *
5383
+ * @returns A Promise that resolves to a function that unsubscribes from the subscription.
5384
+ *
5385
+ * @remarks
5386
+ * This method should not be used directly.
5387
+ * Instead, use `createSubscription` to create a new subscription.
5388
+ */
5389
+ async subscribe(decoders, callback, opts) {
5390
+ const subscription = await this.createSubscription(undefined, opts?.peerId);
5391
+ subscription.subscribe(decoders, callback);
5392
+ const contentTopics = Array.from(groupByContentTopic(Array.isArray(decoders) ? decoders : [decoders]).keys());
5393
+ return async () => {
5394
+ await subscription.unsubscribe(contentTopics);
5395
+ };
5396
+ }
5397
+ onRequest(streamData) {
5398
+ log$3("Receiving message push");
5399
+ try {
5400
+ pipe(streamData.stream, decode, async (source) => {
5401
+ for await (const bytes of source) {
5402
+ const response = FilterPushRpc.decode(bytes.slice());
5403
+ const { pubsubTopic, wakuMessage } = response;
5404
+ if (!wakuMessage) {
5405
+ log$3("Received empty message");
5406
+ return;
5407
+ }
5408
+ if (!pubsubTopic) {
5409
+ log$3("PubSub topic missing from push message");
5410
+ return;
5411
+ }
5412
+ const peerIdStr = streamData.connection.remotePeer.toString();
5413
+ const subscription = this.getActiveSubscription(pubsubTopic, peerIdStr);
5414
+ if (!subscription) {
5415
+ log$3(`No subscription locally registered for topic ${pubsubTopic}`);
5416
+ return;
5417
+ }
5418
+ await subscription.processMessage(wakuMessage);
5419
+ }
5420
+ }).then(() => {
5421
+ log$3("Receiving pipe closed.");
5422
+ }, (e) => {
5423
+ log$3("Error with receiving pipe", e);
5424
+ });
5425
+ }
5426
+ catch (e) {
5427
+ log$3("Error decoding message", e);
5428
+ }
5429
+ }
5430
+ }
5431
+ function wakuFilterV2(init = {}) {
5432
+ return (libp2p) => new FilterV2(libp2p, init);
5433
+ }
5434
+ async function pushMessage(subscriptionCallback, pubSubTopic, message) {
5435
+ const { decoders, callback } = subscriptionCallback;
5436
+ const { contentTopic } = message;
5437
+ if (!contentTopic) {
5438
+ log$3("Message has no content topic, skipping");
5439
+ return;
5440
+ }
5441
+ let didDecodeMsg = false;
5442
+ // We don't want to wait for decoding failure, just attempt to decode
5443
+ // all messages and do the call back on the one that works
5444
+ // noinspection ES6MissingAwait
5445
+ decoders.forEach(async (dec) => {
5446
+ if (didDecodeMsg)
5447
+ return;
5448
+ const decoded = await dec.fromProtoObj(pubSubTopic, message);
5449
+ // const decoded = await dec.fromProtoObj(pubSubTopic, message);
5450
+ if (!decoded) {
5451
+ log$3("Not able to decode message");
5452
+ return;
5453
+ }
5454
+ // This is just to prevent more decoding attempt
5455
+ // TODO: Could be better if we were to abort promises
5456
+ didDecodeMsg = Boolean(decoded);
5457
+ await callback(decoded);
5458
+ });
5459
+ }
5460
+
5461
+ var index$2 = /*#__PURE__*/Object.freeze({
5462
+ __proto__: null,
5463
+ wakuFilterV2: wakuFilterV2
5464
+ });
5465
+
5027
5466
  class PushRpc {
5028
5467
  proto;
5029
5468
  constructor(proto) {
@@ -5974,7 +6413,7 @@ const log = debug("waku:wait-for-remote-peer");
5974
6413
  * Wait for a remote peer to be ready given the passed protocols.
5975
6414
  * Must be used after attempting to connect to nodes, using
5976
6415
  * {@link @waku/core.WakuNode.dial} or a bootstrap method with
5977
- * {@link @waku/create.createLightNode}.
6416
+ * {@link @waku/sdk.createLightNode}.
5978
6417
  *
5979
6418
  * If the passed protocols is a GossipSub protocol, then it resolves only once
5980
6419
  * a peer is in a mesh, to help ensure that other peers will send and receive
@@ -6074,4 +6513,4 @@ function getEnabledProtocols(waku) {
6074
6513
  return protocols;
6075
6514
  }
6076
6515
 
6077
- export { ConnectionManager, DefaultPubSubTopic, DefaultUserAgent, KeepAliveManager, LightPushCodec, PageDirection, StoreCodec, WakuNode, createCursor, createEncoder, index$3 as message, waitForRemotePeer, waku, wakuFilter, wakuLightPush, wakuStore, index$2 as waku_filter, index$1 as waku_light_push, index as waku_store };
6516
+ export { ConnectionManager, DefaultPubSubTopic, DefaultUserAgent, KeepAliveManager, LightPushCodec, PageDirection, StoreCodec, WakuNode, createCursor, createEncoder, index$4 as message, waitForRemotePeer, waku, wakuFilter as wakuFilterV1, wakuFilterV2, wakuLightPush, wakuStore, index$3 as waku_filter_v1, index$2 as waku_filter_v2, index$1 as waku_light_push, index as waku_store };