@unicitylabs/sphere-sdk 0.4.4 → 0.4.6

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 (37) hide show
  1. package/dist/connect/index.cjs +27 -10
  2. package/dist/connect/index.cjs.map +1 -1
  3. package/dist/connect/index.js +27 -10
  4. package/dist/connect/index.js.map +1 -1
  5. package/dist/core/index.cjs +72 -25
  6. package/dist/core/index.cjs.map +1 -1
  7. package/dist/core/index.d.cts +12 -0
  8. package/dist/core/index.d.ts +12 -0
  9. package/dist/core/index.js +72 -25
  10. package/dist/core/index.js.map +1 -1
  11. package/dist/impl/browser/connect/index.cjs +10 -10
  12. package/dist/impl/browser/connect/index.cjs.map +1 -1
  13. package/dist/impl/browser/connect/index.js +10 -10
  14. package/dist/impl/browser/connect/index.js.map +1 -1
  15. package/dist/impl/browser/index.cjs +10 -10
  16. package/dist/impl/browser/index.cjs.map +1 -1
  17. package/dist/impl/browser/index.js +10 -10
  18. package/dist/impl/browser/index.js.map +1 -1
  19. package/dist/impl/browser/ipfs.cjs +10 -10
  20. package/dist/impl/browser/ipfs.cjs.map +1 -1
  21. package/dist/impl/browser/ipfs.js +10 -10
  22. package/dist/impl/browser/ipfs.js.map +1 -1
  23. package/dist/impl/nodejs/connect/index.cjs +10 -10
  24. package/dist/impl/nodejs/connect/index.cjs.map +1 -1
  25. package/dist/impl/nodejs/connect/index.js +10 -10
  26. package/dist/impl/nodejs/connect/index.js.map +1 -1
  27. package/dist/impl/nodejs/index.cjs +10 -10
  28. package/dist/impl/nodejs/index.cjs.map +1 -1
  29. package/dist/impl/nodejs/index.js +10 -10
  30. package/dist/impl/nodejs/index.js.map +1 -1
  31. package/dist/index.cjs +72 -25
  32. package/dist/index.cjs.map +1 -1
  33. package/dist/index.d.cts +30 -18
  34. package/dist/index.d.ts +30 -18
  35. package/dist/index.js +72 -25
  36. package/dist/index.js.map +1 -1
  37. package/package.json +1 -1
