nostr-tools 2.23.8 → 2.23.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/lib/cjs/abstract-pool.js +163 -7
  2. package/lib/cjs/abstract-pool.js.map +4 -4
  3. package/lib/cjs/abstract-relay.js +59 -6
  4. package/lib/cjs/abstract-relay.js.map +2 -2
  5. package/lib/cjs/filter.js.map +1 -1
  6. package/lib/cjs/index.js +412 -64
  7. package/lib/cjs/index.js.map +4 -4
  8. package/lib/cjs/kinds.js +196 -4
  9. package/lib/cjs/kinds.js.map +2 -2
  10. package/lib/cjs/nip17.js.map +2 -2
  11. package/lib/cjs/nip18.js.map +2 -2
  12. package/lib/cjs/nip25.js.map +1 -1
  13. package/lib/cjs/nip28.js.map +2 -2
  14. package/lib/cjs/nip42.js.map +2 -2
  15. package/lib/cjs/nip45.js +183 -0
  16. package/lib/cjs/nip45.js.map +7 -0
  17. package/lib/cjs/nip46.js +163 -7
  18. package/lib/cjs/nip46.js.map +4 -4
  19. package/lib/cjs/nip47.js.map +2 -2
  20. package/lib/cjs/nip57.js.map +1 -1
  21. package/lib/cjs/nip58.js.map +2 -2
  22. package/lib/cjs/nip59.js.map +2 -2
  23. package/lib/cjs/nip75.js.map +2 -2
  24. package/lib/cjs/nip94.js.map +2 -2
  25. package/lib/cjs/nip98.js.map +2 -2
  26. package/lib/cjs/nip99.js.map +2 -2
  27. package/lib/cjs/nipb0.js +1064 -0
  28. package/lib/cjs/nipb0.js.map +7 -0
  29. package/lib/cjs/pool.js +163 -7
  30. package/lib/cjs/pool.js.map +4 -4
  31. package/lib/cjs/relay.js +59 -6
  32. package/lib/cjs/relay.js.map +2 -2
  33. package/lib/esm/abstract-pool.js +163 -7
  34. package/lib/esm/abstract-pool.js.map +4 -4
  35. package/lib/esm/abstract-relay.js +59 -6
  36. package/lib/esm/abstract-relay.js.map +2 -2
  37. package/lib/esm/filter.js.map +1 -1
  38. package/lib/esm/index.js +388 -40
  39. package/lib/esm/index.js.map +4 -4
  40. package/lib/esm/kinds.js +196 -4
  41. package/lib/esm/kinds.js.map +2 -2
  42. package/lib/esm/nip17.js.map +2 -2
  43. package/lib/esm/nip18.js.map +2 -2
  44. package/lib/esm/nip25.js.map +1 -1
  45. package/lib/esm/nip28.js.map +2 -2
  46. package/lib/esm/nip42.js.map +2 -2
  47. package/lib/esm/nip45.js +162 -0
  48. package/lib/esm/nip45.js.map +7 -0
  49. package/lib/esm/nip46.js +163 -7
  50. package/lib/esm/nip46.js.map +4 -4
  51. package/lib/esm/nip47.js.map +2 -2
  52. package/lib/esm/nip57.js.map +1 -1
  53. package/lib/esm/nip58.js.map +2 -2
  54. package/lib/esm/nip59.js.map +2 -2
  55. package/lib/esm/nip75.js.map +2 -2
  56. package/lib/esm/nip94.js.map +2 -2
  57. package/lib/esm/nip98.js.map +2 -2
  58. package/lib/esm/nip99.js.map +2 -2
  59. package/lib/esm/nipb0.js +1049 -0
  60. package/lib/esm/nipb0.js.map +7 -0
  61. package/lib/esm/pool.js +163 -7
  62. package/lib/esm/pool.js.map +4 -4
  63. package/lib/esm/relay.js +59 -6
  64. package/lib/esm/relay.js.map +2 -2
  65. package/lib/nostr.bundle.js +361 -15
  66. package/lib/nostr.bundle.js.map +4 -4
  67. package/lib/types/abstract-pool.d.ts +10 -0
  68. package/lib/types/abstract-relay.d.ts +15 -1
  69. package/lib/types/kinds.d.ts +200 -8
  70. package/lib/types/nip45.d.ts +13 -0
  71. package/lib/types/nip45.test.d.ts +1 -0
  72. package/lib/types/nipb0.d.ts +129 -0
  73. package/lib/types/relay.d.ts +2 -2
  74. package/package.json +13 -1
