@unicitylabs/sphere-sdk 0.3.8 → 0.3.9

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 (43) hide show
  1. package/dist/connect/index.cjs +770 -0
  2. package/dist/connect/index.cjs.map +1 -0
  3. package/dist/connect/index.d.cts +312 -0
  4. package/dist/connect/index.d.ts +312 -0
  5. package/dist/connect/index.js +747 -0
  6. package/dist/connect/index.js.map +1 -0
  7. package/dist/core/index.cjs +87 -7
  8. package/dist/core/index.cjs.map +1 -1
  9. package/dist/core/index.d.cts +57 -1
  10. package/dist/core/index.d.ts +57 -1
  11. package/dist/core/index.js +87 -7
  12. package/dist/core/index.js.map +1 -1
  13. package/dist/impl/browser/connect/index.cjs +271 -0
  14. package/dist/impl/browser/connect/index.cjs.map +1 -0
  15. package/dist/impl/browser/connect/index.d.cts +137 -0
  16. package/dist/impl/browser/connect/index.d.ts +137 -0
  17. package/dist/impl/browser/connect/index.js +248 -0
  18. package/dist/impl/browser/connect/index.js.map +1 -0
  19. package/dist/impl/browser/index.cjs +167 -32
  20. package/dist/impl/browser/index.cjs.map +1 -1
  21. package/dist/impl/browser/index.js +170 -33
  22. package/dist/impl/browser/index.js.map +1 -1
  23. package/dist/impl/browser/ipfs.cjs.map +1 -1
  24. package/dist/impl/browser/ipfs.js.map +1 -1
  25. package/dist/impl/nodejs/connect/index.cjs +372 -0
  26. package/dist/impl/nodejs/connect/index.cjs.map +1 -0
  27. package/dist/impl/nodejs/connect/index.d.cts +178 -0
  28. package/dist/impl/nodejs/connect/index.d.ts +178 -0
  29. package/dist/impl/nodejs/connect/index.js +333 -0
  30. package/dist/impl/nodejs/connect/index.js.map +1 -0
  31. package/dist/impl/nodejs/index.cjs +114 -4
  32. package/dist/impl/nodejs/index.cjs.map +1 -1
  33. package/dist/impl/nodejs/index.d.cts +49 -0
  34. package/dist/impl/nodejs/index.d.ts +49 -0
  35. package/dist/impl/nodejs/index.js +117 -5
  36. package/dist/impl/nodejs/index.js.map +1 -1
  37. package/dist/index.cjs +87 -7
  38. package/dist/index.cjs.map +1 -1
  39. package/dist/index.d.cts +57 -1
  40. package/dist/index.d.ts +57 -1
  41. package/dist/index.js +87 -7
  42. package/dist/index.js.map +1 -1
  43. package/package.json +31 -1
package/dist/index.d.cts CHANGED
@@ -1162,7 +1162,7 @@ interface TrackedAddress extends TrackedAddressEntry {
1162
1162
  /** Primary nametag (from nametag cache, without @ prefix) */
1163
1163
  readonly nametag?: string;
1164
1164
  }
