nostr-tools 2.22.0 → 2.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/lib/cjs/abstract-pool.js +182 -225
  2. package/lib/cjs/abstract-pool.js.map +4 -4
  3. package/lib/cjs/abstract-relay.js +117 -191
  4. package/lib/cjs/abstract-relay.js.map +4 -4
  5. package/lib/cjs/index.js +229 -223
  6. package/lib/cjs/index.js.map +4 -4
  7. package/lib/cjs/nip04.js.map +1 -1
  8. package/lib/cjs/nip13.js.map +1 -1
  9. package/lib/cjs/nip17.js.map +1 -1
  10. package/lib/cjs/nip18.js.map +1 -1
  11. package/lib/cjs/nip19.js.map +1 -1
  12. package/lib/cjs/nip21.js.map +1 -1
  13. package/lib/cjs/nip25.js.map +1 -1
  14. package/lib/cjs/nip27.js +1 -1
  15. package/lib/cjs/nip27.js.map +2 -2
  16. package/lib/cjs/nip28.js.map +1 -1
  17. package/lib/cjs/nip29.js.map +1 -1
  18. package/lib/cjs/nip44.js.map +1 -1
  19. package/lib/cjs/nip46.js +183 -226
  20. package/lib/cjs/nip46.js.map +4 -4
  21. package/lib/cjs/nip47.js.map +1 -1
  22. package/lib/cjs/nip57.js.map +1 -1
  23. package/lib/cjs/nip59.js.map +1 -1
  24. package/lib/cjs/nip98.js.map +1 -1
  25. package/lib/cjs/nipb7.js.map +1 -1
  26. package/lib/cjs/pool.js +183 -226
  27. package/lib/cjs/pool.js.map +4 -4
  28. package/lib/cjs/pure.js.map +1 -1
  29. package/lib/cjs/references.js.map +1 -1
  30. package/lib/cjs/relay.js +117 -191
  31. package/lib/cjs/relay.js.map +4 -4
  32. package/lib/cjs/signer.js.map +1 -1
  33. package/lib/cjs/utils.js +45 -44
  34. package/lib/cjs/utils.js.map +3 -3
  35. package/lib/esm/abstract-pool.js +182 -225
  36. package/lib/esm/abstract-pool.js.map +4 -4
  37. package/lib/esm/abstract-relay.js +117 -191
  38. package/lib/esm/abstract-relay.js.map +4 -4
  39. package/lib/esm/index.js +229 -223
  40. package/lib/esm/index.js.map +4 -4
  41. package/lib/esm/nip04.js.map +1 -1
  42. package/lib/esm/nip13.js.map +1 -1
  43. package/lib/esm/nip17.js.map +1 -1
  44. package/lib/esm/nip18.js.map +1 -1
  45. package/lib/esm/nip19.js.map +1 -1
  46. package/lib/esm/nip21.js.map +1 -1
  47. package/lib/esm/nip25.js.map +1 -1
  48. package/lib/esm/nip27.js +1 -1
  49. package/lib/esm/nip27.js.map +2 -2
  50. package/lib/esm/nip28.js.map +1 -1
  51. package/lib/esm/nip29.js.map +1 -1
  52. package/lib/esm/nip44.js.map +1 -1
  53. package/lib/esm/nip46.js +183 -226
  54. package/lib/esm/nip46.js.map +4 -4
  55. package/lib/esm/nip47.js.map +1 -1
  56. package/lib/esm/nip57.js.map +1 -1
  57. package/lib/esm/nip59.js.map +1 -1
  58. package/lib/esm/nip98.js.map +1 -1
  59. package/lib/esm/nipb7.js.map +1 -1
  60. package/lib/esm/pool.js +183 -226
  61. package/lib/esm/pool.js.map +4 -4
  62. package/lib/esm/pure.js.map +1 -1
  63. package/lib/esm/references.js.map +1 -1
  64. package/lib/esm/relay.js +117 -191
  65. package/lib/esm/relay.js.map +4 -4
  66. package/lib/esm/signer.js.map +1 -1
  67. package/lib/esm/utils.js +45 -44
  68. package/lib/esm/utils.js.map +3 -3
  69. package/lib/nostr.bundle.js +237 -231
  70. package/lib/nostr.bundle.js.map +4 -4
  71. package/lib/types/abstract-pool.d.ts +12 -1
  72. package/lib/types/abstract-relay.d.ts +3 -5
  73. package/lib/types/helpers.d.ts +0 -1
  74. package/lib/types/utils.d.ts +4 -16
  75. package/package.json +44 -1