@@ -2621,6 +2621,7 @@ declare class CommunicationsModule {
2621
2621
  private dmHandlers;
2622
2622
  private composingHandlers;
2623
2623
  private broadcastHandlers;
2624
+ private initializedAt;
2624
2625
  constructor(config?: CommunicationsModuleConfig);
2625
2626
  /**
2626
2627
  * Initialize module with dependencies
@@ -2699,6 +2700,12 @@ declare class CommunicationsModule {
2699
2700
  * Subscribe to incoming broadcasts
2700
2701
  */
2701
2702
  onBroadcast(handler: (message: BroadcastMessage) => void): () => void;
2703
+ /**
2704
+ * Resolve a peer's nametag by their transport pubkey.
2705
+ * Uses transport.resolveTransportPubkeyInfo() for live lookup from relay binding events.
2706
+ * Returns undefined if transport doesn't support resolution or peer has no nametag.
2707
+ */
2708
+ resolvePeerNametag(peerPubkey: string): Promise<string | undefined>;
2702
2709
  private handleIncomingMessage;
2703
2710
  private handleComposingIndicator;
2704
2711
  private handleIncomingBroadcast;
@@ -2748,6 +2755,11 @@ declare class GroupChatModule {
2748
2755
  private destroyConnection;
2749
2756
  connect(): Promise<void>;
2750
2757
  getConnectionStatus(): boolean;
2758
+ /**
2759
+ * Refresh subscriptions after load() switched to a different address.
2760
+ * Clears old subscriptions, restores groups if needed, and re-subscribes.
2761
+ */
2762
+ private refreshSubscriptions;
2751
2763
  private doConnect;
2752
2764
  private scheduleReconnect;
2753
2765
  private subscribeToJoinedGroups;
@@ -2621,6 +2621,7 @@ declare class CommunicationsModule {
2621
2621
  private dmHandlers;
2622
2622
  private composingHandlers;
2623
2623
  private broadcastHandlers;
2624
+ private initializedAt;
2624
2625
  constructor(config?: CommunicationsModuleConfig);
2625
2626
  /**
2626
2627
  * Initialize module with dependencies
@@ -2699,6 +2700,12 @@ declare class CommunicationsModule {
2699
2700
  * Subscribe to incoming broadcasts
2700
2701
  */
2701
2702
  onBroadcast(handler: (message: BroadcastMessage) => void): () => void;
2703
+ /**
2704
+ * Resolve a peer's nametag by their transport pubkey.
2705
+ * Uses transport.resolveTransportPubkeyInfo() for live lookup from relay binding events.
2706
+ * Returns undefined if transport doesn't support resolution or peer has no nametag.
2707
+ */
2708
+ resolvePeerNametag(peerPubkey: string): Promise<string | undefined>;
2702
2709
  private handleIncomingMessage;
2703
2710
  private handleComposingIndicator;
2704
2711
  private handleIncomingBroadcast;
@@ -2748,6 +2755,11 @@ declare class GroupChatModule {
2748
2755
  private destroyConnection;
2749
2756
  connect(): Promise<void>;
2750
2757
  getConnectionStatus(): boolean;
2758
+ /**
2759
+ * Refresh subscriptions after load() switched to a different address.
2760
+ * Clears old subscriptions, restores groups if needed, and re-subscribes.
2761
+ */
2762
+ private refreshSubscriptions;
2751
2763
  private doConnect;
2752
2764
  private scheduleReconnect;
2753
2765
  private subscribeToJoinedGroups;
@@ -2348,15 +2348,7 @@ var STORAGE_KEYS_GLOBAL = {
2348
2348
  TRACKED_ADDRESSES: "tracked_addresses",
2349
2349
  /** Last processed Nostr wallet event timestamp (unix seconds), keyed per pubkey */
2350
2350
  LAST_WALLET_EVENT_TS: "last_wallet_event_ts",
2351
- /** Group chat: joined groups */
2352
- GROUP_CHAT_GROUPS: "group_chat_groups",
2353
- /** Group chat: messages */
2354
- GROUP_CHAT_MESSAGES: "group_chat_messages",
2355
- /** Group chat: members */
2356
- GROUP_CHAT_MEMBERS: "group_chat_members",
2357
- /** Group chat: processed event IDs for deduplication */
2358
- GROUP_CHAT_PROCESSED_EVENTS: "group_chat_processed_events",
2359
- /** Group chat: last used relay URL (stale data detection) */
2351
+ /** Group chat: last used relay URL (stale data detection) — global, same relay for all addresses */
2360
2352
  GROUP_CHAT_RELAY_URL: "group_chat_relay_url",
2361
2353
  /** Cached token registry JSON (fetched from remote) */
2362
2354
  TOKEN_REGISTRY_CACHE: "token_registry_cache",
@@ -2379,7 +2371,15 @@ var STORAGE_KEYS_ADDRESS = {
2379
2371
  /** Transaction history for this address */
2380
2372
  TRANSACTION_HISTORY: "transaction_history",
2381
2373
  /** Pending V5 finalization tokens (unconfirmed instant split tokens) */
2382
- PENDING_V5_TOKENS: "pending_v5_tokens"
2374
+ PENDING_V5_TOKENS: "pending_v5_tokens",
2375
+ /** Group chat: joined groups for this address */
2376
+ GROUP_CHAT_GROUPS: "group_chat_groups",
2377
+ /** Group chat: messages for this address */
2378
+ GROUP_CHAT_MESSAGES: "group_chat_messages",
2379
+ /** Group chat: members for this address */
2380
+ GROUP_CHAT_MEMBERS: "group_chat_members",
2381
+ /** Group chat: processed event IDs for deduplication */
2382
+ GROUP_CHAT_PROCESSED_EVENTS: "group_chat_processed_events"
2383
2383
  };
2384
2384
  var STORAGE_KEYS = {
2385
2385
  ...STORAGE_KEYS_GLOBAL,
@@ -7350,6 +7350,8 @@ var CommunicationsModule = class {
7350
7350
  dmHandlers = /* @__PURE__ */ new Set();
7351
7351
  composingHandlers = /* @__PURE__ */ new Set();
7352
7352
  broadcastHandlers = /* @__PURE__ */ new Set();
7353
+ // Timestamp of module initialization — messages older than this are historical
7354
+ initializedAt = 0;
7353
7355
  constructor(config) {
7354
7356
  this.config = {
7355
7357
  autoSave: config?.autoSave ?? true,
@@ -7368,6 +7370,7 @@ var CommunicationsModule = class {
7368
7370
  this.unsubscribeMessages?.();
7369
7371
  this.unsubscribeComposing?.();
7370
7372
  this.deps = deps;
7373
+ this.initializedAt = Date.now();
7371
7374
  this.unsubscribeMessages = deps.transport.onMessage((msg) => {
7372
7375
  this.handleIncomingMessage(msg);
7373
7376
  });
@@ -7656,9 +7659,27 @@ var CommunicationsModule = class {
7656
7659
  return () => this.broadcastHandlers.delete(handler);
7657
7660
  }
7658
7661
  // ===========================================================================
7662
+ // Public API - Peer Resolution
7663
+ // ===========================================================================
7664
+ /**
7665
+ * Resolve a peer's nametag by their transport pubkey.
7666
+ * Uses transport.resolveTransportPubkeyInfo() for live lookup from relay binding events.
7667
+ * Returns undefined if transport doesn't support resolution or peer has no nametag.
7668
+ */
7669
+ async resolvePeerNametag(peerPubkey) {
7670
+ if (!this.deps?.transport.resolveTransportPubkeyInfo) return void 0;
7671
+ try {
7672
+ const info = await this.deps.transport.resolveTransportPubkeyInfo(peerPubkey);
7673
+ return info?.nametag;
7674
+ } catch {
7675
+ return void 0;
7676
+ }
7677
+ }
7678
+ // ===========================================================================
7659
7679
  // Private: Message Handling
7660
7680
  // ===========================================================================
7661
7681
  handleIncomingMessage(msg) {
7682
+ const isHistorical = msg.timestamp < this.initializedAt;
7662
7683
  if (msg.isSelfWrap && msg.recipientTransportPubkey) {
7663
7684
  if (this.messages.has(msg.id)) return;
7664
7685
  const message2 = {
@@ -7668,7 +7689,7 @@ var CommunicationsModule = class {
7668
7689
  recipientPubkey: msg.recipientTransportPubkey,
7669
7690
  content: msg.content,
7670
7691
  timestamp: msg.timestamp,
7671
- isRead: false
7692
+ isRead: isHistorical
7672
7693
  };
7673
7694
  this.messages.set(message2.id, message2);
7674
7695
  this.deps.emitEvent("message:dm", message2);
@@ -7686,7 +7707,7 @@ var CommunicationsModule = class {
7686
7707
  recipientPubkey: this.deps.identity.chainPubkey,
7687
7708
  content: msg.content,
7688
7709
  timestamp: msg.timestamp,
7689
- isRead: false
7710
+ isRead: isHistorical
7690
7711
  };
7691
7712
  this.messages.set(message.id, message);
7692
7713
  this.deps.emitEvent("message:dm", message);
@@ -7862,22 +7883,24 @@ var GroupChatModule = class {
7862
7883
  async load() {
7863
7884
  this.ensureInitialized();
7864
7885
  const storage = this.deps.storage;
7865
- const groupsJson = await storage.get(STORAGE_KEYS_GLOBAL.GROUP_CHAT_GROUPS);
7886
+ this.groups.clear();
7887
+ this.messages.clear();
7888
+ this.members.clear();
7889
+ this.processedEventIds.clear();
7890
+ const groupsJson = await storage.get(STORAGE_KEYS_ADDRESS.GROUP_CHAT_GROUPS);
7866
7891
  if (groupsJson) {
7867
7892
  try {
7868
7893
  const parsed = JSON.parse(groupsJson);
7869
- this.groups.clear();
7870
7894
  for (const g of parsed) {
7871
7895
  this.groups.set(g.id, g);
7872
7896
  }
7873
7897
  } catch {
7874
7898
  }
7875
7899
  }
7876
- const messagesJson = await storage.get(STORAGE_KEYS_GLOBAL.GROUP_CHAT_MESSAGES);
7900
+ const messagesJson = await storage.get(STORAGE_KEYS_ADDRESS.GROUP_CHAT_MESSAGES);
7877
7901
  if (messagesJson) {
7878
7902
  try {
7879
7903
  const parsed = JSON.parse(messagesJson);
7880
- this.messages.clear();
7881
7904
  for (const m of parsed) {
7882
7905
  const groupId = m.groupId;
7883
7906
  if (!this.messages.has(groupId)) {
@@ -7888,11 +7911,10 @@ var GroupChatModule = class {
7888
7911
  } catch {
7889
7912
  }
7890
7913
  }
7891
- const membersJson = await storage.get(STORAGE_KEYS_GLOBAL.GROUP_CHAT_MEMBERS);
7914
+ const membersJson = await storage.get(STORAGE_KEYS_ADDRESS.GROUP_CHAT_MEMBERS);
7892
7915
  if (membersJson) {
7893
7916
  try {
7894
7917
  const parsed = JSON.parse(membersJson);
7895
- this.members.clear();
7896
7918
  for (const m of parsed) {
7897
7919
  const groupId = m.groupId;
7898
7920
  if (!this.members.has(groupId)) {
@@ -7903,7 +7925,7 @@ var GroupChatModule = class {
7903
7925
  } catch {
7904
7926
  }
7905
7927
  }
7906
- const processedJson = await storage.get(STORAGE_KEYS_GLOBAL.GROUP_CHAT_PROCESSED_EVENTS);
7928
+ const processedJson = await storage.get(STORAGE_KEYS_ADDRESS.GROUP_CHAT_PROCESSED_EVENTS);
7907
7929
  if (processedJson) {
7908
7930
  try {
7909
7931
  const parsed = JSON.parse(processedJson);
@@ -7957,7 +7979,10 @@ var GroupChatModule = class {
7957
7979
  // Connection
7958
7980
  // ===========================================================================
7959
7981
  async connect() {
7960
- if (this.connected) return;
7982
+ if (this.connected) {
7983
+ await this.refreshSubscriptions();
7984
+ return;
7985
+ }
7961
7986
  if (this.connectPromise) {
7962
7987
  return this.connectPromise;
7963
7988
  }
@@ -7971,6 +7996,28 @@ var GroupChatModule = class {
7971
7996
  getConnectionStatus() {
7972
7997
  return this.connected;
7973
7998
  }
7999
+ /**
8000
+ * Refresh subscriptions after load() switched to a different address.
8001
+ * Clears old subscriptions, restores groups if needed, and re-subscribes.
8002
+ */
8003
+ async refreshSubscriptions() {
8004
+ if (!this.client) return;
8005
+ for (const subId of this.subscriptionIds) {
8006
+ try {
8007
+ this.client.unsubscribe(subId);
8008
+ } catch {
8009
+ }
8010
+ }
8011
+ this.subscriptionIds = [];
8012
+ const secretKey = Buffer.from(this.deps.identity.privateKey, "hex");
8013
+ this.keyManager = NostrKeyManager.fromPrivateKey(secretKey);
8014
+ if (this.groups.size === 0) {
8015
+ await this.restoreJoinedGroups();
8016
+ } else {
8017
+ await this.subscribeToJoinedGroups();
8018
+ }
8019
+ this.deps.emitEvent("groupchat:connection", { connected: true });
8020
+ }
7974
8021
  async doConnect() {
7975
8022
  this.ensureInitialized();
7976
8023
  if (!this.keyManager) {
@@ -7986,12 +8033,12 @@ var GroupChatModule = class {
7986
8033
  await this.client.connect(...this.config.relays);
7987
8034
  this.connected = true;
7988
8035
  this.reconnectAttempts = 0;
7989
- this.deps.emitEvent("groupchat:connection", { connected: true });
7990
8036
  if (this.groups.size === 0) {
7991
8037
  await this.restoreJoinedGroups();
7992
8038
  } else {
7993
8039
  await this.subscribeToJoinedGroups();
7994
8040
  }
8041
+ this.deps.emitEvent("groupchat:connection", { connected: true });
7995
8042
  } catch (error) {
7996
8043
  console.error("[GroupChat] Failed to connect to relays", error);
7997
8044
  this.deps.emitEvent("groupchat:connection", { connected: false });
@@ -8974,7 +9021,7 @@ var GroupChatModule = class {
8974
9021
  async persistGroups() {
8975
9022
  if (!this.deps) return;
8976
9023
  const data = Array.from(this.groups.values());
8977
- await this.deps.storage.set(STORAGE_KEYS_GLOBAL.GROUP_CHAT_GROUPS, JSON.stringify(data));
9024
+ await this.deps.storage.set(STORAGE_KEYS_ADDRESS.GROUP_CHAT_GROUPS, JSON.stringify(data));
8978
9025
  }
8979
9026
  async persistMessages() {
8980
9027
  if (!this.deps) return;
@@ -8982,7 +9029,7 @@ var GroupChatModule = class {
8982
9029
  for (const msgs of this.messages.values()) {
8983
9030
  allMessages.push(...msgs);
8984
9031
  }
8985
- await this.deps.storage.set(STORAGE_KEYS_GLOBAL.GROUP_CHAT_MESSAGES, JSON.stringify(allMessages));
9032
+ await this.deps.storage.set(STORAGE_KEYS_ADDRESS.GROUP_CHAT_MESSAGES, JSON.stringify(allMessages));
8986
9033
  }
8987
9034
  async persistMembers() {
8988
9035
  if (!this.deps) return;
@@ -8990,12 +9037,12 @@ var GroupChatModule = class {
8990
9037
  for (const mems of this.members.values()) {
8991
9038
  allMembers.push(...mems);
8992
9039
  }
8993
- await this.deps.storage.set(STORAGE_KEYS_GLOBAL.GROUP_CHAT_MEMBERS, JSON.stringify(allMembers));
9040
+ await this.deps.storage.set(STORAGE_KEYS_ADDRESS.GROUP_CHAT_MEMBERS, JSON.stringify(allMembers));
8994
9041
  }
8995
9042
  async persistProcessedEvents() {
8996
9043
  if (!this.deps) return;
8997
9044
  const arr = Array.from(this.processedEventIds);
8998
- await this.deps.storage.set(STORAGE_KEYS_GLOBAL.GROUP_CHAT_PROCESSED_EVENTS, JSON.stringify(arr));
9045
+ await this.deps.storage.set(STORAGE_KEYS_ADDRESS.GROUP_CHAT_PROCESSED_EVENTS, JSON.stringify(arr));
8999
9046
  }
9000
9047
  // ===========================================================================
9001
9048
  // Private — Relay URL Change Detection