1165
- type SphereEventType = 'transfer:incoming' | 'transfer:confirmed' | 'transfer:failed' | 'payment_request:incoming' | 'payment_request:accepted' | 'payment_request:rejected' | 'payment_request:paid' | 'payment_request:response' | 'message:dm' | 'message:broadcast' | 'sync:started' | 'sync:completed' | 'sync:provider' | 'sync:error' | 'connection:changed' | 'nametag:registered' | 'nametag:recovered' | 'identity:changed' | 'address:activated' | 'address:hidden' | 'address:unhidden' | 'sync:remote-update' | 'groupchat:message' | 'groupchat:joined' | 'groupchat:left' | 'groupchat:kicked' | 'groupchat:group_deleted' | 'groupchat:updated' | 'groupchat:connection';
1165
+ type SphereEventType = 'transfer:incoming' | 'transfer:confirmed' | 'transfer:failed' | 'payment_request:incoming' | 'payment_request:accepted' | 'payment_request:rejected' | 'payment_request:paid' | 'payment_request:response' | 'message:dm' | 'message:read' | 'message:typing' | 'message:broadcast' | 'sync:started' | 'sync:completed' | 'sync:provider' | 'sync:error' | 'connection:changed' | 'nametag:registered' | 'nametag:recovered' | 'identity:changed' | 'address:activated' | 'address:hidden' | 'address:unhidden' | 'sync:remote-update' | 'groupchat:message' | 'groupchat:joined' | 'groupchat:left' | 'groupchat:kicked' | 'groupchat:group_deleted' | 'groupchat:updated' | 'groupchat:connection';
1166
1166
  interface SphereEventMap {
1167
1167
  'transfer:incoming': IncomingTransfer;
1168
1168
  'transfer:confirmed': TransferResult;
@@ -1173,6 +1173,15 @@ interface SphereEventMap {
1173
1173
  'payment_request:paid': IncomingPaymentRequest$1;
1174
1174
  'payment_request:response': PaymentRequestResponse;
1175
1175
  'message:dm': DirectMessage;
1176
+ 'message:read': {
1177
+ messageIds: string[];
1178
+ peerPubkey: string;
1179
+ };
1180
+ 'message:typing': {
1181
+ senderPubkey: string;
1182
+ senderNametag?: string;
1183
+ timestamp: number;
1184
+ };
1176
1185
  'message:broadcast': BroadcastMessage;
1177
1186
  'sync:started': {
1178
1187
  source: string;
@@ -1542,6 +1551,27 @@ interface TransportProvider extends BaseProvider {
1542
1551
  * @returns Unsubscribe function
1543
1552
  */
1544
1553
  onPaymentRequestResponse?(handler: PaymentRequestResponseHandler): () => void;
1554
+ /**
1555
+ * Send a read receipt for a message
1556
+ * @param recipientTransportPubkey - Transport pubkey of the message sender
1557
+ * @param messageEventId - Event ID of the message being acknowledged
1558
+ */
1559
+ sendReadReceipt?(recipientTransportPubkey: string, messageEventId: string): Promise<void>;
1560
+ /**
1561
+ * Subscribe to incoming read receipts
1562
+ * @returns Unsubscribe function
1563
+ */
1564
+ onReadReceipt?(handler: ReadReceiptHandler): () => void;
1565
+ /**
1566
+ * Send typing indicator to a recipient
1567
+ * @param recipientTransportPubkey - Transport pubkey of the conversation partner
1568
+ */
1569
+ sendTypingIndicator?(recipientTransportPubkey: string): Promise<void>;
1570
+ /**
1571
+ * Subscribe to incoming typing indicators
1572
+ * @returns Unsubscribe function
1573
+ */
1574
+ onTypingIndicator?(handler: TypingIndicatorHandler): () => void;
1545
1575
  /**
1546
1576
  * Get list of configured relay URLs
1547
1577
  */
@@ -1631,6 +1661,10 @@ interface IncomingMessage {
1631
1661
  content: string;
1632
1662
  timestamp: number;
1633
1663
  encrypted: boolean;
1664
+ /** Set when this is a self-wrap replay (sent message recovered from relay) */
1665
+ isSelfWrap?: boolean;
1666
+ /** Recipient pubkey — only present on self-wrap replays */
1667
+ recipientTransportPubkey?: string;
1634
1668
  }
1635
1669
  type MessageHandler = (message: IncomingMessage) => void;
1636
1670
  interface TokenTransferPayload {
@@ -1752,6 +1786,24 @@ interface PeerInfo {
1752
1786
  /** Event timestamp */
1753
1787
  timestamp: number;
1754
1788
  }
1789
+ interface IncomingReadReceipt {
1790
+ /** Transport-specific pubkey of the sender who read the message */
1791
+ senderTransportPubkey: string;
1792
+ /** Event ID of the message that was read */
1793
+ messageEventId: string;
1794
+ /** Timestamp */
1795
+ timestamp: number;
1796
+ }
1797
+ type ReadReceiptHandler = (receipt: IncomingReadReceipt) => void;
1798
+ interface IncomingTypingIndicator {
1799
+ /** Transport-specific pubkey of the sender who is typing */
1800
+ senderTransportPubkey: string;
1801
+ /** Sender's nametag (if known) */
1802
+ senderNametag?: string;
1803
+ /** Timestamp */
1804
+ timestamp: number;
1805
+ }
1806
+ type TypingIndicatorHandler = (indicator: IncomingTypingIndicator) => void;
1755
1807
 
1756
1808
  /**
1757
1809
  * L1 Payments Sub-Module
@@ -3202,6 +3254,10 @@ declare class CommunicationsModule {
3202
3254
  * Get unread count
3203
3255
  */
3204
3256
  getUnreadCount(peerPubkey?: string): number;
3257
+ /**
3258
+ * Send typing indicator to a peer
3259
+ */
3260
+ sendTypingIndicator(peerPubkey: string): Promise<void>;
3205
3261
  /**
3206
3262
  * Subscribe to incoming DMs
3207
3263
  */
package/dist/index.d.ts CHANGED
@@ -1162,7 +1162,7 @@ interface TrackedAddress extends TrackedAddressEntry {
1162
1162
  /** Primary nametag (from nametag cache, without @ prefix) */
1163
1163
  readonly nametag?: string;
1164
1164
  }
1165
- type SphereEventType = 'transfer:incoming' | 'transfer:confirmed' | 'transfer:failed' | 'payment_request:incoming' | 'payment_request:accepted' | 'payment_request:rejected' | 'payment_request:paid' | 'payment_request:response' | 'message:dm' | 'message:broadcast' | 'sync:started' | 'sync:completed' | 'sync:provider' | 'sync:error' | 'connection:changed' | 'nametag:registered' | 'nametag:recovered' | 'identity:changed' | 'address:activated' | 'address:hidden' | 'address:unhidden' | 'sync:remote-update' | 'groupchat:message' | 'groupchat:joined' | 'groupchat:left' | 'groupchat:kicked' | 'groupchat:group_deleted' | 'groupchat:updated' | 'groupchat:connection';
1165
+ type SphereEventType = 'transfer:incoming' | 'transfer:confirmed' | 'transfer:failed' | 'payment_request:incoming' | 'payment_request:accepted' | 'payment_request:rejected' | 'payment_request:paid' | 'payment_request:response' | 'message:dm' | 'message:read' | 'message:typing' | 'message:broadcast' | 'sync:started' | 'sync:completed' | 'sync:provider' | 'sync:error' | 'connection:changed' | 'nametag:registered' | 'nametag:recovered' | 'identity:changed' | 'address:activated' | 'address:hidden' | 'address:unhidden' | 'sync:remote-update' | 'groupchat:message' | 'groupchat:joined' | 'groupchat:left' | 'groupchat:kicked' | 'groupchat:group_deleted' | 'groupchat:updated' | 'groupchat:connection';
1166
1166
  interface SphereEventMap {
1167
1167
  'transfer:incoming': IncomingTransfer;
1168
1168
  'transfer:confirmed': TransferResult;
@@ -1173,6 +1173,15 @@ interface SphereEventMap {
1173
1173
  'payment_request:paid': IncomingPaymentRequest$1;
1174
1174
  'payment_request:response': PaymentRequestResponse;
1175
1175
  'message:dm': DirectMessage;
1176
+ 'message:read': {
1177
+ messageIds: string[];
1178
+ peerPubkey: string;
1179
+ };
1180
+ 'message:typing': {
1181
+ senderPubkey: string;
1182
+ senderNametag?: string;
1183
+ timestamp: number;
1184
+ };
1176
1185
  'message:broadcast': BroadcastMessage;
1177
1186
  'sync:started': {
1178
1187
  source: string;
@@ -1542,6 +1551,27 @@ interface TransportProvider extends BaseProvider {
1542
1551
  * @returns Unsubscribe function
1543
1552
  */
1544
1553
  onPaymentRequestResponse?(handler: PaymentRequestResponseHandler): () => void;
1554
+ /**
1555
+ * Send a read receipt for a message
1556
+ * @param recipientTransportPubkey - Transport pubkey of the message sender
1557
+ * @param messageEventId - Event ID of the message being acknowledged
1558
+ */
1559
+ sendReadReceipt?(recipientTransportPubkey: string, messageEventId: string): Promise<void>;
1560
+ /**
1561
+ * Subscribe to incoming read receipts
1562
+ * @returns Unsubscribe function
1563
+ */
1564
+ onReadReceipt?(handler: ReadReceiptHandler): () => void;
1565
+ /**
1566
+ * Send typing indicator to a recipient
1567
+ * @param recipientTransportPubkey - Transport pubkey of the conversation partner
1568
+ */
1569
+ sendTypingIndicator?(recipientTransportPubkey: string): Promise<void>;
1570
+ /**
1571
+ * Subscribe to incoming typing indicators
1572
+ * @returns Unsubscribe function
1573
+ */
1574
+ onTypingIndicator?(handler: TypingIndicatorHandler): () => void;
1545
1575
  /**
1546
1576
  * Get list of configured relay URLs
1547
1577
  */
@@ -1631,6 +1661,10 @@ interface IncomingMessage {
1631
1661
  content: string;
1632
1662
  timestamp: number;
1633
1663
  encrypted: boolean;
1664
+ /** Set when this is a self-wrap replay (sent message recovered from relay) */
1665
+ isSelfWrap?: boolean;
1666
+ /** Recipient pubkey — only present on self-wrap replays */
1667
+ recipientTransportPubkey?: string;
1634
1668
  }
1635
1669
  type MessageHandler = (message: IncomingMessage) => void;
1636
1670
  interface TokenTransferPayload {
@@ -1752,6 +1786,24 @@ interface PeerInfo {
1752
1786
  /** Event timestamp */
1753
1787
  timestamp: number;
1754
1788
  }
1789
+ interface IncomingReadReceipt {
1790
+ /** Transport-specific pubkey of the sender who read the message */
1791
+ senderTransportPubkey: string;
1792
+ /** Event ID of the message that was read */
1793
+ messageEventId: string;
1794
+ /** Timestamp */
1795
+ timestamp: number;
1796
+ }
1797
+ type ReadReceiptHandler = (receipt: IncomingReadReceipt) => void;
1798
+ interface IncomingTypingIndicator {
1799
+ /** Transport-specific pubkey of the sender who is typing */
1800
+ senderTransportPubkey: string;
1801
+ /** Sender's nametag (if known) */
1802
+ senderNametag?: string;
1803
+ /** Timestamp */
1804
+ timestamp: number;
1805
+ }
1806
+ type TypingIndicatorHandler = (indicator: IncomingTypingIndicator) => void;
1755
1807
 
1756
1808
  /**
1757
1809
  * L1 Payments Sub-Module
@@ -3202,6 +3254,10 @@ declare class CommunicationsModule {
3202
3254
  * Get unread count
3203
3255
  */
3204
3256
  getUnreadCount(peerPubkey?: string): number;
3257
+ /**
3258
+ * Send typing indicator to a peer
3259
+ */
3260
+ sendTypingIndicator(peerPubkey: string): Promise<void>;
3205
3261
  /**
3206
3262
  * Subscribe to incoming DMs
3207
3263
  */
package/dist/index.js CHANGED
@@ -7595,6 +7595,28 @@ var CommunicationsModule = class {
7595
7595
  this.unsubscribeMessages = deps.transport.onMessage((msg) => {
7596
7596
  this.handleIncomingMessage(msg);
7597
7597
  });
7598
+ if (deps.transport.onReadReceipt) {
7599
+ deps.transport.onReadReceipt((receipt) => {
7600
+ const msg = this.messages.get(receipt.messageEventId);
7601
+ if (msg && msg.senderPubkey === this.deps.identity.chainPubkey) {
7602
+ msg.isRead = true;
7603
+ this.save();
7604
+ this.deps.emitEvent("message:read", {
7605
+ messageIds: [receipt.messageEventId],
7606
+ peerPubkey: receipt.senderTransportPubkey
7607
+ });
7608
+ }
7609
+ });
7610
+ }
7611
+ if (deps.transport.onTypingIndicator) {
7612
+ deps.transport.onTypingIndicator((indicator) => {
7613
+ this.deps.emitEvent("message:typing", {
7614
+ senderPubkey: indicator.senderTransportPubkey,
7615
+ senderNametag: indicator.senderNametag,
7616
+ timestamp: indicator.timestamp
7617
+ });
7618
+ });
7619
+ }
7598
7620
  }
7599
7621
  /**
7600
7622
  * Load messages from storage
@@ -7638,7 +7660,7 @@ var CommunicationsModule = class {
7638
7660
  recipientPubkey,
7639
7661
  content,
7640
7662
  timestamp: Date.now(),
7641
- isRead: true
7663
+ isRead: false
7642
7664
  };
7643
7665
  this.messages.set(message.id, message);
7644
7666
  if (this.config.autoSave) {
@@ -7684,6 +7706,16 @@ var CommunicationsModule = class {
7684
7706
  if (this.config.autoSave) {
7685
7707
  await this.save();
7686
7708
  }
7709
+ if (this.config.readReceipts && this.deps?.transport.sendReadReceipt) {
7710
+ for (const id of messageIds) {
7711
+ const msg = this.messages.get(id);
7712
+ if (msg && msg.senderPubkey !== this.deps.identity.chainPubkey) {
7713
+ this.deps.transport.sendReadReceipt(msg.senderPubkey, id).catch((err) => {
7714
+ console.warn("[Communications] Failed to send read receipt:", err);
7715
+ });
7716
+ }
7717
+ }
7718
+ }
7687
7719
  }
7688
7720
  /**
7689
7721
  * Get unread count
@@ -7697,6 +7729,15 @@ var CommunicationsModule = class {
7697
7729
  }
7698
7730
  return messages.length;
7699
7731
  }
7732
+ /**
7733
+ * Send typing indicator to a peer
7734
+ */
7735
+ async sendTypingIndicator(peerPubkey) {
7736
+ this.ensureInitialized();
7737
+ if (this.deps.transport.sendTypingIndicator) {
7738
+ await this.deps.transport.sendTypingIndicator(peerPubkey);
7739
+ }
7740
+ }
7700
7741
  /**
7701
7742
  * Subscribe to incoming DMs
7702
7743
  */
@@ -7766,7 +7807,26 @@ var CommunicationsModule = class {
7766
7807
  // Private: Message Handling
7767
7808
  // ===========================================================================
7768
7809
  handleIncomingMessage(msg) {
7810
+ if (msg.isSelfWrap && msg.recipientTransportPubkey) {
7811
+ if (this.messages.has(msg.id)) return;
7812
+ const message2 = {
7813
+ id: msg.id,
7814
+ senderPubkey: this.deps.identity.chainPubkey,
7815
+ senderNametag: msg.senderNametag,
7816
+ recipientPubkey: msg.recipientTransportPubkey,
7817
+ content: msg.content,
7818
+ timestamp: msg.timestamp,
7819
+ isRead: false
7820
+ };
7821
+ this.messages.set(message2.id, message2);
7822
+ this.deps.emitEvent("message:dm", message2);
7823
+ if (this.config.autoSave) {
7824
+ this.save();
7825
+ }
7826
+ return;
7827
+ }
7769
7828
  if (msg.senderTransportPubkey === this.deps?.identity.chainPubkey) return;
7829
+ if (this.messages.has(msg.id)) return;
7770
7830
  const message = {
7771
7831
  id: msg.id,
7772
7832
  senderPubkey: msg.senderTransportPubkey,
@@ -12059,12 +12119,22 @@ var Sphere = class _Sphere {
12059
12119
  return;
12060
12120
  }
12061
12121
  try {
12062
- if (this._identity?.directAddress && this._transport.resolve) {
12122
+ const transportPubkey = this._identity?.chainPubkey?.slice(2);
12123
+ if (transportPubkey && this._transport.resolve) {
12063
12124
  try {
12064
- const existing = await this._transport.resolve(this._identity.directAddress);
12125
+ const existing = await this._transport.resolve(transportPubkey);
12065
12126
  if (existing) {
12066
- if (existing.nametag && !this._identity.nametag) {
12067
- this._identity.nametag = existing.nametag;
12127
+ let recoveredNametag = existing.nametag;
12128
+ let fromLegacy = false;
12129
+ if (!recoveredNametag && !this._identity?.nametag && this._transport.recoverNametag) {
12130
+ try {
12131
+ recoveredNametag = await this._transport.recoverNametag() ?? void 0;
12132
+ if (recoveredNametag) fromLegacy = true;
12133
+ } catch {
12134
+ }
12135
+ }
12136
+ if (recoveredNametag && !this._identity?.nametag) {
12137
+ this._identity.nametag = recoveredNametag;
12068
12138
  await this._updateCachedProxyAddress();
12069
12139
  const entry = await this.ensureAddressTracked(this._currentAddressIndex);
12070
12140
  let nametags = this._addressNametags.get(entry.addressId);
@@ -12073,10 +12143,20 @@ var Sphere = class _Sphere {
12073
12143
  this._addressNametags.set(entry.addressId, nametags);
12074
12144
  }
12075
12145
  if (!nametags.has(0)) {
12076
- nametags.set(0, existing.nametag);
12146
+ nametags.set(0, recoveredNametag);
12077
12147
  await this.persistAddressNametags();
12078
12148
  }
12079
- this.emitEvent("nametag:recovered", { nametag: existing.nametag });
12149
+ this.emitEvent("nametag:recovered", { nametag: recoveredNametag });
12150
+ if (fromLegacy) {
12151
+ await this._transport.publishIdentityBinding(
12152
+ this._identity.chainPubkey,
12153
+ this._identity.l1Address,
12154
+ this._identity.directAddress || "",
12155
+ recoveredNametag
12156
+ );
12157
+ console.log(`[Sphere] Migrated legacy binding with nametag @${recoveredNametag}`);
12158
+ return;
12159
+ }
12080
12160
  }
12081
12161
  console.log("[Sphere] Existing binding found, skipping re-publish");
12082
12162
  return;