@unicitylabs/sphere-sdk 0.3.9 → 0.4.0

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.
@@ -597,6 +597,52 @@ function createView(arr) {
597
597
  function rotr(word, shift) {
598
598
  return word << 32 - shift | word >>> shift;
599
599
  }
600
+ var hasHexBuiltin = /* @__PURE__ */ (() => (
601
+ // @ts-ignore
602
+ typeof Uint8Array.from([]).toHex === "function" && typeof Uint8Array.fromHex === "function"
603
+ ))();
604
+ var hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0"));
605
+ function bytesToHex(bytes) {
606
+ abytes(bytes);
607
+ if (hasHexBuiltin)
608
+ return bytes.toHex();
609
+ let hex = "";
610
+ for (let i = 0; i < bytes.length; i++) {
611
+ hex += hexes[bytes[i]];
612
+ }
613
+ return hex;
614
+ }
615
+ var asciis = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 };
616
+ function asciiToBase16(ch) {
617
+ if (ch >= asciis._0 && ch <= asciis._9)
618
+ return ch - asciis._0;
619
+ if (ch >= asciis.A && ch <= asciis.F)
620
+ return ch - (asciis.A - 10);
621
+ if (ch >= asciis.a && ch <= asciis.f)
622
+ return ch - (asciis.a - 10);
623
+ return;
624
+ }
625
+ function hexToBytes(hex) {
626
+ if (typeof hex !== "string")
627
+ throw new Error("hex string expected, got " + typeof hex);
628
+ if (hasHexBuiltin)
629
+ return Uint8Array.fromHex(hex);
630
+ const hl = hex.length;
631
+ const al = hl / 2;
632
+ if (hl % 2)
633
+ throw new Error("hex string expected, got unpadded hex of length " + hl);
634
+ const array = new Uint8Array(al);
635
+ for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {
636
+ const n1 = asciiToBase16(hex.charCodeAt(hi));
637
+ const n2 = asciiToBase16(hex.charCodeAt(hi + 1));
638
+ if (n1 === void 0 || n2 === void 0) {
639
+ const char = hex[hi] + hex[hi + 1];
640
+ throw new Error('hex string expected, got non-hex character "' + char + '" at index ' + hi);
641
+ }
642
+ array[ai] = n1 * 16 + n2;
643
+ }
644
+ return array;
645
+ }
600
646
  function createHasher(hashCons, info = {}) {
601
647
  const hashC = (msg, opts) => hashCons(opts).update(msg).digest();
602
648
  const tmp = hashCons(void 0);
@@ -1092,7 +1138,7 @@ function publicKeyToAddress(publicKey, prefix = "alpha", witnessVersion = 0) {
1092
1138
  const programBytes = hash160ToBytes(pubKeyHash);
1093
1139
  return encodeBech32(prefix, witnessVersion, programBytes);
1094
1140
  }
1095
- function hexToBytes(hex) {
1141
+ function hexToBytes2(hex) {
1096
1142
  const matches = hex.match(/../g);
1097
1143
  if (!matches) {
1098
1144
  return new Uint8Array(0);
@@ -1119,6 +1165,8 @@ function defaultUUIDGenerator() {
1119
1165
  }
1120
1166
 
1121
1167
  // transport/NostrTransportProvider.ts
1168
+ var COMPOSING_INDICATOR_KIND = 25050;
1169
+ var TIMESTAMP_RANDOMIZATION = 2 * 24 * 60 * 60;
1122
1170
  var EVENT_KINDS = NOSTR_EVENT_KINDS;
1123
1171
  function hashAddressForTag(address) {
1124
1172
  const bytes = new TextEncoder().encode("unicity:address:" + address);
@@ -1201,6 +1249,8 @@ var NostrTransportProvider = class {
1201
1249
  paymentRequestResponseHandlers = /* @__PURE__ */ new Set();
1202
1250
  readReceiptHandlers = /* @__PURE__ */ new Set();
1203
1251
  typingIndicatorHandlers = /* @__PURE__ */ new Set();
1252
+ composingHandlers = /* @__PURE__ */ new Set();
1253
+ pendingMessages = [];
1204
1254
  broadcastHandlers = /* @__PURE__ */ new Map();
1205
1255
  eventCallbacks = /* @__PURE__ */ new Set();
1206
1256
  constructor(config) {
@@ -1473,6 +1523,18 @@ var NostrTransportProvider = class {
1473
1523
  }
1474
1524
  onMessage(handler) {
1475
1525
  this.messageHandlers.add(handler);
1526
+ if (this.pendingMessages.length > 0) {
1527
+ const pending = this.pendingMessages;
1528
+ this.pendingMessages = [];
1529
+ this.log("Flushing", pending.length, "buffered messages to new handler");
1530
+ for (const message of pending) {
1531
+ try {
1532
+ handler(message);
1533
+ } catch (error) {
1534
+ this.log("Message handler error (buffered):", error);
1535
+ }
1536
+ }
1537
+ }
1476
1538
  return () => this.messageHandlers.delete(handler);
1477
1539
  }
1478
1540
  async sendTokenTransfer(recipientPubkey, payload) {
@@ -1593,6 +1655,19 @@ var NostrTransportProvider = class {
1593
1655
  this.typingIndicatorHandlers.add(handler);
1594
1656
  return () => this.typingIndicatorHandlers.delete(handler);
1595
1657
  }
1658
+ // ===========================================================================
1659
+ // Composing Indicators (NIP-59 kind 25050)
1660
+ // ===========================================================================
1661
+ onComposing(handler) {
1662
+ this.composingHandlers.add(handler);
1663
+ return () => this.composingHandlers.delete(handler);
1664
+ }
1665
+ async sendComposingIndicator(recipientPubkey, content) {
1666
+ this.ensureReady();
1667
+ const nostrRecipient = recipientPubkey.length === 66 && (recipientPubkey.startsWith("02") || recipientPubkey.startsWith("03")) ? recipientPubkey.slice(2) : recipientPubkey;
1668
+ const giftWrap = this.createCustomKindGiftWrap(nostrRecipient, content, COMPOSING_INDICATOR_KIND);
1669
+ await this.publishEvent(giftWrap);
1670
+ }
1596
1671
  /**
1597
1672
  * Resolve any identifier to full peer information.
1598
1673
  * Routes to the appropriate specific resolve method based on identifier format.
@@ -2092,6 +2167,30 @@ var NostrTransportProvider = class {
2092
2167
  }
2093
2168
  return;
2094
2169
  }
2170
+ if (pm.kind === COMPOSING_INDICATOR_KIND) {
2171
+ let senderNametag2;
2172
+ let expiresIn = 3e4;
2173
+ try {
2174
+ const parsed = JSON.parse(pm.content);
2175
+ senderNametag2 = parsed.senderNametag || void 0;
2176
+ expiresIn = parsed.expiresIn ?? 3e4;
2177
+ } catch {
2178
+ }
2179
+ const indicator = {
2180
+ senderPubkey: pm.senderPubkey,
2181
+ senderNametag: senderNametag2,
2182
+ expiresIn
2183
+ };
2184
+ this.log("Composing indicator from:", indicator.senderNametag || pm.senderPubkey?.slice(0, 16));
2185
+ for (const handler of this.composingHandlers) {
2186
+ try {
2187
+ handler(indicator);
2188
+ } catch (e) {
2189
+ this.log("Composing handler error:", e);
2190
+ }
2191
+ }
2192
+ return;
2193
+ }
2095
2194
  try {
2096
2195
  const parsed = JSON.parse(pm.content);
2097
2196
  if (parsed?.type === "typing") {
@@ -2138,12 +2237,17 @@ var NostrTransportProvider = class {
2138
2237
  encrypted: true
2139
2238
  };
2140
2239
  this.emitEvent({ type: "message:received", timestamp: Date.now() });
2141
- this.log("Dispatching to", this.messageHandlers.size, "handlers");
2142
- for (const handler of this.messageHandlers) {
2143
- try {
2144
- handler(message);
2145
- } catch (error) {
2146
- this.log("Message handler error:", error);
2240
+ if (this.messageHandlers.size === 0) {
2241
+ this.log("No message handlers registered, buffering message for later delivery");
2242
+ this.pendingMessages.push(message);
2243
+ } else {
2244
+ this.log("Dispatching to", this.messageHandlers.size, "handlers");
2245
+ for (const handler of this.messageHandlers) {
2246
+ try {
2247
+ handler(message);
2248
+ } catch (error) {
2249
+ this.log("Message handler error:", error);
2250
+ }
2147
2251
  }
2148
2252
  }
2149
2253
  } catch (err) {
@@ -2547,6 +2651,39 @@ var NostrTransportProvider = class {
2547
2651
  }
2548
2652
  }
2549
2653
  }
2654
+ /**
2655
+ * Create a NIP-17 gift wrap with a custom inner rumor kind.
2656
+ * Replicates the three-layer NIP-59 envelope (rumor → seal → gift wrap)
2657
+ * because NIP17.createGiftWrap hardcodes kind 14 for the inner rumor.
2658
+ */
2659
+ createCustomKindGiftWrap(recipientPubkeyHex, content, rumorKind) {
2660
+ const senderPubkey = this.keyManager.getPublicKeyHex();
2661
+ const now = Math.floor(Date.now() / 1e3);
2662
+ const rumorTags = [["p", recipientPubkeyHex]];
2663
+ const rumorSerialized = JSON.stringify([0, senderPubkey, now, rumorKind, rumorTags, content]);
2664
+ const rumorId = bytesToHex(sha256(new TextEncoder().encode(rumorSerialized)));
2665
+ const rumor = { id: rumorId, pubkey: senderPubkey, created_at: now, kind: rumorKind, tags: rumorTags, content };
2666
+ const recipientPubkeyBytes = hexToBytes(recipientPubkeyHex);
2667
+ const encryptedRumor = import_nostr_js_sdk.NIP44.encrypt(JSON.stringify(rumor), this.keyManager.getPrivateKey(), recipientPubkeyBytes);
2668
+ const sealTimestamp = now + Math.floor(Math.random() * 2 * TIMESTAMP_RANDOMIZATION) - TIMESTAMP_RANDOMIZATION;
2669
+ const seal = import_nostr_js_sdk.Event.create(this.keyManager, {
2670
+ kind: import_nostr_js_sdk.EventKinds.SEAL,
2671
+ tags: [],
2672
+ content: encryptedRumor,
2673
+ created_at: sealTimestamp
2674
+ });
2675
+ const ephemeralKeys = import_nostr_js_sdk.NostrKeyManager.generate();
2676
+ const encryptedSeal = import_nostr_js_sdk.NIP44.encrypt(JSON.stringify(seal.toJSON()), ephemeralKeys.getPrivateKey(), recipientPubkeyBytes);
2677
+ const wrapTimestamp = now + Math.floor(Math.random() * 2 * TIMESTAMP_RANDOMIZATION) - TIMESTAMP_RANDOMIZATION;
2678
+ const giftWrap = import_nostr_js_sdk.Event.create(ephemeralKeys, {
2679
+ kind: import_nostr_js_sdk.EventKinds.GIFT_WRAP,
2680
+ tags: [["p", recipientPubkeyHex]],
2681
+ content: encryptedSeal,
2682
+ created_at: wrapTimestamp
2683
+ });
2684
+ ephemeralKeys.clear();
2685
+ return giftWrap;
2686
+ }
2550
2687
  log(...args) {
2551
2688
  if (this.config.debug) {
2552
2689
  console.log("[NostrTransportProvider]", ...args);
@@ -3123,7 +3260,7 @@ async function loadLibp2pModules() {
3123
3260
  };
3124
3261
  }
3125
3262
  function deriveEd25519KeyMaterial(privateKeyHex, info = IPNS_HKDF_INFO) {
3126
- const walletSecret = hexToBytes(privateKeyHex);
3263
+ const walletSecret = hexToBytes2(privateKeyHex);
3127
3264
  const infoBytes = new TextEncoder().encode(info);
3128
3265
  return hkdf(sha256, walletSecret, void 0, infoBytes, 32);
3129
3266
  }
@@ -5608,6 +5745,11 @@ function resolveGroupChatConfig(network, config) {
5608
5745
  relays: config.relays ?? [...netConfig.groupRelays]
5609
5746
  };
5610
5747
  }
5748
+ function resolveMarketConfig(config) {
5749
+ if (!config) return void 0;
5750
+ if (config === true) return {};
5751
+ return { apiUrl: config.apiUrl, timeout: config.timeout };
5752
+ }
5611
5753
 
5612
5754
  // impl/nodejs/index.ts
5613
5755
  function createNodeProviders(config) {
@@ -5623,11 +5765,13 @@ function createNodeProviders(config) {
5623
5765
  const ipfsSync = config?.tokenSync?.ipfs;
5624
5766
  const ipfsTokenStorage = ipfsSync?.enabled ? createNodeIpfsStorageProvider(ipfsSync.config, storage) : void 0;
5625
5767
  const groupChat = resolveGroupChatConfig(network, config?.groupChat);
5768
+ const market = resolveMarketConfig(config?.market);
5626
5769
  const networkConfig = getNetworkConfig(network);
5627
5770
  TokenRegistry.configure({ remoteUrl: networkConfig.tokenRegistryUrl, storage });
5628
5771
  return {
5629
5772
  storage,
5630
5773
  groupChat,
5774
+ market,
5631
5775
  tokenStorage: createFileTokenStorageProvider({
5632
5776
  tokensDir: config?.tokensDir ?? "./sphere-tokens"
5633
5777
  }),