@waku/core 0.0.26-678635e.0 → 0.0.26-7eb3375.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.
Files changed (38) hide show
  1. package/bundle/base_protocol-46017f51.js +468 -0
  2. package/bundle/{base_protocol-4bcf7514.js → browser-9a6558bb.js} +742 -451
  3. package/bundle/{index-27b91e3b.js → index-7581d519.js} +1 -1
  4. package/bundle/index.js +3742 -4917
  5. package/bundle/lib/base_protocol.js +3 -3
  6. package/bundle/lib/message/version_0.js +3 -3
  7. package/bundle/lib/predefined_bootstrap_nodes.js +1 -1
  8. package/bundle/{version_0-2f1176e3.js → version_0-7190df43.js} +7 -7
  9. package/dist/.tsbuildinfo +1 -1
  10. package/dist/index.d.ts +1 -1
  11. package/dist/index.js +1 -1
  12. package/dist/lib/connection_manager.d.ts +8 -4
  13. package/dist/lib/connection_manager.js +34 -12
  14. package/dist/lib/connection_manager.js.map +1 -1
  15. package/dist/lib/constants.d.ts +2 -2
  16. package/dist/lib/constants.js +2 -2
  17. package/dist/lib/filter/index.js +4 -4
  18. package/dist/lib/keep_alive_manager.d.ts +1 -0
  19. package/dist/lib/keep_alive_manager.js +4 -1
  20. package/dist/lib/keep_alive_manager.js.map +1 -1
  21. package/dist/lib/light_push/index.js +2 -2
  22. package/dist/lib/message/version_0.d.ts +6 -6
  23. package/dist/lib/message/version_0.js +3 -3
  24. package/dist/lib/store/index.js +11 -11
  25. package/dist/lib/waku.d.ts +4 -3
  26. package/dist/lib/waku.js +3 -0
  27. package/dist/lib/waku.js.map +1 -1
  28. package/package.json +1 -1
  29. package/src/index.ts +1 -1
  30. package/src/lib/connection_manager.ts +44 -13
  31. package/src/lib/constants.ts +2 -2
  32. package/src/lib/filter/index.ts +11 -11
  33. package/src/lib/keep_alive_manager.ts +7 -1
  34. package/src/lib/light_push/index.ts +4 -4
  35. package/src/lib/message/version_0.ts +6 -6
  36. package/src/lib/store/index.ts +13 -13
  37. package/src/lib/waku.ts +6 -2
  38. package/bundle/browser-90197c87.js +0 -754
