@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
@@ -422,6 +422,27 @@ interface TransportProvider extends BaseProvider {
422
422
  * @returns Unsubscribe function
423
423
  */
424
424
  onPaymentRequestResponse?(handler: PaymentRequestResponseHandler): () => void;
425
+ /**
426
+ * Send a read receipt for a message
427
+ * @param recipientTransportPubkey - Transport pubkey of the message sender
428
+ * @param messageEventId - Event ID of the message being acknowledged
429
+ */
430
+ sendReadReceipt?(recipientTransportPubkey: string, messageEventId: string): Promise<void>;
431
+ /**
432
+ * Subscribe to incoming read receipts
433
+ * @returns Unsubscribe function
434
+ */
435
+ onReadReceipt?(handler: ReadReceiptHandler): () => void;
436
+ /**
437
+ * Send typing indicator to a recipient
438
+ * @param recipientTransportPubkey - Transport pubkey of the conversation partner
439
+ */
440
+ sendTypingIndicator?(recipientTransportPubkey: string): Promise<void>;
441
+ /**
442
+ * Subscribe to incoming typing indicators
443
+ * @returns Unsubscribe function
444
+ */
445
+ onTypingIndicator?(handler: TypingIndicatorHandler): () => void;
425
446
  /**
426
447
  * Get list of configured relay URLs
427
448
  */
@@ -511,6 +532,10 @@ interface IncomingMessage {
511
532
  content: string;
512
533
  timestamp: number;
513
534
  encrypted: boolean;
535
+ /** Set when this is a self-wrap replay (sent message recovered from relay) */
536
+ isSelfWrap?: boolean;
537
+ /** Recipient pubkey — only present on self-wrap replays */
538
+ recipientTransportPubkey?: string;
514
539
  }
515
540
  type MessageHandler = (message: IncomingMessage) => void;
516
541
  interface TokenTransferPayload {
@@ -632,6 +657,24 @@ interface PeerInfo {
632
657
  /** Event timestamp */
633
658
  timestamp: number;
634
659
  }
660
+ interface IncomingReadReceipt {
661
+ /** Transport-specific pubkey of the sender who read the message */
662
+ senderTransportPubkey: string;
663
+ /** Event ID of the message that was read */
664
+ messageEventId: string;
665
+ /** Timestamp */
666
+ timestamp: number;
667
+ }
668
+ type ReadReceiptHandler = (receipt: IncomingReadReceipt) => void;
669
+ interface IncomingTypingIndicator {
670
+ /** Transport-specific pubkey of the sender who is typing */
671
+ senderTransportPubkey: string;
672
+ /** Sender's nametag (if known) */
673
+ senderNametag?: string;
674
+ /** Timestamp */
675
+ timestamp: number;
676
+ }
677
+ type TypingIndicatorHandler = (indicator: IncomingTypingIndicator) => void;
635
678
 
636
679
  /**
637
680
  * WebSocket Abstraction
@@ -724,6 +767,8 @@ declare class NostrTransportProvider implements TransportProvider {
724
767
  private transferHandlers;
725
768
  private paymentRequestHandlers;
726
769
  private paymentRequestResponseHandlers;
770
+ private readReceiptHandlers;
771
+ private typingIndicatorHandlers;
727
772
  private broadcastHandlers;
728
773
  private eventCallbacks;
729
774
  constructor(config: NostrTransportProviderConfig);
@@ -773,6 +818,10 @@ declare class NostrTransportProvider implements TransportProvider {
773
818
  onPaymentRequest(handler: PaymentRequestHandler): () => void;
774
819
  sendPaymentRequestResponse(recipientPubkey: string, payload: PaymentRequestResponsePayload): Promise<string>;
775
820
  onPaymentRequestResponse(handler: PaymentRequestResponseHandler): () => void;
821
+ sendReadReceipt(recipientTransportPubkey: string, messageEventId: string): Promise<void>;
822
+ onReadReceipt(handler: ReadReceiptHandler): () => void;
823
+ sendTypingIndicator(recipientTransportPubkey: string): Promise<void>;
824
+ onTypingIndicator(handler: TypingIndicatorHandler): () => void;
776
825
  /**
777
826
  * Resolve any identifier to full peer information.
778
827
  * Routes to the appropriate specific resolve method based on identifier format.
@@ -422,6 +422,27 @@ interface TransportProvider extends BaseProvider {
422
422
  * @returns Unsubscribe function
423
423
  */
424
424
  onPaymentRequestResponse?(handler: PaymentRequestResponseHandler): () => void;
425
+ /**
426
+ * Send a read receipt for a message
427
+ * @param recipientTransportPubkey - Transport pubkey of the message sender
428
+ * @param messageEventId - Event ID of the message being acknowledged
429
+ */
430
+ sendReadReceipt?(recipientTransportPubkey: string, messageEventId: string): Promise<void>;
431
+ /**
432
+ * Subscribe to incoming read receipts
433
+ * @returns Unsubscribe function
434
+ */
435
+ onReadReceipt?(handler: ReadReceiptHandler): () => void;
436
+ /**
437
+ * Send typing indicator to a recipient
438
+ * @param recipientTransportPubkey - Transport pubkey of the conversation partner
439
+ */
440
+ sendTypingIndicator?(recipientTransportPubkey: string): Promise<void>;
441
+ /**
442
+ * Subscribe to incoming typing indicators
443
+ * @returns Unsubscribe function
444
+ */
445
+ onTypingIndicator?(handler: TypingIndicatorHandler): () => void;
425
446
  /**
426
447
  * Get list of configured relay URLs
427
448
  */
@@ -511,6 +532,10 @@ interface IncomingMessage {
511
532
  content: string;
512
533
  timestamp: number;
513
534
  encrypted: boolean;
535
+ /** Set when this is a self-wrap replay (sent message recovered from relay) */
536
+ isSelfWrap?: boolean;
537
+ /** Recipient pubkey — only present on self-wrap replays */
538
+ recipientTransportPubkey?: string;
514
539
  }
515
540
  type MessageHandler = (message: IncomingMessage) => void;
516
541
  interface TokenTransferPayload {
@@ -632,6 +657,24 @@ interface PeerInfo {
632
657
  /** Event timestamp */
633
658
  timestamp: number;
634
659
  }
660
+ interface IncomingReadReceipt {
661
+ /** Transport-specific pubkey of the sender who read the message */
662
+ senderTransportPubkey: string;
663
+ /** Event ID of the message that was read */
664
+ messageEventId: string;
665
+ /** Timestamp */
666
+ timestamp: number;
667
+ }
668
+ type ReadReceiptHandler = (receipt: IncomingReadReceipt) => void;
669
+ interface IncomingTypingIndicator {
670
+ /** Transport-specific pubkey of the sender who is typing */
671
+ senderTransportPubkey: string;
672
+ /** Sender's nametag (if known) */
673
+ senderNametag?: string;
674
+ /** Timestamp */
675
+ timestamp: number;
676
+ }
677
+ type TypingIndicatorHandler = (indicator: IncomingTypingIndicator) => void;
635
678
 
636
679
  /**
637
680
  * WebSocket Abstraction
@@ -724,6 +767,8 @@ declare class NostrTransportProvider implements TransportProvider {
724
767
  private transferHandlers;
725
768
  private paymentRequestHandlers;
726
769
  private paymentRequestResponseHandlers;
770
+ private readReceiptHandlers;
771
+ private typingIndicatorHandlers;
727
772
  private broadcastHandlers;
728
773
  private eventCallbacks;
729
774
  constructor(config: NostrTransportProviderConfig);
@@ -773,6 +818,10 @@ declare class NostrTransportProvider implements TransportProvider {
773
818
  onPaymentRequest(handler: PaymentRequestHandler): () => void;
774
819
  sendPaymentRequestResponse(recipientPubkey: string, payload: PaymentRequestResponsePayload): Promise<string>;
775
820
  onPaymentRequestResponse(handler: PaymentRequestResponseHandler): () => void;
821
+ sendReadReceipt(recipientTransportPubkey: string, messageEventId: string): Promise<void>;
822
+ onReadReceipt(handler: ReadReceiptHandler): () => void;
823
+ sendTypingIndicator(recipientTransportPubkey: string): Promise<void>;
824
+ onTypingIndicator(handler: TypingIndicatorHandler): () => void;
776
825
  /**
777
826
  * Resolve any identifier to full peer information.
778
827
  * Routes to the appropriate specific resolve method based on identifier format.
@@ -941,7 +941,9 @@ import {
941
941
  EventKinds,
942
942
  hashNametag,
943
943
  NostrClient,
944
- Filter
944
+ Filter,
945
+ isChatMessage,
946
+ isReadReceipt
945
947
  } from "@unicitylabs/nostr-js-sdk";
946
948
 
947
949
  // core/crypto.ts
@@ -1159,6 +1161,8 @@ var NostrTransportProvider = class {
1159
1161
  transferHandlers = /* @__PURE__ */ new Set();
1160
1162
  paymentRequestHandlers = /* @__PURE__ */ new Set();
1161
1163
  paymentRequestResponseHandlers = /* @__PURE__ */ new Set();
1164
+ readReceiptHandlers = /* @__PURE__ */ new Set();
1165
+ typingIndicatorHandlers = /* @__PURE__ */ new Set();
1162
1166
  broadcastHandlers = /* @__PURE__ */ new Map();
1163
1167
  eventCallbacks = /* @__PURE__ */ new Set();
1164
1168
  constructor(config) {
@@ -1410,6 +1414,18 @@ var NostrTransportProvider = class {
1410
1414
  const wrappedContent = senderNametag ? JSON.stringify({ senderNametag, text: content }) : content;
1411
1415
  const giftWrap = NIP17.createGiftWrap(this.keyManager, nostrRecipient, wrappedContent);
1412
1416
  await this.publishEvent(giftWrap);
1417
+ const selfWrapContent = JSON.stringify({
1418
+ selfWrap: true,
1419
+ originalId: giftWrap.id,
1420
+ recipientPubkey,
1421
+ senderNametag,
1422
+ text: content
1423
+ });
1424
+ const selfPubkey = this.keyManager.getPublicKeyHex();
1425
+ const selfGiftWrap = NIP17.createGiftWrap(this.keyManager, selfPubkey, selfWrapContent);
1426
+ this.publishEvent(selfGiftWrap).catch((err) => {
1427
+ this.log("Self-wrap publish failed:", err);
1428
+ });
1413
1429
  this.emitEvent({
1414
1430
  type: "message:sent",
1415
1431
  timestamp: Date.now(),
@@ -1508,6 +1524,37 @@ var NostrTransportProvider = class {
1508
1524
  this.paymentRequestResponseHandlers.add(handler);
1509
1525
  return () => this.paymentRequestResponseHandlers.delete(handler);
1510
1526
  }
1527
+ // ===========================================================================
1528
+ // Read Receipts
1529
+ // ===========================================================================
1530
+ async sendReadReceipt(recipientTransportPubkey, messageEventId) {
1531
+ if (!this.keyManager) throw new Error("Not initialized");
1532
+ const nostrRecipient = recipientTransportPubkey.length === 66 ? recipientTransportPubkey.slice(2) : recipientTransportPubkey;
1533
+ const event = NIP17.createReadReceipt(this.keyManager, nostrRecipient, messageEventId);
1534
+ await this.publishEvent(event);
1535
+ this.log("Sent read receipt for:", messageEventId, "to:", nostrRecipient.slice(0, 16));
1536
+ }
1537
+ onReadReceipt(handler) {
1538
+ this.readReceiptHandlers.add(handler);
1539
+ return () => this.readReceiptHandlers.delete(handler);
1540
+ }
1541
+ // ===========================================================================
1542
+ // Typing Indicators
1543
+ // ===========================================================================
1544
+ async sendTypingIndicator(recipientTransportPubkey) {
1545
+ if (!this.keyManager) throw new Error("Not initialized");
1546
+ const nostrRecipient = recipientTransportPubkey.length === 66 ? recipientTransportPubkey.slice(2) : recipientTransportPubkey;
1547
+ const content = JSON.stringify({
1548
+ type: "typing",
1549
+ senderNametag: this.identity?.nametag
1550
+ });
1551
+ const event = NIP17.createGiftWrap(this.keyManager, nostrRecipient, content);
1552
+ await this.publishEvent(event);
1553
+ }
1554
+ onTypingIndicator(handler) {
1555
+ this.typingIndicatorHandlers.add(handler);
1556
+ return () => this.typingIndicatorHandlers.delete(handler);
1557
+ }
1511
1558
  /**
1512
1559
  * Resolve any identifier to full peer information.
1513
1560
  * Routes to the appropriate specific resolve method based on identifier format.
@@ -1961,11 +2008,74 @@ var NostrTransportProvider = class {
1961
2008
  const pm = NIP17.unwrap(event, this.keyManager);
1962
2009
  this.log("Gift wrap unwrapped, sender:", pm.senderPubkey?.slice(0, 16), "kind:", pm.kind);
1963
2010
  if (pm.senderPubkey === this.keyManager.getPublicKeyHex()) {
1964
- this.log("Skipping own message");
2011
+ try {
2012
+ const parsed = JSON.parse(pm.content);
2013
+ if (parsed?.selfWrap && parsed.recipientPubkey) {
2014
+ this.log("Self-wrap replay for recipient:", parsed.recipientPubkey?.slice(0, 16));
2015
+ const message2 = {
2016
+ id: parsed.originalId || pm.eventId,
2017
+ senderTransportPubkey: pm.senderPubkey,
2018
+ senderNametag: parsed.senderNametag,
2019
+ recipientTransportPubkey: parsed.recipientPubkey,
2020
+ content: parsed.text ?? "",
2021
+ timestamp: pm.timestamp * 1e3,
2022
+ encrypted: true,
2023
+ isSelfWrap: true
2024
+ };
2025
+ for (const handler of this.messageHandlers) {
2026
+ try {
2027
+ handler(message2);
2028
+ } catch (e) {
2029
+ this.log("Self-wrap handler error:", e);
2030
+ }
2031
+ }
2032
+ return;
2033
+ }
2034
+ } catch {
2035
+ }
2036
+ this.log("Skipping own non-self-wrap message");
1965
2037
  return;
1966
2038
  }
1967
- if (pm.kind !== EventKinds.CHAT_MESSAGE) {
1968
- this.log("Skipping non-chat message, kind:", pm.kind);
2039
+ if (isReadReceipt(pm)) {
2040
+ this.log("Read receipt from:", pm.senderPubkey?.slice(0, 16), "for:", pm.replyToEventId);
2041
+ if (pm.replyToEventId) {
2042
+ const receipt = {
2043
+ senderTransportPubkey: pm.senderPubkey,
2044
+ messageEventId: pm.replyToEventId,
2045
+ timestamp: pm.timestamp * 1e3
2046
+ };
2047
+ for (const handler of this.readReceiptHandlers) {
2048
+ try {
2049
+ handler(receipt);
2050
+ } catch (e) {
2051
+ this.log("Read receipt handler error:", e);
2052
+ }
2053
+ }
2054
+ }
2055
+ return;
2056
+ }
2057
+ try {
2058
+ const parsed = JSON.parse(pm.content);
2059
+ if (parsed?.type === "typing") {
2060
+ this.log("Typing indicator from:", pm.senderPubkey?.slice(0, 16));
2061
+ const indicator = {
2062
+ senderTransportPubkey: pm.senderPubkey,
2063
+ senderNametag: parsed.senderNametag,
2064
+ timestamp: pm.timestamp * 1e3
2065
+ };
2066
+ for (const handler of this.typingIndicatorHandlers) {
2067
+ try {
2068
+ handler(indicator);
2069
+ } catch (e) {
2070
+ this.log("Typing handler error:", e);
2071
+ }
2072
+ }
2073
+ return;
2074
+ }
2075
+ } catch {
2076
+ }
2077
+ if (!isChatMessage(pm)) {
2078
+ this.log("Skipping unknown message kind:", pm.kind);
1969
2079
  return;
1970
2080
  }
1971
2081
  let content = pm.content;
@@ -1980,7 +2090,9 @@ var NostrTransportProvider = class {
1980
2090
  }
1981
2091
  this.log("DM received from:", senderNametag || pm.senderPubkey?.slice(0, 16), "content:", content?.slice(0, 50));
1982
2092
  const message = {
1983
- id: pm.eventId,
2093
+ // Use outer gift wrap event.id so it matches the sender's stored giftWrap.id.
2094
+ // This ensures read receipts reference an ID the sender recognizes.
2095
+ id: event.id,
1984
2096
  senderTransportPubkey: pm.senderPubkey,
1985
2097
  senderNametag,
1986
2098
  content,