@@ -146,12 +146,14 @@ var AbstractRelay = class {
146
146
  openSubs = /* @__PURE__ */ new Map();
147
147
  enablePing;
148
148
  enableReconnect;
149
+ idleTimeout = 0;
149
150
  idleSince = Date.now();
150
151
  ongoingOperations = 0;
151
152
  reconnectTimeoutHandle;
152
153
  pingIntervalHandle;
153
154
  reconnectAttempts = 0;
154
155
  skipReconnection = false;
156
+ idleTimeoutHandle;
155
157
  connectionPromise;
156
158
  openCountRequests = /* @__PURE__ */ new Map();
157
159
  openEventPublishes = /* @__PURE__ */ new Map();
@@ -167,6 +169,8 @@ var AbstractRelay = class {
167
169
  this._WebSocket = opts.websocketImplementation || WebSocket;
168
170
  this.enablePing = opts.enablePing;
169
171
  this.enableReconnect = opts.enableReconnect || false;
172
+ if (opts.idleTimeout)
173
+ this.idleTimeout = opts.idleTimeout;
170
174
  }
171
175
  static async connect(url, opts) {
172
176
  const relay = new AbstractRelay(url, opts);
@@ -190,6 +194,22 @@ var AbstractRelay = class {
190
194
  get connected() {
191
195
  return this._connected;
192
196
  }
197
+ clearIdleTimeout() {
198
+ if (this.idleTimeoutHandle) {
199
+ clearTimeout(this.idleTimeoutHandle);
200
+ this.idleTimeoutHandle = void 0;
201
+ }
202
+ }
203
+ scheduleIdleClose() {
204
+ this.clearIdleTimeout();
205
+ if (this.idleTimeout > 0) {
206
+ this.idleTimeoutHandle = setTimeout(() => {
207
+ if (this.ongoingOperations === 0 && this.idleSince) {
208
+ this.close();
209
+ }
210
+ }, this.idleTimeout);
211
+ }
212
+ }
193
213
  async reconnect() {
194
214
  const backoff = this.resubscribeBackoff[Math.min(this.reconnectAttempts, this.resubscribeBackoff.length - 1)];
195
215
  this.reconnectAttempts++;
@@ -208,6 +228,7 @@ var AbstractRelay = class {
208
228
  this._connected = false;
209
229
  this.connectionPromise = void 0;
210
230
  this.idleSince = void 0;
231
+ this.clearIdleTimeout();
211
232
  if (this.enableReconnect && !this.skipReconnection) {
212
233
  this.reconnect();
213
234
  } else {
@@ -365,6 +386,7 @@ var AbstractRelay = class {
365
386
  }
366
387
  async publish(event) {
367
388
  this.idleSince = void 0;
389
+ this.clearIdleTimeout();
368
390
  this.ongoingOperations++;
369
391
  const ret = new Promise((resolve, reject) => {
370
392
  const timeout = setTimeout(() => {
@@ -376,24 +398,46 @@ var AbstractRelay = class {
376
398
  }, this.publishTimeout);
377
399
  this.openEventPublishes.set(event.id, { resolve, reject, timeout });
378
400
  });
379
- this.send('["EVENT",' + JSON.stringify(event) + "]");
401
+ try {
402
+ await this.send('["EVENT",' + JSON.stringify(event) + "]");
403
+ } catch (err) {
404
+ const ep = this.openEventPublishes.get(event.id);
405
+ if (ep) {
406
+ ep.reject(err);
407
+ this.openEventPublishes.delete(event.id);
408
+ }
409
+ }
380
410
  this.ongoingOperations--;
381
- if (this.ongoingOperations === 0)
411
+ if (this.ongoingOperations === 0) {
382
412
  this.idleSince = Date.now();
413
+ this.scheduleIdleClose();
414
+ }
383
415
  return ret;
384
416
  }
385
417
  async count(filters, params) {
418
+ return (await this.countWithHLL(filters, params)).count;
419
+ }
420
+ async countWithHLL(filters, params) {
386
421
  this.serial++;
387
422
  const id = params?.id || "count:" + this.serial;
388
423
  const ret = new Promise((resolve, reject) => {
389
424
  this.openCountRequests.set(id, { resolve, reject });
390
425
  });
391
- this.send('["COUNT","' + id + '",' + JSON.stringify(filters).substring(1));
426
+ try {
427
+ await this.send('["COUNT","' + id + '",' + JSON.stringify(filters).substring(1));
428
+ } catch (err) {
429
+ const cr = this.openCountRequests.get(id);
430
+ if (cr) {
431
+ cr.reject(err);
432
+ this.openCountRequests.delete(id);
433
+ }
434
+ }
392
435
  return ret;
393
436
  }
394
437
  subscribe(filters, params) {
395
438
  if (params.label !== "<forced-ping>") {
396
439
  this.idleSince = void 0;
440
+ this.clearIdleTimeout();
397
441
  this.ongoingOperations++;
398
442
  }
399
443
  const sub = this.prepareSubscription(filters, params);
@@ -423,6 +467,7 @@ var AbstractRelay = class {
423
467
  this.closeAllSubscriptions("relay connection closed by us");
424
468
  this._connected = false;
425
469
  this.idleSince = void 0;
470
+ this.clearIdleTimeout();
426
471
  this.onclose?.();
427
472
  if (this.ws?.readyState === this._WebSocket.OPEN) {
428
473
  this.ws?.close();
@@ -466,7 +511,7 @@ var AbstractRelay = class {
466
511
  const payload = data[2];
467
512
  const cr = this.openCountRequests.get(id);
468
513
  if (cr) {
469
- cr.resolve(payload.count);
514
+ cr.resolve(payload);
470
515
  this.openCountRequests.delete(id);
471
516
  }
472
517
  return;
@@ -496,8 +541,14 @@ var AbstractRelay = class {
496
541
  case "CLOSED": {
497
542
  const id = data[1];
498
543
  const so = this.openSubs.get(id);
499
- if (!so)
544
+ if (!so) {
545
+ const cr = this.openCountRequests.get(id);
546
+ if (cr) {
547
+ cr.reject(new Error(data[2]));
548
+ this.openCountRequests.delete(id);
549
+ }
500
550
  return;
551
+ }
501
552
  so.closed = true;
502
553
  so.close(data[2]);
503
554
  return;
@@ -594,8 +645,10 @@ var Subscription = class {
594
645
  }
595
646
  this.relay.openSubs.delete(this.id);
596
647
  this.relay.ongoingOperations--;
597
- if (this.relay.ongoingOperations === 0)
648
+ if (this.relay.ongoingOperations === 0) {
598
649
  this.relay.idleSince = Date.now();
650
+ this.relay.scheduleIdleClose();
651
+ }
599
652
  this.onclose?.(reason);
600
653
  }
601
654
  };
@@ -606,6 +659,63 @@ var alwaysTrue = (t) => {
606
659
  return true;
607
660
  };
608
661
 
662
+ // nip45.ts
663
+ var import_sha2 = require("@noble/hashes/sha2.js");
664
+ var import_utils3 = require("@noble/hashes/utils.js");
665
+ var M = 256;
666
+ var HLL_HEX_LENGTH = M * 2;
667
+ var utf8Encoder2 = new TextEncoder();
668
+ function getCountManyFilter(target, directive) {
669
+ switch (directive) {
670
+ case "reactions":
671
+ return { "#e": [target], kinds: [7] };
672
+ case "reposts":
673
+ return { "#e": [target], kinds: [6] };
674
+ case "quotes":
675
+ return { "#q": [target], kinds: [1, 1111] };
676
+ case "replies":
677
+ return { "#e": [target], kinds: [1] };
678
+ case "comments":
679
+ return { "#E": [target], kinds: [1111] };
680
+ case "followers":
681
+ return { "#p": [target], kinds: [3] };
682
+ }
683
+ }
684
+ function newHll() {
685
+ return new Uint8Array(M);
686
+ }
687
+ function hllDecode(hex) {
688
+ if (hex.length !== HLL_HEX_LENGTH || !/^[0-9a-fA-F]+$/.test(hex))
689
+ return void 0;
690
+ const registers = new Uint8Array(M);
691
+ for (let i = 0; i < M; i++) {
692
+ registers[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
693
+ }
694
+ return registers;
695
+ }
696
+ function hllEncode(registers) {
697
+ if (registers.length !== M)
698
+ throw new Error(`invalid number of registers ${registers.length}`);
699
+ let hex = "";
700
+ for (let i = 0; i < M; i++) {
701
+ hex += registers[i].toString(16).padStart(2, "0");
702
+ }
703
+ return hex;
704
+ }
705
+ function mergeHll(target, source) {
706
+ if (target.length === 0)
707
+ target = newHll();
708
+ if (target.length !== M)
709
+ throw new Error(`invalid number of registers ${target.length}`);
710
+ if (source.length !== M)
711
+ throw new Error(`invalid number of registers ${source.length}`);
712
+ for (let i = 0; i < M; i++) {
713
+ if (source[i] > target[i])
714
+ target[i] = source[i];
715
+ }
716
+ return target;
717
+ }
718
+
609
719
  // abstract-pool.ts
610
720
  var AbstractSimplePool = class {
611
721
  relays = /* @__PURE__ */ new Map();
@@ -614,6 +724,7 @@ var AbstractSimplePool = class {
614
724
  verifyEvent;
615
725
  enablePing;
616
726
  enableReconnect;
727
+ idleTimeout = 2e4;
617
728
  automaticallyAuth;
618
729
  trustedRelayURLs = /* @__PURE__ */ new Set();
619
730
  onRelayConnectionFailure;
@@ -626,6 +737,8 @@ var AbstractSimplePool = class {
626
737
  this._WebSocket = opts.websocketImplementation;
627
738
  this.enablePing = opts.enablePing;
628
739
  this.enableReconnect = opts.enableReconnect || false;
740
+ if (opts.idleTimeout)
741
+ this.idleTimeout = opts.idleTimeout;
629
742
  this.automaticallyAuth = opts.automaticallyAuth;
630
743
  this.onRelayConnectionFailure = opts.onRelayConnectionFailure;
631
744
  this.onRelayConnectionSuccess = opts.onRelayConnectionSuccess;
@@ -640,7 +753,8 @@ var AbstractSimplePool = class {
640
753
  verifyEvent: this.trustedRelayURLs.has(url) ? alwaysTrue : this.verifyEvent,
641
754
  websocketImplementation: this._WebSocket,
642
755
  enablePing: this.enablePing,
643
- enableReconnect: this.enableReconnect
756
+ enableReconnect: this.enableReconnect,
757
+ idleTimeout: this.idleTimeout
644
758
  });
645
759
  relay.onclose = () => {
646
760
  this.relays.delete(url);
@@ -833,6 +947,48 @@ var AbstractSimplePool = class {
833
947
  events.sort((a, b) => b.created_at - a.created_at);
834
948
  return events[0] || null;
835
949
  }
950
+ async countMany(relays, target, directive, params) {
951
+ const filter = getCountManyFilter(target, directive);
952
+ const urls = [];
953
+ for (let i = 0; i < relays.length; i++) {
954
+ const url = normalizeURL(relays[i]);
955
+ if (urls.indexOf(url) === -1)
956
+ urls.push(url);
957
+ }
958
+ const responses = await Promise.all(
959
+ urls.map(async (url) => {
960
+ if (this.allowConnectingToRelay?.(url, ["read", [filter]]) === false)
961
+ return null;
962
+ let relay;
963
+ try {
964
+ relay = await this.ensureRelay(url, {
965
+ connectionTimeout: this.maxWaitForConnection < (params?.maxWait || 0) ? Math.max(params.maxWait * 0.8, params.maxWait - 1e3) : this.maxWaitForConnection,
966
+ abort: params?.abort
967
+ });
968
+ } catch (err) {
969
+ this.onRelayConnectionFailure?.(url);
970
+ return null;
971
+ }
972
+ this.onRelayConnectionSuccess?.(url);
973
+ return relay.countWithHLL([filter], { id: params?.id }).catch(() => null);
974
+ })
975
+ );
976
+ let count = 0;
977
+ let hll;
978
+ for (const response of responses) {
979
+ if (!response)
980
+ continue;
981
+ if (response.count > count)
982
+ count = response.count;
983
+ if (!response.hll || response.hll.length !== 512)
984
+ continue;
985
+ const registers = hllDecode(response.hll);
986
+ if (!registers)
987
+ continue;
988
+ hll = mergeHll(hll || new Uint8Array(0), registers);
989
+ }
990
+ return hll ? { count, hll: hllEncode(hll) } : { count };
991
+ }
836
992
  publish(relays, event, params) {
837
993
  return relays.map(normalizeURL).map(async (url, i, arr) => {
838
994
  if (arr.indexOf(url) !== i) {