@@ -0,0 +1,468 @@
1
+ import { i as identityBase, h as base2, j as base8, k as base10, l as base16, m as base32, n as base36, o as base58, p as base64, q as base256emoji } from './browser-9a6558bb.js';
2
+ import { L as Logger } from './index-7581d519.js';
3
+
4
+ // @ts-check
5
+
6
+
7
+ const bases = { ...identityBase, ...base2, ...base8, ...base10, ...base16, ...base32, ...base36, ...base58, ...base64, ...base256emoji };
8
+
9
+ /**
10
+ * To guarantee Uint8Array semantics, convert nodejs Buffers
11
+ * into vanilla Uint8Arrays
12
+ */
13
+ function asUint8Array(buf) {
14
+ if (globalThis.Buffer != null) {
15
+ return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
16
+ }
17
+ return buf;
18
+ }
19
+
20
+ /**
21
+ * Returns a `Uint8Array` of the requested size. Referenced memory will
22
+ * be initialized to 0.
23
+ */
24
+ function alloc(size = 0) {
25
+ if (globalThis.Buffer?.alloc != null) {
26
+ return asUint8Array(globalThis.Buffer.alloc(size));
27
+ }
28
+ return new Uint8Array(size);
29
+ }
30
+ /**
31
+ * Where possible returns a Uint8Array of the requested size that references
32
+ * uninitialized memory. Only use if you are certain you will immediately
33
+ * overwrite every value in the returned `Uint8Array`.
34
+ */
35
+ function allocUnsafe(size = 0) {
36
+ if (globalThis.Buffer?.allocUnsafe != null) {
37
+ return asUint8Array(globalThis.Buffer.allocUnsafe(size));
38
+ }
39
+ return new Uint8Array(size);
40
+ }
41
+
42
+ function createCodec(name, prefix, encode, decode) {
43
+ return {
44
+ name,
45
+ prefix,
46
+ encoder: {
47
+ name,
48
+ prefix,
49
+ encode
50
+ },
51
+ decoder: {
52
+ decode
53
+ }
54
+ };
55
+ }
56
+ const string = createCodec('utf8', 'u', (buf) => {
57
+ const decoder = new TextDecoder('utf8');
58
+ return 'u' + decoder.decode(buf);
59
+ }, (str) => {
60
+ const encoder = new TextEncoder();
61
+ return encoder.encode(str.substring(1));
62
+ });
63
+ const ascii = createCodec('ascii', 'a', (buf) => {
64
+ let string = 'a';
65
+ for (let i = 0; i < buf.length; i++) {
66
+ string += String.fromCharCode(buf[i]);
67
+ }
68
+ return string;
69
+ }, (str) => {
70
+ str = str.substring(1);
71
+ const buf = allocUnsafe(str.length);
72
+ for (let i = 0; i < str.length; i++) {
73
+ buf[i] = str.charCodeAt(i);
74
+ }
75
+ return buf;
76
+ });
77
+ const BASES = {
78
+ utf8: string,
79
+ 'utf-8': string,
80
+ hex: bases.base16,
81
+ latin1: ascii,
82
+ ascii,
83
+ binary: ascii,
84
+ ...bases
85
+ };
86
+
87
+ /**
88
+ * Turns a `Uint8Array` into a string.
89
+ *
90
+ * Supports `utf8`, `utf-8` and any encoding supported by the multibase module.
91
+ *
92
+ * Also `ascii` which is similar to node's 'binary' encoding.
93
+ */
94
+ function toString(array, encoding = 'utf8') {
95
+ const base = BASES[encoding];
96
+ if (base == null) {
97
+ throw new Error(`Unsupported encoding "${encoding}"`);
98
+ }
99
+ if ((encoding === 'utf8' || encoding === 'utf-8') && globalThis.Buffer != null && globalThis.Buffer.from != null) {
100
+ return globalThis.Buffer.from(array.buffer, array.byteOffset, array.byteLength).toString('utf8');
101
+ }
102
+ // strip multibase prefix
103
+ return base.encoder.encode(array).substring(1);
104
+ }
105
+
106
+ /**
107
+ * Create a `Uint8Array` from the passed string
108
+ *
109
+ * Supports `utf8`, `utf-8`, `hex`, and any encoding supported by the multiformats module.
110
+ *
111
+ * Also `ascii` which is similar to node's 'binary' encoding.
112
+ */
113
+ function fromString(string, encoding = 'utf8') {
114
+ const base = BASES[encoding];
115
+ if (base == null) {
116
+ throw new Error(`Unsupported encoding "${encoding}"`);
117
+ }
118
+ if ((encoding === 'utf8' || encoding === 'utf-8') && globalThis.Buffer != null && globalThis.Buffer.from != null) {
119
+ return asUint8Array(globalThis.Buffer.from(string, 'utf-8'));
120
+ }
121
+ // add multibase prefix
122
+ return base.decoder.decode(`${base.prefix}${string}`); // eslint-disable-line @typescript-eslint/restrict-template-expressions
123
+ }
124
+
125
+ var Protocols;
126
+ (function (Protocols) {
127
+ Protocols["Relay"] = "relay";
128
+ Protocols["Store"] = "store";
129
+ Protocols["LightPush"] = "lightpush";
130
+ Protocols["Filter"] = "filter";
131
+ })(Protocols || (Protocols = {}));
132
+ var SendError;
133
+ (function (SendError) {
134
+ /** Could not determine the origin of the fault. Best to check connectivity and try again */
135
+ SendError["GENERIC_FAIL"] = "Generic error";
136
+ /**
137
+ * Failure to protobuf encode the message. This is not recoverable and needs
138
+ * further investigation.
139
+ */
140
+ SendError["ENCODE_FAILED"] = "Failed to encode";
141
+ /**
142
+ * Failure to protobuf decode the message. May be due to a remote peer issue,
143
+ * ensuring that messages are sent via several peer enable mitigation of this error.
144
+ */
145
+ SendError["DECODE_FAILED"] = "Failed to decode";
146
+ /**
147
+ * The message payload is empty, making the message invalid. Ensure that a non-empty
148
+ * payload is set on the outgoing message.
149
+ */
150
+ SendError["EMPTY_PAYLOAD"] = "Payload is empty";
151
+ /**
152
+ * The message size is above the maximum message size allowed on the Waku Network.
153
+ * Compressing the message or using an alternative strategy for large messages is recommended.
154
+ */
155
+ SendError["SIZE_TOO_BIG"] = "Size is too big";
156
+ /**
157
+ * The PubsubTopic passed to the send function is not configured on the Waku node.
158
+ * Please ensure that the PubsubTopic is used when initializing the Waku node.
159
+ */
160
+ SendError["TOPIC_NOT_CONFIGURED"] = "Topic not configured";
161
+ /**
162
+ * Failure to find a peer with suitable protocols. This may due to a connection issue.
163
+ * Mitigation can be: retrying after a given time period, display connectivity issue
164
+ * to user or listening for `peer:connected:bootstrap` or `peer:connected:peer-exchange`
165
+ * on the connection manager before retrying.
166
+ */
167
+ SendError["NO_PEER_AVAILABLE"] = "No peer available";
168
+ /**
169
+ * The remote peer did not behave as expected. Mitigation for `NO_PEER_AVAILABLE`
170
+ * or `DECODE_FAILED` can be used.
171
+ */
172
+ SendError["REMOTE_PEER_FAULT"] = "Remote peer fault";
173
+ /**
174
+ * The remote peer rejected the message. Information provided by the remote peer
175
+ * is logged. Review message validity, or mitigation for `NO_PEER_AVAILABLE`
176
+ * or `DECODE_FAILED` can be used.
177
+ */
178
+ SendError["REMOTE_PEER_REJECTED"] = "Remote peer rejected";
179
+ })(SendError || (SendError = {}));
180
+
181
+ var PageDirection;
182
+ (function (PageDirection) {
183
+ PageDirection["BACKWARD"] = "backward";
184
+ PageDirection["FORWARD"] = "forward";
185
+ })(PageDirection || (PageDirection = {}));
186
+
187
+ var Tags;
188
+ (function (Tags) {
189
+ Tags["BOOTSTRAP"] = "bootstrap";
190
+ Tags["PEER_EXCHANGE"] = "peer-exchange";
191
+ })(Tags || (Tags = {}));
192
+ var EPeersByDiscoveryEvents;
193
+ (function (EPeersByDiscoveryEvents) {
194
+ EPeersByDiscoveryEvents["PEER_DISCOVERY_BOOTSTRAP"] = "peer:discovery:bootstrap";
195
+ EPeersByDiscoveryEvents["PEER_DISCOVERY_PEER_EXCHANGE"] = "peer:discovery:peer-exchange";
196
+ EPeersByDiscoveryEvents["PEER_CONNECT_BOOTSTRAP"] = "peer:connected:bootstrap";
197
+ EPeersByDiscoveryEvents["PEER_CONNECT_PEER_EXCHANGE"] = "peer:connected:peer-exchange";
198
+ })(EPeersByDiscoveryEvents || (EPeersByDiscoveryEvents = {}));
199
+ var EConnectionStateEvents;
200
+ (function (EConnectionStateEvents) {
201
+ EConnectionStateEvents["CONNECTION_STATUS"] = "waku:connection";
202
+ })(EConnectionStateEvents || (EConnectionStateEvents = {}));
203
+
204
+ /**
205
+ * Decode byte array to utf-8 string.
206
+ */
207
+ const bytesToUtf8 = (b) => toString(b, "utf8");
208
+ /**
209
+ * Encode utf-8 string to byte array.
210
+ */
211
+ const utf8ToBytes = (s) => fromString(s, "utf8");
212
+ /**
213
+ * Concatenate using Uint8Arrays as `Buffer` has a different behavior with `DataView`
214
+ */
215
+ function concat(byteArrays, totalLength) {
216
+ const len = totalLength ?? byteArrays.reduce((acc, curr) => acc + curr.length, 0);
217
+ const res = new Uint8Array(len);
218
+ let offset = 0;
219
+ for (const bytes of byteArrays) {
220
+ res.set(bytes, offset);
221
+ offset += bytes.length;
222
+ }
223
+ return res;
224
+ }
225
+
226
+ /**
227
+ * Returns a pseudo-random peer that supports the given protocol.
228
+ * Useful for protocols such as store and light push
229
+ */
230
+ function selectRandomPeer(peers) {
231
+ if (peers.length === 0)
232
+ return;
233
+ const index = Math.round(Math.random() * (peers.length - 1));
234
+ return peers[index];
235
+ }
236
+ /**
237
+ * Returns the peer with the lowest latency.
238
+ * @param peerStore - The Libp2p PeerStore
239
+ * @param peers - The list of peers to choose from
240
+ * @returns The peer with the lowest latency, or undefined if no peer could be reached
241
+ */
242
+ async function selectLowestLatencyPeer(peerStore, peers) {
243
+ if (peers.length === 0)
244
+ return;
245
+ const results = await Promise.all(peers.map(async (peer) => {
246
+ const pingBytes = (await peerStore.get(peer.id)).metadata.get("ping");
247
+ if (!pingBytes)
248
+ return { peer, ping: Infinity };
249
+ const ping = Number(bytesToUtf8(pingBytes)) ?? Infinity;
250
+ return { peer, ping };
251
+ }));
252
+ const lowestLatencyResult = results.sort((a, b) => a.ping - b.ping)[0];
253
+ if (!lowestLatencyResult) {
254
+ return undefined;
255
+ }
256
+ return lowestLatencyResult.ping !== Infinity
257
+ ? lowestLatencyResult.peer
258
+ : undefined;
259
+ }
260
+ /**
261
+ * Returns the list of peers that supports the given protocol.
262
+ */
263
+ async function getPeersForProtocol(peerStore, protocols) {
264
+ const peers = [];
265
+ await peerStore.forEach((peer) => {
266
+ for (let i = 0; i < protocols.length; i++) {
267
+ if (peer.protocols.includes(protocols[i])) {
268
+ peers.push(peer);
269
+ break;
270
+ }
271
+ }
272
+ });
273
+ return peers;
274
+ }
275
+ /**
276
+ * Returns a peer that supports the given protocol.
277
+ * If peerId is provided, the peer with that id is returned.
278
+ * Otherwise, the peer with the lowest latency is returned.
279
+ * If no peer is found from the above criteria, a random peer is returned.
280
+ */
281
+ async function selectPeerForProtocol(peerStore, protocols, peerId) {
282
+ let peer;
283
+ if (peerId) {
284
+ peer = await peerStore.get(peerId);
285
+ if (!peer) {
286
+ throw new Error(`Failed to retrieve connection details for provided peer in peer store: ${peerId.toString()}`);
287
+ }
288
+ }
289
+ else {
290
+ const peers = await getPeersForProtocol(peerStore, protocols);
291
+ peer = await selectLowestLatencyPeer(peerStore, peers);
292
+ if (!peer) {
293
+ peer = selectRandomPeer(peers);
294
+ if (!peer)
295
+ throw new Error(`Failed to find known peer that registers protocols: ${protocols}`);
296
+ }
297
+ }
298
+ let protocol;
299
+ for (const codec of protocols) {
300
+ if (peer.protocols.includes(codec)) {
301
+ protocol = codec;
302
+ // Do not break as we want to keep the last value
303
+ }
304
+ }
305
+ if (!protocol) {
306
+ throw new Error(`Peer does not register required protocols (${peer.id.toString()}): ${protocols}`);
307
+ }
308
+ return { peer, protocol };
309
+ }
310
+ function selectConnection(connections) {
311
+ if (!connections.length)
312
+ return;
313
+ if (connections.length === 1)
314
+ return connections[0];
315
+ let latestConnection;
316
+ connections.forEach((connection) => {
317
+ if (connection.status === "open") {
318
+ if (!latestConnection) {
319
+ latestConnection = connection;
320
+ }
321
+ else if (connection.timeline.open > latestConnection.timeline.open) {
322
+ latestConnection = connection;
323
+ }
324
+ }
325
+ });
326
+ return latestConnection;
327
+ }
328
+
329
+ /**
330
+ * Retrieves a list of peers based on the specified criteria.
331
+ *
332
+ * @param peers - The list of peers to filter from.
333
+ * @param numPeers - The total number of peers to retrieve. If 0, all peers are returned.
334
+ * @param maxBootstrapPeers - The maximum number of bootstrap peers to retrieve.
335
+ * @returns A Promise that resolves to an array of peers based on the specified criteria.
336
+ */
337
+ async function filterPeers(peers, numPeers, maxBootstrapPeers) {
338
+ // Collect the bootstrap peers up to the specified maximum
339
+ const bootstrapPeers = peers
340
+ .filter((peer) => peer.tags.has(Tags.BOOTSTRAP))
341
+ .slice(0, maxBootstrapPeers);
342
+ // Collect non-bootstrap peers
343
+ const nonBootstrapPeers = peers.filter((peer) => !peer.tags.has(Tags.BOOTSTRAP));
344
+ // If numPeers is 0, return all peers
345
+ if (numPeers === 0) {
346
+ return [...bootstrapPeers, ...nonBootstrapPeers];
347
+ }
348
+ // Initialize the list of selected peers with the bootstrap peers
349
+ const selectedPeers = [...bootstrapPeers];
350
+ // Fill up to numPeers with remaining random peers if needed
351
+ while (selectedPeers.length < numPeers && nonBootstrapPeers.length > 0) {
352
+ const randomIndex = Math.floor(Math.random() * nonBootstrapPeers.length);
353
+ const randomPeer = nonBootstrapPeers.splice(randomIndex, 1)[0];
354
+ selectedPeers.push(randomPeer);
355
+ }
356
+ return selectedPeers;
357
+ }
358
+
359
+ class StreamManager {
360
+ multicodec;
361
+ getConnections;
362
+ addEventListener;
363
+ streamPool;
364
+ log;
365
+ constructor(multicodec, getConnections, addEventListener) {
366
+ this.multicodec = multicodec;
367
+ this.getConnections = getConnections;
368
+ this.addEventListener = addEventListener;
369
+ this.log = new Logger(`stream-manager:${multicodec}`);
370
+ this.addEventListener("peer:update", this.handlePeerUpdateStreamPool.bind(this));
371
+ this.getStream = this.getStream.bind(this);
372
+ this.streamPool = new Map();
373
+ }
374
+ async getStream(peer) {
375
+ const peerIdStr = peer.id.toString();
376
+ const streamPromise = this.streamPool.get(peerIdStr);
377
+ if (!streamPromise) {
378
+ return this.newStream(peer); // fallback by creating a new stream on the spot
379
+ }
380
+ // We have the stream, let's remove it from the map
381
+ this.streamPool.delete(peerIdStr);
382
+ this.prepareNewStream(peer);
383
+ const stream = await streamPromise;
384
+ if (!stream || stream.status === "closed") {
385
+ return this.newStream(peer); // fallback by creating a new stream on the spot
386
+ }
387
+ return stream;
388
+ }
389
+ async newStream(peer) {
390
+ const connections = this.getConnections(peer.id);
391
+ const connection = selectConnection(connections);
392
+ if (!connection) {
393
+ throw new Error("Failed to get a connection to the peer");
394
+ }
395
+ return connection.newStream(this.multicodec);
396
+ }
397
+ prepareNewStream(peer) {
398
+ const streamPromise = this.newStream(peer).catch(() => {
399
+ // No error thrown as this call is not triggered by the user
400
+ this.log.error(`Failed to prepare a new stream for ${peer.id.toString()}`);
401
+ });
402
+ this.streamPool.set(peer.id.toString(), streamPromise);
403
+ }
404
+ handlePeerUpdateStreamPool = (evt) => {
405
+ const peer = evt.detail.peer;
406
+ if (peer.protocols.includes(this.multicodec)) {
407
+ this.log.info(`Preemptively opening a stream to ${peer.id.toString()}`);
408
+ this.prepareNewStream(peer);
409
+ }
410
+ };
411
+ }
412
+
413
+ /**
414
+ * A class with predefined helpers, to be used as a base to implement Waku
415
+ * Protocols.
416
+ */
417
+ class BaseProtocol {
418
+ multicodec;
419
+ components;
420
+ addLibp2pEventListener;
421
+ removeLibp2pEventListener;
422
+ streamManager;
423
+ constructor(multicodec, components) {
424
+ this.multicodec = multicodec;
425
+ this.components = components;
426
+ this.addLibp2pEventListener = components.events.addEventListener.bind(components.events);
427
+ this.removeLibp2pEventListener = components.events.removeEventListener.bind(components.events);
428
+ this.streamManager = new StreamManager(multicodec, components.connectionManager.getConnections.bind(components.connectionManager), this.addLibp2pEventListener);
429
+ }
430
+ async getStream(peer) {
431
+ return this.streamManager.getStream(peer);
432
+ }
433
+ get peerStore() {
434
+ return this.components.peerStore;
435
+ }
436
+ /**
437
+ * Returns known peers from the address book (`libp2p.peerStore`) that support
438
+ * the class protocol. Waku may or may not be currently connected to these
439
+ * peers.
440
+ */
441
+ async peers() {
442
+ return getPeersForProtocol(this.peerStore, [this.multicodec]);
443
+ }
444
+ async getPeer(peerId) {
445
+ const { peer } = await selectPeerForProtocol(this.peerStore, [this.multicodec], peerId);
446
+ return peer;
447
+ }
448
+ /**
449
+ * Retrieves a list of peers based on the specified criteria.
450
+ *
451
+ * @param numPeers - The total number of peers to retrieve. If 0, all peers are returned.
452
+ * @param maxBootstrapPeers - The maximum number of bootstrap peers to retrieve.
453
+ * @returns A Promise that resolves to an array of peers based on the specified criteria.
454
+ */
455
+ async getPeers({ numPeers, maxBootstrapPeers } = {
456
+ maxBootstrapPeers: 1,
457
+ numPeers: 0
458
+ }) {
459
+ // Retrieve all peers that support the protocol
460
+ const allPeersForProtocol = await getPeersForProtocol(this.peerStore, [
461
+ this.multicodec
462
+ ]);
463
+ // Filter the peers based on the specified criteria
464
+ return filterPeers(allPeersForProtocol, numPeers, maxBootstrapPeers);
465
+ }
466
+ }
467
+
468
+ export { BaseProtocol as B, EConnectionStateEvents as E, Protocols as P, SendError as S, Tags as T, allocUnsafe as a, asUint8Array as b, bases as c, EPeersByDiscoveryEvents as d, alloc as e, fromString as f, concat as g, StreamManager as h, toString as t, utf8ToBytes as u };