@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
@@ -685,7 +685,7 @@ interface TrackedAddress extends TrackedAddressEntry {
685
685
  /** Primary nametag (from nametag cache, without @ prefix) */
686
686
  readonly nametag?: string;
687
687
  }
688
- 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';
688
+ 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';
689
689
  interface SphereEventMap {
690
690
  'transfer:incoming': IncomingTransfer;
691
691
  'transfer:confirmed': TransferResult;
@@ -696,6 +696,15 @@ interface SphereEventMap {
696
696
  'payment_request:paid': IncomingPaymentRequest$1;
697
697
  'payment_request:response': PaymentRequestResponse;
698
698
  'message:dm': DirectMessage;
699
+ 'message:read': {
700
+ messageIds: string[];
701
+ peerPubkey: string;
702
+ };
703
+ 'message:typing': {
704
+ senderPubkey: string;
705
+ senderNametag?: string;
706
+ timestamp: number;
707
+ };
699
708
  'message:broadcast': BroadcastMessage;
700
709
  'sync:started': {
701
710
  source: string;
@@ -1023,6 +1032,27 @@ interface TransportProvider extends BaseProvider {
1023
1032
  * @returns Unsubscribe function
1024
1033
  */
1025
1034
  onPaymentRequestResponse?(handler: PaymentRequestResponseHandler): () => void;
1035
+ /**
1036
+ * Send a read receipt for a message
1037
+ * @param recipientTransportPubkey - Transport pubkey of the message sender
1038
+ * @param messageEventId - Event ID of the message being acknowledged
1039
+ */
1040
+ sendReadReceipt?(recipientTransportPubkey: string, messageEventId: string): Promise<void>;
1041
+ /**
1042
+ * Subscribe to incoming read receipts
1043
+ * @returns Unsubscribe function
1044
+ */
1045
+ onReadReceipt?(handler: ReadReceiptHandler): () => void;
1046
+ /**
1047
+ * Send typing indicator to a recipient
1048
+ * @param recipientTransportPubkey - Transport pubkey of the conversation partner
1049
+ */
1050
+ sendTypingIndicator?(recipientTransportPubkey: string): Promise<void>;
1051
+ /**
1052
+ * Subscribe to incoming typing indicators
1053
+ * @returns Unsubscribe function
1054
+ */
1055
+ onTypingIndicator?(handler: TypingIndicatorHandler): () => void;
1026
1056
  /**
1027
1057
  * Get list of configured relay URLs
1028
1058
  */
@@ -1112,6 +1142,10 @@ interface IncomingMessage {
1112
1142
  content: string;
1113
1143
  timestamp: number;
1114
1144
  encrypted: boolean;
1145
+ /** Set when this is a self-wrap replay (sent message recovered from relay) */
1146
+ isSelfWrap?: boolean;
1147
+ /** Recipient pubkey — only present on self-wrap replays */
1148
+ recipientTransportPubkey?: string;
1115
1149
  }
1116
1150
  type MessageHandler = (message: IncomingMessage) => void;
1117
1151
  interface TokenTransferPayload {
@@ -1225,6 +1259,24 @@ interface PeerInfo {
1225
1259
  /** Event timestamp */
1226
1260
  timestamp: number;
1227
1261
  }
1262
+ interface IncomingReadReceipt {
1263
+ /** Transport-specific pubkey of the sender who read the message */
1264
+ senderTransportPubkey: string;
1265
+ /** Event ID of the message that was read */
1266
+ messageEventId: string;
1267
+ /** Timestamp */
1268
+ timestamp: number;
1269
+ }
1270
+ type ReadReceiptHandler = (receipt: IncomingReadReceipt) => void;
1271
+ interface IncomingTypingIndicator {
1272
+ /** Transport-specific pubkey of the sender who is typing */
1273
+ senderTransportPubkey: string;
1274
+ /** Sender's nametag (if known) */
1275
+ senderNametag?: string;
1276
+ /** Timestamp */
1277
+ timestamp: number;
1278
+ }
1279
+ type TypingIndicatorHandler = (indicator: IncomingTypingIndicator) => void;
1228
1280
 
1229
1281
  /**
1230
1282
  * L1 Payments Sub-Module
@@ -2557,6 +2609,10 @@ declare class CommunicationsModule {
2557
2609
  * Get unread count
2558
2610
  */
2559
2611
  getUnreadCount(peerPubkey?: string): number;
2612
+ /**
2613
+ * Send typing indicator to a peer
2614
+ */
2615
+ sendTypingIndicator(peerPubkey: string): Promise<void>;
2560
2616
  /**
2561
2617
  * Subscribe to incoming DMs
2562
2618
  */
@@ -685,7 +685,7 @@ interface TrackedAddress extends TrackedAddressEntry {
685
685
  /** Primary nametag (from nametag cache, without @ prefix) */
686
686
  readonly nametag?: string;
687
687
  }
688
- 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';
688
+ 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';
689
689
  interface SphereEventMap {
690
690
  'transfer:incoming': IncomingTransfer;
691
691
  'transfer:confirmed': TransferResult;
@@ -696,6 +696,15 @@ interface SphereEventMap {
696
696
  'payment_request:paid': IncomingPaymentRequest$1;
697
697
  'payment_request:response': PaymentRequestResponse;
698
698
  'message:dm': DirectMessage;
699
+ 'message:read': {
700
+ messageIds: string[];
701
+ peerPubkey: string;
702
+ };
703
+ 'message:typing': {
704
+ senderPubkey: string;
705
+ senderNametag?: string;
706
+ timestamp: number;
707
+ };
699
708
  'message:broadcast': BroadcastMessage;
700
709
  'sync:started': {
701
710
  source: string;
@@ -1023,6 +1032,27 @@ interface TransportProvider extends BaseProvider {
1023
1032
  * @returns Unsubscribe function
1024
1033
  */
1025
1034
  onPaymentRequestResponse?(handler: PaymentRequestResponseHandler): () => void;
1035
+ /**
1036
+ * Send a read receipt for a message
1037
+ * @param recipientTransportPubkey - Transport pubkey of the message sender
1038
+ * @param messageEventId - Event ID of the message being acknowledged
1039
+ */
1040
+ sendReadReceipt?(recipientTransportPubkey: string, messageEventId: string): Promise<void>;
1041
+ /**
1042
+ * Subscribe to incoming read receipts
1043
+ * @returns Unsubscribe function
1044
+ */
1045
+ onReadReceipt?(handler: ReadReceiptHandler): () => void;
1046
+ /**
1047
+ * Send typing indicator to a recipient
1048
+ * @param recipientTransportPubkey - Transport pubkey of the conversation partner
1049
+ */
1050
+ sendTypingIndicator?(recipientTransportPubkey: string): Promise<void>;
1051
+ /**
1052
+ * Subscribe to incoming typing indicators
1053
+ * @returns Unsubscribe function
1054
+ */
1055
+ onTypingIndicator?(handler: TypingIndicatorHandler): () => void;
1026
1056
  /**
1027
1057
  * Get list of configured relay URLs
1028
1058
  */
@@ -1112,6 +1142,10 @@ interface IncomingMessage {
1112
1142
  content: string;
1113
1143
  timestamp: number;
1114
1144
  encrypted: boolean;
1145
+ /** Set when this is a self-wrap replay (sent message recovered from relay) */
1146
+ isSelfWrap?: boolean;
1147
+ /** Recipient pubkey — only present on self-wrap replays */
1148
+ recipientTransportPubkey?: string;
1115
1149
  }
1116
1150
  type MessageHandler = (message: IncomingMessage) => void;
1117
1151
  interface TokenTransferPayload {
@@ -1225,6 +1259,24 @@ interface PeerInfo {
1225
1259
  /** Event timestamp */
1226
1260
  timestamp: number;
1227
1261
  }
1262
+ interface IncomingReadReceipt {
1263
+ /** Transport-specific pubkey of the sender who read the message */
1264
+ senderTransportPubkey: string;
1265
+ /** Event ID of the message that was read */
1266
+ messageEventId: string;
1267
+ /** Timestamp */
1268
+ timestamp: number;
1269
+ }
1270
+ type ReadReceiptHandler = (receipt: IncomingReadReceipt) => void;
1271
+ interface IncomingTypingIndicator {
1272
+ /** Transport-specific pubkey of the sender who is typing */
1273
+ senderTransportPubkey: string;
1274
+ /** Sender's nametag (if known) */
1275
+ senderNametag?: string;
1276
+ /** Timestamp */
1277
+ timestamp: number;
1278
+ }
1279
+ type TypingIndicatorHandler = (indicator: IncomingTypingIndicator) => void;
1228
1280
 
1229
1281
  /**
1230
1282
  * L1 Payments Sub-Module
@@ -2557,6 +2609,10 @@ declare class CommunicationsModule {
2557
2609
  * Get unread count
2558
2610
  */
2559
2611
  getUnreadCount(peerPubkey?: string): number;
2612
+ /**
2613
+ * Send typing indicator to a peer
2614
+ */
2615
+ sendTypingIndicator(peerPubkey: string): Promise<void>;
2560
2616
  /**
2561
2617
  * Subscribe to incoming DMs
2562
2618
  */
@@ -7303,6 +7303,28 @@ var CommunicationsModule = class {
7303
7303
  this.unsubscribeMessages = deps.transport.onMessage((msg) => {
7304
7304
  this.handleIncomingMessage(msg);
7305
7305
  });
7306
+ if (deps.transport.onReadReceipt) {
7307
+ deps.transport.onReadReceipt((receipt) => {
7308
+ const msg = this.messages.get(receipt.messageEventId);
7309
+ if (msg && msg.senderPubkey === this.deps.identity.chainPubkey) {
7310
+ msg.isRead = true;
7311
+ this.save();
7312
+ this.deps.emitEvent("message:read", {
7313
+ messageIds: [receipt.messageEventId],
7314
+ peerPubkey: receipt.senderTransportPubkey
7315
+ });
7316
+ }
7317
+ });
7318
+ }
7319
+ if (deps.transport.onTypingIndicator) {
7320
+ deps.transport.onTypingIndicator((indicator) => {
7321
+ this.deps.emitEvent("message:typing", {
7322
+ senderPubkey: indicator.senderTransportPubkey,
7323
+ senderNametag: indicator.senderNametag,
7324
+ timestamp: indicator.timestamp
7325
+ });
7326
+ });
7327
+ }
7306
7328
  }
7307
7329
  /**
7308
7330
  * Load messages from storage
@@ -7346,7 +7368,7 @@ var CommunicationsModule = class {
7346
7368
  recipientPubkey,
7347
7369
  content,
7348
7370
  timestamp: Date.now(),
7349
- isRead: true
7371
+ isRead: false
7350
7372
  };
7351
7373
  this.messages.set(message.id, message);
7352
7374
  if (this.config.autoSave) {
@@ -7392,6 +7414,16 @@ var CommunicationsModule = class {
7392
7414
  if (this.config.autoSave) {
7393
7415
  await this.save();
7394
7416
  }
7417
+ if (this.config.readReceipts && this.deps?.transport.sendReadReceipt) {
7418
+ for (const id of messageIds) {
7419
+ const msg = this.messages.get(id);
7420
+ if (msg && msg.senderPubkey !== this.deps.identity.chainPubkey) {
7421
+ this.deps.transport.sendReadReceipt(msg.senderPubkey, id).catch((err) => {
7422
+ console.warn("[Communications] Failed to send read receipt:", err);
7423
+ });
7424
+ }
7425
+ }
7426
+ }
7395
7427
  }
7396
7428
  /**
7397
7429
  * Get unread count
@@ -7405,6 +7437,15 @@ var CommunicationsModule = class {
7405
7437
  }
7406
7438
  return messages.length;
7407
7439
  }
7440
+ /**
7441
+ * Send typing indicator to a peer
7442
+ */
7443
+ async sendTypingIndicator(peerPubkey) {
7444
+ this.ensureInitialized();
7445
+ if (this.deps.transport.sendTypingIndicator) {
7446
+ await this.deps.transport.sendTypingIndicator(peerPubkey);
7447
+ }
7448
+ }
7408
7449
  /**
7409
7450
  * Subscribe to incoming DMs
7410
7451
  */
@@ -7474,7 +7515,26 @@ var CommunicationsModule = class {
7474
7515
  // Private: Message Handling
7475
7516
  // ===========================================================================
7476
7517
  handleIncomingMessage(msg) {
7518
+ if (msg.isSelfWrap && msg.recipientTransportPubkey) {
7519
+ if (this.messages.has(msg.id)) return;
7520
+ const message2 = {
7521
+ id: msg.id,
7522
+ senderPubkey: this.deps.identity.chainPubkey,
7523
+ senderNametag: msg.senderNametag,
7524
+ recipientPubkey: msg.recipientTransportPubkey,
7525
+ content: msg.content,
7526
+ timestamp: msg.timestamp,
7527
+ isRead: false
7528
+ };
7529
+ this.messages.set(message2.id, message2);
7530
+ this.deps.emitEvent("message:dm", message2);
7531
+ if (this.config.autoSave) {
7532
+ this.save();
7533
+ }
7534
+ return;
7535
+ }
7477
7536
  if (msg.senderTransportPubkey === this.deps?.identity.chainPubkey) return;
7537
+ if (this.messages.has(msg.id)) return;
7478
7538
  const message = {
7479
7539
  id: msg.id,
7480
7540
  senderPubkey: msg.senderTransportPubkey,
@@ -11852,12 +11912,22 @@ var Sphere = class _Sphere {
11852
11912
  return;
11853
11913
  }
11854
11914
  try {
11855
- if (this._identity?.directAddress && this._transport.resolve) {
11915
+ const transportPubkey = this._identity?.chainPubkey?.slice(2);
11916
+ if (transportPubkey && this._transport.resolve) {
11856
11917
  try {
11857
- const existing = await this._transport.resolve(this._identity.directAddress);
11918
+ const existing = await this._transport.resolve(transportPubkey);
11858
11919
  if (existing) {
11859
- if (existing.nametag && !this._identity.nametag) {
11860
- this._identity.nametag = existing.nametag;
11920
+ let recoveredNametag = existing.nametag;
11921
+ let fromLegacy = false;
11922
+ if (!recoveredNametag && !this._identity?.nametag && this._transport.recoverNametag) {
11923
+ try {
11924
+ recoveredNametag = await this._transport.recoverNametag() ?? void 0;
11925
+ if (recoveredNametag) fromLegacy = true;
11926
+ } catch {
11927
+ }
11928
+ }
11929
+ if (recoveredNametag && !this._identity?.nametag) {
11930
+ this._identity.nametag = recoveredNametag;
11861
11931
  await this._updateCachedProxyAddress();
11862
11932
  const entry = await this.ensureAddressTracked(this._currentAddressIndex);
11863
11933
  let nametags = this._addressNametags.get(entry.addressId);
@@ -11866,10 +11936,20 @@ var Sphere = class _Sphere {
11866
11936
  this._addressNametags.set(entry.addressId, nametags);
11867
11937
  }
11868
11938
  if (!nametags.has(0)) {
11869
- nametags.set(0, existing.nametag);
11939
+ nametags.set(0, recoveredNametag);
11870
11940
  await this.persistAddressNametags();
11871
11941
  }
11872
- this.emitEvent("nametag:recovered", { nametag: existing.nametag });
11942
+ this.emitEvent("nametag:recovered", { nametag: recoveredNametag });
11943
+ if (fromLegacy) {
11944
+ await this._transport.publishIdentityBinding(
11945
+ this._identity.chainPubkey,
11946
+ this._identity.l1Address,
11947
+ this._identity.directAddress || "",
11948
+ recoveredNametag
11949
+ );
11950
+ console.log(`[Sphere] Migrated legacy binding with nametag @${recoveredNametag}`);
11951
+ return;
11952
+ }
11873
11953
  }
11874
11954
  console.log("[Sphere] Existing binding found, skipping re-publish");
11875
11955
  return;