@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.
@@ -532,6 +532,33 @@ function waitForConnection() {
532
532
  connectionCallbacks.push(callback);
533
533
  });
534
534
  }
535
+ function startPingTimer() {
536
+ stopPingTimer();
537
+ pingTimer = setInterval(() => {
538
+ if (!ws || ws.readyState !== WebSocket.OPEN) return;
539
+ try {
540
+ const id = ++requestId;
541
+ ws.send(JSON.stringify({ jsonrpc: "2.0", id, method: "server.ping", params: [] }));
542
+ pending[id] = {
543
+ resolve: () => {
544
+ },
545
+ reject: () => {
546
+ }
547
+ };
548
+ const timeoutId = setTimeout(() => {
549
+ delete pending[id];
550
+ }, 1e4);
551
+ pending[id].timeoutId = timeoutId;
552
+ } catch {
553
+ }
554
+ }, PING_INTERVAL);
555
+ }
556
+ function stopPingTimer() {
557
+ if (pingTimer) {
558
+ clearInterval(pingTimer);
559
+ pingTimer = null;
560
+ }
561
+ }
535
562
  function connect(endpoint = DEFAULT_ENDPOINT) {
536
563
  if (isConnected) {
537
564
  return Promise.resolve();
@@ -554,6 +581,7 @@ function connect(endpoint = DEFAULT_ENDPOINT) {
554
581
  isConnected = true;
555
582
  isConnecting = false;
556
583
  reconnectAttempts = 0;
584
+ startPingTimer();
557
585
  hasResolved = true;
558
586
  resolve();
559
587
  connectionCallbacks.forEach((cb) => {
@@ -565,6 +593,7 @@ function connect(endpoint = DEFAULT_ENDPOINT) {
565
593
  ws.onclose = () => {
566
594
  isConnected = false;
567
595
  isBlockSubscribed = false;
596
+ stopPingTimer();
568
597
  Object.values(pending).forEach((req) => {
569
598
  if (req.timeoutId) clearTimeout(req.timeoutId);
570
599
  req.reject(new Error("WebSocket connection closed"));
@@ -747,6 +776,7 @@ async function getCurrentBlockHeight() {
747
776
  }
748
777
  }
749
778
  function disconnect() {
779
+ stopPingTimer();
750
780
  if (ws) {
751
781
  intentionalClose = true;
752
782
  ws.close();
@@ -767,7 +797,7 @@ function disconnect() {
767
797
  blockSubscribers.length = 0;
768
798
  lastBlockHeader = null;
769
799
  }
770
- 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;
800
+ 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;
771
801
  var init_network = __esm({
772
802
  "l1/network.ts"() {
773
803
  "use strict";
@@ -783,6 +813,7 @@ var init_network = __esm({
783
813
  reconnectAttempts = 0;
784
814
  isBlockSubscribed = false;
785
815
  lastBlockHeader = null;
816
+ pingTimer = null;
786
817
  pending = {};
787
818
  blockSubscribers = [];
788
819
  connectionCallbacks = [];
@@ -791,6 +822,7 @@ var init_network = __esm({
791
822
  MAX_DELAY = 6e4;
792
823
  RPC_TIMEOUT = 3e4;
793
824
  CONNECTION_TIMEOUT = 3e4;
825
+ PING_INTERVAL = 3e4;
794
826
  }
795
827
  });
796
828
 
@@ -2514,9 +2546,9 @@ var NostrTransportProvider = class _NostrTransportProvider {
2514
2546
  if (subId) {
2515
2547
  this.nostrClient?.unsubscribe(subId);
2516
2548
  }
2517
- logger.warn("Nostr", `queryEvents timed out after 5s, returning ${events.length} event(s)`, { kinds: filterObj.kinds, limit: filterObj.limit });
2549
+ logger.warn("Nostr", `queryEvents timed out after 15s, returning ${events.length} event(s)`, { kinds: filterObj.kinds, limit: filterObj.limit });
2518
2550
  resolve(events);
2519
- }, 5e3);
2551
+ }, 15e3);
2520
2552
  const subId = this.nostrClient.subscribe(filter, {
2521
2553
  onEvent: (event) => {
2522
2554
  events.push({
@@ -12190,6 +12222,15 @@ var GroupChatModule = class {
12190
12222
  }
12191
12223
  destroy() {
12192
12224
  this.destroyConnection();
12225
+ if (this.persistTimer) {
12226
+ clearTimeout(this.persistTimer);
12227
+ this.persistTimer = null;
12228
+ if (this.deps) {
12229
+ this.doPersistAll().catch(
12230
+ (err) => logger.debug("GroupChat", "Persist on destroy failed", err)
12231
+ );
12232
+ }
12233
+ }
12193
12234
  this.groups.clear();
12194
12235
  this.messages.clear();
12195
12236
  this.members.clear();
@@ -12198,10 +12239,7 @@ var GroupChatModule = class {
12198
12239
  this.messageHandlers.clear();
12199
12240
  this.relayAdminPubkeys = null;
12200
12241
  this.relayAdminFetchPromise = null;
12201
- if (this.persistTimer) {
12202
- clearTimeout(this.persistTimer);
12203
- this.persistTimer = null;
12204
- }
12242
+ this.persistPromise = null;
12205
12243
  this.deps = null;
12206
12244
  }
12207
12245
  destroyConnection() {
@@ -12326,12 +12364,12 @@ var GroupChatModule = class {
12326
12364
  if (!this.client) return;
12327
12365
  const groupIds = Array.from(this.groups.keys());
12328
12366
  if (groupIds.length === 0) return;
12329
- const latestTimestamp = this.getLatestMessageTimestamp(groupIds);
12367
+ const sinceTimestamp = this.getLatestKnownTimestamp(groupIds);
12330
12368
  this.trackSubscription(
12331
12369
  createNip29Filter({
12332
12370
  kinds: [NIP29_KINDS.CHAT_MESSAGE, NIP29_KINDS.THREAD_ROOT, NIP29_KINDS.THREAD_REPLY],
12333
12371
  "#h": groupIds,
12334
- ...latestTimestamp ? { since: latestTimestamp } : {}
12372
+ ...sinceTimestamp ? { since: sinceTimestamp } : {}
12335
12373
  }),
12336
12374
  { onEvent: (event) => this.handleGroupEvent(event) }
12337
12375
  );
@@ -12352,12 +12390,12 @@ var GroupChatModule = class {
12352
12390
  }
12353
12391
  subscribeToGroup(groupId) {
12354
12392
  if (!this.client) return;
12355
- const latestTimestamp = this.getLatestMessageTimestamp([groupId]);
12393
+ const sinceTimestamp = this.getLatestKnownTimestamp([groupId]);
12356
12394
  this.trackSubscription(
12357
12395
  createNip29Filter({
12358
12396
  kinds: [NIP29_KINDS.CHAT_MESSAGE, NIP29_KINDS.THREAD_ROOT, NIP29_KINDS.THREAD_REPLY],
12359
12397
  "#h": [groupId],
12360
- ...latestTimestamp ? { since: latestTimestamp } : {}
12398
+ ...sinceTimestamp ? { since: sinceTimestamp } : {}
12361
12399
  }),
12362
12400
  { onEvent: (event) => this.handleGroupEvent(event) }
12363
12401
  );
@@ -12431,7 +12469,7 @@ var GroupChatModule = class {
12431
12469
  }
12432
12470
  group.updatedAt = event.created_at * 1e3;
12433
12471
  this.groups.set(groupId, group);
12434
- this.persistGroups();
12472
+ this.schedulePersist();
12435
12473
  } else if (event.kind === NIP29_KINDS.GROUP_MEMBERS) {
12436
12474
  this.updateMembersFromEvent(groupId, event);
12437
12475
  } else if (event.kind === NIP29_KINDS.GROUP_ADMINS) {
@@ -12452,7 +12490,7 @@ var GroupChatModule = class {
12452
12490
  }
12453
12491
  }
12454
12492
  this.deps.emitEvent("groupchat:updated", {});
12455
- this.persistMessages();
12493
+ this.schedulePersist();
12456
12494
  } else if (event.kind === NIP29_KINDS.REMOVE_USER) {
12457
12495
  if (this.processedEventIds.has(event.id)) return;
12458
12496
  const eventTimestampMs = event.created_at * 1e3;
@@ -12513,7 +12551,7 @@ var GroupChatModule = class {
12513
12551
  };
12514
12552
  this.saveMemberToMemory(member);
12515
12553
  }
12516
- this.persistMembers();
12554
+ this.schedulePersist();
12517
12555
  }
12518
12556
  updateAdminsFromEvent(groupId, event) {
12519
12557
  const pTags = event.tags.filter((t) => t[0] === "p");
@@ -12533,7 +12571,7 @@ var GroupChatModule = class {
12533
12571
  });
12534
12572
  }
12535
12573
  }
12536
- this.persistMembers();
12574
+ this.schedulePersist();
12537
12575
  }
12538
12576
  // ===========================================================================
12539
12577
  // Group Membership Restoration
@@ -12544,13 +12582,11 @@ var GroupChatModule = class {
12544
12582
  if (!myPubkey) return [];
12545
12583
  const groupIdsWithMembership = /* @__PURE__ */ new Set();
12546
12584
  await this.oneshotSubscription(
12547
- new import_nostr_js_sdk4.Filter({ kinds: [NIP29_KINDS.GROUP_MEMBERS] }),
12585
+ createNip29Filter({ kinds: [NIP29_KINDS.GROUP_MEMBERS], "#p": [myPubkey] }),
12548
12586
  {
12549
12587
  onEvent: (event) => {
12550
12588
  const groupId = this.getGroupIdFromMetadataEvent(event);
12551
- if (!groupId) return;
12552
- const pTags = event.tags.filter((t) => t[0] === "p");
12553
- if (pTags.some((tag) => tag[1] === myPubkey)) {
12589
+ if (groupId) {
12554
12590
  groupIdsWithMembership.add(groupId);
12555
12591
  }
12556
12592
  },
@@ -12562,22 +12598,24 @@ var GroupChatModule = class {
12562
12598
  );
12563
12599
  if (groupIdsWithMembership.size === 0) return [];
12564
12600
  const restoredGroups = [];
12565
- for (const groupId of groupIdsWithMembership) {
12566
- if (this.groups.has(groupId)) continue;
12567
- try {
12568
- const group = await this.fetchGroupMetadataInternal(groupId);
12569
- if (group) {
12570
- this.groups.set(groupId, group);
12571
- restoredGroups.push(group);
12572
- await Promise.all([
12573
- this.fetchAndSaveMembers(groupId),
12574
- this.fetchMessages(groupId)
12575
- ]);
12601
+ await Promise.all(
12602
+ Array.from(groupIdsWithMembership).map(async (groupId) => {
12603
+ if (this.groups.has(groupId)) return;
12604
+ try {
12605
+ const group = await this.fetchGroupMetadataInternal(groupId);
12606
+ if (group) {
12607
+ this.groups.set(groupId, group);
12608
+ restoredGroups.push(group);
12609
+ await Promise.all([
12610
+ this.fetchAndSaveMembers(groupId),
12611
+ this.fetchMessages(groupId)
12612
+ ]);
12613
+ }
12614
+ } catch (error) {
12615
+ logger.warn("GroupChat", "Failed to restore group", groupId, error);
12576
12616
  }
12577
- } catch (error) {
12578
- logger.warn("GroupChat", "Failed to restore group", groupId, error);
12579
- }
12580
- }
12617
+ })
12618
+ );
12581
12619
  if (restoredGroups.length > 0) {
12582
12620
  await this.subscribeToJoinedGroups();
12583
12621
  this.deps.emitEvent("groupchat:updated", {});
@@ -12963,7 +13001,7 @@ var GroupChatModule = class {
12963
13001
  if (group && (group.unreadCount || 0) > 0) {
12964
13002
  group.unreadCount = 0;
12965
13003
  this.groups.set(groupId, group);
12966
- this.persistGroups();
13004
+ this.schedulePersist();
12967
13005
  }
12968
13006
  }
12969
13007
  // ===========================================================================
@@ -12985,7 +13023,7 @@ var GroupChatModule = class {
12985
13023
  if (eventId) {
12986
13024
  this.removeMemberFromMemory(groupId, userPubkey);
12987
13025
  this.deps.emitEvent("groupchat:updated", {});
12988
- this.persistMembers();
13026
+ this.schedulePersist();
12989
13027
  return true;
12990
13028
  }
12991
13029
  return false;
@@ -13008,7 +13046,7 @@ var GroupChatModule = class {
13008
13046
  if (eventId) {
13009
13047
  this.deleteMessageFromMemory(groupId, messageId);
13010
13048
  this.deps.emitEvent("groupchat:updated", {});
13011
- this.persistMessages();
13049
+ this.schedulePersist();
13012
13050
  return true;
13013
13051
  }
13014
13052
  return false;
@@ -13088,7 +13126,7 @@ var GroupChatModule = class {
13088
13126
  * or 0 if no messages exist. Used to set `since` on subscriptions so the relay
13089
13127
  * only sends events we don't already have.
13090
13128
  */
13091
- getLatestMessageTimestamp(groupIds) {
13129
+ getLatestKnownTimestamp(groupIds) {
13092
13130
  let latest = 0;
13093
13131
  for (const gid of groupIds) {
13094
13132
  const msgs = this.messages.get(gid);
@@ -13169,7 +13207,7 @@ var GroupChatModule = class {
13169
13207
  });
13170
13208
  }
13171
13209
  }
13172
- this.persistMembers();
13210
+ this.schedulePersist();
13173
13211
  }
13174
13212
  async fetchGroupMembersInternal(groupId) {
13175
13213
  if (!this.client) return [];
@@ -13296,8 +13334,11 @@ var GroupChatModule = class {
13296
13334
  addProcessedEventId(eventId) {
13297
13335
  this.processedEventIds.add(eventId);
13298
13336
  if (this.processedEventIds.size > 1e4) {
13299
- const arr = Array.from(this.processedEventIds);
13300
- this.processedEventIds = new Set(arr.slice(arr.length - 1e4));
13337
+ let toDelete = 5e3;
13338
+ for (const id of this.processedEventIds) {
13339
+ if (toDelete-- <= 0) break;
13340
+ this.processedEventIds.delete(id);
13341
+ }
13301
13342
  }
13302
13343
  }
13303
13344
  // ===========================================================================