@waku/core 0.0.36-383e0b2.0 → 0.0.36-4c18ca2.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.
- package/bundle/index.js +1098 -593
- package/bundle/lib/message/version_0.js +1 -2
- package/bundle/{version_0-4TwtF5aQ.js → version_0-9DPFjcJG.js} +1661 -65
- package/dist/.tsbuildinfo +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/filter/filter.d.ts +8 -5
- package/dist/lib/filter/filter.js +30 -10
- package/dist/lib/filter/filter.js.map +1 -1
- package/dist/lib/light_push/light_push.d.ts +4 -3
- package/dist/lib/light_push/light_push.js +6 -4
- package/dist/lib/light_push/light_push.js.map +1 -1
- package/dist/lib/message/version_0.d.ts +3 -4
- package/dist/lib/message/version_0.js +1 -4
- package/dist/lib/message/version_0.js.map +1 -1
- package/dist/lib/message_hash/index.d.ts +1 -0
- package/dist/lib/message_hash/index.js +2 -0
- package/dist/lib/message_hash/index.js.map +1 -0
- package/dist/lib/message_hash/message_hash.d.ts +52 -0
- package/dist/lib/message_hash/message_hash.js +84 -0
- package/dist/lib/message_hash/message_hash.js.map +1 -0
- package/dist/lib/metadata/metadata.js +6 -4
- package/dist/lib/metadata/metadata.js.map +1 -1
- package/dist/lib/store/rpc.js +0 -4
- package/dist/lib/store/rpc.js.map +1 -1
- package/dist/lib/store/store.d.ts +4 -3
- package/dist/lib/store/store.js +6 -4
- package/dist/lib/store/store.js.map +1 -1
- package/dist/lib/stream_manager/stream_manager.d.ts +3 -4
- package/dist/lib/stream_manager/stream_manager.js +6 -8
- package/dist/lib/stream_manager/stream_manager.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +2 -0
- package/src/lib/filter/filter.ts +46 -14
- package/src/lib/light_push/light_push.ts +8 -5
- package/src/lib/message/version_0.ts +3 -7
- package/src/lib/message_hash/index.ts +1 -0
- package/src/lib/message_hash/message_hash.ts +106 -0
- package/src/lib/metadata/metadata.ts +8 -5
- package/src/lib/store/rpc.ts +0 -4
- package/src/lib/store/store.ts +8 -5
- package/src/lib/stream_manager/stream_manager.ts +8 -6
- package/bundle/base_protocol-DvQrudwy.js +0 -152
- package/bundle/index-CTo1my9M.js +0 -1543
- package/bundle/lib/base_protocol.js +0 -2
- package/dist/lib/base_protocol.d.ts +0 -18
- package/dist/lib/base_protocol.js +0 -25
- package/dist/lib/base_protocol.js.map +0 -1
- package/src/lib/base_protocol.ts +0 -44
package/bundle/index.js
CHANGED
@@ -1,8 +1,5 @@
|
|
1
|
-
import { v as version_0,
|
2
|
-
export {
|
3
|
-
import { e as equals$2, c as coerce, b as base32, a as base58btc, d as base36, L as Logger, P as ProtocolError, T as Tags, E as EPeersByDiscoveryEvents, f as EConnectionStateEvents } from './index-CTo1my9M.js';
|
4
|
-
import { B as BaseProtocol } from './base_protocol-DvQrudwy.js';
|
5
|
-
export { S as StreamManager } from './base_protocol-DvQrudwy.js';
|
1
|
+
import { e as equals$2, c as coerce, b as base32, a as base58btc, d as base36, v as version_0, f as allocUnsafe, g as alloc, h as encodingLength$1, i as encode$2, j as decode$4, L as Logger, F as FilterSubscribeRequest, k as FilterSubscribeResponse$1, M as MessagePush, P as ProtocolError, l as PushRpc$1, m as PushResponse, S as StoreQueryRequest$1, n as StoreQueryResponse$1, o as bases, t as toString, p as fromString, q as base64url, r as encodeUint8Array, u as utf8ToBytes, s as createEncoder, w as pubsubTopicToSingleShardInfo, x as bytesToUtf8, T as Tags, E as EPeersByDiscoveryEvents, y as shardInfoToPubsubTopics, z as EConnectionStateEvents, W as WakuMetadataRequest, A as pubsubTopicsToShardInfo, B as WakuMetadataResponse, C as concat$1, D as sha256, G as bytesToHex, H as numberToBytes } from './version_0-9DPFjcJG.js';
|
2
|
+
export { I as createDecoder } from './version_0-9DPFjcJG.js';
|
6
3
|
|
7
4
|
/* eslint-disable */
|
8
5
|
var encode_1 = encode$1;
|
@@ -514,6 +511,10 @@ function encodeCID(version, code, multihash) {
|
|
514
511
|
}
|
515
512
|
const cidSymbol = Symbol.for('@ipld/js-cid/CID');
|
516
513
|
|
514
|
+
function isDefined(value) {
|
515
|
+
return Boolean(value);
|
516
|
+
}
|
517
|
+
|
517
518
|
const MB = 1024 ** 2;
|
518
519
|
const SIZE_CAP_IN_MB = 1;
|
519
520
|
/**
|
@@ -2008,25 +2009,38 @@ function queuelessPushable() {
|
|
2008
2009
|
function isAsyncIterable$1(thing) {
|
2009
2010
|
return thing[Symbol.asyncIterator] != null;
|
2010
2011
|
}
|
2011
|
-
async function addAllToPushable(sources, output) {
|
2012
|
+
async function addAllToPushable(sources, output, signal) {
|
2012
2013
|
try {
|
2013
2014
|
await Promise.all(sources.map(async (source) => {
|
2014
2015
|
for await (const item of source) {
|
2015
|
-
await output.push(item
|
2016
|
+
await output.push(item, {
|
2017
|
+
signal
|
2018
|
+
});
|
2019
|
+
signal.throwIfAborted();
|
2016
2020
|
}
|
2017
2021
|
}));
|
2018
|
-
await output.end(
|
2022
|
+
await output.end(undefined, {
|
2023
|
+
signal
|
2024
|
+
});
|
2019
2025
|
}
|
2020
2026
|
catch (err) {
|
2021
|
-
await output.end(err
|
2027
|
+
await output.end(err, {
|
2028
|
+
signal
|
2029
|
+
})
|
2022
2030
|
.catch(() => { });
|
2023
2031
|
}
|
2024
2032
|
}
|
2025
2033
|
async function* mergeSources(sources) {
|
2034
|
+
const controller = new AbortController();
|
2026
2035
|
const output = queuelessPushable();
|
2027
|
-
addAllToPushable(sources, output)
|
2036
|
+
addAllToPushable(sources, output, controller.signal)
|
2028
2037
|
.catch(() => { });
|
2029
|
-
|
2038
|
+
try {
|
2039
|
+
yield* output;
|
2040
|
+
}
|
2041
|
+
finally {
|
2042
|
+
controller.abort();
|
2043
|
+
}
|
2030
2044
|
}
|
2031
2045
|
function* mergeSyncSources(syncSources) {
|
2032
2046
|
for (const source of syncSources) {
|
@@ -2132,6 +2146,129 @@ const duplexPipelineFn = (duplex) => {
|
|
2132
2146
|
};
|
2133
2147
|
};
|
2134
2148
|
|
2149
|
+
function selectOpenConnection(connections) {
|
2150
|
+
return connections
|
2151
|
+
.filter((c) => c.status === "open")
|
2152
|
+
.sort((left, right) => right.timeline.open - left.timeline.open)
|
2153
|
+
.at(0);
|
2154
|
+
}
|
2155
|
+
|
2156
|
+
const STREAM_LOCK_KEY = "consumed";
|
2157
|
+
class StreamManager {
|
2158
|
+
multicodec;
|
2159
|
+
libp2p;
|
2160
|
+
log;
|
2161
|
+
ongoingCreation = new Set();
|
2162
|
+
streamPool = new Map();
|
2163
|
+
constructor(multicodec, libp2p) {
|
2164
|
+
this.multicodec = multicodec;
|
2165
|
+
this.libp2p = libp2p;
|
2166
|
+
this.log = new Logger(`stream-manager:${multicodec}`);
|
2167
|
+
this.libp2p.events.addEventListener("peer:update", this.handlePeerUpdateStreamPool);
|
2168
|
+
}
|
2169
|
+
async getStream(peerId) {
|
2170
|
+
const peerIdStr = peerId.toString();
|
2171
|
+
const scheduledStream = this.streamPool.get(peerIdStr);
|
2172
|
+
if (scheduledStream) {
|
2173
|
+
this.streamPool.delete(peerIdStr);
|
2174
|
+
await scheduledStream;
|
2175
|
+
}
|
2176
|
+
let stream = this.getOpenStreamForCodec(peerId);
|
2177
|
+
if (stream) {
|
2178
|
+
this.log.info(`Found existing stream peerId=${peerIdStr} multicodec=${this.multicodec}`);
|
2179
|
+
this.lockStream(peerIdStr, stream);
|
2180
|
+
return stream;
|
2181
|
+
}
|
2182
|
+
stream = await this.createStream(peerId);
|
2183
|
+
this.lockStream(peerIdStr, stream);
|
2184
|
+
return stream;
|
2185
|
+
}
|
2186
|
+
async createStream(peerId, retries = 0) {
|
2187
|
+
const connections = this.libp2p.connectionManager.getConnections(peerId);
|
2188
|
+
const connection = selectOpenConnection(connections);
|
2189
|
+
if (!connection) {
|
2190
|
+
throw new Error(`Failed to get a connection to the peer peerId=${peerId.toString()} multicodec=${this.multicodec}`);
|
2191
|
+
}
|
2192
|
+
let lastError;
|
2193
|
+
let stream;
|
2194
|
+
for (let i = 0; i < retries + 1; i++) {
|
2195
|
+
try {
|
2196
|
+
this.log.info(`Attempting to create a stream for peerId=${peerId.toString()} multicodec=${this.multicodec}`);
|
2197
|
+
stream = await connection.newStream(this.multicodec);
|
2198
|
+
this.log.info(`Created stream for peerId=${peerId.toString()} multicodec=${this.multicodec}`);
|
2199
|
+
break;
|
2200
|
+
}
|
2201
|
+
catch (error) {
|
2202
|
+
lastError = error;
|
2203
|
+
}
|
2204
|
+
}
|
2205
|
+
if (!stream) {
|
2206
|
+
throw new Error(`Failed to create a new stream for ${peerId.toString()} -- ` + lastError);
|
2207
|
+
}
|
2208
|
+
return stream;
|
2209
|
+
}
|
2210
|
+
async createStreamWithLock(peer) {
|
2211
|
+
const peerId = peer.id.toString();
|
2212
|
+
if (this.ongoingCreation.has(peerId)) {
|
2213
|
+
this.log.info(`Skipping creation of a stream due to lock for peerId=${peerId} multicodec=${this.multicodec}`);
|
2214
|
+
return;
|
2215
|
+
}
|
2216
|
+
try {
|
2217
|
+
this.ongoingCreation.add(peerId);
|
2218
|
+
await this.createStream(peer.id);
|
2219
|
+
}
|
2220
|
+
catch (error) {
|
2221
|
+
this.log.error(`Failed to createStreamWithLock:`, error);
|
2222
|
+
}
|
2223
|
+
finally {
|
2224
|
+
this.ongoingCreation.delete(peerId);
|
2225
|
+
}
|
2226
|
+
return;
|
2227
|
+
}
|
2228
|
+
handlePeerUpdateStreamPool = (evt) => {
|
2229
|
+
const { peer } = evt.detail;
|
2230
|
+
if (!peer.protocols.includes(this.multicodec)) {
|
2231
|
+
return;
|
2232
|
+
}
|
2233
|
+
const stream = this.getOpenStreamForCodec(peer.id);
|
2234
|
+
if (stream) {
|
2235
|
+
return;
|
2236
|
+
}
|
2237
|
+
this.scheduleNewStream(peer);
|
2238
|
+
};
|
2239
|
+
scheduleNewStream(peer) {
|
2240
|
+
this.log.info(`Scheduling creation of a stream for peerId=${peer.id.toString()} multicodec=${this.multicodec}`);
|
2241
|
+
// abandon previous attempt
|
2242
|
+
if (this.streamPool.has(peer.id.toString())) {
|
2243
|
+
this.streamPool.delete(peer.id.toString());
|
2244
|
+
}
|
2245
|
+
this.streamPool.set(peer.id.toString(), this.createStreamWithLock(peer));
|
2246
|
+
}
|
2247
|
+
getOpenStreamForCodec(peerId) {
|
2248
|
+
const connections = this.libp2p.connectionManager.getConnections(peerId);
|
2249
|
+
const connection = selectOpenConnection(connections);
|
2250
|
+
if (!connection) {
|
2251
|
+
return;
|
2252
|
+
}
|
2253
|
+
const stream = connection.streams.find((s) => s.protocol === this.multicodec);
|
2254
|
+
if (!stream) {
|
2255
|
+
return;
|
2256
|
+
}
|
2257
|
+
const isStreamUnusable = ["done", "closed", "closing"].includes(stream.writeStatus || "");
|
2258
|
+
if (isStreamUnusable || this.isStreamLocked(stream)) {
|
2259
|
+
return;
|
2260
|
+
}
|
2261
|
+
return stream;
|
2262
|
+
}
|
2263
|
+
lockStream(peerId, stream) {
|
2264
|
+
this.log.info(`Locking stream for peerId:${peerId}\tstreamId:${stream.id}`);
|
2265
|
+
stream.metadata[STREAM_LOCK_KEY] = true;
|
2266
|
+
}
|
2267
|
+
isStreamLocked(stream) {
|
2268
|
+
return !!stream.metadata[STREAM_LOCK_KEY];
|
2269
|
+
}
|
2270
|
+
}
|
2271
|
+
|
2135
2272
|
// Unique ID creation requires a high quality random # generator. In the browser we therefore
|
2136
2273
|
// require the crypto API and do not support built-in fallback to lower quality random number
|
2137
2274
|
// generators (like Math.random()).
|
@@ -2299,13 +2436,33 @@ const FilterCodecs = {
|
|
2299
2436
|
SUBSCRIBE: "/vac/waku/filter-subscribe/2.0.0-beta1",
|
2300
2437
|
PUSH: "/vac/waku/filter-push/2.0.0-beta1"
|
2301
2438
|
};
|
2302
|
-
class FilterCore
|
2303
|
-
handleIncomingMessage;
|
2439
|
+
class FilterCore {
|
2304
2440
|
pubsubTopics;
|
2441
|
+
streamManager;
|
2442
|
+
static handleIncomingMessage;
|
2443
|
+
multicodec = FilterCodecs.SUBSCRIBE;
|
2305
2444
|
constructor(handleIncomingMessage, pubsubTopics, libp2p) {
|
2306
|
-
super(FilterCodecs.SUBSCRIBE, libp2p.components, pubsubTopics);
|
2307
|
-
this.handleIncomingMessage = handleIncomingMessage;
|
2308
2445
|
this.pubsubTopics = pubsubTopics;
|
2446
|
+
this.streamManager = new StreamManager(FilterCodecs.SUBSCRIBE, libp2p.components);
|
2447
|
+
// TODO(weboko): remove when @waku/sdk 0.0.33 is released
|
2448
|
+
const prevHandler = FilterCore.handleIncomingMessage;
|
2449
|
+
FilterCore.handleIncomingMessage = !prevHandler
|
2450
|
+
? handleIncomingMessage
|
2451
|
+
: async (pubsubTopic, message, peerIdStr) => {
|
2452
|
+
try {
|
2453
|
+
await prevHandler(pubsubTopic, message, peerIdStr);
|
2454
|
+
}
|
2455
|
+
catch (e) {
|
2456
|
+
log$5.error("Previous FilterCore incoming message handler failed ", e);
|
2457
|
+
}
|
2458
|
+
try {
|
2459
|
+
await handleIncomingMessage(pubsubTopic, message, peerIdStr);
|
2460
|
+
}
|
2461
|
+
catch (e) {
|
2462
|
+
log$5.error("Present FilterCore incoming message handler failed ", e);
|
2463
|
+
}
|
2464
|
+
return;
|
2465
|
+
};
|
2309
2466
|
libp2p
|
2310
2467
|
.handle(FilterCodecs.PUSH, this.onRequest.bind(this), {
|
2311
2468
|
maxInboundStreams: 100
|
@@ -2315,7 +2472,7 @@ class FilterCore extends BaseProtocol {
|
|
2315
2472
|
});
|
2316
2473
|
}
|
2317
2474
|
async subscribe(pubsubTopic, peerId, contentTopics) {
|
2318
|
-
const stream = await this.getStream(peerId);
|
2475
|
+
const stream = await this.streamManager.getStream(peerId);
|
2319
2476
|
const request = FilterSubscribeRpc.createSubscribeRequest(pubsubTopic, contentTopics);
|
2320
2477
|
let res;
|
2321
2478
|
try {
|
@@ -2350,7 +2507,7 @@ class FilterCore extends BaseProtocol {
|
|
2350
2507
|
async unsubscribe(pubsubTopic, peerId, contentTopics) {
|
2351
2508
|
let stream;
|
2352
2509
|
try {
|
2353
|
-
stream = await this.getStream(peerId);
|
2510
|
+
stream = await this.streamManager.getStream(peerId);
|
2354
2511
|
}
|
2355
2512
|
catch (error) {
|
2356
2513
|
log$5.error(`Failed to get a stream for remote peer${peerId.toString()}`, error);
|
@@ -2382,7 +2539,7 @@ class FilterCore extends BaseProtocol {
|
|
2382
2539
|
};
|
2383
2540
|
}
|
2384
2541
|
async unsubscribeAll(pubsubTopic, peerId) {
|
2385
|
-
const stream = await this.getStream(peerId);
|
2542
|
+
const stream = await this.streamManager.getStream(peerId);
|
2386
2543
|
const request = FilterSubscribeRpc.createUnsubscribeAllRequest(pubsubTopic);
|
2387
2544
|
const res = await pipe([request.encode()], encode, stream, decode, async (source) => await all(source));
|
2388
2545
|
if (!res || !res.length) {
|
@@ -2413,7 +2570,7 @@ class FilterCore extends BaseProtocol {
|
|
2413
2570
|
async ping(peerId) {
|
2414
2571
|
let stream;
|
2415
2572
|
try {
|
2416
|
-
stream = await this.getStream(peerId);
|
2573
|
+
stream = await this.streamManager.getStream(peerId);
|
2417
2574
|
}
|
2418
2575
|
catch (error) {
|
2419
2576
|
log$5.error(`Failed to get a stream for remote peer${peerId.toString()}`, error);
|
@@ -2482,7 +2639,7 @@ class FilterCore extends BaseProtocol {
|
|
2482
2639
|
log$5.error("Pubsub topic missing from push message");
|
2483
2640
|
return;
|
2484
2641
|
}
|
2485
|
-
await
|
2642
|
+
await FilterCore.handleIncomingMessage?.(pubsubTopic, wakuMessage, connection.remotePeer.toString());
|
2486
2643
|
}
|
2487
2644
|
}).then(() => {
|
2488
2645
|
log$5.info("Receiving pipe closed.");
|
@@ -2555,11 +2712,13 @@ const LightPushCodec = "/vac/waku/lightpush/2.0.0-beta1";
|
|
2555
2712
|
/**
|
2556
2713
|
* Implements the [Waku v2 Light Push protocol](https://rfc.vac.dev/spec/19/).
|
2557
2714
|
*/
|
2558
|
-
class LightPushCore
|
2715
|
+
class LightPushCore {
|
2559
2716
|
pubsubTopics;
|
2717
|
+
streamManager;
|
2718
|
+
multicodec = LightPushCodec;
|
2560
2719
|
constructor(pubsubTopics, libp2p) {
|
2561
|
-
super(LightPushCodec, libp2p.components, pubsubTopics);
|
2562
2720
|
this.pubsubTopics = pubsubTopics;
|
2721
|
+
this.streamManager = new StreamManager(LightPushCodec, libp2p.components);
|
2563
2722
|
}
|
2564
2723
|
async preparePushMessage(encoder, message) {
|
2565
2724
|
try {
|
@@ -2603,7 +2762,7 @@ class LightPushCore extends BaseProtocol {
|
|
2603
2762
|
}
|
2604
2763
|
let stream;
|
2605
2764
|
try {
|
2606
|
-
stream = await this.getStream(peerId);
|
2765
|
+
stream = await this.streamManager.getStream(peerId);
|
2607
2766
|
}
|
2608
2767
|
catch (error) {
|
2609
2768
|
log$4.error("Failed to get stream", error);
|
@@ -2727,19 +2886,15 @@ class StoreQueryRequest {
|
|
2727
2886
|
? BigInt(params.paginationLimit)
|
2728
2887
|
: undefined
|
2729
2888
|
});
|
2730
|
-
// Validate request parameters based on RFC
|
2731
2889
|
const isHashQuery = params.messageHashes && params.messageHashes.length > 0;
|
2732
2890
|
const hasContentTopics = params.contentTopics && params.contentTopics.length > 0;
|
2733
2891
|
const hasTimeFilter = params.timeStart || params.timeEnd;
|
2734
2892
|
if (isHashQuery) {
|
2735
|
-
// Message hash lookup queries cannot include content topics or time filters
|
2736
|
-
// but pubsubTopic is allowed/required
|
2737
2893
|
if (hasContentTopics || hasTimeFilter) {
|
2738
2894
|
throw new Error("Message hash lookup queries cannot include content filter criteria (contentTopics, timeStart, or timeEnd)");
|
2739
2895
|
}
|
2740
2896
|
}
|
2741
2897
|
else {
|
2742
|
-
// Content-filtered queries require both pubsubTopic and contentTopics to be set together
|
2743
2898
|
if ((params.pubsubTopic &&
|
2744
2899
|
(!params.contentTopics || params.contentTopics.length === 0)) ||
|
2745
2900
|
(!params.pubsubTopic &&
|
@@ -2786,11 +2941,13 @@ class StoreQueryResponse {
|
|
2786
2941
|
|
2787
2942
|
const log$3 = new Logger("store");
|
2788
2943
|
const StoreCodec = "/vac/waku/store-query/3.0.0";
|
2789
|
-
class StoreCore
|
2944
|
+
class StoreCore {
|
2790
2945
|
pubsubTopics;
|
2946
|
+
streamManager;
|
2947
|
+
multicodec = StoreCodec;
|
2791
2948
|
constructor(pubsubTopics, libp2p) {
|
2792
|
-
super(StoreCodec, libp2p.components, pubsubTopics);
|
2793
2949
|
this.pubsubTopics = pubsubTopics;
|
2950
|
+
this.streamManager = new StreamManager(StoreCodec, libp2p.components);
|
2794
2951
|
}
|
2795
2952
|
async *queryPerPage(queryOpts, decoders, peerId) {
|
2796
2953
|
// Only validate decoder content topics for content-filtered queries
|
@@ -2815,7 +2972,7 @@ class StoreCore extends BaseProtocol {
|
|
2815
2972
|
});
|
2816
2973
|
let stream;
|
2817
2974
|
try {
|
2818
|
-
stream = await this.getStream(peerId);
|
2975
|
+
stream = await this.streamManager.getStream(peerId);
|
2819
2976
|
}
|
2820
2977
|
catch (e) {
|
2821
2978
|
log$3.error("Failed to get stream", e);
|
@@ -2887,9 +3044,71 @@ function isPeerId(other) {
|
|
2887
3044
|
return Boolean(other?.[peerIdSymbol]);
|
2888
3045
|
}
|
2889
3046
|
|
3047
|
+
/**
|
3048
|
+
* @packageDocumentation
|
3049
|
+
*
|
3050
|
+
* Adds types to the EventTarget class.
|
3051
|
+
*
|
3052
|
+
* Hopefully this won't be necessary
|
3053
|
+
* forever:
|
3054
|
+
*
|
3055
|
+
* - https://github.com/microsoft/TypeScript/issues/28357
|
3056
|
+
* - https://github.com/microsoft/TypeScript/issues/43477
|
3057
|
+
* - https://github.com/microsoft/TypeScript/issues/299
|
3058
|
+
* - https://www.npmjs.com/package/typed-events
|
3059
|
+
* - https://www.npmjs.com/package/typed-event-emitter
|
3060
|
+
* - https://www.npmjs.com/package/typed-event-target
|
3061
|
+
* - etc
|
3062
|
+
*
|
3063
|
+
* In addition to types, a `safeDispatchEvent` method is available which
|
3064
|
+
* prevents dispatching events that aren't in the event map, and a
|
3065
|
+
* `listenerCount` method which reports the number of listeners that are
|
3066
|
+
* currently registered for a given event.
|
3067
|
+
*
|
3068
|
+
* @example
|
3069
|
+
*
|
3070
|
+
* ```ts
|
3071
|
+
* import { TypedEventEmitter } from 'main-event'
|
3072
|
+
* import type { TypedEventTarget } from 'main-event'
|
3073
|
+
*
|
3074
|
+
* interface EventTypes {
|
3075
|
+
* 'test': CustomEvent<string>
|
3076
|
+
* }
|
3077
|
+
*
|
3078
|
+
* const target = new TypedEventEmitter<EventTypes>()
|
3079
|
+
*
|
3080
|
+
* // it's a regular EventTarget
|
3081
|
+
* console.info(target instanceof EventTarget) // true
|
3082
|
+
*
|
3083
|
+
* // register listeners normally
|
3084
|
+
* target.addEventListener('test', (evt) => {
|
3085
|
+
* // evt is CustomEvent<string>
|
3086
|
+
* })
|
3087
|
+
*
|
3088
|
+
* // @ts-expect-error 'derp' is not in the event map
|
3089
|
+
* target.addEventListener('derp', () => {})
|
3090
|
+
*
|
3091
|
+
* // use normal dispatchEvent method
|
3092
|
+
* target.dispatchEvent(new CustomEvent('test', {
|
3093
|
+
* detail: 'hello'
|
3094
|
+
* }))
|
3095
|
+
*
|
3096
|
+
* // use type safe dispatch method
|
3097
|
+
* target.safeDispatchEvent('test', {
|
3098
|
+
* detail: 'world'
|
3099
|
+
* })
|
3100
|
+
*
|
3101
|
+
* // report listener count
|
3102
|
+
* console.info(target.listenerCount('test')) // 0
|
3103
|
+
*
|
3104
|
+
* // event emitters can be used purely as interfaces too
|
3105
|
+
* function acceptTarget (target: TypedEventTarget<EventTypes>) {
|
3106
|
+
* // ...
|
3107
|
+
* }
|
3108
|
+
* ```
|
3109
|
+
*/
|
2890
3110
|
/**
|
2891
3111
|
* An implementation of a typed event target
|
2892
|
-
* etc
|
2893
3112
|
*/
|
2894
3113
|
class TypedEventEmitter extends EventTarget {
|
2895
3114
|
#listeners = new Map();
|
@@ -2939,6 +3158,26 @@ class TypedEventEmitter extends EventTarget {
|
|
2939
3158
|
}
|
2940
3159
|
}
|
2941
3160
|
|
3161
|
+
/**
|
3162
|
+
* Thrown when an invalid multiaddr is encountered
|
3163
|
+
*/
|
3164
|
+
class InvalidMultiaddrError extends Error {
|
3165
|
+
static name = 'InvalidMultiaddrError';
|
3166
|
+
name = 'InvalidMultiaddrError';
|
3167
|
+
}
|
3168
|
+
class ValidationError extends Error {
|
3169
|
+
static name = 'ValidationError';
|
3170
|
+
name = 'ValidationError';
|
3171
|
+
}
|
3172
|
+
class InvalidParametersError extends Error {
|
3173
|
+
static name = 'InvalidParametersError';
|
3174
|
+
name = 'InvalidParametersError';
|
3175
|
+
}
|
3176
|
+
class UnknownProtocolError extends Error {
|
3177
|
+
static name = 'UnknownProtocolError';
|
3178
|
+
name = 'UnknownProtocolError';
|
3179
|
+
}
|
3180
|
+
|
2942
3181
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
2943
3182
|
class Parser {
|
2944
3183
|
index = 0;
|
@@ -3162,24 +3401,6 @@ function parseIPv6(input) {
|
|
3162
3401
|
}
|
3163
3402
|
return parser.new(input).parseWith(() => parser.readIPv6Addr());
|
3164
3403
|
}
|
3165
|
-
/** Parse `input` into IPv4 or IPv6 bytes. */
|
3166
|
-
function parseIP(input, mapIPv4ToIPv6 = false) {
|
3167
|
-
// strip zone index if it is present
|
3168
|
-
if (input.includes("%")) {
|
3169
|
-
input = input.split("%")[0];
|
3170
|
-
}
|
3171
|
-
if (input.length > MAX_IPV6_LENGTH) {
|
3172
|
-
return undefined;
|
3173
|
-
}
|
3174
|
-
const addr = parser.new(input).parseWith(() => parser.readIPAddr());
|
3175
|
-
if (!addr) {
|
3176
|
-
return undefined;
|
3177
|
-
}
|
3178
|
-
if (mapIPv4ToIPv6 && addr.length === 4) {
|
3179
|
-
return Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, addr[0], addr[1], addr[2], addr[3]]);
|
3180
|
-
}
|
3181
|
-
return addr;
|
3182
|
-
}
|
3183
3404
|
|
3184
3405
|
/** Check if `input` is IPv4. */
|
3185
3406
|
function isIPv4(input) {
|
@@ -3189,347 +3410,69 @@ function isIPv4(input) {
|
|
3189
3410
|
function isIPv6(input) {
|
3190
3411
|
return Boolean(parseIPv6(input));
|
3191
3412
|
}
|
3192
|
-
/** Check if `input` is IPv4 or IPv6. */
|
3193
|
-
function isIP(input) {
|
3194
|
-
return Boolean(parseIP(input));
|
3195
|
-
}
|
3196
3413
|
|
3197
|
-
|
3198
|
-
const
|
3199
|
-
|
3200
|
-
|
3201
|
-
const
|
3202
|
-
|
3203
|
-
|
3204
|
-
|
3205
|
-
|
3206
|
-
|
3207
|
-
|
3208
|
-
|
3209
|
-
|
3210
|
-
|
3211
|
-
|
3212
|
-
|
3213
|
-
|
3214
|
-
|
3215
|
-
|
3216
|
-
|
3217
|
-
|
3218
|
-
|
3219
|
-
|
3220
|
-
|
3221
|
-
|
3222
|
-
|
3223
|
-
|
3224
|
-
|
3225
|
-
|
3226
|
-
|
3227
|
-
|
3228
|
-
|
3229
|
-
|
3230
|
-
|
3231
|
-
|
3232
|
-
|
3233
|
-
|
3234
|
-
|
3235
|
-
|
3236
|
-
|
3237
|
-
|
3238
|
-
argv.push('0');
|
3239
|
-
}
|
3240
|
-
sections.splice.apply(sections, argv);
|
3241
|
-
}
|
3242
|
-
const bytes = new Uint8Array(offset + 16);
|
3243
|
-
for (i = 0; i < sections.length; i++) {
|
3244
|
-
const word = parseInt(sections[i], 16);
|
3245
|
-
bytes[offset++] = (word >> 8) & 0xff;
|
3246
|
-
bytes[offset++] = word & 0xff;
|
3247
|
-
}
|
3248
|
-
return bytes;
|
3249
|
-
}
|
3250
|
-
throw new Error('invalid ip address');
|
3251
|
-
};
|
3252
|
-
// Copied from https://github.com/indutny/node-ip/blob/master/lib/ip.js#L63
|
3253
|
-
const toString = function (buf, offset = 0, length) {
|
3254
|
-
offset = ~~offset;
|
3255
|
-
length = length ?? (buf.length - offset);
|
3256
|
-
const view = new DataView(buf.buffer);
|
3257
|
-
if (length === 4) {
|
3258
|
-
const result = [];
|
3259
|
-
// IPv4
|
3260
|
-
for (let i = 0; i < length; i++) {
|
3261
|
-
result.push(buf[offset + i]);
|
3262
|
-
}
|
3263
|
-
return result.join('.');
|
3264
|
-
}
|
3265
|
-
if (length === 16) {
|
3266
|
-
const result = [];
|
3267
|
-
// IPv6
|
3268
|
-
for (let i = 0; i < length; i += 2) {
|
3269
|
-
result.push(view.getUint16(offset + i).toString(16));
|
3270
|
-
}
|
3271
|
-
return result.join(':')
|
3272
|
-
.replace(/(^|:)0(:0)*:0(:|$)/, '$1::$3')
|
3273
|
-
.replace(/:{3,4}/, '::');
|
3274
|
-
}
|
3275
|
-
return '';
|
3276
|
-
};
|
3414
|
+
// the values here come from https://github.com/multiformats/multiaddr/blob/master/protocols.csv
|
3415
|
+
const CODE_IP4 = 4;
|
3416
|
+
const CODE_TCP = 6;
|
3417
|
+
const CODE_UDP = 273;
|
3418
|
+
const CODE_DCCP = 33;
|
3419
|
+
const CODE_IP6 = 41;
|
3420
|
+
const CODE_IP6ZONE = 42;
|
3421
|
+
const CODE_IPCIDR = 43;
|
3422
|
+
const CODE_DNS = 53;
|
3423
|
+
const CODE_DNS4 = 54;
|
3424
|
+
const CODE_DNS6 = 55;
|
3425
|
+
const CODE_DNSADDR = 56;
|
3426
|
+
const CODE_SCTP = 132;
|
3427
|
+
const CODE_UDT = 301;
|
3428
|
+
const CODE_UTP = 302;
|
3429
|
+
const CODE_UNIX = 400;
|
3430
|
+
const CODE_P2P = 421; // also IPFS
|
3431
|
+
const CODE_ONION = 444;
|
3432
|
+
const CODE_ONION3 = 445;
|
3433
|
+
const CODE_GARLIC64 = 446;
|
3434
|
+
const CODE_GARLIC32 = 447;
|
3435
|
+
const CODE_TLS = 448;
|
3436
|
+
const CODE_SNI = 449;
|
3437
|
+
const CODE_NOISE = 454;
|
3438
|
+
const CODE_QUIC = 460;
|
3439
|
+
const CODE_QUIC_V1 = 461;
|
3440
|
+
const CODE_WEBTRANSPORT = 465;
|
3441
|
+
const CODE_CERTHASH = 466;
|
3442
|
+
const CODE_HTTP = 480;
|
3443
|
+
const CODE_HTTP_PATH = 481;
|
3444
|
+
const CODE_HTTPS = 443;
|
3445
|
+
const CODE_WS = 477;
|
3446
|
+
const CODE_WSS = 478;
|
3447
|
+
const CODE_P2P_WEBSOCKET_STAR = 479;
|
3448
|
+
const CODE_P2P_STARDUST = 277;
|
3449
|
+
const CODE_P2P_WEBRTC_STAR = 275;
|
3450
|
+
const CODE_P2P_WEBRTC_DIRECT = 276;
|
3451
|
+
const CODE_WEBRTC_DIRECT = 280;
|
3452
|
+
const CODE_WEBRTC = 281;
|
3453
|
+
const CODE_P2P_CIRCUIT = 290;
|
3454
|
+
const CODE_MEMORY = 777;
|
3277
3455
|
|
3278
|
-
|
3279
|
-
|
3280
|
-
|
3281
|
-
const table = [
|
3282
|
-
[4, 32, 'ip4'],
|
3283
|
-
[6, 16, 'tcp'],
|
3284
|
-
[33, 16, 'dccp'],
|
3285
|
-
[41, 128, 'ip6'],
|
3286
|
-
[42, V, 'ip6zone'],
|
3287
|
-
[43, 8, 'ipcidr'],
|
3288
|
-
[53, V, 'dns', true],
|
3289
|
-
[54, V, 'dns4', true],
|
3290
|
-
[55, V, 'dns6', true],
|
3291
|
-
[56, V, 'dnsaddr', true],
|
3292
|
-
[132, 16, 'sctp'],
|
3293
|
-
[273, 16, 'udp'],
|
3294
|
-
[275, 0, 'p2p-webrtc-star'],
|
3295
|
-
[276, 0, 'p2p-webrtc-direct'],
|
3296
|
-
[277, 0, 'p2p-stardust'],
|
3297
|
-
[280, 0, 'webrtc-direct'],
|
3298
|
-
[281, 0, 'webrtc'],
|
3299
|
-
[290, 0, 'p2p-circuit'],
|
3300
|
-
[301, 0, 'udt'],
|
3301
|
-
[302, 0, 'utp'],
|
3302
|
-
[400, V, 'unix', false, true],
|
3303
|
-
// `ipfs` is added before `p2p` for legacy support.
|
3304
|
-
// All text representations will default to `p2p`, but `ipfs` will
|
3305
|
-
// still be supported
|
3306
|
-
[421, V, 'ipfs'],
|
3307
|
-
// `p2p` is the preferred name for 421, and is now the default
|
3308
|
-
[421, V, 'p2p'],
|
3309
|
-
[443, 0, 'https'],
|
3310
|
-
[444, 96, 'onion'],
|
3311
|
-
[445, 296, 'onion3'],
|
3312
|
-
[446, V, 'garlic64'],
|
3313
|
-
[448, 0, 'tls'],
|
3314
|
-
[449, V, 'sni'],
|
3315
|
-
[460, 0, 'quic'],
|
3316
|
-
[461, 0, 'quic-v1'],
|
3317
|
-
[465, 0, 'webtransport'],
|
3318
|
-
[466, V, 'certhash'],
|
3319
|
-
[477, 0, 'ws'],
|
3320
|
-
[478, 0, 'wss'],
|
3321
|
-
[479, 0, 'p2p-websocket-star'],
|
3322
|
-
[480, 0, 'http'],
|
3323
|
-
[481, V, 'http-path'],
|
3324
|
-
[777, V, 'memory']
|
3325
|
-
];
|
3326
|
-
// populate tables
|
3327
|
-
table.forEach(row => {
|
3328
|
-
const proto = createProtocol(...row);
|
3329
|
-
codes[proto.code] = proto;
|
3330
|
-
names[proto.name] = proto;
|
3331
|
-
});
|
3332
|
-
function createProtocol(code, size, name, resolvable, path) {
|
3333
|
-
return {
|
3334
|
-
code,
|
3335
|
-
size,
|
3336
|
-
name,
|
3337
|
-
resolvable: Boolean(resolvable),
|
3338
|
-
path: Boolean(path)
|
3456
|
+
function bytesToString(base) {
|
3457
|
+
return (buf) => {
|
3458
|
+
return toString(buf, base);
|
3339
3459
|
};
|
3340
3460
|
}
|
3341
|
-
|
3342
|
-
|
3343
|
-
|
3344
|
-
|
3345
|
-
*
|
3346
|
-
* ```js
|
3347
|
-
* import { protocol } from '@multiformats/multiaddr'
|
3348
|
-
*
|
3349
|
-
* console.info(protocol(4))
|
3350
|
-
* // { code: 4, size: 32, name: 'ip4', resolvable: false, path: false }
|
3351
|
-
* ```
|
3352
|
-
*/
|
3353
|
-
function getProtocol(proto) {
|
3354
|
-
if (typeof proto === 'number') {
|
3355
|
-
if (codes[proto] != null) {
|
3356
|
-
return codes[proto];
|
3357
|
-
}
|
3358
|
-
throw new Error(`no protocol with code: ${proto}`);
|
3359
|
-
}
|
3360
|
-
else if (typeof proto === 'string') {
|
3361
|
-
if (names[proto] != null) {
|
3362
|
-
return names[proto];
|
3363
|
-
}
|
3364
|
-
throw new Error(`no protocol with name: ${proto}`);
|
3365
|
-
}
|
3366
|
-
throw new Error(`invalid protocol id type: ${typeof proto}`);
|
3367
|
-
}
|
3368
|
-
|
3369
|
-
getProtocol('ip4');
|
3370
|
-
getProtocol('ip6');
|
3371
|
-
getProtocol('ipcidr');
|
3372
|
-
/**
|
3373
|
-
* Convert [code,Uint8Array] to string
|
3374
|
-
*/
|
3375
|
-
// eslint-disable-next-line complexity
|
3376
|
-
function convertToString(proto, buf) {
|
3377
|
-
const protocol = getProtocol(proto);
|
3378
|
-
switch (protocol.code) {
|
3379
|
-
case 4: // ipv4
|
3380
|
-
case 41: // ipv6
|
3381
|
-
return bytes2ip(buf);
|
3382
|
-
case 42: // ipv6zone
|
3383
|
-
return bytes2str(buf);
|
3384
|
-
case 43: // ipcidr
|
3385
|
-
return toString$1(buf, 'base10');
|
3386
|
-
case 6: // tcp
|
3387
|
-
case 273: // udp
|
3388
|
-
case 33: // dccp
|
3389
|
-
case 132: // sctp
|
3390
|
-
return bytes2port(buf).toString();
|
3391
|
-
case 53: // dns
|
3392
|
-
case 54: // dns4
|
3393
|
-
case 55: // dns6
|
3394
|
-
case 56: // dnsaddr
|
3395
|
-
case 400: // unix
|
3396
|
-
case 449: // sni
|
3397
|
-
case 777: // memory
|
3398
|
-
return bytes2str(buf);
|
3399
|
-
case 421: // ipfs
|
3400
|
-
return bytes2mh(buf);
|
3401
|
-
case 444: // onion
|
3402
|
-
return bytes2onion(buf);
|
3403
|
-
case 445: // onion3
|
3404
|
-
return bytes2onion(buf);
|
3405
|
-
case 466: // certhash
|
3406
|
-
return bytes2mb(buf);
|
3407
|
-
case 481: // http-path
|
3408
|
-
return globalThis.encodeURIComponent(bytes2str(buf));
|
3409
|
-
default:
|
3410
|
-
return toString$1(buf, 'base16'); // no clue. convert to hex
|
3411
|
-
}
|
3412
|
-
}
|
3413
|
-
// eslint-disable-next-line complexity
|
3414
|
-
function convertToBytes(proto, str) {
|
3415
|
-
const protocol = getProtocol(proto);
|
3416
|
-
switch (protocol.code) {
|
3417
|
-
case 4: // ipv4
|
3418
|
-
return ip2bytes(str);
|
3419
|
-
case 41: // ipv6
|
3420
|
-
return ip2bytes(str);
|
3421
|
-
case 42: // ipv6zone
|
3422
|
-
return str2bytes(str);
|
3423
|
-
case 43: // ipcidr
|
3424
|
-
return fromString(str, 'base10');
|
3425
|
-
case 6: // tcp
|
3426
|
-
case 273: // udp
|
3427
|
-
case 33: // dccp
|
3428
|
-
case 132: // sctp
|
3429
|
-
return port2bytes(parseInt(str, 10));
|
3430
|
-
case 53: // dns
|
3431
|
-
case 54: // dns4
|
3432
|
-
case 55: // dns6
|
3433
|
-
case 56: // dnsaddr
|
3434
|
-
case 400: // unix
|
3435
|
-
case 449: // sni
|
3436
|
-
case 777: // memory
|
3437
|
-
return str2bytes(str);
|
3438
|
-
case 421: // ipfs
|
3439
|
-
return mh2bytes(str);
|
3440
|
-
case 444: // onion
|
3441
|
-
return onion2bytes(str);
|
3442
|
-
case 445: // onion3
|
3443
|
-
return onion32bytes(str);
|
3444
|
-
case 466: // certhash
|
3445
|
-
return mb2bytes(str);
|
3446
|
-
case 481: // http-path
|
3447
|
-
return str2bytes(globalThis.decodeURIComponent(str));
|
3448
|
-
default:
|
3449
|
-
return fromString(str, 'base16'); // no clue. convert from hex
|
3450
|
-
}
|
3451
|
-
}
|
3452
|
-
const decoders = Object.values(bases).map((c) => c.decoder);
|
3453
|
-
const anybaseDecoder = (function () {
|
3454
|
-
let acc = decoders[0].or(decoders[1]);
|
3455
|
-
decoders.slice(2).forEach((d) => (acc = acc.or(d)));
|
3456
|
-
return acc;
|
3457
|
-
})();
|
3458
|
-
function ip2bytes(ipString) {
|
3459
|
-
if (!isIP(ipString)) {
|
3460
|
-
throw new Error('invalid ip address');
|
3461
|
-
}
|
3462
|
-
return toBytes(ipString);
|
3461
|
+
function stringToBytes(base) {
|
3462
|
+
return (buf) => {
|
3463
|
+
return fromString(buf, base);
|
3464
|
+
};
|
3463
3465
|
}
|
3464
|
-
function
|
3465
|
-
const
|
3466
|
-
|
3467
|
-
throw new Error('ipBuff is required');
|
3468
|
-
}
|
3469
|
-
if (!isIP(ipString)) {
|
3470
|
-
throw new Error('invalid ip address');
|
3471
|
-
}
|
3472
|
-
return ipString;
|
3466
|
+
function bytes2port(buf) {
|
3467
|
+
const view = new DataView(buf.buffer);
|
3468
|
+
return view.getUint16(buf.byteOffset).toString();
|
3473
3469
|
}
|
3474
3470
|
function port2bytes(port) {
|
3475
3471
|
const buf = new ArrayBuffer(2);
|
3476
3472
|
const view = new DataView(buf);
|
3477
|
-
view.setUint16(0, port);
|
3473
|
+
view.setUint16(0, typeof port === 'string' ? parseInt(port) : port);
|
3478
3474
|
return new Uint8Array(buf);
|
3479
3475
|
}
|
3480
|
-
function bytes2port(buf) {
|
3481
|
-
const view = new DataView(buf.buffer);
|
3482
|
-
return view.getUint16(buf.byteOffset);
|
3483
|
-
}
|
3484
|
-
function str2bytes(str) {
|
3485
|
-
const buf = fromString(str);
|
3486
|
-
const size = Uint8Array.from(encode$2(buf.length));
|
3487
|
-
return concat([size, buf], size.length + buf.length);
|
3488
|
-
}
|
3489
|
-
function bytes2str(buf) {
|
3490
|
-
const size = decode$4(buf);
|
3491
|
-
buf = buf.slice(encodingLength$1(size));
|
3492
|
-
if (buf.length !== size) {
|
3493
|
-
throw new Error('inconsistent lengths');
|
3494
|
-
}
|
3495
|
-
return toString$1(buf);
|
3496
|
-
}
|
3497
|
-
function mh2bytes(hash) {
|
3498
|
-
let mh;
|
3499
|
-
if (hash[0] === 'Q' || hash[0] === '1') {
|
3500
|
-
mh = decode$1(base58btc.decode(`z${hash}`)).bytes;
|
3501
|
-
}
|
3502
|
-
else {
|
3503
|
-
mh = CID.parse(hash).multihash.bytes;
|
3504
|
-
}
|
3505
|
-
// the address is a varint prefixed multihash string representation
|
3506
|
-
const size = Uint8Array.from(encode$2(mh.length));
|
3507
|
-
return concat([size, mh], size.length + mh.length);
|
3508
|
-
}
|
3509
|
-
function mb2bytes(mbstr) {
|
3510
|
-
const mb = anybaseDecoder.decode(mbstr);
|
3511
|
-
const size = Uint8Array.from(encode$2(mb.length));
|
3512
|
-
return concat([size, mb], size.length + mb.length);
|
3513
|
-
}
|
3514
|
-
function bytes2mb(buf) {
|
3515
|
-
const size = decode$4(buf);
|
3516
|
-
const hash = buf.slice(encodingLength$1(size));
|
3517
|
-
if (hash.length !== size) {
|
3518
|
-
throw new Error('inconsistent lengths');
|
3519
|
-
}
|
3520
|
-
return 'u' + toString$1(hash, 'base64url');
|
3521
|
-
}
|
3522
|
-
/**
|
3523
|
-
* Converts bytes to bas58btc string
|
3524
|
-
*/
|
3525
|
-
function bytes2mh(buf) {
|
3526
|
-
const size = decode$4(buf);
|
3527
|
-
const address = buf.slice(encodingLength$1(size));
|
3528
|
-
if (address.length !== size) {
|
3529
|
-
throw new Error('inconsistent lengths');
|
3530
|
-
}
|
3531
|
-
return toString$1(address, 'base58btc');
|
3532
|
-
}
|
3533
3476
|
function onion2bytes(str) {
|
3534
3477
|
const addr = str.split(':');
|
3535
3478
|
if (addr.length !== 2) {
|
@@ -3539,7 +3482,7 @@ function onion2bytes(str) {
|
|
3539
3482
|
throw new Error(`failed to parse onion addr: ${addr[0]} not a Tor onion address.`);
|
3540
3483
|
}
|
3541
3484
|
// onion addresses do not include the multibase prefix, add it before decoding
|
3542
|
-
const buf =
|
3485
|
+
const buf = fromString(addr[0], 'base32');
|
3543
3486
|
// onion port number
|
3544
3487
|
const port = parseInt(addr[1], 10);
|
3545
3488
|
if (port < 1 || port > 65536) {
|
@@ -3567,167 +3510,558 @@ function onion32bytes(str) {
|
|
3567
3510
|
return concat([buf, portBuf], buf.length + portBuf.length);
|
3568
3511
|
}
|
3569
3512
|
function bytes2onion(buf) {
|
3570
|
-
const addrBytes = buf.
|
3571
|
-
const portBytes = buf.
|
3572
|
-
const addr = toString
|
3513
|
+
const addrBytes = buf.subarray(0, buf.length - 2);
|
3514
|
+
const portBytes = buf.subarray(buf.length - 2);
|
3515
|
+
const addr = toString(addrBytes, 'base32');
|
3573
3516
|
const port = bytes2port(portBytes);
|
3574
3517
|
return `${addr}:${port}`;
|
3575
3518
|
}
|
3519
|
+
// Copied from https://github.com/indutny/node-ip/blob/master/lib/ip.js#L7
|
3520
|
+
// but with buf/offset args removed because we don't use them
|
3521
|
+
const ip4ToBytes = function (ip) {
|
3522
|
+
ip = ip.toString().trim();
|
3523
|
+
const bytes = new Uint8Array(4);
|
3524
|
+
ip.split(/\./g).forEach((byte, index) => {
|
3525
|
+
const value = parseInt(byte, 10);
|
3526
|
+
if (isNaN(value) || value < 0 || value > 0xff) {
|
3527
|
+
throw new InvalidMultiaddrError('Invalid byte value in IP address');
|
3528
|
+
}
|
3529
|
+
bytes[index] = value;
|
3530
|
+
});
|
3531
|
+
return bytes;
|
3532
|
+
};
|
3533
|
+
// Copied from https://github.com/indutny/node-ip/blob/master/lib/ip.js#L7
|
3534
|
+
// but with buf/offset args removed because we don't use them
|
3535
|
+
const ip6ToBytes = function (ip) {
|
3536
|
+
let offset = 0;
|
3537
|
+
ip = ip.toString().trim();
|
3538
|
+
const sections = ip.split(':', 8);
|
3539
|
+
let i;
|
3540
|
+
for (i = 0; i < sections.length; i++) {
|
3541
|
+
const isv4 = isIPv4(sections[i]);
|
3542
|
+
let v4Buffer;
|
3543
|
+
if (isv4) {
|
3544
|
+
v4Buffer = ip4ToBytes(sections[i]);
|
3545
|
+
sections[i] = toString(v4Buffer.subarray(0, 2), 'base16');
|
3546
|
+
}
|
3547
|
+
if (v4Buffer != null && ++i < 8) {
|
3548
|
+
sections.splice(i, 0, toString(v4Buffer.subarray(2, 4), 'base16'));
|
3549
|
+
}
|
3550
|
+
}
|
3551
|
+
if (sections[0] === '') {
|
3552
|
+
while (sections.length < 8) {
|
3553
|
+
sections.unshift('0');
|
3554
|
+
}
|
3555
|
+
}
|
3556
|
+
else if (sections[sections.length - 1] === '') {
|
3557
|
+
while (sections.length < 8) {
|
3558
|
+
sections.push('0');
|
3559
|
+
}
|
3560
|
+
}
|
3561
|
+
else if (sections.length < 8) {
|
3562
|
+
for (i = 0; i < sections.length && sections[i] !== ''; i++) { }
|
3563
|
+
const argv = [i, 1];
|
3564
|
+
for (i = 9 - sections.length; i > 0; i--) {
|
3565
|
+
argv.push('0');
|
3566
|
+
}
|
3567
|
+
sections.splice.apply(sections, argv);
|
3568
|
+
}
|
3569
|
+
const bytes = new Uint8Array(offset + 16);
|
3570
|
+
for (i = 0; i < sections.length; i++) {
|
3571
|
+
if (sections[i] === '') {
|
3572
|
+
sections[i] = '0';
|
3573
|
+
}
|
3574
|
+
const word = parseInt(sections[i], 16);
|
3575
|
+
if (isNaN(word) || word < 0 || word > 0xffff) {
|
3576
|
+
throw new InvalidMultiaddrError('Invalid byte value in IP address');
|
3577
|
+
}
|
3578
|
+
bytes[offset++] = (word >> 8) & 0xff;
|
3579
|
+
bytes[offset++] = word & 0xff;
|
3580
|
+
}
|
3581
|
+
return bytes;
|
3582
|
+
};
|
3583
|
+
// Copied from https://github.com/indutny/node-ip/blob/master/lib/ip.js#L63
|
3584
|
+
const ip4ToString = function (buf) {
|
3585
|
+
if (buf.byteLength !== 4) {
|
3586
|
+
throw new InvalidMultiaddrError('IPv4 address was incorrect length');
|
3587
|
+
}
|
3588
|
+
const result = [];
|
3589
|
+
for (let i = 0; i < buf.byteLength; i++) {
|
3590
|
+
result.push(buf[i]);
|
3591
|
+
}
|
3592
|
+
return result.join('.');
|
3593
|
+
};
|
3594
|
+
const ip6ToString = function (buf) {
|
3595
|
+
if (buf.byteLength !== 16) {
|
3596
|
+
throw new InvalidMultiaddrError('IPv6 address was incorrect length');
|
3597
|
+
}
|
3598
|
+
const result = [];
|
3599
|
+
for (let i = 0; i < buf.byteLength; i += 2) {
|
3600
|
+
const byte1 = buf[i];
|
3601
|
+
const byte2 = buf[i + 1];
|
3602
|
+
const tuple = `${byte1.toString(16).padStart(2, '0')}${byte2.toString(16).padStart(2, '0')}`;
|
3603
|
+
result.push(tuple);
|
3604
|
+
}
|
3605
|
+
const ip = result.join(':');
|
3606
|
+
try {
|
3607
|
+
const url = new URL(`http://[${ip}]`);
|
3608
|
+
return url.hostname.substring(1, url.hostname.length - 1);
|
3609
|
+
}
|
3610
|
+
catch {
|
3611
|
+
throw new InvalidMultiaddrError(`Invalid IPv6 address "${ip}"`);
|
3612
|
+
}
|
3613
|
+
};
|
3614
|
+
function ip6StringToValue(str) {
|
3615
|
+
try {
|
3616
|
+
const url = new URL(`http://[${str}]`);
|
3617
|
+
return url.hostname.substring(1, url.hostname.length - 1);
|
3618
|
+
}
|
3619
|
+
catch {
|
3620
|
+
throw new InvalidMultiaddrError(`Invalid IPv6 address "${str}"`);
|
3621
|
+
}
|
3622
|
+
}
|
3623
|
+
const decoders = Object.values(bases).map((c) => c.decoder);
|
3624
|
+
const anybaseDecoder = (function () {
|
3625
|
+
let acc = decoders[0].or(decoders[1]);
|
3626
|
+
decoders.slice(2).forEach((d) => (acc = acc.or(d)));
|
3627
|
+
return acc;
|
3628
|
+
})();
|
3629
|
+
function mb2bytes(mbstr) {
|
3630
|
+
return anybaseDecoder.decode(mbstr);
|
3631
|
+
}
|
3632
|
+
function bytes2mb(base) {
|
3633
|
+
return (buf) => {
|
3634
|
+
return base.encoder.encode(buf);
|
3635
|
+
};
|
3636
|
+
}
|
3576
3637
|
|
3577
|
-
function
|
3578
|
-
|
3579
|
-
|
3580
|
-
|
3581
|
-
let path = null;
|
3582
|
-
const parts = str.split('/').slice(1);
|
3583
|
-
if (parts.length === 1 && parts[0] === '') {
|
3584
|
-
return {
|
3585
|
-
bytes: new Uint8Array(),
|
3586
|
-
string: '/',
|
3587
|
-
tuples: [],
|
3588
|
-
stringTuples: [],
|
3589
|
-
path: null
|
3590
|
-
};
|
3638
|
+
function integer(value) {
|
3639
|
+
const int = parseInt(value);
|
3640
|
+
if (int.toString() !== value) {
|
3641
|
+
throw new ValidationError('Value must be an integer');
|
3591
3642
|
}
|
3592
|
-
|
3593
|
-
|
3594
|
-
|
3595
|
-
|
3596
|
-
|
3597
|
-
|
3598
|
-
|
3599
|
-
|
3600
|
-
|
3601
|
-
|
3602
|
-
|
3603
|
-
|
3604
|
-
|
3605
|
-
|
3606
|
-
|
3607
|
-
|
3608
|
-
|
3609
|
-
|
3610
|
-
path = cleanPath(parts.slice(p).join('/'));
|
3611
|
-
tuples.push([proto.code, convertToBytes(proto.code, path)]);
|
3612
|
-
stringTuples.push([proto.code, path]);
|
3613
|
-
break;
|
3614
|
-
}
|
3615
|
-
const bytes = convertToBytes(proto.code, parts[p]);
|
3616
|
-
tuples.push([proto.code, bytes]);
|
3617
|
-
stringTuples.push([proto.code, convertToString(proto.code, bytes)]);
|
3618
|
-
}
|
3619
|
-
return {
|
3620
|
-
string: stringTuplesToString(stringTuples),
|
3621
|
-
bytes: tuplesToBytes(tuples),
|
3622
|
-
tuples,
|
3623
|
-
stringTuples,
|
3624
|
-
path
|
3643
|
+
}
|
3644
|
+
function positive(value) {
|
3645
|
+
if (value < 0) {
|
3646
|
+
throw new ValidationError('Value must be a positive integer, or zero');
|
3647
|
+
}
|
3648
|
+
}
|
3649
|
+
function maxValue(max) {
|
3650
|
+
return (value) => {
|
3651
|
+
if (value > max) {
|
3652
|
+
throw new ValidationError(`Value must be smaller than or equal to ${max}`);
|
3653
|
+
}
|
3654
|
+
};
|
3655
|
+
}
|
3656
|
+
function validate$1(...funcs) {
|
3657
|
+
return (value) => {
|
3658
|
+
for (const fn of funcs) {
|
3659
|
+
fn(value);
|
3660
|
+
}
|
3625
3661
|
};
|
3626
3662
|
}
|
3627
|
-
|
3628
|
-
|
3629
|
-
|
3630
|
-
|
3663
|
+
const validatePort = validate$1(integer, positive, maxValue(65_535));
|
3664
|
+
|
3665
|
+
const V = -1;
|
3666
|
+
class Registry {
|
3667
|
+
protocolsByCode = new Map();
|
3668
|
+
protocolsByName = new Map();
|
3669
|
+
getProtocol(key) {
|
3670
|
+
let codec;
|
3671
|
+
if (typeof key === 'string') {
|
3672
|
+
codec = this.protocolsByName.get(key);
|
3673
|
+
}
|
3674
|
+
else {
|
3675
|
+
codec = this.protocolsByCode.get(key);
|
3676
|
+
}
|
3677
|
+
if (codec == null) {
|
3678
|
+
throw new UnknownProtocolError(`Protocol ${key} was unknown`);
|
3679
|
+
}
|
3680
|
+
return codec;
|
3681
|
+
}
|
3682
|
+
addProtocol(codec) {
|
3683
|
+
this.protocolsByCode.set(codec.code, codec);
|
3684
|
+
this.protocolsByName.set(codec.name, codec);
|
3685
|
+
codec.aliases?.forEach(alias => {
|
3686
|
+
this.protocolsByName.set(alias, codec);
|
3687
|
+
});
|
3688
|
+
}
|
3689
|
+
removeProtocol(code) {
|
3690
|
+
const codec = this.protocolsByCode.get(code);
|
3691
|
+
if (codec == null) {
|
3692
|
+
return;
|
3693
|
+
}
|
3694
|
+
this.protocolsByCode.delete(codec.code);
|
3695
|
+
this.protocolsByName.delete(codec.name);
|
3696
|
+
codec.aliases?.forEach(alias => {
|
3697
|
+
this.protocolsByName.delete(alias);
|
3698
|
+
});
|
3699
|
+
}
|
3700
|
+
}
|
3701
|
+
const registry = new Registry();
|
3702
|
+
const codecs = [{
|
3703
|
+
code: CODE_IP4,
|
3704
|
+
name: 'ip4',
|
3705
|
+
size: 32,
|
3706
|
+
valueToBytes: ip4ToBytes,
|
3707
|
+
bytesToValue: ip4ToString,
|
3708
|
+
validate: (value) => {
|
3709
|
+
if (!isIPv4(value)) {
|
3710
|
+
throw new ValidationError(`Invalid IPv4 address "${value}"`);
|
3711
|
+
}
|
3712
|
+
}
|
3713
|
+
}, {
|
3714
|
+
code: CODE_TCP,
|
3715
|
+
name: 'tcp',
|
3716
|
+
size: 16,
|
3717
|
+
valueToBytes: port2bytes,
|
3718
|
+
bytesToValue: bytes2port,
|
3719
|
+
validate: validatePort
|
3720
|
+
}, {
|
3721
|
+
code: CODE_UDP,
|
3722
|
+
name: 'udp',
|
3723
|
+
size: 16,
|
3724
|
+
valueToBytes: port2bytes,
|
3725
|
+
bytesToValue: bytes2port,
|
3726
|
+
validate: validatePort
|
3727
|
+
}, {
|
3728
|
+
code: CODE_DCCP,
|
3729
|
+
name: 'dccp',
|
3730
|
+
size: 16,
|
3731
|
+
valueToBytes: port2bytes,
|
3732
|
+
bytesToValue: bytes2port,
|
3733
|
+
validate: validatePort
|
3734
|
+
}, {
|
3735
|
+
code: CODE_IP6,
|
3736
|
+
name: 'ip6',
|
3737
|
+
size: 128,
|
3738
|
+
valueToBytes: ip6ToBytes,
|
3739
|
+
bytesToValue: ip6ToString,
|
3740
|
+
stringToValue: ip6StringToValue,
|
3741
|
+
validate: (value) => {
|
3742
|
+
if (!isIPv6(value)) {
|
3743
|
+
throw new ValidationError(`Invalid IPv6 address "${value}"`);
|
3744
|
+
}
|
3745
|
+
}
|
3746
|
+
}, {
|
3747
|
+
code: CODE_IP6ZONE,
|
3748
|
+
name: 'ip6zone',
|
3749
|
+
size: V
|
3750
|
+
}, {
|
3751
|
+
code: CODE_IPCIDR,
|
3752
|
+
name: 'ipcidr',
|
3753
|
+
size: 8,
|
3754
|
+
bytesToValue: bytesToString('base10'),
|
3755
|
+
valueToBytes: stringToBytes('base10')
|
3756
|
+
}, {
|
3757
|
+
code: CODE_DNS,
|
3758
|
+
name: 'dns',
|
3759
|
+
size: V,
|
3760
|
+
resolvable: true
|
3761
|
+
}, {
|
3762
|
+
code: CODE_DNS4,
|
3763
|
+
name: 'dns4',
|
3764
|
+
size: V,
|
3765
|
+
resolvable: true
|
3766
|
+
}, {
|
3767
|
+
code: CODE_DNS6,
|
3768
|
+
name: 'dns6',
|
3769
|
+
size: V,
|
3770
|
+
resolvable: true
|
3771
|
+
}, {
|
3772
|
+
code: CODE_DNSADDR,
|
3773
|
+
name: 'dnsaddr',
|
3774
|
+
size: V,
|
3775
|
+
resolvable: true
|
3776
|
+
}, {
|
3777
|
+
code: CODE_SCTP,
|
3778
|
+
name: 'sctp',
|
3779
|
+
size: 16,
|
3780
|
+
valueToBytes: port2bytes,
|
3781
|
+
bytesToValue: bytes2port,
|
3782
|
+
validate: validatePort
|
3783
|
+
}, {
|
3784
|
+
code: CODE_UDT,
|
3785
|
+
name: 'udt'
|
3786
|
+
}, {
|
3787
|
+
code: CODE_UTP,
|
3788
|
+
name: 'utp'
|
3789
|
+
}, {
|
3790
|
+
code: CODE_UNIX,
|
3791
|
+
name: 'unix',
|
3792
|
+
size: V,
|
3793
|
+
path: true,
|
3794
|
+
stringToValue: (str) => decodeURIComponent(str),
|
3795
|
+
valueToString: (val) => encodeURIComponent(val)
|
3796
|
+
}, {
|
3797
|
+
code: CODE_P2P,
|
3798
|
+
name: 'p2p',
|
3799
|
+
aliases: ['ipfs'],
|
3800
|
+
size: V,
|
3801
|
+
bytesToValue: bytesToString('base58btc'),
|
3802
|
+
valueToBytes: (val) => {
|
3803
|
+
if (val.startsWith('Q') || val.startsWith('1')) {
|
3804
|
+
return stringToBytes('base58btc')(val);
|
3805
|
+
}
|
3806
|
+
return CID.parse(val).multihash.bytes;
|
3807
|
+
}
|
3808
|
+
}, {
|
3809
|
+
code: CODE_ONION,
|
3810
|
+
name: 'onion',
|
3811
|
+
size: 96,
|
3812
|
+
bytesToValue: bytes2onion,
|
3813
|
+
valueToBytes: onion2bytes
|
3814
|
+
}, {
|
3815
|
+
code: CODE_ONION3,
|
3816
|
+
name: 'onion3',
|
3817
|
+
size: 296,
|
3818
|
+
bytesToValue: bytes2onion,
|
3819
|
+
valueToBytes: onion32bytes
|
3820
|
+
}, {
|
3821
|
+
code: CODE_GARLIC64,
|
3822
|
+
name: 'garlic64',
|
3823
|
+
size: V
|
3824
|
+
}, {
|
3825
|
+
code: CODE_GARLIC32,
|
3826
|
+
name: 'garlic32',
|
3827
|
+
size: V
|
3828
|
+
}, {
|
3829
|
+
code: CODE_TLS,
|
3830
|
+
name: 'tls'
|
3831
|
+
}, {
|
3832
|
+
code: CODE_SNI,
|
3833
|
+
name: 'sni',
|
3834
|
+
size: V
|
3835
|
+
}, {
|
3836
|
+
code: CODE_NOISE,
|
3837
|
+
name: 'noise'
|
3838
|
+
}, {
|
3839
|
+
code: CODE_QUIC,
|
3840
|
+
name: 'quic'
|
3841
|
+
}, {
|
3842
|
+
code: CODE_QUIC_V1,
|
3843
|
+
name: 'quic-v1'
|
3844
|
+
}, {
|
3845
|
+
code: CODE_WEBTRANSPORT,
|
3846
|
+
name: 'webtransport'
|
3847
|
+
}, {
|
3848
|
+
code: CODE_CERTHASH,
|
3849
|
+
name: 'certhash',
|
3850
|
+
size: V,
|
3851
|
+
bytesToValue: bytes2mb(base64url),
|
3852
|
+
valueToBytes: mb2bytes
|
3853
|
+
}, {
|
3854
|
+
code: CODE_HTTP,
|
3855
|
+
name: 'http'
|
3856
|
+
}, {
|
3857
|
+
code: CODE_HTTP_PATH,
|
3858
|
+
name: 'http-path',
|
3859
|
+
size: V,
|
3860
|
+
stringToValue: (str) => `/${decodeURIComponent(str)}`,
|
3861
|
+
valueToString: (val) => encodeURIComponent(val.substring(1))
|
3862
|
+
}, {
|
3863
|
+
code: CODE_HTTPS,
|
3864
|
+
name: 'https'
|
3865
|
+
}, {
|
3866
|
+
code: CODE_WS,
|
3867
|
+
name: 'ws'
|
3868
|
+
}, {
|
3869
|
+
code: CODE_WSS,
|
3870
|
+
name: 'wss'
|
3871
|
+
}, {
|
3872
|
+
code: CODE_P2P_WEBSOCKET_STAR,
|
3873
|
+
name: 'p2p-websocket-star'
|
3874
|
+
}, {
|
3875
|
+
code: CODE_P2P_STARDUST,
|
3876
|
+
name: 'p2p-stardust'
|
3877
|
+
}, {
|
3878
|
+
code: CODE_P2P_WEBRTC_STAR,
|
3879
|
+
name: 'p2p-webrtc-star'
|
3880
|
+
}, {
|
3881
|
+
code: CODE_P2P_WEBRTC_DIRECT,
|
3882
|
+
name: 'p2p-webrtc-direct'
|
3883
|
+
}, {
|
3884
|
+
code: CODE_WEBRTC_DIRECT,
|
3885
|
+
name: 'webrtc-direct'
|
3886
|
+
}, {
|
3887
|
+
code: CODE_WEBRTC,
|
3888
|
+
name: 'webrtc'
|
3889
|
+
}, {
|
3890
|
+
code: CODE_P2P_CIRCUIT,
|
3891
|
+
name: 'p2p-circuit'
|
3892
|
+
}, {
|
3893
|
+
code: CODE_MEMORY,
|
3894
|
+
name: 'memory',
|
3895
|
+
size: V
|
3896
|
+
}];
|
3897
|
+
codecs.forEach(codec => {
|
3898
|
+
registry.addProtocol(codec);
|
3899
|
+
});
|
3900
|
+
|
3901
|
+
function bytesToComponents(bytes) {
|
3902
|
+
const components = [];
|
3631
3903
|
let i = 0;
|
3632
3904
|
while (i < bytes.length) {
|
3633
3905
|
const code = decode$4(bytes, i);
|
3634
|
-
const
|
3635
|
-
const
|
3636
|
-
const size = sizeForAddr(
|
3637
|
-
|
3638
|
-
|
3639
|
-
|
3640
|
-
|
3641
|
-
|
3642
|
-
|
3643
|
-
|
3644
|
-
|
3645
|
-
|
3646
|
-
|
3647
|
-
|
3648
|
-
|
3649
|
-
|
3650
|
-
|
3651
|
-
|
3652
|
-
|
3653
|
-
|
3654
|
-
|
3655
|
-
|
3656
|
-
// however it would have issues if the path had a protocol name in the path
|
3657
|
-
path = stringAddr;
|
3658
|
-
break;
|
3659
|
-
}
|
3660
|
-
}
|
3661
|
-
return {
|
3662
|
-
bytes: Uint8Array.from(bytes),
|
3663
|
-
string: stringTuplesToString(stringTuples),
|
3664
|
-
tuples,
|
3665
|
-
stringTuples,
|
3666
|
-
path
|
3667
|
-
};
|
3906
|
+
const codec = registry.getProtocol(code);
|
3907
|
+
const codeLength = encodingLength$1(code);
|
3908
|
+
const size = sizeForAddr(codec, bytes, i + codeLength);
|
3909
|
+
let sizeLength = 0;
|
3910
|
+
if (size > 0 && codec.size === V) {
|
3911
|
+
sizeLength = encodingLength$1(size);
|
3912
|
+
}
|
3913
|
+
const componentLength = codeLength + sizeLength + size;
|
3914
|
+
const component = {
|
3915
|
+
code,
|
3916
|
+
name: codec.name,
|
3917
|
+
bytes: bytes.subarray(i, i + componentLength)
|
3918
|
+
};
|
3919
|
+
if (size > 0) {
|
3920
|
+
const valueOffset = i + codeLength + sizeLength;
|
3921
|
+
const valueBytes = bytes.subarray(valueOffset, valueOffset + size);
|
3922
|
+
component.value = codec.bytesToValue?.(valueBytes) ?? toString(valueBytes);
|
3923
|
+
}
|
3924
|
+
components.push(component);
|
3925
|
+
i += componentLength;
|
3926
|
+
}
|
3927
|
+
return components;
|
3668
3928
|
}
|
3669
|
-
|
3670
|
-
|
3671
|
-
|
3672
|
-
|
3673
|
-
|
3674
|
-
|
3675
|
-
|
3676
|
-
|
3677
|
-
|
3678
|
-
|
3929
|
+
function componentsToBytes(components) {
|
3930
|
+
let length = 0;
|
3931
|
+
const bytes = [];
|
3932
|
+
for (const component of components) {
|
3933
|
+
if (component.bytes == null) {
|
3934
|
+
const codec = registry.getProtocol(component.code);
|
3935
|
+
const codecLength = encodingLength$1(component.code);
|
3936
|
+
let valueBytes;
|
3937
|
+
let valueLength = 0;
|
3938
|
+
let valueLengthLength = 0;
|
3939
|
+
if (component.value != null) {
|
3940
|
+
valueBytes = codec.valueToBytes?.(component.value) ?? fromString(component.value);
|
3941
|
+
valueLength = valueBytes.byteLength;
|
3942
|
+
if (codec.size === V) {
|
3943
|
+
valueLengthLength = encodingLength$1(valueLength);
|
3944
|
+
}
|
3945
|
+
}
|
3946
|
+
const bytes = new Uint8Array(codecLength + valueLengthLength + valueLength);
|
3947
|
+
// encode the protocol code
|
3948
|
+
let offset = 0;
|
3949
|
+
encodeUint8Array(component.code, bytes, offset);
|
3950
|
+
offset += codecLength;
|
3951
|
+
// if there is a value
|
3952
|
+
if (valueBytes != null) {
|
3953
|
+
// if the value has variable length, encode the length
|
3954
|
+
if (codec.size === V) {
|
3955
|
+
encodeUint8Array(valueLength, bytes, offset);
|
3956
|
+
offset += valueLengthLength;
|
3957
|
+
}
|
3958
|
+
// finally encode the value
|
3959
|
+
bytes.set(valueBytes, offset);
|
3960
|
+
}
|
3961
|
+
component.bytes = bytes;
|
3679
3962
|
}
|
3680
|
-
|
3681
|
-
|
3682
|
-
|
3963
|
+
bytes.push(component.bytes);
|
3964
|
+
length += component.bytes.byteLength;
|
3965
|
+
}
|
3966
|
+
return concat(bytes, length);
|
3683
3967
|
}
|
3684
|
-
|
3685
|
-
|
3686
|
-
|
3687
|
-
|
3688
|
-
|
3689
|
-
|
3690
|
-
|
3691
|
-
|
3692
|
-
|
3693
|
-
|
3694
|
-
|
3695
|
-
|
3968
|
+
function stringToComponents(string) {
|
3969
|
+
if (string.charAt(0) !== '/') {
|
3970
|
+
throw new InvalidMultiaddrError('String multiaddr must start with "/"');
|
3971
|
+
}
|
3972
|
+
const components = [];
|
3973
|
+
let collecting = 'protocol';
|
3974
|
+
let value = '';
|
3975
|
+
let protocol = '';
|
3976
|
+
for (let i = 1; i < string.length; i++) {
|
3977
|
+
const char = string.charAt(i);
|
3978
|
+
if (char !== '/') {
|
3979
|
+
if (collecting === 'protocol') {
|
3980
|
+
protocol += string.charAt(i);
|
3981
|
+
}
|
3982
|
+
else {
|
3983
|
+
value += string.charAt(i);
|
3984
|
+
}
|
3985
|
+
}
|
3986
|
+
const ended = i === string.length - 1;
|
3987
|
+
if (char === '/' || ended) {
|
3988
|
+
const codec = registry.getProtocol(protocol);
|
3989
|
+
if (collecting === 'protocol') {
|
3990
|
+
if (codec.size == null || codec.size === 0) {
|
3991
|
+
// a protocol without an address, eg. `/tls`
|
3992
|
+
components.push({
|
3993
|
+
code: codec.code,
|
3994
|
+
name: codec.name
|
3995
|
+
});
|
3996
|
+
value = '';
|
3997
|
+
protocol = '';
|
3998
|
+
collecting = 'protocol';
|
3999
|
+
continue;
|
4000
|
+
}
|
4001
|
+
else if (ended) {
|
4002
|
+
throw new InvalidMultiaddrError(`Component ${protocol} was missing value`);
|
4003
|
+
}
|
4004
|
+
// continue collecting value
|
4005
|
+
collecting = 'value';
|
4006
|
+
}
|
4007
|
+
else if (collecting === 'value') {
|
4008
|
+
const component = {
|
4009
|
+
code: codec.code,
|
4010
|
+
name: codec.name
|
4011
|
+
};
|
4012
|
+
if (codec.size != null && codec.size !== 0) {
|
4013
|
+
if (value === '') {
|
4014
|
+
throw new InvalidMultiaddrError(`Component ${protocol} was missing value`);
|
4015
|
+
}
|
4016
|
+
component.value = codec.stringToValue?.(value) ?? value;
|
4017
|
+
}
|
4018
|
+
components.push(component);
|
4019
|
+
value = '';
|
4020
|
+
protocol = '';
|
4021
|
+
collecting = 'protocol';
|
4022
|
+
}
|
4023
|
+
}
|
4024
|
+
}
|
4025
|
+
if (protocol !== '' && value !== '') {
|
4026
|
+
throw new InvalidMultiaddrError('Incomplete multiaddr');
|
4027
|
+
}
|
4028
|
+
return components;
|
4029
|
+
}
|
4030
|
+
function componentsToString(components) {
|
4031
|
+
return `/${components.flatMap(component => {
|
4032
|
+
if (component.value == null) {
|
4033
|
+
return component.name;
|
4034
|
+
}
|
4035
|
+
const codec = registry.getProtocol(component.code);
|
4036
|
+
if (codec == null) {
|
4037
|
+
throw new InvalidMultiaddrError(`Unknown protocol code ${component.code}`);
|
4038
|
+
}
|
4039
|
+
return [
|
4040
|
+
component.name,
|
4041
|
+
codec.valueToString?.(component.value) ?? component.value
|
4042
|
+
];
|
4043
|
+
}).join('/')}`;
|
3696
4044
|
}
|
3697
4045
|
/**
|
3698
4046
|
* For the passed address, return the serialized size
|
3699
4047
|
*/
|
3700
|
-
function sizeForAddr(
|
3701
|
-
if (
|
3702
|
-
return p.size / 8;
|
3703
|
-
}
|
3704
|
-
else if (p.size === 0) {
|
4048
|
+
function sizeForAddr(codec, bytes, offset) {
|
4049
|
+
if (codec.size == null || codec.size === 0) {
|
3705
4050
|
return 0;
|
3706
4051
|
}
|
3707
|
-
|
3708
|
-
|
3709
|
-
return size + encodingLength$1(size);
|
3710
|
-
}
|
3711
|
-
}
|
3712
|
-
function cleanPath(str) {
|
3713
|
-
return '/' + str.trim().split('/').filter((a) => a).join('/');
|
3714
|
-
}
|
3715
|
-
class ParseError extends Error {
|
3716
|
-
static name = 'ParseError';
|
3717
|
-
name = 'ParseError';
|
3718
|
-
constructor(str) {
|
3719
|
-
super(`Error parsing address: ${str}`);
|
4052
|
+
if (codec.size > 0) {
|
4053
|
+
return codec.size / 8;
|
3720
4054
|
}
|
4055
|
+
return decode$4(bytes, offset);
|
3721
4056
|
}
|
3722
4057
|
|
3723
|
-
/* eslint-disable complexity */
|
3724
4058
|
const inspect = Symbol.for('nodejs.util.inspect.custom');
|
3725
|
-
const symbol = Symbol.for('@multiformats/
|
4059
|
+
const symbol = Symbol.for('@multiformats/multiaddr');
|
3726
4060
|
const DNS_CODES = [
|
3727
|
-
|
3728
|
-
|
3729
|
-
|
3730
|
-
|
4061
|
+
CODE_DNS,
|
4062
|
+
CODE_DNS4,
|
4063
|
+
CODE_DNS6,
|
4064
|
+
CODE_DNSADDR
|
3731
4065
|
];
|
3732
4066
|
class NoAvailableResolverError extends Error {
|
3733
4067
|
constructor(message = 'No available resolver') {
|
@@ -3735,44 +4069,56 @@ class NoAvailableResolverError extends Error {
|
|
3735
4069
|
this.name = 'NoAvailableResolverError';
|
3736
4070
|
}
|
3737
4071
|
}
|
4072
|
+
function toComponents(addr) {
|
4073
|
+
if (addr == null) {
|
4074
|
+
addr = '/';
|
4075
|
+
}
|
4076
|
+
if (isMultiaddr(addr)) {
|
4077
|
+
return addr.getComponents();
|
4078
|
+
}
|
4079
|
+
if (addr instanceof Uint8Array) {
|
4080
|
+
return bytesToComponents(addr);
|
4081
|
+
}
|
4082
|
+
if (typeof addr === 'string') {
|
4083
|
+
addr = addr
|
4084
|
+
.replace(/\/(\/)+/, '/')
|
4085
|
+
.replace(/(\/)+$/, '');
|
4086
|
+
if (addr === '') {
|
4087
|
+
addr = '/';
|
4088
|
+
}
|
4089
|
+
return stringToComponents(addr);
|
4090
|
+
}
|
4091
|
+
if (Array.isArray(addr)) {
|
4092
|
+
return addr;
|
4093
|
+
}
|
4094
|
+
throw new InvalidMultiaddrError('Must be a string, Uint8Array, Component[], or another Multiaddr');
|
4095
|
+
}
|
3738
4096
|
/**
|
3739
4097
|
* Creates a {@link Multiaddr} from a {@link MultiaddrInput}
|
3740
4098
|
*/
|
3741
4099
|
class Multiaddr {
|
3742
|
-
bytes;
|
3743
|
-
#string;
|
3744
|
-
#tuples;
|
3745
|
-
#stringTuples;
|
3746
|
-
#path;
|
3747
4100
|
[symbol] = true;
|
3748
|
-
|
3749
|
-
|
3750
|
-
|
3751
|
-
|
3752
|
-
|
3753
|
-
|
3754
|
-
|
3755
|
-
|
3756
|
-
|
3757
|
-
else if (typeof addr === 'string') {
|
3758
|
-
if (addr.length > 0 && addr.charAt(0) !== '/') {
|
3759
|
-
throw new Error(`multiaddr "${addr}" must start with a "/"`);
|
3760
|
-
}
|
3761
|
-
parts = stringToMultiaddrParts(addr);
|
3762
|
-
}
|
3763
|
-
else if (isMultiaddr(addr)) { // Multiaddr
|
3764
|
-
parts = bytesToMultiaddrParts(addr.bytes);
|
4101
|
+
#components;
|
4102
|
+
// cache string representation
|
4103
|
+
#string;
|
4104
|
+
// cache byte representation
|
4105
|
+
#bytes;
|
4106
|
+
constructor(addr = '/', options = {}) {
|
4107
|
+
this.#components = toComponents(addr);
|
4108
|
+
if (options.validate !== false) {
|
4109
|
+
validate(this);
|
3765
4110
|
}
|
3766
|
-
|
3767
|
-
|
4111
|
+
}
|
4112
|
+
get bytes() {
|
4113
|
+
if (this.#bytes == null) {
|
4114
|
+
this.#bytes = componentsToBytes(this.#components);
|
3768
4115
|
}
|
3769
|
-
this
|
3770
|
-
this.#string = parts.string;
|
3771
|
-
this.#tuples = parts.tuples;
|
3772
|
-
this.#stringTuples = parts.stringTuples;
|
3773
|
-
this.#path = parts.path;
|
4116
|
+
return this.#bytes;
|
3774
4117
|
}
|
3775
4118
|
toString() {
|
4119
|
+
if (this.#string == null) {
|
4120
|
+
this.#string = componentsToString(this.#components);
|
4121
|
+
}
|
3776
4122
|
return this.#string;
|
3777
4123
|
}
|
3778
4124
|
toJSON() {
|
@@ -3784,31 +4130,25 @@ class Multiaddr {
|
|
3784
4130
|
let host;
|
3785
4131
|
let port;
|
3786
4132
|
let zone = '';
|
3787
|
-
const
|
3788
|
-
|
3789
|
-
const ip4 = getProtocol('ip4');
|
3790
|
-
const ip6 = getProtocol('ip6');
|
3791
|
-
const dns6 = getProtocol('dns6');
|
3792
|
-
const ip6zone = getProtocol('ip6zone');
|
3793
|
-
for (const [code, value] of this.stringTuples()) {
|
3794
|
-
if (code === ip6zone.code) {
|
4133
|
+
for (const { code, name, value } of this.#components) {
|
4134
|
+
if (code === CODE_IP6ZONE) {
|
3795
4135
|
zone = `%${value ?? ''}`;
|
3796
4136
|
}
|
3797
4137
|
// default to https when protocol & port are omitted from DNS addrs
|
3798
4138
|
if (DNS_CODES.includes(code)) {
|
3799
|
-
transport =
|
4139
|
+
transport = 'tcp';
|
3800
4140
|
port = 443;
|
3801
4141
|
host = `${value ?? ''}${zone}`;
|
3802
|
-
family = code ===
|
4142
|
+
family = code === CODE_DNS6 ? 6 : 4;
|
3803
4143
|
}
|
3804
|
-
if (code ===
|
3805
|
-
transport =
|
4144
|
+
if (code === CODE_TCP || code === CODE_UDP) {
|
4145
|
+
transport = name === 'tcp' ? 'tcp' : 'udp';
|
3806
4146
|
port = parseInt(value ?? '');
|
3807
4147
|
}
|
3808
|
-
if (code ===
|
3809
|
-
transport =
|
4148
|
+
if (code === CODE_IP4 || code === CODE_IP6) {
|
4149
|
+
transport = 'tcp';
|
3810
4150
|
host = `${value ?? ''}${zone}`;
|
3811
|
-
family = code ===
|
4151
|
+
family = code === CODE_IP6 ? 6 : 4;
|
3812
4152
|
}
|
3813
4153
|
}
|
3814
4154
|
if (family == null || transport == null || host == null || port == null) {
|
@@ -3822,25 +4162,44 @@ class Multiaddr {
|
|
3822
4162
|
};
|
3823
4163
|
return opts;
|
3824
4164
|
}
|
4165
|
+
getComponents() {
|
4166
|
+
return [
|
4167
|
+
...this.#components
|
4168
|
+
];
|
4169
|
+
}
|
3825
4170
|
protos() {
|
3826
|
-
return this.#
|
4171
|
+
return this.#components.map(({ code, value }) => {
|
4172
|
+
const codec = registry.getProtocol(code);
|
4173
|
+
return {
|
4174
|
+
code,
|
4175
|
+
size: codec.size ?? 0,
|
4176
|
+
name: codec.name,
|
4177
|
+
resolvable: Boolean(codec.resolvable),
|
4178
|
+
path: Boolean(codec.path)
|
4179
|
+
};
|
4180
|
+
});
|
3827
4181
|
}
|
3828
4182
|
protoCodes() {
|
3829
|
-
return this.#
|
4183
|
+
return this.#components.map(({ code }) => code);
|
3830
4184
|
}
|
3831
4185
|
protoNames() {
|
3832
|
-
return this.#
|
4186
|
+
return this.#components.map(({ name }) => name);
|
3833
4187
|
}
|
3834
4188
|
tuples() {
|
3835
|
-
return this.#
|
4189
|
+
return this.#components.map(({ code, value }) => {
|
3836
4190
|
if (value == null) {
|
3837
4191
|
return [code];
|
3838
4192
|
}
|
3839
|
-
|
4193
|
+
const codec = registry.getProtocol(code);
|
4194
|
+
const output = [code];
|
4195
|
+
if (value != null) {
|
4196
|
+
output.push(codec.valueToBytes?.(value) ?? fromString(value));
|
4197
|
+
}
|
4198
|
+
return output;
|
3840
4199
|
});
|
3841
4200
|
}
|
3842
4201
|
stringTuples() {
|
3843
|
-
return this.#
|
4202
|
+
return this.#components.map(({ code, value }) => {
|
3844
4203
|
if (value == null) {
|
3845
4204
|
return [code];
|
3846
4205
|
}
|
@@ -3848,37 +4207,47 @@ class Multiaddr {
|
|
3848
4207
|
});
|
3849
4208
|
}
|
3850
4209
|
encapsulate(addr) {
|
3851
|
-
|
3852
|
-
return new Multiaddr(
|
4210
|
+
const ma = new Multiaddr(addr);
|
4211
|
+
return new Multiaddr([
|
4212
|
+
...this.#components,
|
4213
|
+
...ma.getComponents()
|
4214
|
+
], {
|
4215
|
+
validate: false
|
4216
|
+
});
|
3853
4217
|
}
|
3854
4218
|
decapsulate(addr) {
|
3855
4219
|
const addrString = addr.toString();
|
3856
4220
|
const s = this.toString();
|
3857
4221
|
const i = s.lastIndexOf(addrString);
|
3858
4222
|
if (i < 0) {
|
3859
|
-
throw new
|
4223
|
+
throw new InvalidParametersError(`Address ${this.toString()} does not contain subaddress: ${addr.toString()}`);
|
3860
4224
|
}
|
3861
|
-
return new Multiaddr(s.slice(0, i)
|
4225
|
+
return new Multiaddr(s.slice(0, i), {
|
4226
|
+
validate: false
|
4227
|
+
});
|
3862
4228
|
}
|
3863
4229
|
decapsulateCode(code) {
|
3864
|
-
|
3865
|
-
for (let i =
|
3866
|
-
if (
|
3867
|
-
|
4230
|
+
let index;
|
4231
|
+
for (let i = this.#components.length - 1; i > -1; i--) {
|
4232
|
+
if (this.#components[i].code === code) {
|
4233
|
+
index = i;
|
4234
|
+
break;
|
3868
4235
|
}
|
3869
4236
|
}
|
3870
|
-
return this
|
4237
|
+
return new Multiaddr(this.#components.slice(0, index), {
|
4238
|
+
validate: false
|
4239
|
+
});
|
3871
4240
|
}
|
3872
4241
|
getPeerId() {
|
3873
4242
|
try {
|
3874
4243
|
let tuples = [];
|
3875
|
-
this.
|
3876
|
-
if (code ===
|
3877
|
-
tuples.push([code,
|
4244
|
+
this.#components.forEach(({ code, value }) => {
|
4245
|
+
if (code === CODE_P2P) {
|
4246
|
+
tuples.push([code, value]);
|
3878
4247
|
}
|
3879
4248
|
// if this is a p2p-circuit address, return the target peer id if present
|
3880
4249
|
// not the peer id of the relay
|
3881
|
-
if (code ===
|
4250
|
+
if (code === CODE_P2P_CIRCUIT) {
|
3882
4251
|
tuples = [];
|
3883
4252
|
}
|
3884
4253
|
});
|
@@ -3889,10 +4258,10 @@ class Multiaddr {
|
|
3889
4258
|
// peer id is base58btc encoded string but not multibase encoded so add the `z`
|
3890
4259
|
// prefix so we can validate that it is correctly encoded
|
3891
4260
|
if (peerIdStr[0] === 'Q' || peerIdStr[0] === '1') {
|
3892
|
-
return toString
|
4261
|
+
return toString(base58btc.decode(`z${peerIdStr}`), 'base58btc');
|
3893
4262
|
}
|
3894
4263
|
// try to parse peer id as CID
|
3895
|
-
return toString
|
4264
|
+
return toString(CID.parse(peerIdStr).multihash.bytes, 'base58btc');
|
3896
4265
|
}
|
3897
4266
|
return null;
|
3898
4267
|
}
|
@@ -3901,7 +4270,14 @@ class Multiaddr {
|
|
3901
4270
|
}
|
3902
4271
|
}
|
3903
4272
|
getPath() {
|
3904
|
-
|
4273
|
+
for (const component of this.#components) {
|
4274
|
+
const codec = registry.getProtocol(component.code);
|
4275
|
+
if (!codec.path) {
|
4276
|
+
continue;
|
4277
|
+
}
|
4278
|
+
return component.value ?? null;
|
4279
|
+
}
|
4280
|
+
return null;
|
3905
4281
|
}
|
3906
4282
|
equals(addr) {
|
3907
4283
|
return equals(this.bytes, addr.bytes);
|
@@ -3930,15 +4306,14 @@ class Multiaddr {
|
|
3930
4306
|
port: options.port
|
3931
4307
|
};
|
3932
4308
|
}
|
3933
|
-
isThinWaistAddress(
|
3934
|
-
|
3935
|
-
if (protos.length !== 2) {
|
4309
|
+
isThinWaistAddress() {
|
4310
|
+
if (this.#components.length !== 2) {
|
3936
4311
|
return false;
|
3937
4312
|
}
|
3938
|
-
if (
|
4313
|
+
if (this.#components[0].code !== CODE_IP4 && this.#components[0].code !== CODE_IP6) {
|
3939
4314
|
return false;
|
3940
4315
|
}
|
3941
|
-
if (
|
4316
|
+
if (this.#components[1].code !== CODE_TCP && this.#components[1].code !== CODE_UDP) {
|
3942
4317
|
return false;
|
3943
4318
|
}
|
3944
4319
|
return true;
|
@@ -3956,9 +4331,23 @@ class Multiaddr {
|
|
3956
4331
|
* ```
|
3957
4332
|
*/
|
3958
4333
|
[inspect]() {
|
3959
|
-
return `Multiaddr(${this
|
4334
|
+
return `Multiaddr(${this.toString()})`;
|
3960
4335
|
}
|
3961
4336
|
}
|
4337
|
+
/**
|
4338
|
+
* Ensures all multiaddr tuples are correct. Throws if any invalid protocols or
|
4339
|
+
* values are encountered.
|
4340
|
+
*/
|
4341
|
+
function validate(addr) {
|
4342
|
+
addr.getComponents()
|
4343
|
+
.forEach(component => {
|
4344
|
+
const codec = registry.getProtocol(component.code);
|
4345
|
+
if (component.value == null) {
|
4346
|
+
return;
|
4347
|
+
}
|
4348
|
+
codec.validate?.(component.value);
|
4349
|
+
});
|
4350
|
+
}
|
3962
4351
|
|
3963
4352
|
/**
|
3964
4353
|
* @packageDocumentation
|
@@ -4053,9 +4442,42 @@ class Multiaddr {
|
|
4053
4442
|
* console.info(resolved)
|
4054
4443
|
* // [Multiaddr('/ip4/147.75...'), Multiaddr('/ip4/147.75...'), Multiaddr('/ip4/147.75...')...]
|
4055
4444
|
* ```
|
4445
|
+
*
|
4446
|
+
* @example Adding custom protocols
|
4447
|
+
*
|
4448
|
+
* To add application-specific or experimental protocols, add a protocol codec
|
4449
|
+
* to the protocol registry:
|
4450
|
+
*
|
4451
|
+
* ```ts
|
4452
|
+
* import { registry, V, multiaddr } from '@multiformats/multiaddr'
|
4453
|
+
* import type { ProtocolCodec } from '@multiformats/multiaddr'
|
4454
|
+
*
|
4455
|
+
* const maWithCustomTuple = '/custom-protocol/hello'
|
4456
|
+
*
|
4457
|
+
* // throws UnknownProtocolError
|
4458
|
+
* multiaddr(maWithCustomTuple)
|
4459
|
+
*
|
4460
|
+
* const protocol: ProtocolCodec = {
|
4461
|
+
* code: 2059,
|
4462
|
+
* name: 'custom-protocol',
|
4463
|
+
* size: V
|
4464
|
+
* // V means variable length, can also be 0, a positive integer (e.g. a fixed
|
4465
|
+
* // length or omitted
|
4466
|
+
* }
|
4467
|
+
*
|
4468
|
+
* registry.addProtocol(protocol)
|
4469
|
+
*
|
4470
|
+
* // does not throw UnknownProtocolError
|
4471
|
+
* multiaddr(maWithCustomTuple)
|
4472
|
+
*
|
4473
|
+
* // protocols can also be removed
|
4474
|
+
* registry.removeProtocol(protocol.code)
|
4475
|
+
* ```
|
4056
4476
|
*/
|
4057
4477
|
/**
|
4058
4478
|
* All configured {@link Resolver}s
|
4479
|
+
*
|
4480
|
+
* @deprecated DNS resolving will be removed in a future release
|
4059
4481
|
*/
|
4060
4482
|
const resolvers = new Map();
|
4061
4483
|
/**
|
@@ -4765,13 +5187,15 @@ class ConnectionManager extends TypedEventEmitter {
|
|
4765
5187
|
|
4766
5188
|
const log = new Logger("metadata");
|
4767
5189
|
const MetadataCodec = "/vac/waku/metadata/1.0.0";
|
4768
|
-
class Metadata
|
5190
|
+
class Metadata {
|
4769
5191
|
pubsubTopics;
|
5192
|
+
streamManager;
|
4770
5193
|
libp2pComponents;
|
4771
5194
|
handshakesConfirmed = new Map();
|
5195
|
+
multicodec = MetadataCodec;
|
4772
5196
|
constructor(pubsubTopics, libp2p) {
|
4773
|
-
super(MetadataCodec, libp2p.components, pubsubTopics);
|
4774
5197
|
this.pubsubTopics = pubsubTopics;
|
5198
|
+
this.streamManager = new StreamManager(MetadataCodec, libp2p);
|
4775
5199
|
this.libp2pComponents = libp2p;
|
4776
5200
|
void libp2p.registrar.handle(MetadataCodec, (streamData) => {
|
4777
5201
|
void this.onRequest(streamData);
|
@@ -4791,7 +5215,7 @@ class Metadata extends BaseProtocol {
|
|
4791
5215
|
}
|
4792
5216
|
let stream;
|
4793
5217
|
try {
|
4794
|
-
stream = await this.getStream(peerId);
|
5218
|
+
stream = await this.streamManager.getStream(peerId);
|
4795
5219
|
}
|
4796
5220
|
catch (error) {
|
4797
5221
|
log.error("Failed to get stream", error);
|
@@ -4874,4 +5298,85 @@ function wakuMetadata(pubsubTopics) {
|
|
4874
5298
|
return (components) => new Metadata(pubsubTopics, components);
|
4875
5299
|
}
|
4876
5300
|
|
4877
|
-
|
5301
|
+
/**
|
5302
|
+
* Deterministic Message Hashing as defined in
|
5303
|
+
* [14/WAKU2-MESSAGE](https://rfc.vac.dev/spec/14/#deterministic-message-hashing)
|
5304
|
+
*
|
5305
|
+
* Computes a SHA-256 hash of the concatenation of pubsub topic, payload, content topic, meta, and timestamp.
|
5306
|
+
*
|
5307
|
+
* @param pubsubTopic - The pubsub topic string
|
5308
|
+
* @param message - The message to be hashed
|
5309
|
+
* @returns A Uint8Array containing the SHA-256 hash
|
5310
|
+
*
|
5311
|
+
* @example
|
5312
|
+
* ```typescript
|
5313
|
+
* import { messageHash } from "@waku/core";
|
5314
|
+
*
|
5315
|
+
* const pubsubTopic = "/waku/2/default-waku/proto";
|
5316
|
+
* const message = {
|
5317
|
+
* payload: new Uint8Array([1, 2, 3, 4]),
|
5318
|
+
* contentTopic: "/waku/2/default-content/proto",
|
5319
|
+
* meta: new Uint8Array([5, 6, 7, 8]),
|
5320
|
+
* timestamp: new Date()
|
5321
|
+
* };
|
5322
|
+
*
|
5323
|
+
* const hash = messageHash(pubsubTopic, message);
|
5324
|
+
* ```
|
5325
|
+
*/
|
5326
|
+
function messageHash(pubsubTopic, message) {
|
5327
|
+
const pubsubTopicBytes = utf8ToBytes(pubsubTopic);
|
5328
|
+
const contentTopicBytes = utf8ToBytes(message.contentTopic);
|
5329
|
+
const timestampBytes = tryConvertTimestampToBytes(message.timestamp);
|
5330
|
+
const bytes = concat$1([
|
5331
|
+
pubsubTopicBytes,
|
5332
|
+
message.payload,
|
5333
|
+
contentTopicBytes,
|
5334
|
+
message.meta,
|
5335
|
+
timestampBytes
|
5336
|
+
].filter(isDefined));
|
5337
|
+
return sha256(bytes);
|
5338
|
+
}
|
5339
|
+
function tryConvertTimestampToBytes(timestamp) {
|
5340
|
+
if (!timestamp) {
|
5341
|
+
return;
|
5342
|
+
}
|
5343
|
+
let bigIntTimestamp;
|
5344
|
+
if (typeof timestamp === "bigint") {
|
5345
|
+
bigIntTimestamp = timestamp;
|
5346
|
+
}
|
5347
|
+
else {
|
5348
|
+
bigIntTimestamp = BigInt(timestamp.valueOf()) * 1000000n;
|
5349
|
+
}
|
5350
|
+
return numberToBytes(bigIntTimestamp);
|
5351
|
+
}
|
5352
|
+
/**
|
5353
|
+
* Computes a deterministic message hash and returns it as a hexadecimal string.
|
5354
|
+
* This is a convenience wrapper around messageHash that converts the result to a hex string.
|
5355
|
+
*
|
5356
|
+
* @param pubsubTopic - The pubsub topic string
|
5357
|
+
* @param message - The message to be hashed
|
5358
|
+
* @returns A string containing the hex representation of the SHA-256 hash
|
5359
|
+
*
|
5360
|
+
* @example
|
5361
|
+
* ```typescript
|
5362
|
+
* import { messageHashStr } from "@waku/core";
|
5363
|
+
*
|
5364
|
+
* const pubsubTopic = "/waku/2/default-waku/proto";
|
5365
|
+
* const message = {
|
5366
|
+
* payload: new Uint8Array([1, 2, 3, 4]),
|
5367
|
+
* contentTopic: "/waku/2/default-content/proto",
|
5368
|
+
* meta: new Uint8Array([5, 6, 7, 8]),
|
5369
|
+
* timestamp: new Date()
|
5370
|
+
* };
|
5371
|
+
*
|
5372
|
+
* const hashString = messageHashStr(pubsubTopic, message);
|
5373
|
+
* console.log(hashString); // e.g. "a1b2c3d4..."
|
5374
|
+
* ```
|
5375
|
+
*/
|
5376
|
+
function messageHashStr(pubsubTopic, message) {
|
5377
|
+
const hash = messageHash(pubsubTopic, message);
|
5378
|
+
const hashStr = bytesToHex(hash);
|
5379
|
+
return hashStr;
|
5380
|
+
}
|
5381
|
+
|
5382
|
+
export { ConnectionManager, FilterCodecs, FilterCore, LightPushCodec, LightPushCore, MetadataCodec, StoreCodec, StoreCore, StreamManager, createEncoder, index$3 as message, messageHash, messageHashStr, wakuMetadata, index$2 as waku_filter, index$1 as waku_light_push, index as waku_store };
|