@leofcoin/peernet 1.1.72 → 1.1.73

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.
@@ -0,0 +1,601 @@
1
+ import { L as LittlePubSub } from './peernet-0298b289.js';
2
+ import './value-4e80eeeb.js';
3
+
4
+ class Api {
5
+ _pubsub;
6
+ constructor(_pubsub) {
7
+ this._pubsub = _pubsub;
8
+ }
9
+ subscribe(topic, cb) {
10
+ this._pubsub.subscribe(topic, cb);
11
+ }
12
+ unsubscribe(topic, cb) {
13
+ this._pubsub.unsubscribe(topic, cb);
14
+ }
15
+ publish(topic, value) {
16
+ this._pubsub.publish(topic, value);
17
+ }
18
+ subscribers() {
19
+ this._pubsub.subscribers;
20
+ }
21
+ connectionState(state) {
22
+ switch (state) {
23
+ case 0:
24
+ return 'connecting';
25
+ case 1:
26
+ return 'open';
27
+ case 2:
28
+ return 'closing';
29
+ case 3:
30
+ return 'closed';
31
+ }
32
+ }
33
+ /**
34
+ * @param {string} type
35
+ * @param {string} name
36
+ * @param {object} params
37
+ */
38
+ request(client, request) {
39
+ return new Promise((resolve, reject) => {
40
+ const state = this.connectionState(client.readyState);
41
+ if (state !== 'open')
42
+ return reject(`coudn't send request to ${client.id}, no open connection found.`);
43
+ request.id = Math.random().toString(36).slice(-12);
44
+ const handler = result => {
45
+ if (result && result.error)
46
+ return reject(result.error);
47
+ resolve({ result, id: request.id, handler });
48
+ this.unsubscribe(request.id, handler);
49
+ };
50
+ this.subscribe(request.id, handler);
51
+ this.send(client, request);
52
+ });
53
+ }
54
+ async send(client, request) {
55
+ return client.send(JSON.stringify(request));
56
+ }
57
+ pubsub(client) {
58
+ return {
59
+ publish: (topic = 'pubsub', value) => {
60
+ return this.send(client, { url: 'pubsub', params: { topic, value } });
61
+ },
62
+ subscribe: (topic = 'pubsub', cb) => {
63
+ this.subscribe(topic, cb);
64
+ return this.send(client, { url: 'pubsub', params: { topic, subscribe: true } });
65
+ },
66
+ unsubscribe: (topic = 'pubsub', cb) => {
67
+ this.unsubscribe(topic, cb);
68
+ return this.send(client, { url: 'pubsub', params: { topic, unsubscribe: true } });
69
+ },
70
+ subscribers: this._pubsub.subscribers
71
+ };
72
+ }
73
+ server(client) {
74
+ return {
75
+ uptime: async () => {
76
+ try {
77
+ const { result, id, handler } = await this.request(client, { url: 'uptime' });
78
+ this.unsubscribe(id, handler);
79
+ return result;
80
+ }
81
+ catch (e) {
82
+ throw e;
83
+ }
84
+ },
85
+ ping: async () => {
86
+ try {
87
+ const now = new Date().getTime();
88
+ const { result, id, handler } = await this.request(client, { url: 'ping' });
89
+ this.unsubscribe(id, handler);
90
+ return (Number(result) - now);
91
+ }
92
+ catch (e) {
93
+ throw e;
94
+ }
95
+ }
96
+ };
97
+ }
98
+ peernet(client) {
99
+ return {
100
+ join: async (params) => {
101
+ try {
102
+ params.join = true;
103
+ const requested = { url: 'peernet', params };
104
+ const { result, id, handler } = await this.request(client, requested);
105
+ this.unsubscribe(id, handler);
106
+ return result;
107
+ }
108
+ catch (e) {
109
+ throw e;
110
+ }
111
+ },
112
+ leave: async (params) => {
113
+ try {
114
+ params.join = false;
115
+ const requested = { url: 'peernet', params };
116
+ const { result, id, handler } = await this.request(client, requested);
117
+ this.unsubscribe(id, handler);
118
+ return result;
119
+ }
120
+ catch (e) {
121
+ throw e;
122
+ }
123
+ }
124
+ };
125
+ }
126
+ }
127
+
128
+ class ClientConnection {
129
+ client;
130
+ api;
131
+ #startTime;
132
+ constructor(client, api) {
133
+ this.#startTime = new Date().getTime();
134
+ this.client = client;
135
+ this.api = api;
136
+ }
137
+ request = async (req) => {
138
+ const { result, id, handler } = await this.api.request(this.client, req);
139
+ globalThis.pubsub.unsubscribe(id, handler);
140
+ return result;
141
+ };
142
+ send = (req) => this.api.send(this.client, req);
143
+ get subscribe() {
144
+ return this.api.subscribe;
145
+ }
146
+ get unsubscribe() {
147
+ return this.api.unsubscribe;
148
+ }
149
+ get subscribers() {
150
+ return this.api.subscribers;
151
+ }
152
+ get publish() {
153
+ return this.api.publish;
154
+ }
155
+ get pubsub() {
156
+ return this.api.pubsub(this.client);
157
+ }
158
+ uptime = () => {
159
+ const now = new Date().getTime();
160
+ return (now - this.#startTime);
161
+ };
162
+ get peernet() {
163
+ return this.api.peernet(this.client);
164
+ }
165
+ get server() {
166
+ return this.api.server(this.client);
167
+ }
168
+ connectionState = () => this.api.connectionState(this.client.readyState);
169
+ close = exit => {
170
+ // client.onclose = message => {
171
+ // if (exit) process.exit()
172
+ // }
173
+ this.client.close();
174
+ };
175
+ }
176
+
177
+ if (!globalThis.PubSub)
178
+ globalThis.PubSub = LittlePubSub;
179
+ if (!globalThis.pubsub)
180
+ globalThis.pubsub = new LittlePubSub(false);
181
+ class SocketRequestClient {
182
+ api;
183
+ clientConnection;
184
+ #tries = 0;
185
+ #retry = false;
186
+ #timeout = 10000;
187
+ #times = 10;
188
+ #options;
189
+ #protocol;
190
+ #url;
191
+ constructor(url, protocol, options) {
192
+ let { retry, timeout, times } = options || {};
193
+ if (retry !== undefined)
194
+ this.#retry = retry;
195
+ if (timeout !== undefined)
196
+ this.#timeout = timeout;
197
+ if (times !== undefined)
198
+ this.#times = times;
199
+ this.#url = url;
200
+ this.#protocol = protocol;
201
+ this.#options = options;
202
+ this.api = new Api(globalThis.pubsub);
203
+ }
204
+ init() {
205
+ return new Promise(async (resolve, reject) => {
206
+ const init = async () => {
207
+ // @ts-ignore
208
+ if (!globalThis.WebSocket)
209
+ globalThis.WebSocket = (await import('./browser-dc41c03f.js').then(function (n) { return n.b; })).default.w3cwebsocket;
210
+ const client = new WebSocket(this.#url, this.#protocol);
211
+ client.onmessage = this.onmessage;
212
+ client.onerror = this.onerror;
213
+ client.onopen = () => {
214
+ this.#tries = 0;
215
+ resolve(new ClientConnection(client, this.api));
216
+ };
217
+ client.onclose = message => {
218
+ this.#tries++;
219
+ if (!this.#retry)
220
+ return reject(this.#options);
221
+ if (this.#tries > this.#times) {
222
+ console.log(`${this.#options.protocol} Client Closed`);
223
+ console.error(`could not connect to - ${this.#url}/`);
224
+ return resolve(new ClientConnection(client, this.api));
225
+ }
226
+ if (message.code === 1006) {
227
+ console.log(`Retrying in ${this.#timeout} ms`);
228
+ setTimeout(() => {
229
+ return init();
230
+ }, this.#timeout);
231
+ }
232
+ };
233
+ };
234
+ return init();
235
+ });
236
+ }
237
+ onerror = error => {
238
+ if (globalThis.pubsub.subscribers['error']) {
239
+ globalThis.pubsub.publish('error', error);
240
+ }
241
+ else {
242
+ console.error(error);
243
+ }
244
+ };
245
+ onmessage(message) {
246
+ const { value, url, status, id } = JSON.parse(message.data.toString());
247
+ const publisher = id ? id : url;
248
+ if (status === 200) {
249
+ globalThis.pubsub.publish(publisher, value);
250
+ }
251
+ else {
252
+ globalThis.pubsub.publish(publisher, { error: value });
253
+ }
254
+ }
255
+ }
256
+
257
+ const MAX_MESSAGE_SIZE = 16000;
258
+ const defaultOptions = {
259
+ networkVersion: 'peach',
260
+ version: 'v1',
261
+ stars: ['wss://star.leofcoin.org'],
262
+ connectEvent: 'peer:connected'
263
+ };
264
+
265
+ const iceServers = [
266
+ {
267
+ urls: 'stun:stun.l.google.com:19302' // Google's public STUN server
268
+ },
269
+ {
270
+ urls: 'stun:openrelay.metered.ca:80'
271
+ },
272
+ {
273
+ urls: 'turn:openrelay.metered.ca:443',
274
+ username: 'openrelayproject',
275
+ credential: 'openrelayproject'
276
+ },
277
+ {
278
+ urls: 'turn:openrelay.metered.ca:443?transport=tcp',
279
+ username: 'openrelayproject',
280
+ credential: 'openrelayproject'
281
+ }
282
+ ];
283
+ const SimplePeer = (await import('./index-fd97ecae.js').then(function (n) { return n.i; })).default;
284
+ class Peer extends SimplePeer {
285
+ peerId;
286
+ channelName;
287
+ version;
288
+ bw = { up: 0, down: 0 };
289
+ constructor(options) {
290
+ const { from, to, initiator, trickle, config, version } = options;
291
+ const channelName = initiator ? `${from}:${to}` : `${to}:${from}`;
292
+ super({
293
+ channelName,
294
+ initiator,
295
+ trickle: trickle || true,
296
+ config: { iceServers, ...config },
297
+ wrtc: globalThis.wrtc
298
+ });
299
+ this.version = String(version);
300
+ this.peerId = to;
301
+ this.channelName = channelName;
302
+ }
303
+ async #chunkit(data, id) {
304
+ this.bw.up = data.length;
305
+ const size = data.length;
306
+ // no needles chunking, keep it simple, if data is smaller then max size just send it
307
+ if (data.length <= MAX_MESSAGE_SIZE) {
308
+ return super.send(JSON.stringify({ chunk: data, id, size: data.length }));
309
+ }
310
+ async function* chunks(data) {
311
+ while (data.length !== 0) {
312
+ const amountToSlice = data.length >= MAX_MESSAGE_SIZE ? MAX_MESSAGE_SIZE : data.length;
313
+ const subArray = data.subarray(0, amountToSlice);
314
+ data = data.subarray(amountToSlice, data.length);
315
+ yield subArray;
316
+ // super.send(JSON.stringify({ chunk: subArray, id, size }))
317
+ }
318
+ }
319
+ // while (data.length !== 0) {
320
+ // const amountToSlice =
321
+ // data.length >= MAX_MESSAGE_SIZE ? MAX_MESSAGE_SIZE : data.length
322
+ // const subArray = data.subarray(0, amountToSlice)
323
+ // data = data.subarray(amountToSlice, data.length)
324
+ // super.send(JSON.stringify({ chunk: subArray, id, size }))
325
+ // }
326
+ for await (const chunk of chunks(data)) {
327
+ super.send(JSON.stringify({ chunk, id, size }));
328
+ }
329
+ }
330
+ /**
331
+ * send to peer
332
+ * @param data ArrayLike
333
+ * @param id custom id to listen to
334
+ */
335
+ send(data, id = crypto.randomUUID()) {
336
+ // send chuncks till ndata support for SCTP is added
337
+ // wraps data
338
+ this.#chunkit(data, id);
339
+ }
340
+ /**
341
+ * send to peer & wait for response
342
+ * @param data ArrayLike
343
+ * @param id custom id to listen to
344
+ */
345
+ request(data, id = crypto.randomUUID()) {
346
+ return new Promise((resolve, reject) => {
347
+ const timeout = setTimeout(() => reject(`request for ${id} timed out`), 30000);
348
+ const onrequest = ({ data }) => {
349
+ clearTimeout(timeout);
350
+ resolve(data);
351
+ globalThis.pubsub.unsubscribe(id, onrequest);
352
+ };
353
+ globalThis.pubsub.subscribe(id, onrequest);
354
+ this.send(data, id);
355
+ });
356
+ }
357
+ }
358
+
359
+ class Client {
360
+ #peerId;
361
+ #connections = {};
362
+ #stars = {};
363
+ #connectEvent = 'peer:connected';
364
+ id;
365
+ networkVersion;
366
+ starsConfig;
367
+ socketClient;
368
+ messageSize = 262144;
369
+ version;
370
+ #messagesToHandle = {};
371
+ get peerId() {
372
+ return this.#peerId;
373
+ }
374
+ get connections() {
375
+ return { ...this.#connections };
376
+ }
377
+ get peers() {
378
+ return Object.entries(this.#connections);
379
+ }
380
+ getPeer(peerId) {
381
+ return this.#connections[peerId];
382
+ }
383
+ /**
384
+ *
385
+ * @param options {object}
386
+ * @param options.peerId {string}
387
+ * @param options.networkVersion {string}
388
+ * @param options.version {string}
389
+ * @param options.stars {string[]}
390
+ * @param options.connectEvent {string} defaults to peer:connected, can be renamed to handle different protocols, like peer:discovered (setup peer props before fireing the connect event)
391
+ */
392
+ constructor(options) {
393
+ const { peerId, networkVersion, version, connectEvent, stars } = {
394
+ ...defaultOptions,
395
+ ...options
396
+ };
397
+ this.#peerId = peerId;
398
+ this.networkVersion = networkVersion;
399
+ this.version = version;
400
+ this.#connectEvent = connectEvent;
401
+ this.starsConfig = stars;
402
+ this._init();
403
+ }
404
+ async _init() {
405
+ // reconnectJob()
406
+ if (!globalThis.RTCPeerConnection)
407
+ globalThis.wrtc = (await import('./browser-2c73e2ef.js').then(function (n) { return n.b; })).default;
408
+ for (const star of this.starsConfig) {
409
+ try {
410
+ const client = new SocketRequestClient(star, this.networkVersion);
411
+ this.#stars[star] = await client.init();
412
+ this.setupStarListeners(this.#stars[star]);
413
+ this.#stars[star].send({
414
+ url: 'join',
415
+ params: { version: this.version, peerId: this.peerId }
416
+ });
417
+ }
418
+ catch (e) {
419
+ if (this.starsConfig.indexOf(star) === this.starsConfig.length - 1 &&
420
+ !this.socketClient)
421
+ throw new Error(`No star available to connect`);
422
+ }
423
+ }
424
+ if (globalThis.navigator) {
425
+ globalThis.addEventListener('beforeunload', async () => this.close());
426
+ }
427
+ else {
428
+ process.on('SIGINT', async () => {
429
+ process.stdin.resume();
430
+ await this.close();
431
+ process.exit();
432
+ });
433
+ }
434
+ }
435
+ setupStarListeners(star) {
436
+ star.pubsub.subscribe('peer:joined', (id) => this.#peerJoined(id, star));
437
+ star.pubsub.subscribe('peer:left', (id) => this.#peerLeft(id, star));
438
+ star.pubsub.subscribe('star:joined', this.#starJoined);
439
+ star.pubsub.subscribe('star:left', this.#starLeft);
440
+ star.pubsub.subscribe('signal', (message) => this.#inComingSignal(message, star));
441
+ }
442
+ #starJoined = (id) => {
443
+ if (this.#stars[id]) {
444
+ this.#stars[id].close(0);
445
+ delete this.#stars[id];
446
+ }
447
+ console.log(`star ${id} joined`);
448
+ };
449
+ #starLeft = async (id) => {
450
+ if (this.#stars[id]) {
451
+ this.#stars[id].close(0);
452
+ delete this.#stars[id];
453
+ }
454
+ if (Object.keys(this.#stars).length === 0) {
455
+ for (const star of this.starsConfig) {
456
+ try {
457
+ const socketClient = await new SocketRequestClient(star, this.networkVersion).init();
458
+ if (!socketClient?.client?.OPEN)
459
+ return;
460
+ this.#stars[star] = socketClient;
461
+ this.#stars[star].send({
462
+ url: 'join',
463
+ params: { peerId: this.peerId, version: this.version }
464
+ });
465
+ this.setupStarListeners(socketClient);
466
+ }
467
+ catch (e) {
468
+ if (this.starsConfig.indexOf(star) === this.starsConfig.length - 1)
469
+ throw new Error(`No star available to connect`);
470
+ }
471
+ }
472
+ }
473
+ globalThis.debug(`star ${id} left`);
474
+ };
475
+ #peerLeft = (peer, star) => {
476
+ const id = peer.peerId || peer;
477
+ if (this.#connections[id]) {
478
+ this.#connections[id].destroy();
479
+ delete this.#connections[id];
480
+ }
481
+ globalThis.debug(`peer ${id} left`);
482
+ };
483
+ #createRTCPeerConnection = (peerId, star, version, initiator = false) => {
484
+ const peer = new Peer({
485
+ initiator: initiator,
486
+ from: this.peerId,
487
+ to: peerId,
488
+ version
489
+ });
490
+ peer.on('signal', (signal) => this.#peerSignal(peer, signal, star, this.version));
491
+ peer.on('connect', () => this.#peerConnect(peer));
492
+ peer.on('close', () => this.#peerClose(peer));
493
+ peer.on('data', (data) => this.#peerData(peer, data));
494
+ peer.on('error', (error) => this.#peerError(peer, error));
495
+ this.#connections[peerId] = peer;
496
+ };
497
+ #peerJoined = async ({ peerId, version }, star) => {
498
+ // check if peer rejoined before the previous connection closed
499
+ if (this.#connections[peerId]) {
500
+ this.#connections[peerId].destroy();
501
+ delete this.#connections[peerId];
502
+ }
503
+ // RTCPeerConnection
504
+ this.#createRTCPeerConnection(peerId, star, version, true);
505
+ globalThis.debug(`peer ${peerId} joined`);
506
+ };
507
+ #inComingSignal = async ({ from, signal, channelName, version }, star) => {
508
+ if (version !== this.version) {
509
+ console.warn(`${from} joined using the wrong version.\nexpected: ${this.version} but got:${version}`);
510
+ return;
511
+ }
512
+ let peer = this.#connections[from];
513
+ if (!peer) {
514
+ this.#createRTCPeerConnection(from, star, version);
515
+ peer = this.#connections[from];
516
+ }
517
+ if (String(peer.channelName) !== String(channelName))
518
+ console.warn(`channelNames don't match: got ${peer.channelName}, expected: ${channelName}`);
519
+ peer.signal(signal);
520
+ };
521
+ #peerSignal = (peer, signal, star, version) => {
522
+ let client = this.#stars[star];
523
+ if (!client)
524
+ client = this.#stars[Object.keys(this.#stars)[0]];
525
+ client.send({
526
+ url: 'signal',
527
+ params: {
528
+ from: this.peerId,
529
+ to: peer.peerId,
530
+ channelName: peer.channelName,
531
+ version,
532
+ signal
533
+ }
534
+ });
535
+ };
536
+ #peerClose = (peer) => {
537
+ if (this.#connections[peer.peerId]) {
538
+ peer.destroy();
539
+ delete this.#connections[peer.peerId];
540
+ }
541
+ globalThis.debug(`closed ${peer.peerId}'s connection`);
542
+ };
543
+ #peerConnect = (peer) => {
544
+ globalThis.debug(`${peer.peerId} connected`);
545
+ globalThis.pubsub.publishVerbose(this.#connectEvent, peer.peerId);
546
+ };
547
+ #noticeMessage = (message, id, from, peer) => {
548
+ if (globalThis.pubsub.subscribers[id]) {
549
+ globalThis.pubsub.publish(id, {
550
+ data: new Uint8Array(Object.values(message)),
551
+ id,
552
+ from,
553
+ peer
554
+ });
555
+ }
556
+ else {
557
+ globalThis.pubsub.publish('peer:data', {
558
+ data: new Uint8Array(Object.values(message)),
559
+ id,
560
+ from,
561
+ peer
562
+ });
563
+ }
564
+ };
565
+ #peerData = (peer, data) => {
566
+ const { id, size, chunk } = JSON.parse(new TextDecoder().decode(data));
567
+ peer.bw.down += size;
568
+ if (size <= MAX_MESSAGE_SIZE) {
569
+ this.#noticeMessage(chunk, id, peer.peerId, peer);
570
+ }
571
+ else {
572
+ if (!this.#messagesToHandle[id])
573
+ this.#messagesToHandle[id] = [];
574
+ this.#messagesToHandle[id] = [
575
+ ...this.#messagesToHandle[id],
576
+ ...Object.values(chunk)
577
+ ];
578
+ if (this.#messagesToHandle[id].length === Number(size)) {
579
+ this.#noticeMessage(this.#messagesToHandle[id], id, peer.peerId, peer);
580
+ delete this.#messagesToHandle[id];
581
+ }
582
+ }
583
+ };
584
+ #peerError = (peer, error) => {
585
+ console.warn(`Connection error: ${error.message}`);
586
+ peer.destroy();
587
+ };
588
+ async close() {
589
+ for (const star in this.#stars) {
590
+ if (this.#stars[star].connectionState() === 'open')
591
+ await this.#stars[star].send({ url: 'leave', params: this.peerId });
592
+ }
593
+ const promises = [
594
+ Object.values(this.#connections).map((connection) => connection.destroy()),
595
+ Object.values(this.#stars).map((connection) => connection.close(0))
596
+ ];
597
+ return Promise.allSettled(promises);
598
+ }
599
+ }
600
+
601
+ export { Client as default };
@@ -1,4 +1,4 @@
1
- import { M as MultiWallet, e as encrypt, b as base58$1 } from './peernet-fa94440c.js';
1
+ import { M as MultiWallet, e as encrypt, b as base58$1 } from './peernet-0298b289.js';
2
2
  import './value-4e80eeeb.js';
3
3
 
4
4
  /**