@unicitylabs/sphere-sdk 0.4.5 → 0.4.7

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.
@@ -2700,6 +2700,12 @@ declare class CommunicationsModule {
2700
2700
  * Subscribe to incoming broadcasts
2701
2701
  */
2702
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>;
2703
2709
  private handleIncomingMessage;
2704
2710
  private handleComposingIndicator;
2705
2711
  private handleIncomingBroadcast;
@@ -2794,6 +2800,12 @@ declare class GroupChatModule {
2794
2800
  onMessage(handler: (message: GroupMessageData) => void): () => void;
2795
2801
  getRelayUrls(): string[];
2796
2802
  getMyPublicKey(): string | null;
2803
+ /**
2804
+ * Returns the latest message timestamp (in Nostr seconds) across the given groups,
2805
+ * or 0 if no messages exist. Used to set `since` on subscriptions so the relay
2806
+ * only sends events we don't already have.
2807
+ */
2808
+ private getLatestMessageTimestamp;
2797
2809
  private fetchRelayAdmins;
2798
2810
  private doFetchRelayAdmins;
2799
2811
  private fetchGroupMetadataInternal;
@@ -3503,14 +3515,9 @@ declare class Sphere {
3503
3515
  * });
3504
3516
  * ```
3505
3517
  */
3506
- static importFromJSON(options: {
3518
+ static importFromJSON(options: Omit<SphereImportOptions, 'mnemonic' | 'masterKey' | 'chainCode' | 'derivationPath' | 'basePath' | 'derivationMode'> & {
3507
3519
  jsonContent: string;
3508
3520
  password?: string;
3509
- storage: StorageProvider;
3510
- transport: TransportProvider;
3511
- oracle: OracleProvider;
3512
- tokenStorage?: TokenStorageProvider<TxfStorageDataBase>;
3513
- l1?: L1Config;
3514
3521
  }): Promise<{
3515
3522
  success: boolean;
3516
3523
  mnemonic?: string;
@@ -3546,7 +3553,7 @@ declare class Sphere {
3546
3553
  * });
3547
3554
  * ```
3548
3555
  */
3549
- static importFromLegacyFile(options: {
3556
+ static importFromLegacyFile(options: Omit<SphereImportOptions, 'mnemonic' | 'masterKey' | 'chainCode' | 'derivationPath' | 'basePath' | 'derivationMode'> & {
3550
3557
  /** File content - Uint8Array for .dat, string for .txt */
3551
3558
  fileContent: string | Uint8Array;
3552
3559
  /** File name (used for type detection) */
@@ -3555,18 +3562,6 @@ declare class Sphere {
3555
3562
  password?: string;
3556
3563
  /** Progress callback for long decryption operations */
3557
3564
  onDecryptProgress?: DecryptionProgressCallback;
3558
- /** Storage provider instance */
3559
- storage: StorageProvider;
3560
- /** Transport provider instance */
3561
- transport: TransportProvider;
3562
- /** Oracle provider instance */
3563
- oracle: OracleProvider;
3564
- /** Optional token storage provider */
3565
- tokenStorage?: TokenStorageProvider<TxfStorageDataBase>;
3566
- /** Optional nametag to register */
3567
- nametag?: string;
3568
- /** L1 (ALPHA blockchain) configuration */
3569
- l1?: L1Config;
3570
3565
  }): Promise<{
3571
3566
  success: boolean;
3572
3567
  sphere?: Sphere;
@@ -2700,6 +2700,12 @@ declare class CommunicationsModule {
2700
2700
  * Subscribe to incoming broadcasts
2701
2701
  */
2702
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>;
2703
2709
  private handleIncomingMessage;
2704
2710
  private handleComposingIndicator;
2705
2711
  private handleIncomingBroadcast;
@@ -2794,6 +2800,12 @@ declare class GroupChatModule {
2794
2800
  onMessage(handler: (message: GroupMessageData) => void): () => void;
2795
2801
  getRelayUrls(): string[];
2796
2802
  getMyPublicKey(): string | null;
2803
+ /**
2804
+ * Returns the latest message timestamp (in Nostr seconds) across the given groups,
2805
+ * or 0 if no messages exist. Used to set `since` on subscriptions so the relay
2806
+ * only sends events we don't already have.
2807
+ */
2808
+ private getLatestMessageTimestamp;
2797
2809
  private fetchRelayAdmins;
2798
2810
  private doFetchRelayAdmins;
2799
2811
  private fetchGroupMetadataInternal;
@@ -3503,14 +3515,9 @@ declare class Sphere {
3503
3515
  * });
3504
3516
  * ```
3505
3517
  */
3506
- static importFromJSON(options: {
3518
+ static importFromJSON(options: Omit<SphereImportOptions, 'mnemonic' | 'masterKey' | 'chainCode' | 'derivationPath' | 'basePath' | 'derivationMode'> & {
3507
3519
  jsonContent: string;
3508
3520
  password?: string;
3509
- storage: StorageProvider;
3510
- transport: TransportProvider;
3511
- oracle: OracleProvider;
3512
- tokenStorage?: TokenStorageProvider<TxfStorageDataBase>;
3513
- l1?: L1Config;
3514
3521
  }): Promise<{
3515
3522
  success: boolean;
3516
3523
  mnemonic?: string;
@@ -3546,7 +3553,7 @@ declare class Sphere {
3546
3553
  * });
3547
3554
  * ```
3548
3555
  */
3549
- static importFromLegacyFile(options: {
3556
+ static importFromLegacyFile(options: Omit<SphereImportOptions, 'mnemonic' | 'masterKey' | 'chainCode' | 'derivationPath' | 'basePath' | 'derivationMode'> & {
3550
3557
  /** File content - Uint8Array for .dat, string for .txt */
3551
3558
  fileContent: string | Uint8Array;
3552
3559
  /** File name (used for type detection) */
@@ -3555,18 +3562,6 @@ declare class Sphere {
3555
3562
  password?: string;
3556
3563
  /** Progress callback for long decryption operations */
3557
3564
  onDecryptProgress?: DecryptionProgressCallback;
3558
- /** Storage provider instance */
3559
- storage: StorageProvider;
3560
- /** Transport provider instance */
3561
- transport: TransportProvider;
3562
- /** Oracle provider instance */
3563
- oracle: OracleProvider;
3564
- /** Optional token storage provider */
3565
- tokenStorage?: TokenStorageProvider<TxfStorageDataBase>;
3566
- /** Optional nametag to register */
3567
- nametag?: string;
3568
- /** L1 (ALPHA blockchain) configuration */
3569
- l1?: L1Config;
3570
3565
  }): Promise<{
3571
3566
  success: boolean;
3572
3567
  sphere?: Sphere;
@@ -7659,6 +7659,23 @@ var CommunicationsModule = class {
7659
7659
  return () => this.broadcastHandlers.delete(handler);
7660
7660
  }
7661
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
+ // ===========================================================================
7662
7679
  // Private: Message Handling
7663
7680
  // ===========================================================================
7664
7681
  handleIncomingMessage(msg) {
@@ -8048,10 +8065,12 @@ var GroupChatModule = class {
8048
8065
  if (!this.client) return;
8049
8066
  const groupIds = Array.from(this.groups.keys());
8050
8067
  if (groupIds.length === 0) return;
8068
+ const latestTimestamp = this.getLatestMessageTimestamp(groupIds);
8051
8069
  this.trackSubscription(
8052
8070
  createNip29Filter({
8053
8071
  kinds: [NIP29_KINDS.CHAT_MESSAGE, NIP29_KINDS.THREAD_ROOT, NIP29_KINDS.THREAD_REPLY],
8054
- "#h": groupIds
8072
+ "#h": groupIds,
8073
+ ...latestTimestamp ? { since: latestTimestamp } : {}
8055
8074
  }),
8056
8075
  { onEvent: (event) => this.handleGroupEvent(event) }
8057
8076
  );
@@ -8072,10 +8091,12 @@ var GroupChatModule = class {
8072
8091
  }
8073
8092
  subscribeToGroup(groupId) {
8074
8093
  if (!this.client) return;
8094
+ const latestTimestamp = this.getLatestMessageTimestamp([groupId]);
8075
8095
  this.trackSubscription(
8076
8096
  createNip29Filter({
8077
8097
  kinds: [NIP29_KINDS.CHAT_MESSAGE, NIP29_KINDS.THREAD_ROOT, NIP29_KINDS.THREAD_REPLY],
8078
- "#h": [groupId]
8098
+ "#h": [groupId],
8099
+ ...latestTimestamp ? { since: latestTimestamp } : {}
8079
8100
  }),
8080
8101
  { onEvent: (event) => this.handleGroupEvent(event) }
8081
8102
  );
@@ -8767,6 +8788,23 @@ var GroupChatModule = class {
8767
8788
  getMyPublicKey() {
8768
8789
  return this.keyManager?.getPublicKeyHex() || null;
8769
8790
  }
8791
+ /**
8792
+ * Returns the latest message timestamp (in Nostr seconds) across the given groups,
8793
+ * or 0 if no messages exist. Used to set `since` on subscriptions so the relay
8794
+ * only sends events we don't already have.
8795
+ */
8796
+ getLatestMessageTimestamp(groupIds) {
8797
+ let latest = 0;
8798
+ for (const gid of groupIds) {
8799
+ const msgs = this.messages.get(gid);
8800
+ if (!msgs) continue;
8801
+ for (const m of msgs) {
8802
+ const ts = Math.floor(m.timestamp / 1e3);
8803
+ if (ts > latest) latest = ts;
8804
+ }
8805
+ }
8806
+ return latest;
8807
+ }
8770
8808
  // ===========================================================================
8771
8809
  // Private — Relay Admin
8772
8810
  // ===========================================================================
@@ -13298,55 +13336,44 @@ var Sphere = class _Sphere {
13298
13336
  * ```
13299
13337
  */
13300
13338
  static async importFromJSON(options) {
13339
+ const { jsonContent, password, ...baseOptions } = options;
13301
13340
  try {
13302
- const data = JSON.parse(options.jsonContent);
13341
+ const data = JSON.parse(jsonContent);
13303
13342
  if (data.version !== "1.0" || data.type !== "sphere-wallet") {
13304
13343
  return { success: false, error: "Invalid wallet format" };
13305
13344
  }
13306
13345
  let mnemonic = data.mnemonic;
13307
13346
  let masterKey = data.wallet.masterPrivateKey;
13308
- if (data.encrypted && options.password) {
13347
+ if (data.encrypted && password) {
13309
13348
  if (mnemonic) {
13310
- const decrypted = decryptSimple(mnemonic, options.password);
13349
+ const decrypted = decryptSimple(mnemonic, password);
13311
13350
  if (!decrypted) {
13312
13351
  return { success: false, error: "Failed to decrypt mnemonic - wrong password?" };
13313
13352
  }
13314
13353
  mnemonic = decrypted;
13315
13354
  }
13316
13355
  if (masterKey) {
13317
- const decrypted = decryptSimple(masterKey, options.password);
13356
+ const decrypted = decryptSimple(masterKey, password);
13318
13357
  if (!decrypted) {
13319
13358
  return { success: false, error: "Failed to decrypt master key - wrong password?" };
13320
13359
  }
13321
13360
  masterKey = decrypted;
13322
13361
  }
13323
- } else if (data.encrypted && !options.password) {
13362
+ } else if (data.encrypted && !password) {
13324
13363
  return { success: false, error: "Password required for encrypted wallet" };
13325
13364
  }
13326
13365
  const basePath = data.wallet.descriptorPath ? `m/${data.wallet.descriptorPath}` : DEFAULT_BASE_PATH;
13327
13366
  if (mnemonic) {
13328
- await _Sphere.import({
13329
- mnemonic,
13330
- basePath,
13331
- storage: options.storage,
13332
- transport: options.transport,
13333
- oracle: options.oracle,
13334
- tokenStorage: options.tokenStorage,
13335
- l1: options.l1
13336
- });
13367
+ await _Sphere.import({ ...baseOptions, mnemonic, basePath });
13337
13368
  return { success: true, mnemonic };
13338
13369
  }
13339
13370
  if (masterKey) {
13340
13371
  await _Sphere.import({
13372
+ ...baseOptions,
13341
13373
  masterKey,
13342
13374
  chainCode: data.wallet.chainCode,
13343
13375
  basePath,
13344
- derivationMode: data.derivationMode || (data.wallet.isBIP32 ? "bip32" : "wif_hmac"),
13345
- storage: options.storage,
13346
- transport: options.transport,
13347
- oracle: options.oracle,
13348
- tokenStorage: options.tokenStorage,
13349
- l1: options.l1
13376
+ derivationMode: data.derivationMode || (data.wallet.isBIP32 ? "bip32" : "wif_hmac")
13350
13377
  });
13351
13378
  return { success: true };
13352
13379
  }
@@ -13389,7 +13416,7 @@ var Sphere = class _Sphere {
13389
13416
  * ```
13390
13417
  */
13391
13418
  static async importFromLegacyFile(options) {
13392
- const { fileContent, fileName, password, onDecryptProgress } = options;
13419
+ const { fileContent, fileName, password, onDecryptProgress, ...baseOptions } = options;
13393
13420
  const fileType = _Sphere.detectLegacyFileType(fileName, fileContent);
13394
13421
  if (fileType === "unknown") {
13395
13422
  return { success: false, error: "Unknown file format" };
@@ -13399,15 +13426,7 @@ var Sphere = class _Sphere {
13399
13426
  if (!_Sphere.validateMnemonic(mnemonic)) {
13400
13427
  return { success: false, error: "Invalid mnemonic phrase" };
13401
13428
  }
13402
- const sphere = await _Sphere.import({
13403
- mnemonic,
13404
- storage: options.storage,
13405
- transport: options.transport,
13406
- oracle: options.oracle,
13407
- tokenStorage: options.tokenStorage,
13408
- nametag: options.nametag,
13409
- l1: options.l1
13410
- });
13429
+ const sphere = await _Sphere.import({ ...baseOptions, mnemonic });
13411
13430
  return { success: true, sphere, mnemonic };
13412
13431
  }
13413
13432
  if (fileType === "dat") {
@@ -13427,16 +13446,11 @@ var Sphere = class _Sphere {
13427
13446
  const { masterKey, chainCode, descriptorPath, derivationMode } = parseResult.data;
13428
13447
  const basePath = descriptorPath ? `m/${descriptorPath}` : DEFAULT_BASE_PATH;
13429
13448
  const sphere = await _Sphere.import({
13449
+ ...baseOptions,
13430
13450
  masterKey,
13431
13451
  chainCode,
13432
13452
  basePath,
13433
- derivationMode: derivationMode || (chainCode ? "bip32" : "wif_hmac"),
13434
- storage: options.storage,
13435
- transport: options.transport,
13436
- oracle: options.oracle,
13437
- tokenStorage: options.tokenStorage,
13438
- nametag: options.nametag,
13439
- l1: options.l1
13453
+ derivationMode: derivationMode || (chainCode ? "bip32" : "wif_hmac")
13440
13454
  });
13441
13455
  return { success: true, sphere };
13442
13456
  }
@@ -13459,16 +13473,11 @@ var Sphere = class _Sphere {
13459
13473
  const { masterKey, chainCode, descriptorPath, derivationMode } = parseResult.data;
13460
13474
  const basePath = descriptorPath ? `m/${descriptorPath}` : DEFAULT_BASE_PATH;
13461
13475
  const sphere = await _Sphere.import({
13476
+ ...baseOptions,
13462
13477
  masterKey,
13463
13478
  chainCode,
13464
13479
  basePath,
13465
- derivationMode: derivationMode || (chainCode ? "bip32" : "wif_hmac"),
13466
- storage: options.storage,
13467
- transport: options.transport,
13468
- oracle: options.oracle,
13469
- tokenStorage: options.tokenStorage,
13470
- nametag: options.nametag,
13471
- l1: options.l1
13480
+ derivationMode: derivationMode || (chainCode ? "bip32" : "wif_hmac")
13472
13481
  });
13473
13482
  return { success: true, sphere };
13474
13483
  }
@@ -13482,13 +13491,9 @@ var Sphere = class _Sphere {
13482
13491
  }
13483
13492
  if (parsed.type === "sphere-wallet") {
13484
13493
  const result = await _Sphere.importFromJSON({
13494
+ ...baseOptions,
13485
13495
  jsonContent: content,
13486
- password,
13487
- storage: options.storage,
13488
- transport: options.transport,
13489
- oracle: options.oracle,
13490
- tokenStorage: options.tokenStorage,
13491
- l1: options.l1
13496
+ password
13492
13497
  });
13493
13498
  if (result.success) {
13494
13499
  const sphere2 = _Sphere.getInstance();
@@ -13530,29 +13535,15 @@ var Sphere = class _Sphere {
13530
13535
  const isBIP32 = derivationMode === "bip32" || !!chainCode;
13531
13536
  const basePath = descriptorPath ? `m/${descriptorPath}` : isBIP32 ? "m/84'/1'/0'" : DEFAULT_BASE_PATH;
13532
13537
  if (mnemonic) {
13533
- const sphere2 = await _Sphere.import({
13534
- mnemonic,
13535
- basePath,
13536
- storage: options.storage,
13537
- transport: options.transport,
13538
- oracle: options.oracle,
13539
- tokenStorage: options.tokenStorage,
13540
- nametag: options.nametag,
13541
- l1: options.l1
13542
- });
13538
+ const sphere2 = await _Sphere.import({ ...baseOptions, mnemonic, basePath });
13543
13539
  return { success: true, sphere: sphere2, mnemonic };
13544
13540
  }
13545
13541
  const sphere = await _Sphere.import({
13542
+ ...baseOptions,
13546
13543
  masterKey,
13547
13544
  chainCode,
13548
13545
  basePath,
13549
- derivationMode: derivationMode || (chainCode ? "bip32" : "wif_hmac"),
13550
- storage: options.storage,
13551
- transport: options.transport,
13552
- oracle: options.oracle,
13553
- tokenStorage: options.tokenStorage,
13554
- nametag: options.nametag,
13555
- l1: options.l1
13546
+ derivationMode: derivationMode || (chainCode ? "bip32" : "wif_hmac")
13556
13547
  });
13557
13548
  return { success: true, sphere };
13558
13549
  }