@@ -52,54 +52,6 @@ function normalizeURL(url) {
52
52
  throw new Error(`Invalid URL: ${url}`);
53
53
  }
54
54
  }
55
- var QueueNode = class {
56
- value;
57
- next = null;
58
- prev = null;
59
- constructor(message) {
60
- this.value = message;
61
- }
62
- };
63
- var Queue = class {
64
- first;
65
- last;
66
- constructor() {
67
- this.first = null;
68
- this.last = null;
69
- }
70
- enqueue(value) {
71
- const newNode = new QueueNode(value);
72
- if (!this.last) {
73
- this.first = newNode;
74
- this.last = newNode;
75
- } else if (this.last === this.first) {
76
- this.last = newNode;
77
- this.last.prev = this.first;
78
- this.first.next = newNode;
79
- } else {
80
- newNode.prev = this.last;
81
- this.last.next = newNode;
82
- this.last = newNode;
83
- }
84
- return true;
85
- }
86
- dequeue() {
87
- if (!this.first)
88
- return null;
89
- if (this.first === this.last) {
90
- const target2 = this.first;
91
- this.first = null;
92
- this.last = null;
93
- return target2.value;
94
- }
95
- const target = this.first;
96
- this.first = target.next;
97
- if (this.first) {
98
- this.first.prev = null;
99
- }
100
- return target.value;
101
- }
102
- };
103
55
 
104
56
  // kinds.ts
105
57
  var ClientAuth = 22242;
@@ -173,39 +125,6 @@ function makeAuthEvent(relayURL, challenge) {
173
125
  };
174
126
  }
175
127
 
176
- // helpers.ts
177
- async function yieldThread() {
178
- return new Promise((resolve, reject) => {
179
- try {
180
- if (typeof MessageChannel !== "undefined") {
181
- const ch = new MessageChannel();
182
- const handler = () => {
183
- ch.port1.removeEventListener("message", handler);
184
- resolve();
185
- };
186
- ch.port1.addEventListener("message", handler);
187
- ch.port2.postMessage(0);
188
- ch.port1.start();
189
- } else {
190
- if (typeof setImmediate !== "undefined") {
191
- setImmediate(resolve);
192
- } else if (typeof setTimeout !== "undefined") {
193
- setTimeout(resolve, 0);
194
- } else {
195
- resolve();
196
- }
197
- }
198
- } catch (e) {
199
- console.error("during yield: ", e);
200
- reject(e);
201
- }
202
- });
203
- }
204
- var alwaysTrue = (t) => {
205
- t[verifiedSymbol] = true;
206
- return true;
207
- };
208
-
209
128
  // abstract-relay.ts
