@unicitylabs/sphere-sdk 0.6.10-dev.4 → 0.6.10-dev.5

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/index.cjs CHANGED
@@ -559,6 +559,33 @@ function waitForConnection() {
559
559
  connectionCallbacks.push(callback);
560
560
  });
561
561
  }
562
+ function startPingTimer() {
563
+ stopPingTimer();
564
+ pingTimer = setInterval(() => {
565
+ if (!ws || ws.readyState !== WebSocket.OPEN) return;
566
+ try {
567
+ const id = ++requestId;
568
+ ws.send(JSON.stringify({ jsonrpc: "2.0", id, method: "server.ping", params: [] }));
569
+ pending[id] = {
570
+ resolve: () => {
571
+ },
572
+ reject: () => {
573
+ }
574
+ };
575
+ const timeoutId = setTimeout(() => {
576
+ delete pending[id];
577
+ }, 1e4);
578
+ pending[id].timeoutId = timeoutId;
579
+ } catch {
580
+ }
581
+ }, PING_INTERVAL);
582
+ }
583
+ function stopPingTimer() {
584
+ if (pingTimer) {
585
+ clearInterval(pingTimer);
586
+ pingTimer = null;
587
+ }
588
+ }
562
589
  function connect(endpoint = DEFAULT_ENDPOINT) {
563
590
  if (isConnected) {
564
591
  return Promise.resolve();
@@ -581,6 +608,7 @@ function connect(endpoint = DEFAULT_ENDPOINT) {
581
608
  isConnected = true;
582
609
  isConnecting = false;
583
610
  reconnectAttempts = 0;
611
+ startPingTimer();
584
612
  hasResolved = true;
585
613
  resolve();
586
614
  connectionCallbacks.forEach((cb) => {
@@ -592,6 +620,7 @@ function connect(endpoint = DEFAULT_ENDPOINT) {
592
620
  ws.onclose = () => {
593
621
  isConnected = false;
594
622
  isBlockSubscribed = false;
623
+ stopPingTimer();
595
624
  Object.values(pending).forEach((req) => {
596
625
  if (req.timeoutId) clearTimeout(req.timeoutId);
597
626
  req.reject(new Error("WebSocket connection closed"));
@@ -774,6 +803,7 @@ async function getCurrentBlockHeight() {
774
803
  }
775
804
  }
776
805
  function disconnect() {
806
+ stopPingTimer();
777
807
  if (ws) {
778
808
  intentionalClose = true;
779
809
  ws.close();
@@ -794,7 +824,7 @@ function disconnect() {
794
824
  blockSubscribers.length = 0;
795
825
  lastBlockHeader = null;
796
826
  }
797
- var DEFAULT_ENDPOINT, ws, isConnected, isConnecting, requestId, intentionalClose, reconnectAttempts, isBlockSubscribed, lastBlockHeader, pending, blockSubscribers, connectionCallbacks, MAX_RECONNECT_ATTEMPTS, BASE_DELAY, MAX_DELAY, RPC_TIMEOUT, CONNECTION_TIMEOUT;
827
+ var DEFAULT_ENDPOINT, ws, isConnected, isConnecting, requestId, intentionalClose, reconnectAttempts, isBlockSubscribed, lastBlockHeader, pingTimer, pending, blockSubscribers, connectionCallbacks, MAX_RECONNECT_ATTEMPTS, BASE_DELAY, MAX_DELAY, RPC_TIMEOUT, CONNECTION_TIMEOUT, PING_INTERVAL;
798
828
  var init_network = __esm({
799
829
  "l1/network.ts"() {
800
830
  "use strict";
@@ -810,6 +840,7 @@ var init_network = __esm({
810
840
  reconnectAttempts = 0;
811
841
  isBlockSubscribed = false;
812
842
  lastBlockHeader = null;
843
+ pingTimer = null;
813
844
  pending = {};
814
845
  blockSubscribers = [];
815
846
  connectionCallbacks = [];
@@ -818,6 +849,7 @@ var init_network = __esm({
818
849
  MAX_DELAY = 6e4;
819
850
  RPC_TIMEOUT = 3e4;
820
851
  CONNECTION_TIMEOUT = 3e4;
852
+ PING_INTERVAL = 3e4;
821
853
  }
822
854
  });
823
855
 
@@ -2612,9 +2644,9 @@ var NostrTransportProvider = class _NostrTransportProvider {
2612
2644
  if (subId) {
2613
2645
  this.nostrClient?.unsubscribe(subId);
2614
2646
  }
2615
- logger.warn("Nostr", `queryEvents timed out after 5s, returning ${events.length} event(s)`, { kinds: filterObj.kinds, limit: filterObj.limit });
2647
+ logger.warn("Nostr", `queryEvents timed out after 15s, returning ${events.length} event(s)`, { kinds: filterObj.kinds, limit: filterObj.limit });
2616
2648
  resolve(events);
2617
- }, 5e3);
2649
+ }, 15e3);
2618
2650
  const subId = this.nostrClient.subscribe(filter, {
2619
2651
  onEvent: (event) => {
2620
2652
  events.push({
@@ -12530,6 +12562,15 @@ var GroupChatModule = class {
12530
12562
  }
12531
12563
  destroy() {
12532
12564
  this.destroyConnection();
12565
+ if (this.persistTimer) {
12566
+ clearTimeout(this.persistTimer);
12567
+ this.persistTimer = null;
12568
+ if (this.deps) {
12569
+ this.doPersistAll().catch(
12570
+ (err) => logger.debug("GroupChat", "Persist on destroy failed", err)
12571
+ );
12572
+ }
12573
+ }
12533
12574
  this.groups.clear();
12534
12575
  this.messages.clear();
12535
12576
  this.members.clear();
@@ -12538,10 +12579,7 @@ var GroupChatModule = class {
12538
12579
  this.messageHandlers.clear();
12539
12580
  this.relayAdminPubkeys = null;
12540
12581
  this.relayAdminFetchPromise = null;
12541
- if (this.persistTimer) {
12542
- clearTimeout(this.persistTimer);
12543
- this.persistTimer = null;
12544
- }
12582
+ this.persistPromise = null;
12545
12583
  this.deps = null;
12546
12584
  }
12547
12585
  destroyConnection() {
@@ -12666,12 +12704,12 @@ var GroupChatModule = class {
12666
12704
  if (!this.client) return;
12667
12705
  const groupIds = Array.from(this.groups.keys());
12668
12706
  if (groupIds.length === 0) return;
12669
- const latestTimestamp = this.getLatestMessageTimestamp(groupIds);
12707
+ const sinceTimestamp = this.getLatestKnownTimestamp(groupIds);
12670
12708
  this.trackSubscription(
12671
12709
  createNip29Filter({
12672
12710
  kinds: [NIP29_KINDS.CHAT_MESSAGE, NIP29_KINDS.THREAD_ROOT, NIP29_KINDS.THREAD_REPLY],
12673
12711
  "#h": groupIds,
12674
- ...latestTimestamp ? { since: latestTimestamp } : {}
12712
+ ...sinceTimestamp ? { since: sinceTimestamp } : {}
12675
12713
  }),
12676
12714
  { onEvent: (event) => this.handleGroupEvent(event) }
12677
12715
  );
@@ -12692,12 +12730,12 @@ var GroupChatModule = class {
12692
12730
  }
12693
12731
  subscribeToGroup(groupId) {
12694
12732
  if (!this.client) return;
12695
- const latestTimestamp = this.getLatestMessageTimestamp([groupId]);
12733
+ const sinceTimestamp = this.getLatestKnownTimestamp([groupId]);
12696
12734
  this.trackSubscription(
12697
12735
  createNip29Filter({
12698
12736
  kinds: [NIP29_KINDS.CHAT_MESSAGE, NIP29_KINDS.THREAD_ROOT, NIP29_KINDS.THREAD_REPLY],
12699
12737
  "#h": [groupId],
12700
- ...latestTimestamp ? { since: latestTimestamp } : {}
12738
+ ...sinceTimestamp ? { since: sinceTimestamp } : {}
12701
12739
  }),
12702
12740
  { onEvent: (event) => this.handleGroupEvent(event) }
12703
12741
  );
@@ -12771,7 +12809,7 @@ var GroupChatModule = class {
12771
12809
  }
12772
12810
  group.updatedAt = event.created_at * 1e3;
12773
12811
  this.groups.set(groupId, group);
12774
- this.persistGroups();
12812
+ this.schedulePersist();
12775
12813
  } else if (event.kind === NIP29_KINDS.GROUP_MEMBERS) {
12776
12814
  this.updateMembersFromEvent(groupId, event);
12777
12815
  } else if (event.kind === NIP29_KINDS.GROUP_ADMINS) {
@@ -12792,7 +12830,7 @@ var GroupChatModule = class {
12792
12830
  }
12793
12831
  }
12794
12832
  this.deps.emitEvent("groupchat:updated", {});
12795
- this.persistMessages();
12833
+ this.schedulePersist();
12796
12834
  } else if (event.kind === NIP29_KINDS.REMOVE_USER) {
12797
12835
  if (this.processedEventIds.has(event.id)) return;
12798
12836
  const eventTimestampMs = event.created_at * 1e3;
@@ -12853,7 +12891,7 @@ var GroupChatModule = class {
12853
12891
  };
12854
12892
  this.saveMemberToMemory(member);
12855
12893
  }
12856
- this.persistMembers();
12894
+ this.schedulePersist();
12857
12895
  }
12858
12896
  updateAdminsFromEvent(groupId, event) {
12859
12897
  const pTags = event.tags.filter((t) => t[0] === "p");
@@ -12873,7 +12911,7 @@ var GroupChatModule = class {
12873
12911
  });
12874
12912
  }
12875
12913
  }
12876
- this.persistMembers();
12914
+ this.schedulePersist();
12877
12915
  }
12878
12916
  // ===========================================================================
12879
12917
  // Group Membership Restoration
@@ -12884,13 +12922,11 @@ var GroupChatModule = class {
12884
12922
  if (!myPubkey) return [];
12885
12923
  const groupIdsWithMembership = /* @__PURE__ */ new Set();
12886
12924
  await this.oneshotSubscription(
12887
- new import_nostr_js_sdk4.Filter({ kinds: [NIP29_KINDS.GROUP_MEMBERS] }),
12925
+ createNip29Filter({ kinds: [NIP29_KINDS.GROUP_MEMBERS], "#p": [myPubkey] }),
12888
12926
  {
12889
12927
  onEvent: (event) => {
12890
12928
  const groupId = this.getGroupIdFromMetadataEvent(event);
12891
- if (!groupId) return;
12892
- const pTags = event.tags.filter((t) => t[0] === "p");
12893
- if (pTags.some((tag) => tag[1] === myPubkey)) {
12929
+ if (groupId) {
12894
12930
  groupIdsWithMembership.add(groupId);
12895
12931
  }
12896
12932
  },
@@ -12902,22 +12938,24 @@ var GroupChatModule = class {
12902
12938
  );
12903
12939
  if (groupIdsWithMembership.size === 0) return [];
12904
12940
  const restoredGroups = [];
12905
- for (const groupId of groupIdsWithMembership) {
12906
- if (this.groups.has(groupId)) continue;
12907
- try {
12908
- const group = await this.fetchGroupMetadataInternal(groupId);
12909
- if (group) {
12910
- this.groups.set(groupId, group);
12911
- restoredGroups.push(group);
12912
- await Promise.all([
12913
- this.fetchAndSaveMembers(groupId),
12914
- this.fetchMessages(groupId)
12915
- ]);
12941
+ await Promise.all(
12942
+ Array.from(groupIdsWithMembership).map(async (groupId) => {
12943
+ if (this.groups.has(groupId)) return;
12944
+ try {
12945
+ const group = await this.fetchGroupMetadataInternal(groupId);
12946
+ if (group) {
12947
+ this.groups.set(groupId, group);
12948
+ restoredGroups.push(group);
12949
+ await Promise.all([
12950
+ this.fetchAndSaveMembers(groupId),
12951
+ this.fetchMessages(groupId)
12952
+ ]);
12953
+ }
12954
+ } catch (error) {
12955
+ logger.warn("GroupChat", "Failed to restore group", groupId, error);
12916
12956
  }
12917
- } catch (error) {
12918
- logger.warn("GroupChat", "Failed to restore group", groupId, error);
12919
- }
12920
- }
12957
+ })
12958
+ );
12921
12959
  if (restoredGroups.length > 0) {
12922
12960
  await this.subscribeToJoinedGroups();
12923
12961
  this.deps.emitEvent("groupchat:updated", {});
@@ -13303,7 +13341,7 @@ var GroupChatModule = class {
13303
13341
  if (group && (group.unreadCount || 0) > 0) {
13304
13342
  group.unreadCount = 0;
13305
13343
  this.groups.set(groupId, group);
13306
- this.persistGroups();
13344
+ this.schedulePersist();
13307
13345
  }
13308
13346
  }
13309
13347
  // ===========================================================================
@@ -13325,7 +13363,7 @@ var GroupChatModule = class {
13325
13363
  if (eventId) {
13326
13364
  this.removeMemberFromMemory(groupId, userPubkey);
13327
13365
  this.deps.emitEvent("groupchat:updated", {});
13328
- this.persistMembers();
13366
+ this.schedulePersist();
13329
13367
  return true;
13330
13368
  }
13331
13369
  return false;
@@ -13348,7 +13386,7 @@ var GroupChatModule = class {
13348
13386
  if (eventId) {
13349
13387
  this.deleteMessageFromMemory(groupId, messageId);
13350
13388
  this.deps.emitEvent("groupchat:updated", {});
13351
- this.persistMessages();
13389
+ this.schedulePersist();
13352
13390
  return true;
13353
13391
  }
13354
13392
  return false;
@@ -13428,7 +13466,7 @@ var GroupChatModule = class {
13428
13466
  * or 0 if no messages exist. Used to set `since` on subscriptions so the relay
13429
13467
  * only sends events we don't already have.
13430
13468
  */
13431
- getLatestMessageTimestamp(groupIds) {
13469
+ getLatestKnownTimestamp(groupIds) {
13432
13470
  let latest = 0;
13433
13471
  for (const gid of groupIds) {
13434
13472
  const msgs = this.messages.get(gid);
@@ -13509,7 +13547,7 @@ var GroupChatModule = class {
13509
13547
  });
13510
13548
  }
13511
13549
  }
13512
- this.persistMembers();
13550
+ this.schedulePersist();
13513
13551
  }
13514
13552
  async fetchGroupMembersInternal(groupId) {
13515
13553
  if (!this.client) return [];
@@ -13636,8 +13674,11 @@ var GroupChatModule = class {
13636
13674
  addProcessedEventId(eventId) {
13637
13675
  this.processedEventIds.add(eventId);
13638
13676
  if (this.processedEventIds.size > 1e4) {
13639
- const arr = Array.from(this.processedEventIds);
13640
- this.processedEventIds = new Set(arr.slice(arr.length - 1e4));
13677
+ let toDelete = 5e3;
13678
+ for (const id of this.processedEventIds) {
13679
+ if (toDelete-- <= 0) break;
13680
+ this.processedEventIds.delete(id);
13681
+ }
13641
13682
  }
13642
13683
  }
13643
13684
  // ===========================================================================