holosphere 2.0.0-alpha4 → 2.0.0-alpha5
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/dist/cjs/holosphere.cjs +1 -1
- package/dist/esm/holosphere.js +1 -1
- package/dist/{index-CBitK71M.cjs → index-BG8FStkt.cjs} +2 -2
- package/dist/{index-CBitK71M.cjs.map → index-BG8FStkt.cjs.map} +1 -1
- package/dist/{index-CV0eOogK.js → index-Bbey4GkP.js} +502 -56
- package/dist/index-Bbey4GkP.js.map +1 -0
- package/dist/{index-Cz-PLCUR.js → index-Cp3xctq8.js} +2 -2
- package/dist/{index-Cz-PLCUR.js.map → index-Cp3xctq8.js.map} +1 -1
- package/dist/index-hfVGRwSr.cjs +5 -0
- package/dist/index-hfVGRwSr.cjs.map +1 -0
- package/dist/{indexeddb-storage-CRsZyB2f.cjs → indexeddb-storage-BD70pN7q.cjs} +2 -2
- package/dist/{indexeddb-storage-CRsZyB2f.cjs.map → indexeddb-storage-BD70pN7q.cjs.map} +1 -1
- package/dist/{indexeddb-storage-DZaGlY_a.js → indexeddb-storage-Bjg84U5R.js} +2 -2
- package/dist/{indexeddb-storage-DZaGlY_a.js.map → indexeddb-storage-Bjg84U5R.js.map} +1 -1
- package/dist/{memory-storage-BkUi6sZG.js → memory-storage-CD0XFayE.js} +2 -2
- package/dist/{memory-storage-BkUi6sZG.js.map → memory-storage-CD0XFayE.js.map} +1 -1
- package/dist/{memory-storage-C0DuUsdY.cjs → memory-storage-DmMyJtOo.cjs} +2 -2
- package/dist/{memory-storage-C0DuUsdY.cjs.map → memory-storage-DmMyJtOo.cjs.map} +1 -1
- package/dist/{secp256k1-DN4FVXcv.js → secp256k1-69sS9O-P.js} +2 -2
- package/dist/{secp256k1-DN4FVXcv.js.map → secp256k1-69sS9O-P.js.map} +1 -1
- package/dist/{secp256k1-0kPdAVkK.cjs → secp256k1-TcN6vWGh.cjs} +2 -2
- package/dist/{secp256k1-0kPdAVkK.cjs.map → secp256k1-TcN6vWGh.cjs.map} +1 -1
- package/package.json +1 -1
- package/src/storage/nostr-async.js +149 -14
- package/src/storage/nostr-client.js +569 -46
- package/dist/index-BB_vVJgv.cjs +0 -5
- package/dist/index-BB_vVJgv.cjs.map +0 -1
- package/dist/index-CV0eOogK.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getPublicKey as getPublicKey$2,
|
|
1
|
+
import { getPublicKey as getPublicKey$2, finalizeEvent, SimplePool, nip19, nip04, verifyEvent as verifyEvent$1 } from "nostr-tools";
|
|
2
2
|
import * as h3 from "h3-js";
|
|
3
3
|
import { latLngToCell, getResolution, cellToParent, cellToChildren, isValidCell } from "h3-js";
|
|
4
4
|
import Ajv from "ajv";
|
|
@@ -361,17 +361,84 @@ async function createPersistentStorage(namespace, options = {}) {
|
|
|
361
361
|
const isBrowser = typeof window !== "undefined" && typeof window.indexedDB !== "undefined";
|
|
362
362
|
typeof process !== "undefined" && process.versions && void 0;
|
|
363
363
|
if (isBrowser) {
|
|
364
|
-
const { IndexedDBStorage } = await import("./indexeddb-storage-
|
|
364
|
+
const { IndexedDBStorage } = await import("./indexeddb-storage-Bjg84U5R.js");
|
|
365
365
|
const storage = new IndexedDBStorage();
|
|
366
366
|
await storage.init(namespace);
|
|
367
367
|
return storage;
|
|
368
368
|
} else {
|
|
369
|
-
const { MemoryStorage } = await import("./memory-storage-
|
|
369
|
+
const { MemoryStorage } = await import("./memory-storage-CD0XFayE.js");
|
|
370
370
|
const storage = new MemoryStorage();
|
|
371
371
|
await storage.init(namespace);
|
|
372
372
|
return storage;
|
|
373
373
|
}
|
|
374
374
|
}
|
|
375
|
+
let globalPool = null;
|
|
376
|
+
let globalPoolRelays = /* @__PURE__ */ new Set();
|
|
377
|
+
function getGlobalPool(config = {}) {
|
|
378
|
+
if (!globalPool) {
|
|
379
|
+
globalPool = new SimplePool({
|
|
380
|
+
enableReconnect: config.enableReconnect !== false,
|
|
381
|
+
enablePing: config.enablePing !== false
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
return globalPool;
|
|
385
|
+
}
|
|
386
|
+
const pendingQueries$1 = /* @__PURE__ */ new Map();
|
|
387
|
+
const PENDING_QUERY_TIMEOUT = 5e3;
|
|
388
|
+
const activeSubscriptions = /* @__PURE__ */ new Map();
|
|
389
|
+
const backgroundRefreshThrottle = /* @__PURE__ */ new Map();
|
|
390
|
+
const BACKGROUND_REFRESH_INTERVAL = 3e4;
|
|
391
|
+
const pendingWrites = /* @__PURE__ */ new Map();
|
|
392
|
+
const WRITE_DEBOUNCE_MS = 500;
|
|
393
|
+
const authorSubscriptions = /* @__PURE__ */ new Map();
|
|
394
|
+
const AUTHOR_SUB_INIT_TIMEOUT = 5e3;
|
|
395
|
+
class LRUCache {
|
|
396
|
+
constructor(maxSize = 500) {
|
|
397
|
+
this.maxSize = maxSize;
|
|
398
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
399
|
+
}
|
|
400
|
+
get(key) {
|
|
401
|
+
if (!this.cache.has(key)) return void 0;
|
|
402
|
+
const value = this.cache.get(key);
|
|
403
|
+
this.cache.delete(key);
|
|
404
|
+
this.cache.set(key, value);
|
|
405
|
+
return value;
|
|
406
|
+
}
|
|
407
|
+
set(key, value) {
|
|
408
|
+
if (this.cache.has(key)) {
|
|
409
|
+
this.cache.delete(key);
|
|
410
|
+
}
|
|
411
|
+
this.cache.set(key, value);
|
|
412
|
+
while (this.cache.size > this.maxSize) {
|
|
413
|
+
const oldestKey = this.cache.keys().next().value;
|
|
414
|
+
this.cache.delete(oldestKey);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
has(key) {
|
|
418
|
+
return this.cache.has(key);
|
|
419
|
+
}
|
|
420
|
+
delete(key) {
|
|
421
|
+
return this.cache.delete(key);
|
|
422
|
+
}
|
|
423
|
+
clear() {
|
|
424
|
+
this.cache.clear();
|
|
425
|
+
}
|
|
426
|
+
get size() {
|
|
427
|
+
return this.cache.size;
|
|
428
|
+
}
|
|
429
|
+
keys() {
|
|
430
|
+
return this.cache.keys();
|
|
431
|
+
}
|
|
432
|
+
values() {
|
|
433
|
+
return this.cache.values();
|
|
434
|
+
}
|
|
435
|
+
entries() {
|
|
436
|
+
return this.cache.entries();
|
|
437
|
+
}
|
|
438
|
+
forEach(callback) {
|
|
439
|
+
this.cache.forEach(callback);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
375
442
|
let webSocketPolyfillPromise = null;
|
|
376
443
|
function ensureWebSocket() {
|
|
377
444
|
if (typeof globalThis.WebSocket !== "undefined") {
|
|
@@ -407,8 +474,12 @@ class NostrClient {
|
|
|
407
474
|
this.publicKey = getPublicKey$2(this.privateKey);
|
|
408
475
|
this.config = config;
|
|
409
476
|
this._subscriptions = /* @__PURE__ */ new Map();
|
|
410
|
-
this._eventCache =
|
|
477
|
+
this._eventCache = new LRUCache(config.cacheSize || 500);
|
|
478
|
+
this._cacheIndex = /* @__PURE__ */ new Map();
|
|
411
479
|
this.persistentStorage = null;
|
|
480
|
+
this._persistQueue = /* @__PURE__ */ new Map();
|
|
481
|
+
this._persistTimer = null;
|
|
482
|
+
this._persistBatchMs = config.persistBatchMs || 100;
|
|
412
483
|
this._initReady = this._initialize();
|
|
413
484
|
}
|
|
414
485
|
/**
|
|
@@ -418,10 +489,8 @@ class NostrClient {
|
|
|
418
489
|
async _initialize() {
|
|
419
490
|
await ensureWebSocket();
|
|
420
491
|
if (this.relays.length > 0) {
|
|
421
|
-
this.pool =
|
|
422
|
-
|
|
423
|
-
enablePing: this.config.enablePing !== false
|
|
424
|
-
});
|
|
492
|
+
this.pool = getGlobalPool(this.config);
|
|
493
|
+
this.relays.forEach((r) => globalPoolRelays.add(r));
|
|
425
494
|
} else {
|
|
426
495
|
this.pool = {
|
|
427
496
|
publish: (relays, event) => [Promise.resolve()],
|
|
@@ -433,6 +502,69 @@ class NostrClient {
|
|
|
433
502
|
};
|
|
434
503
|
}
|
|
435
504
|
await this._initPersistentStorage();
|
|
505
|
+
if (this.relays.length > 0) {
|
|
506
|
+
this._initLongLivedSubscription();
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Initialize a long-lived subscription to keep cache fresh
|
|
511
|
+
* This replaces polling with a single persistent subscription
|
|
512
|
+
* @private
|
|
513
|
+
*/
|
|
514
|
+
_initLongLivedSubscription() {
|
|
515
|
+
const subKey = this.publicKey;
|
|
516
|
+
if (authorSubscriptions.has(subKey)) {
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
const subInfo = {
|
|
520
|
+
subscription: null,
|
|
521
|
+
initialized: false,
|
|
522
|
+
initPromise: null,
|
|
523
|
+
initResolve: null
|
|
524
|
+
};
|
|
525
|
+
subInfo.initPromise = new Promise((resolve) => {
|
|
526
|
+
subInfo.initResolve = resolve;
|
|
527
|
+
});
|
|
528
|
+
authorSubscriptions.set(subKey, subInfo);
|
|
529
|
+
const filter = {
|
|
530
|
+
kinds: [3e4],
|
|
531
|
+
authors: [this.publicKey]
|
|
532
|
+
};
|
|
533
|
+
const sub = this.pool.subscribeMany(
|
|
534
|
+
this.relays,
|
|
535
|
+
[filter],
|
|
536
|
+
{
|
|
537
|
+
onevent: (event) => {
|
|
538
|
+
if (event.pubkey !== this.publicKey) {
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
this._cacheEvent(event);
|
|
542
|
+
},
|
|
543
|
+
oneose: () => {
|
|
544
|
+
if (!subInfo.initialized) {
|
|
545
|
+
subInfo.initialized = true;
|
|
546
|
+
subInfo.initResolve();
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
);
|
|
551
|
+
subInfo.subscription = sub;
|
|
552
|
+
setTimeout(() => {
|
|
553
|
+
if (!subInfo.initialized) {
|
|
554
|
+
subInfo.initialized = true;
|
|
555
|
+
subInfo.initResolve();
|
|
556
|
+
}
|
|
557
|
+
}, AUTHOR_SUB_INIT_TIMEOUT);
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Wait for long-lived subscription to complete initial load
|
|
561
|
+
* @private
|
|
562
|
+
*/
|
|
563
|
+
async _waitForSubscriptionInit() {
|
|
564
|
+
const subInfo = authorSubscriptions.get(this.publicKey);
|
|
565
|
+
if (subInfo && subInfo.initPromise) {
|
|
566
|
+
await subInfo.initPromise;
|
|
567
|
+
}
|
|
436
568
|
}
|
|
437
569
|
/**
|
|
438
570
|
* Initialize persistent storage
|
|
@@ -542,13 +674,61 @@ class NostrClient {
|
|
|
542
674
|
}
|
|
543
675
|
/**
|
|
544
676
|
* Publish event to relays
|
|
677
|
+
* Supports debouncing for replaceable events (kind 30000-39999) to avoid rapid updates
|
|
545
678
|
* @param {Object} event - Unsigned event object
|
|
546
679
|
* @param {Object} options - Publish options
|
|
547
680
|
* @param {boolean} options.waitForRelays - Wait for relay confirmation (default: false for speed)
|
|
681
|
+
* @param {boolean} options.debounce - Debounce rapid writes to same d-tag (default: true for replaceable events)
|
|
548
682
|
* @returns {Promise<Object>} Signed event with relay publish results
|
|
549
683
|
*/
|
|
550
684
|
async publish(event, options = {}) {
|
|
551
685
|
await this._initReady;
|
|
686
|
+
const waitForRelays = options.waitForRelays || false;
|
|
687
|
+
const isReplaceable = event.kind >= 3e4 && event.kind < 4e4;
|
|
688
|
+
const shouldDebounce = isReplaceable && options.debounce !== false && !waitForRelays;
|
|
689
|
+
if (shouldDebounce) {
|
|
690
|
+
const dTag = event.tags?.find((t) => t[0] === "d");
|
|
691
|
+
if (dTag && dTag[1]) {
|
|
692
|
+
return this._debouncedPublish(event, dTag[1], options);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
return this._doPublish(event, options);
|
|
696
|
+
}
|
|
697
|
+
/**
|
|
698
|
+
* Debounced publish - coalesces rapid writes to the same d-tag
|
|
699
|
+
* @private
|
|
700
|
+
*/
|
|
701
|
+
_debouncedPublish(event, dTagPath, options) {
|
|
702
|
+
return new Promise((resolve, reject) => {
|
|
703
|
+
const existing = pendingWrites.get(dTagPath);
|
|
704
|
+
if (existing) {
|
|
705
|
+
clearTimeout(existing.timer);
|
|
706
|
+
existing.resolve({
|
|
707
|
+
event: null,
|
|
708
|
+
results: [],
|
|
709
|
+
debounced: true,
|
|
710
|
+
supersededBy: event
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
const timer = setTimeout(async () => {
|
|
714
|
+
pendingWrites.delete(dTagPath);
|
|
715
|
+
try {
|
|
716
|
+
const result = await this._doPublish(event, options);
|
|
717
|
+
resolve(result);
|
|
718
|
+
} catch (err) {
|
|
719
|
+
reject(err);
|
|
720
|
+
}
|
|
721
|
+
}, WRITE_DEBOUNCE_MS);
|
|
722
|
+
pendingWrites.set(dTagPath, { event, timer, resolve, reject });
|
|
723
|
+
const signedEvent = finalizeEvent(event, this.privateKey);
|
|
724
|
+
this._cacheEvent(signedEvent);
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
/**
|
|
728
|
+
* Internal publish implementation
|
|
729
|
+
* @private
|
|
730
|
+
*/
|
|
731
|
+
async _doPublish(event, options = {}) {
|
|
552
732
|
const waitForRelays = options.waitForRelays || false;
|
|
553
733
|
const signedEvent = finalizeEvent(event, this.privateKey);
|
|
554
734
|
await this._cacheEvent(signedEvent);
|
|
@@ -613,20 +793,39 @@ class NostrClient {
|
|
|
613
793
|
}
|
|
614
794
|
/**
|
|
615
795
|
* Query events from relays
|
|
796
|
+
* Uses long-lived subscription for cache updates - avoids polling
|
|
616
797
|
* @param {Object} filter - Nostr filter object
|
|
617
798
|
* @param {Object} options - Query options
|
|
618
799
|
* @param {number} options.timeout - Query timeout in ms (default: 30000, set to 0 for no timeout)
|
|
619
800
|
* @param {boolean} options.localFirst - Return local cache immediately, refresh in background (default: true)
|
|
801
|
+
* @param {boolean} options.forceRelay - Force relay query even if subscription cache is available (default: false)
|
|
620
802
|
* @returns {Promise<Array>} Array of events
|
|
621
803
|
*/
|
|
622
804
|
async query(filter, options = {}) {
|
|
623
805
|
await this._initReady;
|
|
624
806
|
const timeout = options.timeout !== void 0 ? options.timeout : 3e4;
|
|
625
807
|
const localFirst = options.localFirst !== false;
|
|
808
|
+
const forceRelay = options.forceRelay === true;
|
|
626
809
|
if (this.relays.length === 0) {
|
|
627
810
|
const matchingEvents = this._getMatchingCachedEvents(filter);
|
|
628
811
|
return matchingEvents;
|
|
629
812
|
}
|
|
813
|
+
const subInfo = authorSubscriptions.get(this.publicKey);
|
|
814
|
+
const isOwnDataQuery = filter.authors && filter.authors.length === 1 && filter.authors[0] === this.publicKey;
|
|
815
|
+
if (!forceRelay && isOwnDataQuery && subInfo && subInfo.initialized) {
|
|
816
|
+
const matchingEvents = this._getMatchingCachedEvents(filter);
|
|
817
|
+
return matchingEvents;
|
|
818
|
+
}
|
|
819
|
+
if (isOwnDataQuery && subInfo && !subInfo.initialized) {
|
|
820
|
+
await Promise.race([
|
|
821
|
+
subInfo.initPromise,
|
|
822
|
+
new Promise((resolve) => setTimeout(resolve, Math.min(timeout, AUTHOR_SUB_INIT_TIMEOUT)))
|
|
823
|
+
]);
|
|
824
|
+
if (subInfo.initialized) {
|
|
825
|
+
const matchingEvents = this._getMatchingCachedEvents(filter);
|
|
826
|
+
return matchingEvents;
|
|
827
|
+
}
|
|
828
|
+
}
|
|
630
829
|
if (filter["#d"] && filter["#d"].length === 1 && filter.kinds && filter.kinds.length === 1) {
|
|
631
830
|
const dTagKey = `d:${filter.kinds[0]}:${filter["#d"][0]}`;
|
|
632
831
|
const dTagCached = this._eventCache.get(dTagKey);
|
|
@@ -647,22 +846,78 @@ class NostrClient {
|
|
|
647
846
|
}
|
|
648
847
|
/**
|
|
649
848
|
* Query relays and update cache
|
|
849
|
+
* Uses global pending queries map to deduplicate identical concurrent queries
|
|
650
850
|
* @private
|
|
651
851
|
*/
|
|
652
852
|
async _queryRelaysAndCache(filter, cacheKey, timeout) {
|
|
653
|
-
|
|
654
|
-
if (
|
|
655
|
-
|
|
853
|
+
const pending = pendingQueries$1.get(cacheKey);
|
|
854
|
+
if (pending && Date.now() - pending.timestamp < PENDING_QUERY_TIMEOUT) {
|
|
855
|
+
return pending.promise;
|
|
656
856
|
}
|
|
657
|
-
|
|
658
|
-
|
|
857
|
+
const queryPromise = (async () => {
|
|
858
|
+
try {
|
|
859
|
+
let events = await this.pool.querySync(this.relays, filter, { timeout });
|
|
860
|
+
if (filter.authors && filter.authors.length > 0) {
|
|
861
|
+
events = events.filter((event) => filter.authors.includes(event.pubkey));
|
|
862
|
+
}
|
|
863
|
+
this._eventCache.set(cacheKey, {
|
|
864
|
+
events,
|
|
865
|
+
timestamp: Date.now()
|
|
866
|
+
});
|
|
867
|
+
this._indexCacheEntry(cacheKey, filter);
|
|
868
|
+
return events;
|
|
869
|
+
} finally {
|
|
870
|
+
pendingQueries$1.delete(cacheKey);
|
|
871
|
+
}
|
|
872
|
+
})();
|
|
873
|
+
pendingQueries$1.set(cacheKey, {
|
|
874
|
+
promise: queryPromise,
|
|
659
875
|
timestamp: Date.now()
|
|
660
876
|
});
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
877
|
+
return queryPromise;
|
|
878
|
+
}
|
|
879
|
+
/**
|
|
880
|
+
* Limit cache size (called after cache operations)
|
|
881
|
+
* Note: LRU cache handles this automatically, kept for API compatibility
|
|
882
|
+
* @private
|
|
883
|
+
*/
|
|
884
|
+
_limitCacheSize() {
|
|
885
|
+
}
|
|
886
|
+
/**
|
|
887
|
+
* Add cache entry to reverse index for fast invalidation
|
|
888
|
+
* @private
|
|
889
|
+
*/
|
|
890
|
+
_indexCacheEntry(cacheKey, filter) {
|
|
891
|
+
if (filter.kinds) {
|
|
892
|
+
for (const kind2 of filter.kinds) {
|
|
893
|
+
if (!this._cacheIndex.has(kind2)) {
|
|
894
|
+
this._cacheIndex.set(kind2, /* @__PURE__ */ new Set());
|
|
895
|
+
}
|
|
896
|
+
this._cacheIndex.get(kind2).add(cacheKey);
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
/**
|
|
901
|
+
* Remove cache entry from reverse index
|
|
902
|
+
* @private
|
|
903
|
+
*/
|
|
904
|
+
_unindexCacheEntry(cacheKey) {
|
|
905
|
+
if (!cacheKey.startsWith("{")) return;
|
|
906
|
+
try {
|
|
907
|
+
const filter = JSON.parse(cacheKey);
|
|
908
|
+
if (filter.kinds) {
|
|
909
|
+
for (const kind2 of filter.kinds) {
|
|
910
|
+
const indexSet = this._cacheIndex.get(kind2);
|
|
911
|
+
if (indexSet) {
|
|
912
|
+
indexSet.delete(cacheKey);
|
|
913
|
+
if (indexSet.size === 0) {
|
|
914
|
+
this._cacheIndex.delete(kind2);
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
} catch {
|
|
664
920
|
}
|
|
665
|
-
return events;
|
|
666
921
|
}
|
|
667
922
|
/**
|
|
668
923
|
* Refresh cache in background (fire and forget)
|
|
@@ -687,18 +942,33 @@ class NostrClient {
|
|
|
687
942
|
}
|
|
688
943
|
/**
|
|
689
944
|
* Internal method to refresh a path from relays
|
|
945
|
+
* Throttled to avoid flooding the relay with repeated requests
|
|
690
946
|
* @private
|
|
691
947
|
*/
|
|
692
948
|
async _doBackgroundPathRefresh(path, kind2, options) {
|
|
693
949
|
if (this.relays.length === 0) return;
|
|
950
|
+
const lastRefresh = backgroundRefreshThrottle.get(path);
|
|
951
|
+
if (lastRefresh && Date.now() - lastRefresh < BACKGROUND_REFRESH_INTERVAL) {
|
|
952
|
+
return;
|
|
953
|
+
}
|
|
954
|
+
backgroundRefreshThrottle.set(path, Date.now());
|
|
955
|
+
if (backgroundRefreshThrottle.size > 1e3) {
|
|
956
|
+
const cutoff = Date.now() - BACKGROUND_REFRESH_INTERVAL;
|
|
957
|
+
for (const [key, timestamp] of backgroundRefreshThrottle) {
|
|
958
|
+
if (timestamp < cutoff) {
|
|
959
|
+
backgroundRefreshThrottle.delete(key);
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
}
|
|
694
963
|
const filter = {
|
|
695
964
|
kinds: [kind2],
|
|
696
965
|
authors: options.authors || [this.publicKey],
|
|
697
966
|
"#d": [path],
|
|
698
967
|
limit: 1
|
|
699
968
|
};
|
|
969
|
+
const cacheKey = JSON.stringify(filter);
|
|
700
970
|
const timeout = options.timeout || 3e4;
|
|
701
|
-
const events = await this.
|
|
971
|
+
const events = await this._queryRelaysAndCache(filter, cacheKey, timeout);
|
|
702
972
|
const authorFiltered = events.filter(
|
|
703
973
|
(e) => (options.authors || [this.publicKey]).includes(e.pubkey)
|
|
704
974
|
);
|
|
@@ -720,17 +990,25 @@ class NostrClient {
|
|
|
720
990
|
}
|
|
721
991
|
/**
|
|
722
992
|
* Internal method to refresh a prefix from relays
|
|
993
|
+
* Throttled to avoid flooding the relay with repeated requests
|
|
723
994
|
* @private
|
|
724
995
|
*/
|
|
725
996
|
async _doBackgroundPrefixRefresh(prefix, kind2, options) {
|
|
726
997
|
if (this.relays.length === 0) return;
|
|
998
|
+
const throttleKey = `prefix:${prefix}`;
|
|
999
|
+
const lastRefresh = backgroundRefreshThrottle.get(throttleKey);
|
|
1000
|
+
if (lastRefresh && Date.now() - lastRefresh < BACKGROUND_REFRESH_INTERVAL) {
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
backgroundRefreshThrottle.set(throttleKey, Date.now());
|
|
727
1004
|
const filter = {
|
|
728
1005
|
kinds: [kind2],
|
|
729
1006
|
authors: options.authors || [this.publicKey],
|
|
730
1007
|
limit: options.limit || 1e3
|
|
731
1008
|
};
|
|
1009
|
+
const cacheKey = JSON.stringify(filter);
|
|
732
1010
|
const timeout = options.timeout || 3e4;
|
|
733
|
-
let events = await this.
|
|
1011
|
+
let events = await this._queryRelaysAndCache(filter, cacheKey, timeout);
|
|
734
1012
|
events = events.filter(
|
|
735
1013
|
(e) => (options.authors || [this.publicKey]).includes(e.pubkey)
|
|
736
1014
|
);
|
|
@@ -805,6 +1083,7 @@ class NostrClient {
|
|
|
805
1083
|
}
|
|
806
1084
|
/**
|
|
807
1085
|
* Subscribe to events
|
|
1086
|
+
* Uses subscription deduplication to avoid creating multiple identical subscriptions
|
|
808
1087
|
* @param {Object} filter - Nostr filter object
|
|
809
1088
|
* @param {Function} onEvent - Callback for each event
|
|
810
1089
|
* @param {Object} options - Subscription options
|
|
@@ -812,7 +1091,8 @@ class NostrClient {
|
|
|
812
1091
|
*/
|
|
813
1092
|
async subscribe(filter, onEvent, options = {}) {
|
|
814
1093
|
await this._initReady;
|
|
815
|
-
const subId = `sub-${Date.now()}-${Math.random().toString(36).
|
|
1094
|
+
const subId = `sub-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
|
|
1095
|
+
const filterKey = JSON.stringify(filter);
|
|
816
1096
|
if (this.relays.length === 0) {
|
|
817
1097
|
const matchingEvents = this._getMatchingCachedEvents(filter);
|
|
818
1098
|
const mockSub = {
|
|
@@ -837,6 +1117,32 @@ class NostrClient {
|
|
|
837
1117
|
}
|
|
838
1118
|
};
|
|
839
1119
|
}
|
|
1120
|
+
const existing = activeSubscriptions.get(filterKey);
|
|
1121
|
+
if (existing) {
|
|
1122
|
+
existing.callbacks.add(onEvent);
|
|
1123
|
+
existing.refCount++;
|
|
1124
|
+
return {
|
|
1125
|
+
id: subId,
|
|
1126
|
+
unsubscribe: () => {
|
|
1127
|
+
existing.callbacks.delete(onEvent);
|
|
1128
|
+
existing.refCount--;
|
|
1129
|
+
if (existing.refCount === 0) {
|
|
1130
|
+
if (existing.subscription && existing.subscription.close) {
|
|
1131
|
+
existing.subscription.close();
|
|
1132
|
+
}
|
|
1133
|
+
activeSubscriptions.delete(filterKey);
|
|
1134
|
+
}
|
|
1135
|
+
this._subscriptions.delete(subId);
|
|
1136
|
+
}
|
|
1137
|
+
};
|
|
1138
|
+
}
|
|
1139
|
+
const callbacks = /* @__PURE__ */ new Set([onEvent]);
|
|
1140
|
+
const subscriptionInfo = {
|
|
1141
|
+
callbacks,
|
|
1142
|
+
refCount: 1,
|
|
1143
|
+
subscription: null
|
|
1144
|
+
};
|
|
1145
|
+
activeSubscriptions.set(filterKey, subscriptionInfo);
|
|
840
1146
|
const sub = this.pool.subscribeMany(
|
|
841
1147
|
this.relays,
|
|
842
1148
|
[filter],
|
|
@@ -848,18 +1154,33 @@ class NostrClient {
|
|
|
848
1154
|
}
|
|
849
1155
|
}
|
|
850
1156
|
this._cacheEvent(event);
|
|
851
|
-
|
|
1157
|
+
const subInfo = activeSubscriptions.get(filterKey);
|
|
1158
|
+
if (subInfo) {
|
|
1159
|
+
for (const cb of subInfo.callbacks) {
|
|
1160
|
+
try {
|
|
1161
|
+
cb(event);
|
|
1162
|
+
} catch (err) {
|
|
1163
|
+
console.warn("[nostr] Subscription callback error:", err.message);
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
852
1167
|
},
|
|
853
1168
|
oneose: () => {
|
|
854
1169
|
if (options.onEOSE) options.onEOSE();
|
|
855
1170
|
}
|
|
856
1171
|
}
|
|
857
1172
|
);
|
|
1173
|
+
subscriptionInfo.subscription = sub;
|
|
858
1174
|
this._subscriptions.set(subId, sub);
|
|
859
1175
|
return {
|
|
860
1176
|
id: subId,
|
|
861
1177
|
unsubscribe: () => {
|
|
862
|
-
|
|
1178
|
+
callbacks.delete(onEvent);
|
|
1179
|
+
subscriptionInfo.refCount--;
|
|
1180
|
+
if (subscriptionInfo.refCount === 0) {
|
|
1181
|
+
if (sub.close) sub.close();
|
|
1182
|
+
activeSubscriptions.delete(filterKey);
|
|
1183
|
+
}
|
|
863
1184
|
this._subscriptions.delete(subId);
|
|
864
1185
|
}
|
|
865
1186
|
};
|
|
@@ -895,42 +1216,52 @@ class NostrClient {
|
|
|
895
1216
|
}
|
|
896
1217
|
/**
|
|
897
1218
|
* Invalidate query caches that might be affected by a new event
|
|
1219
|
+
* Uses reverse index for O(1) lookup instead of O(n) scan
|
|
898
1220
|
* @private
|
|
899
1221
|
*/
|
|
900
1222
|
_invalidateQueryCachesForEvent(event) {
|
|
1223
|
+
const indexedKeys = this._cacheIndex.get(event.kind);
|
|
1224
|
+
if (!indexedKeys || indexedKeys.size === 0) {
|
|
1225
|
+
return;
|
|
1226
|
+
}
|
|
901
1227
|
const keysToDelete = [];
|
|
902
|
-
for (const
|
|
903
|
-
|
|
1228
|
+
for (const cacheKey of indexedKeys) {
|
|
1229
|
+
const cached = this._eventCache.get(cacheKey);
|
|
1230
|
+
if (!cached) {
|
|
1231
|
+
indexedKeys.delete(cacheKey);
|
|
1232
|
+
continue;
|
|
1233
|
+
}
|
|
904
1234
|
try {
|
|
905
1235
|
const filter = JSON.parse(cacheKey);
|
|
906
1236
|
if (this._eventMatchesFilter(event, filter)) {
|
|
907
1237
|
keysToDelete.push(cacheKey);
|
|
908
1238
|
}
|
|
909
1239
|
} catch {
|
|
1240
|
+
indexedKeys.delete(cacheKey);
|
|
910
1241
|
}
|
|
911
1242
|
}
|
|
912
1243
|
for (const key of keysToDelete) {
|
|
913
1244
|
this._eventCache.delete(key);
|
|
1245
|
+
this._unindexCacheEntry(key);
|
|
914
1246
|
}
|
|
915
1247
|
}
|
|
916
1248
|
/**
|
|
917
|
-
* Cache event in memory and persist
|
|
1249
|
+
* Cache event in memory and persist (batched)
|
|
918
1250
|
* @private
|
|
919
1251
|
*/
|
|
920
1252
|
async _cacheEvent(event) {
|
|
921
1253
|
this._cacheEventSync(event);
|
|
922
1254
|
if (this.persistentStorage) {
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
storageKey = dTag[1];
|
|
929
|
-
}
|
|
1255
|
+
let storageKey = event.id;
|
|
1256
|
+
if (event.kind >= 3e4 && event.kind < 4e4) {
|
|
1257
|
+
const dTag = event.tags.find((t) => t[0] === "d");
|
|
1258
|
+
if (dTag && dTag[1]) {
|
|
1259
|
+
storageKey = dTag[1];
|
|
930
1260
|
}
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
1261
|
+
}
|
|
1262
|
+
this._persistQueue.set(storageKey, event);
|
|
1263
|
+
if (!this._persistTimer) {
|
|
1264
|
+
this._persistTimer = setTimeout(() => this._flushPersistQueue(), this._persistBatchMs);
|
|
934
1265
|
}
|
|
935
1266
|
}
|
|
936
1267
|
if (this.relays.length === 0) {
|
|
@@ -945,6 +1276,26 @@ class NostrClient {
|
|
|
945
1276
|
}
|
|
946
1277
|
}
|
|
947
1278
|
}
|
|
1279
|
+
/**
|
|
1280
|
+
* Flush batched persistent writes
|
|
1281
|
+
* @private
|
|
1282
|
+
*/
|
|
1283
|
+
async _flushPersistQueue() {
|
|
1284
|
+
this._persistTimer = null;
|
|
1285
|
+
if (!this.persistentStorage || this._persistQueue.size === 0) {
|
|
1286
|
+
return;
|
|
1287
|
+
}
|
|
1288
|
+
const toWrite = Array.from(this._persistQueue.entries());
|
|
1289
|
+
this._persistQueue.clear();
|
|
1290
|
+
const writePromises = toWrite.map(async ([key, event]) => {
|
|
1291
|
+
try {
|
|
1292
|
+
await this.persistentStorage.put(key, event);
|
|
1293
|
+
} catch (error) {
|
|
1294
|
+
console.warn(`Failed to persist event ${key}:`, error.message);
|
|
1295
|
+
}
|
|
1296
|
+
});
|
|
1297
|
+
await Promise.all(writePromises);
|
|
1298
|
+
}
|
|
948
1299
|
/**
|
|
949
1300
|
* Get cached events matching a filter
|
|
950
1301
|
* @private
|
|
@@ -1032,11 +1383,25 @@ class NostrClient {
|
|
|
1032
1383
|
}
|
|
1033
1384
|
/**
|
|
1034
1385
|
* Close all connections and subscriptions
|
|
1386
|
+
* @param {Object} options - Close options
|
|
1387
|
+
* @param {boolean} options.flush - Flush pending writes before closing (default: true)
|
|
1035
1388
|
*/
|
|
1036
|
-
close() {
|
|
1389
|
+
async close(options = {}) {
|
|
1390
|
+
const shouldFlush = options.flush !== false;
|
|
1391
|
+
if (shouldFlush && this._persistTimer) {
|
|
1392
|
+
clearTimeout(this._persistTimer);
|
|
1393
|
+
await this._flushPersistQueue();
|
|
1394
|
+
}
|
|
1037
1395
|
if (this.syncService) {
|
|
1038
1396
|
this.syncService.stop();
|
|
1039
1397
|
}
|
|
1398
|
+
const authorSub = authorSubscriptions.get(this.publicKey);
|
|
1399
|
+
if (authorSub && authorSub.subscription) {
|
|
1400
|
+
if (authorSub.subscription.close) {
|
|
1401
|
+
authorSub.subscription.close();
|
|
1402
|
+
}
|
|
1403
|
+
authorSubscriptions.delete(this.publicKey);
|
|
1404
|
+
}
|
|
1040
1405
|
for (const sub of this._subscriptions.values()) {
|
|
1041
1406
|
if (sub.close) {
|
|
1042
1407
|
sub.close();
|
|
@@ -1047,6 +1412,7 @@ class NostrClient {
|
|
|
1047
1412
|
this._subscriptions.clear();
|
|
1048
1413
|
this.pool.close(this.relays);
|
|
1049
1414
|
this._eventCache.clear();
|
|
1415
|
+
this._cacheIndex.clear();
|
|
1050
1416
|
}
|
|
1051
1417
|
/**
|
|
1052
1418
|
* Get relay status
|
|
@@ -3196,11 +3562,11 @@ class GunDBBackend extends StorageBackend {
|
|
|
3196
3562
|
}
|
|
3197
3563
|
}
|
|
3198
3564
|
const name = "holosphere";
|
|
3199
|
-
const version$1 = "2.0.0-
|
|
3565
|
+
const version$1 = "2.0.0-alpha5";
|
|
3200
3566
|
const description = "Holonic geospatial communication infrastructure combining H3 hexagonal indexing with distributed P2P storage";
|
|
3201
3567
|
const type = "module";
|
|
3202
3568
|
const bin = {
|
|
3203
|
-
"holosphere-activitypub": "
|
|
3569
|
+
"holosphere-activitypub": "bin/holosphere-activitypub.js"
|
|
3204
3570
|
};
|
|
3205
3571
|
const main = "./dist/cjs/holosphere.cjs";
|
|
3206
3572
|
const module = "./dist/esm/holosphere.js";
|
|
@@ -3941,6 +4307,9 @@ const h3Operations = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.define
|
|
|
3941
4307
|
toHolon
|
|
3942
4308
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
3943
4309
|
const globalSubscriptions = /* @__PURE__ */ new Map();
|
|
4310
|
+
const singlePathSubscriptions = /* @__PURE__ */ new Map();
|
|
4311
|
+
const pendingQueries = /* @__PURE__ */ new Map();
|
|
4312
|
+
const QUERY_DEDUP_WINDOW = 2e3;
|
|
3944
4313
|
async function nostrPut(client, path, data, kind2 = 3e4) {
|
|
3945
4314
|
const dataEvent = {
|
|
3946
4315
|
kind: kind2,
|
|
@@ -3977,6 +4346,27 @@ async function nostrGet(client, path, kind2 = 3e4, options = {}) {
|
|
|
3977
4346
|
}
|
|
3978
4347
|
}
|
|
3979
4348
|
}
|
|
4349
|
+
const queryKey = `get:${client.publicKey}:${kind2}:${path}:${authors.join(",")}`;
|
|
4350
|
+
const pending = pendingQueries.get(queryKey);
|
|
4351
|
+
if (pending && Date.now() - pending.timestamp < QUERY_DEDUP_WINDOW) {
|
|
4352
|
+
return pending.promise;
|
|
4353
|
+
}
|
|
4354
|
+
const queryPromise = _executeNostrGet(client, path, kind2, authors, timeout, options);
|
|
4355
|
+
pendingQueries.set(queryKey, {
|
|
4356
|
+
promise: queryPromise,
|
|
4357
|
+
timestamp: Date.now()
|
|
4358
|
+
});
|
|
4359
|
+
queryPromise.finally(() => {
|
|
4360
|
+
setTimeout(() => {
|
|
4361
|
+
const current = pendingQueries.get(queryKey);
|
|
4362
|
+
if (current && current.promise === queryPromise) {
|
|
4363
|
+
pendingQueries.delete(queryKey);
|
|
4364
|
+
}
|
|
4365
|
+
}, QUERY_DEDUP_WINDOW);
|
|
4366
|
+
});
|
|
4367
|
+
return queryPromise;
|
|
4368
|
+
}
|
|
4369
|
+
async function _executeNostrGet(client, path, kind2, authors, timeout, options) {
|
|
3980
4370
|
const filter = {
|
|
3981
4371
|
kinds: [kind2],
|
|
3982
4372
|
authors,
|
|
@@ -4010,24 +4400,24 @@ async function nostrGetAll(client, pathPrefix, kind2 = 3e4, options = {}) {
|
|
|
4010
4400
|
if (!options.skipPersistent && client.persistentGetAll) {
|
|
4011
4401
|
const persistedEvents = await client.persistentGetAll(pathPrefix);
|
|
4012
4402
|
if (persistedEvents.length > 0) {
|
|
4013
|
-
const
|
|
4403
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
4014
4404
|
for (const event of persistedEvents) {
|
|
4015
4405
|
if (!event || !event.tags) continue;
|
|
4016
4406
|
const dTag = event.tags.find((t) => t[0] === "d");
|
|
4017
4407
|
if (!dTag || !dTag[1] || !dTag[1].startsWith(pathPrefix)) continue;
|
|
4018
4408
|
const path = dTag[1];
|
|
4019
|
-
const existing =
|
|
4409
|
+
const existing = byPath.get(path);
|
|
4020
4410
|
if (!existing || event.created_at > existing.created_at) {
|
|
4021
4411
|
try {
|
|
4022
4412
|
const data = JSON.parse(event.content);
|
|
4023
4413
|
if (data._deleted) {
|
|
4024
|
-
|
|
4414
|
+
byPath.delete(path);
|
|
4025
4415
|
continue;
|
|
4026
4416
|
}
|
|
4027
4417
|
if (options.includeAuthor) {
|
|
4028
4418
|
data._author = event.pubkey;
|
|
4029
4419
|
}
|
|
4030
|
-
|
|
4420
|
+
byPath.set(path, { data, created_at: event.created_at });
|
|
4031
4421
|
} catch (error) {
|
|
4032
4422
|
}
|
|
4033
4423
|
}
|
|
@@ -4035,9 +4425,30 @@ async function nostrGetAll(client, pathPrefix, kind2 = 3e4, options = {}) {
|
|
|
4035
4425
|
if (client.refreshPrefixInBackground) {
|
|
4036
4426
|
client.refreshPrefixInBackground(pathPrefix, kind2, { authors, timeout, limit: limit2 });
|
|
4037
4427
|
}
|
|
4038
|
-
return Array.from(
|
|
4428
|
+
return Array.from(byPath.values()).map((item) => item.data);
|
|
4039
4429
|
}
|
|
4040
4430
|
}
|
|
4431
|
+
const queryKey = `getAll:${client.publicKey}:${kind2}:${pathPrefix}:${authors.join(",")}`;
|
|
4432
|
+
const pending = pendingQueries.get(queryKey);
|
|
4433
|
+
if (pending && Date.now() - pending.timestamp < QUERY_DEDUP_WINDOW) {
|
|
4434
|
+
return pending.promise;
|
|
4435
|
+
}
|
|
4436
|
+
const queryPromise = _executeNostrGetAll(client, pathPrefix, kind2, authors, timeout, limit2, options);
|
|
4437
|
+
pendingQueries.set(queryKey, {
|
|
4438
|
+
promise: queryPromise,
|
|
4439
|
+
timestamp: Date.now()
|
|
4440
|
+
});
|
|
4441
|
+
queryPromise.finally(() => {
|
|
4442
|
+
setTimeout(() => {
|
|
4443
|
+
const current = pendingQueries.get(queryKey);
|
|
4444
|
+
if (current && current.promise === queryPromise) {
|
|
4445
|
+
pendingQueries.delete(queryKey);
|
|
4446
|
+
}
|
|
4447
|
+
}, QUERY_DEDUP_WINDOW);
|
|
4448
|
+
});
|
|
4449
|
+
return queryPromise;
|
|
4450
|
+
}
|
|
4451
|
+
async function _executeNostrGetAll(client, pathPrefix, kind2, authors, timeout, limit2, options) {
|
|
4041
4452
|
const filter = {
|
|
4042
4453
|
kinds: [kind2],
|
|
4043
4454
|
authors,
|
|
@@ -4226,7 +4637,29 @@ async function nostrDeleteAll(client, pathPrefix, kind2 = 3e4) {
|
|
|
4226
4637
|
}
|
|
4227
4638
|
function nostrSubscribe(client, path, callback, options = {}) {
|
|
4228
4639
|
const kind2 = options.kind || 3e4;
|
|
4229
|
-
|
|
4640
|
+
const subscriptionKey = `single:${client.publicKey}:${kind2}:${path}`;
|
|
4641
|
+
const existing = singlePathSubscriptions.get(subscriptionKey);
|
|
4642
|
+
if (existing) {
|
|
4643
|
+
existing.callbacks.push(callback);
|
|
4644
|
+
return {
|
|
4645
|
+
unsubscribe: () => {
|
|
4646
|
+
const idx = existing.callbacks.indexOf(callback);
|
|
4647
|
+
if (idx > -1) {
|
|
4648
|
+
existing.callbacks.splice(idx, 1);
|
|
4649
|
+
}
|
|
4650
|
+
if (existing.callbacks.length === 0) {
|
|
4651
|
+
existing.subscription.unsubscribe();
|
|
4652
|
+
singlePathSubscriptions.delete(subscriptionKey);
|
|
4653
|
+
}
|
|
4654
|
+
}
|
|
4655
|
+
};
|
|
4656
|
+
}
|
|
4657
|
+
const callbacks = [callback];
|
|
4658
|
+
const subscriptionInfo = {
|
|
4659
|
+
callbacks,
|
|
4660
|
+
subscription: null
|
|
4661
|
+
};
|
|
4662
|
+
singlePathSubscriptions.set(subscriptionKey, subscriptionInfo);
|
|
4230
4663
|
const filter = {
|
|
4231
4664
|
kinds: [kind2],
|
|
4232
4665
|
authors: [client.publicKey],
|
|
@@ -4239,11 +4672,6 @@ function nostrSubscribe(client, path, callback, options = {}) {
|
|
|
4239
4672
|
filter,
|
|
4240
4673
|
(event) => {
|
|
4241
4674
|
if (event.pubkey !== client.publicKey) {
|
|
4242
|
-
console.warn("[nostrSubscribe] Rejecting event from different author:", {
|
|
4243
|
-
expected: client.publicKey,
|
|
4244
|
-
received: event.pubkey,
|
|
4245
|
-
eventId: event.id
|
|
4246
|
-
});
|
|
4247
4675
|
return;
|
|
4248
4676
|
}
|
|
4249
4677
|
try {
|
|
@@ -4251,7 +4679,13 @@ function nostrSubscribe(client, path, callback, options = {}) {
|
|
|
4251
4679
|
if (data._deleted) {
|
|
4252
4680
|
return;
|
|
4253
4681
|
}
|
|
4254
|
-
|
|
4682
|
+
for (const cb of callbacks) {
|
|
4683
|
+
try {
|
|
4684
|
+
cb(data, event);
|
|
4685
|
+
} catch (err) {
|
|
4686
|
+
console.error("Subscription callback error:", err);
|
|
4687
|
+
}
|
|
4688
|
+
}
|
|
4255
4689
|
} catch (error) {
|
|
4256
4690
|
console.error("Failed to parse event in subscription:", error);
|
|
4257
4691
|
}
|
|
@@ -4261,7 +4695,19 @@ function nostrSubscribe(client, path, callback, options = {}) {
|
|
|
4261
4695
|
}
|
|
4262
4696
|
}
|
|
4263
4697
|
);
|
|
4264
|
-
|
|
4698
|
+
subscriptionInfo.subscription = subscription;
|
|
4699
|
+
return {
|
|
4700
|
+
unsubscribe: () => {
|
|
4701
|
+
const idx = callbacks.indexOf(callback);
|
|
4702
|
+
if (idx > -1) {
|
|
4703
|
+
callbacks.splice(idx, 1);
|
|
4704
|
+
}
|
|
4705
|
+
if (callbacks.length === 0) {
|
|
4706
|
+
subscription.unsubscribe();
|
|
4707
|
+
singlePathSubscriptions.delete(subscriptionKey);
|
|
4708
|
+
}
|
|
4709
|
+
}
|
|
4710
|
+
};
|
|
4265
4711
|
}
|
|
4266
4712
|
async function nostrSubscribeMany(client, pathPrefix, callback, options = {}) {
|
|
4267
4713
|
const kind2 = options.kind || 3e4;
|
|
@@ -5013,7 +5459,7 @@ const sha256 = sha256$1;
|
|
|
5013
5459
|
let secp256k1 = null;
|
|
5014
5460
|
async function loadCrypto() {
|
|
5015
5461
|
if (!secp256k1) {
|
|
5016
|
-
const module2 = await import("./secp256k1-
|
|
5462
|
+
const module2 = await import("./secp256k1-69sS9O-P.js");
|
|
5017
5463
|
secp256k1 = module2.secp256k1;
|
|
5018
5464
|
}
|
|
5019
5465
|
return secp256k1;
|
|
@@ -16643,7 +17089,7 @@ class ChainManager {
|
|
|
16643
17089
|
*/
|
|
16644
17090
|
async _loadEthers() {
|
|
16645
17091
|
if (!this.ethers) {
|
|
16646
|
-
const ethersModule = await import("./index-
|
|
17092
|
+
const ethersModule = await import("./index-Cp3xctq8.js");
|
|
16647
17093
|
this.ethers = ethersModule;
|
|
16648
17094
|
}
|
|
16649
17095
|
return this.ethers;
|
|
@@ -25407,7 +25853,7 @@ class ContractDeployer {
|
|
|
25407
25853
|
*/
|
|
25408
25854
|
async _loadEthers() {
|
|
25409
25855
|
if (!this.ethers) {
|
|
25410
|
-
this.ethers = await import("./index-
|
|
25856
|
+
this.ethers = await import("./index-Cp3xctq8.js");
|
|
25411
25857
|
}
|
|
25412
25858
|
return this.ethers;
|
|
25413
25859
|
}
|
|
@@ -25745,7 +26191,7 @@ class ContractOperations {
|
|
|
25745
26191
|
*/
|
|
25746
26192
|
async _loadEthers() {
|
|
25747
26193
|
if (!this.ethers) {
|
|
25748
|
-
this.ethers = await import("./index-
|
|
26194
|
+
this.ethers = await import("./index-Cp3xctq8.js");
|
|
25749
26195
|
}
|
|
25750
26196
|
return this.ethers;
|
|
25751
26197
|
}
|
|
@@ -37420,4 +37866,4 @@ export {
|
|
|
37420
37866
|
exists as y,
|
|
37421
37867
|
bytes as z
|
|
37422
37868
|
};
|
|
37423
|
-
//# sourceMappingURL=index-
|
|
37869
|
+
//# sourceMappingURL=index-Bbey4GkP.js.map
|