210
129
  var SendingOnClosedConnection = class extends Error {
211
130
  constructor(message, relay) {
@@ -227,16 +146,16 @@ var AbstractRelay = class {
227
146
  openSubs = /* @__PURE__ */ new Map();
228
147
  enablePing;
229
148
  enableReconnect;
149
+ idleSince = Date.now();
150
+ ongoingOperations = 0;
230
151
  reconnectTimeoutHandle;
231
152
  pingIntervalHandle;
232
153
  reconnectAttempts = 0;
233
- closedIntentionally = false;
154
+ skipReconnection = false;
234
155
  connectionPromise;
235
156
  openCountRequests = /* @__PURE__ */ new Map();
236
157
  openEventPublishes = /* @__PURE__ */ new Map();
237
158
  ws;
238
- incomingMessageQueue = new Queue();
239
- queueRunning = false;
240
159
  challenge;
241
160
  authPromise;
242
161
  serial = 0;
@@ -288,12 +207,11 @@ var AbstractRelay = class {
288
207
  }
289
208
  this._connected = false;
290
209
  this.connectionPromise = void 0;
291
- const wasIntentional = this.closedIntentionally;
292
- this.closedIntentionally = false;
293
- this.onclose?.();
294
- if (this.enableReconnect && !wasIntentional) {
210
+ this.idleSince = void 0;
211
+ if (this.enableReconnect && !this.skipReconnection) {
295
212
  this.reconnect();
296
213
  } else {
214
+ this.onclose?.();
297
215
  this.closeAllSubscriptions(reason);
298
216
  }
299
217
  }
@@ -303,13 +221,15 @@ var AbstractRelay = class {
303
221
  return this.connectionPromise;
304
222
  this.challenge = void 0;
305
223
  this.authPromise = void 0;
224
+ this.skipReconnection = false;
306
225
  this.connectionPromise = new Promise((resolve, reject) => {
307
226
  if (opts?.timeout) {
308
227
  connectionTimeoutHandle = setTimeout(() => {
309
228
  reject("connection timed out");
310
229
  this.connectionPromise = void 0;
230
+ this.skipReconnection = true;
311
231
  this.onclose?.();
312
- this.closeAllSubscriptions("relay connection timed out");
232
+ this.handleHardClose("relay connection timed out");
313
233
  }, opts.timeout);
314
234
  }
315
235
  if (opts?.abort) {
@@ -347,10 +267,13 @@ var AbstractRelay = class {
347
267
  }
348
268
  resolve();
349
269
  };
350
- this.ws.onerror = (ev) => {
270
+ this.ws.onerror = () => {
351
271
  clearTimeout(connectionTimeoutHandle);
352
- reject(ev.message || "websocket error");
353
- this.handleHardClose("relay connection errored");
272
+ reject("connection failed");
273
+ this.connectionPromise = void 0;
274
+ this.skipReconnection = true;
275
+ this.onclose?.();
276
+ this.handleHardClose("relay connection failed");
354
277
  };
355
278
  this.ws.onclose = (ev) => {
356
279
  clearTimeout(connectionTimeoutHandle);
@@ -376,7 +299,7 @@ var AbstractRelay = class {
376
299
  const sub = this.subscribe(
377
300
  [{ ids: ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"], limit: 0 }],
378
301
  {
379
- label: "forced-ping",
302
+ label: "<forced-ping>",
380
303
  oneose: () => {
381
304
  resolve(true);
382
305
  sub.close();
@@ -405,20 +328,106 @@ var AbstractRelay = class {
405
328
  }
406
329
  }
407
330
  }
408
- async runQueue() {
409
- this.queueRunning = true;
410
- while (true) {
411
- if (false === this.handleNext()) {
412
- break;
331
+ async send(message) {
332
+ if (!this.connectionPromise)
333
+ throw new SendingOnClosedConnection(message, this.url);
334
+ this.connectionPromise.then(() => {
335
+ this.ws?.send(message);
336
+ });
337
+ }
338
+ async auth(signAuthEvent) {
339
+ const challenge = this.challenge;
340
+ if (!challenge)
341
+ throw new Error("can't perform auth, no challenge was received");
342
+ if (this.authPromise)
343
+ return this.authPromise;
344
+ this.authPromise = new Promise(async (resolve, reject) => {
345
+ try {
346
+ let evt = await signAuthEvent(makeAuthEvent(this.url, challenge));
347
+ let timeout = setTimeout(() => {
348
+ let ep = this.openEventPublishes.get(evt.id);
349
+ if (ep) {
350
+ ep.reject(new Error("auth timed out"));
351
+ this.openEventPublishes.delete(evt.id);
352
+ }
353
+ }, this.publishTimeout);
354
+ this.openEventPublishes.set(evt.id, { resolve, reject, timeout });
355
+ this.send('["AUTH",' + JSON.stringify(evt) + "]");
356
+ } catch (err) {
357
+ console.warn("subscribe auth function failed:", err);
413
358
  }
414
- await yieldThread();
359
+ });
360
+ return this.authPromise;
361
+ }
362
+ async publish(event) {
363
+ this.idleSince = void 0;
364
+ this.ongoingOperations++;
365
+ const ret = new Promise((resolve, reject) => {
366
+ const timeout = setTimeout(() => {
367
+ const ep = this.openEventPublishes.get(event.id);
368
+ if (ep) {
369
+ ep.reject(new Error("publish timed out"));
370
+ this.openEventPublishes.delete(event.id);
371
+ }
372
+ }, this.publishTimeout);
373
+ this.openEventPublishes.set(event.id, { resolve, reject, timeout });
374
+ });
375
+ this.send('["EVENT",' + JSON.stringify(event) + "]");
376
+ this.ongoingOperations--;
377
+ if (this.ongoingOperations === 0)
378
+ this.idleSince = Date.now();
379
+ return ret;
380
+ }
381
+ async count(filters, params) {
382
+ this.serial++;
383
+ const id = params?.id || "count:" + this.serial;
384
+ const ret = new Promise((resolve, reject) => {
385
+ this.openCountRequests.set(id, { resolve, reject });
386
+ });
387
+ this.send('["COUNT","' + id + '",' + JSON.stringify(filters).substring(1));
388
+ return ret;
389
+ }
390
+ subscribe(filters, params) {
391
+ if (params.label !== "<forced-ping>") {
392
+ this.idleSince = void 0;
393
+ this.ongoingOperations++;
394
+ }
395
+ const sub = this.prepareSubscription(filters, params);
396
+ sub.fire();
397
+ if (params.abort) {
398
+ params.abort.onabort = () => sub.close(String(params.abort.reason || "<aborted>"));
415
399
  }
416
- this.queueRunning = false;
400
+ return sub;
417
401
  }
418
- handleNext() {
419
- const json = this.incomingMessageQueue.dequeue();
402
+ prepareSubscription(filters, params) {
403
+ this.serial++;
404
+ const id = params.id || (params.label ? params.label + ":" : "sub:") + this.serial;
405
+ const sub = new Subscription(this, id, filters, params);
406
+ this.openSubs.set(id, sub);
407
+ return sub;
408
+ }
409
+ close() {
410
+ this.skipReconnection = true;
411
+ if (this.reconnectTimeoutHandle) {
412
+ clearTimeout(this.reconnectTimeoutHandle);
413
+ this.reconnectTimeoutHandle = void 0;
414
+ }
415
+ if (this.pingIntervalHandle) {
416
+ clearInterval(this.pingIntervalHandle);
417
+ this.pingIntervalHandle = void 0;
418
+ }
419
+ this.closeAllSubscriptions("relay connection closed by us");
420
+ this._connected = false;
421
+ this.idleSince = void 0;
422
+ this.onclose?.();
423
+ if (this.ws?.readyState === this._WebSocket.OPEN) {
424
+ this.ws?.close();
425
+ }
426
+ }
427
+ _onmessage(ev) {
428
+ const json = ev.data;
420
429
  if (!json) {
421
- return false;
430
+ return;
422
431
  }
423
432
  const subid = getSubscriptionId(json);
424
433
  if (subid) {
@@ -505,101 +514,11 @@ var AbstractRelay = class {
505
514
  }
506
515
  }
507
516
  } catch (err) {
517
+ const [_, __, event] = JSON.parse(json);
518
+ window.printer.maybe(event.pubkey, ":: caught err", event, this.url, err);
508
519
  return;
509
520
  }
510
521
  }
511
- async send(message) {
512
- if (!this.connectionPromise)
513
- throw new SendingOnClosedConnection(message, this.url);
514
- this.connectionPromise.then(() => {
515
- this.ws?.send(message);
516
- });
517
- }
518
- async auth(signAuthEvent) {
519
- const challenge = this.challenge;
520
- if (!challenge)
521
- throw new Error("can't perform auth, no challenge was received");
522
- if (this.authPromise)
523
- return this.authPromise;
524
- this.authPromise = new Promise(async (resolve, reject) => {
525
- try {
526
- let evt = await signAuthEvent(makeAuthEvent(this.url, challenge));
527
- let timeout = setTimeout(() => {
528
- let ep = this.openEventPublishes.get(evt.id);
529
- if (ep) {
530
- ep.reject(new Error("auth timed out"));
531
- this.openEventPublishes.delete(evt.id);
532
- }
533
- }, this.publishTimeout);
534
- this.openEventPublishes.set(evt.id, { resolve, reject, timeout });
535
- this.send('["AUTH",' + JSON.stringify(evt) + "]");
536
- } catch (err) {
537
- console.warn("subscribe auth function failed:", err);
538
- }
539
- });
540
- return this.authPromise;
541
- }
542
- async publish(event) {
543
- const ret = new Promise((resolve, reject) => {
544
- const timeout = setTimeout(() => {
545
- const ep = this.openEventPublishes.get(event.id);
546
- if (ep) {
547
- ep.reject(new Error("publish timed out"));
548
- this.openEventPublishes.delete(event.id);
549
- }
550
- }, this.publishTimeout);
551
- this.openEventPublishes.set(event.id, { resolve, reject, timeout });
552
- });
553
- this.send('["EVENT",' + JSON.stringify(event) + "]");
554
- return ret;
555
- }
556
- async count(filters, params) {
557
- this.serial++;
558
- const id = params?.id || "count:" + this.serial;
559
- const ret = new Promise((resolve, reject) => {
560
- this.openCountRequests.set(id, { resolve, reject });
561
- });
562
- this.send('["COUNT","' + id + '",' + JSON.stringify(filters).substring(1));
563
- return ret;
564
- }
565
- subscribe(filters, params) {
566
- const sub = this.prepareSubscription(filters, params);
567
- sub.fire();
568
- if (params.abort) {
569
- params.abort.onabort = () => sub.close(String(params.abort.reason || "<aborted>"));
570
- }
571
- return sub;
572
- }
573
- prepareSubscription(filters, params) {
574
- this.serial++;
575
- const id = params.id || (params.label ? params.label + ":" : "sub:") + this.serial;
576
- const subscription = new Subscription(this, id, filters, params);
577
- this.openSubs.set(id, subscription);
578
- return subscription;
579
- }
580
- close() {
581
- this.closedIntentionally = true;
582
- if (this.reconnectTimeoutHandle) {
583
- clearTimeout(this.reconnectTimeoutHandle);
584
- this.reconnectTimeoutHandle = void 0;
585
- }
586
- if (this.pingIntervalHandle) {
587
- clearInterval(this.pingIntervalHandle);
588
- this.pingIntervalHandle = void 0;
589
- }
590
- this.closeAllSubscriptions("relay connection closed by us");
591
- this._connected = false;
592
- this.onclose?.();
593
- if (this.ws?.readyState === this._WebSocket.OPEN) {
594
- this.ws?.close();
595
- }
596
- }
597
- _onmessage(ev) {
598
- this.incomingMessageQueue.enqueue(ev.data);
599
- if (!this.queueRunning) {
600
- this.runQueue();
601
- }
602
- }
603
522
  };
604
523
  var Subscription = class {
605
524
  relay;
@@ -658,10 +577,19 @@ var Subscription = class {
658
577
  this.closed = true;
659
578
  }
660
579
  this.relay.openSubs.delete(this.id);
580
+ this.relay.ongoingOperations--;
581
+ if (this.relay.ongoingOperations === 0)
582
+ this.relay.idleSince = Date.now();
661
583
  this.onclose?.(reason);
662
584
  }
663
585
  };
664
586
 
587
+ // helpers.ts
588
+ var alwaysTrue = (t) => {
589
+ t[verifiedSymbol] = true;
590
+ return true;
591
+ };
592
+
665
593
  // abstract-pool.ts
666
594
  var AbstractSimplePool = class {
667
595
  relays = /* @__PURE__ */ new Map();
@@ -672,6 +600,10 @@ var AbstractSimplePool = class {
672
600
  enableReconnect;
673
601
  automaticallyAuth;
674
602
  trustedRelayURLs = /* @__PURE__ */ new Set();
603
+ onRelayConnectionFailure;
604
+ onRelayConnectionSuccess;
605
+ allowConnectingToRelay;
606
+ maxWaitForConnection;
675
607
  _WebSocket;
676
608
  constructor(opts) {
677
609
  this.verifyEvent = opts.verifyEvent;
@@ -679,6 +611,10 @@ var AbstractSimplePool = class {
679
611
  this.enablePing = opts.enablePing;
680
612
  this.enableReconnect = opts.enableReconnect || false;
681
613
  this.automaticallyAuth = opts.automaticallyAuth;
614
+ this.onRelayConnectionFailure = opts.onRelayConnectionFailure;
615
+ this.onRelayConnectionSuccess = opts.onRelayConnectionSuccess;
616
+ this.allowConnectingToRelay = opts.allowConnectingToRelay;
617
+ this.maxWaitForConnection = opts.maxWaitForConnection || 3e3;
682
618
  }
683
619
  async ensureRelay(url, params) {
684
620
  url = normalizeURL(url);
@@ -691,9 +627,7 @@ var AbstractSimplePool = class {
691
627
  enableReconnect: this.enableReconnect
692
628
  });
693
629
  relay.onclose = () => {
694
- if (relay && !relay.enableReconnect) {
695
- this.relays.delete(url);
696
- }
630
+ this.relays.delete(url);
697
631
  };
698
632
  this.relays.set(url, relay);
699
633
  }
@@ -703,10 +637,15 @@ var AbstractSimplePool = class {
703
637
  relay.onauth = authSignerFn;
704
638
  }
705
639
  }
706
- await relay.connect({
707
- timeout: params?.connectionTimeout,
708
- abort: params?.abort
709
- });
640
+ try {
641
+ await relay.connect({
642
+ timeout: params?.connectionTimeout,
643
+ abort: params?.abort
644
+ });
645
+ } catch (err) {
646
+ this.relays.delete(url);
647
+ throw err;
648
+ }
710
649
  return relay;
711
650
  }
712
651
  close(relays) {
@@ -717,25 +656,20 @@ var AbstractSimplePool = class {
717
656
  }
718
657
  subscribe(relays, filter, params) {
719
658
  const request = [];
659
+ const uniqUrls = [];
720
660
  for (let i = 0; i < relays.length; i++) {
721
661
  const url = normalizeURL(relays[i]);
722
662
  if (!request.find((r) => r.url === url)) {
723
- request.push({ url, filter });
663
+ if (uniqUrls.indexOf(url) === -1) {
664
+ uniqUrls.push(url);
665
+ request.push({ url, filter });
666
+ }
724
667
  }
725
668
  }
726
669
  return this.subscribeMap(request, params);
727
670
  }
728
671
  subscribeMany(relays, filter, params) {
729
- const request = [];
730
- const uniqUrls = [];
731
- for (let i = 0; i < relays.length; i++) {
732
- const url = normalizeURL(relays[i]);
733
- if (uniqUrls.indexOf(url) === -1) {
734
- uniqUrls.push(url);
735
- request.push({ url, filter });
736
- }
737
- }
738
- return this.subscribeMap(request, params);
672
+ return this.subscribe(relays, filter, params);
739
673
  }
740
674
  subscribeMap(requests, params) {
741
675
  const grouped = /* @__PURE__ */ new Map();
@@ -791,16 +725,22 @@ var AbstractSimplePool = class {
791
725
  };
792
726
  const allOpened = Promise.all(
793
727
  groupedRequests.map(async ({ url, filters }, i) => {
728
+ if (this.allowConnectingToRelay?.(url, ["read", filters]) === false) {
729
+ handleClose(i, "connection skipped by allowConnectingToRelay");
730
+ return;
731
+ }
794
732
  let relay;
795
733
  try {
796
734
  relay = await this.ensureRelay(url, {
797
- connectionTimeout: params.maxWait ? Math.max(params.maxWait * 0.8, params.maxWait - 1e3) : void 0,
735
+ connectionTimeout: this.maxWaitForConnection < (params.maxWait || 0) ? Math.max(params.maxWait * 0.8, params.maxWait - 1e3) : this.maxWaitForConnection,
798
736
  abort: params.abort
799
737
  });
800
738
  } catch (err) {
739
+ this.onRelayConnectionFailure?.(url);
801
740
  handleClose(i, err?.message || String(err));
802
741
  return;
803
742
  }
743
+ this.onRelayConnectionSuccess?.(url);
804
744
  let subscription = relay.subscribe(filters, {
805
745
  ...params,
806
746
  oneose: () => handleEose(i),
@@ -850,13 +790,7 @@ var AbstractSimplePool = class {
850
790
  return subcloser;
851
791
  }
852
792
  subscribeManyEose(relays, filter, params) {
853
- const subcloser = this.subscribeMany(relays, filter, {
854
- ...params,
855
- oneose() {
856
- subcloser.close("closed automatically on eose");
857
- }
858
- });
859
- return subcloser;
793
+ return this.subscribeEose(relays, filter, params);
860
794
  }
861
795
  async querySync(relays, filter, params) {
862
796
  return new Promise(async (resolve) => {
@@ -878,15 +812,27 @@ var AbstractSimplePool = class {
878
812
  events.sort((a, b) => b.created_at - a.created_at);
879
813
  return events[0] || null;
880
814
  }
881
- publish(relays, event, options) {
815
+ publish(relays, event, params) {
882
816
  return relays.map(normalizeURL).map(async (url, i, arr) => {
883
817
  if (arr.indexOf(url) !== i) {
884
818
  return Promise.reject("duplicate url");
885
819
  }
886
- let r = await this.ensureRelay(url);
820
+ if (this.allowConnectingToRelay?.(url, ["write", event]) === false) {
821
+ return Promise.reject("connection skipped by allowConnectingToRelay");
822
+ }
823
+ let r;
824
+ try {
825
+ r = await this.ensureRelay(url, {
826
+ connectionTimeout: this.maxWaitForConnection < (params?.maxWait || 0) ? Math.max(params.maxWait * 0.8, params.maxWait - 1e3) : this.maxWaitForConnection,
827
+ abort: params?.abort
828
+ });
829
+ } catch (err) {
830
+ this.onRelayConnectionFailure?.(url);
831
+ return String("connection failure: " + String(err));
832
+ }
887
833
  return r.publish(event).catch(async (err) => {
888
- if (err instanceof Error && err.message.startsWith("auth-required: ") && options?.onauth) {
889
- await r.auth(options.onauth);
834
+ if (err instanceof Error && err.message.startsWith("auth-required: ") && params?.onauth) {
835
+ await r.auth(params.onauth);
890
836
  return r.publish(event);
891
837
  }
892
838
  throw err;
@@ -912,4 +858,15 @@ var AbstractSimplePool = class {
912
858
  this.relays.forEach((conn) => conn.close());
913
859
  this.relays = /* @__PURE__ */ new Map();
914
860
  }
861
+ pruneIdleRelays(idleThresholdMs = 1e4) {
862
+ const prunedUrls = [];
863
+ for (const [url, relay] of this.relays) {
864
+ if (relay.idleSince && Date.now() - relay.idleSince >= idleThresholdMs) {
865
+ this.relays.delete(url);
866
+ prunedUrls.push(url);
867
+ relay.close();
868
+ }
869
+ }
870
+ return prunedUrls;
871
+ }
915
872
  };