@unicitylabs/sphere-sdk 0.2.5 → 0.3.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.
@@ -1,6 +1,73 @@
1
1
  import { Token as Token$1 } from '@unicitylabs/state-transition-sdk/lib/token/Token';
2
2
  import elliptic from 'elliptic';
3
3
 
4
+ /**
5
+ * Group Chat Types (NIP-29)
6
+ * Plain interfaces for SDK consumers — no classes, no UI helpers.
7
+ */
8
+ declare const GroupRole: {
9
+ readonly ADMIN: "ADMIN";
10
+ readonly MODERATOR: "MODERATOR";
11
+ readonly MEMBER: "MEMBER";
12
+ };
13
+ type GroupRole = (typeof GroupRole)[keyof typeof GroupRole];
14
+ declare const GroupVisibility: {
15
+ readonly PUBLIC: "PUBLIC";
16
+ readonly PRIVATE: "PRIVATE";
17
+ };
18
+ type GroupVisibility = (typeof GroupVisibility)[keyof typeof GroupVisibility];
19
+ interface GroupData {
20
+ id: string;
21
+ relayUrl: string;
22
+ name: string;
23
+ description?: string;
24
+ picture?: string;
25
+ visibility: GroupVisibility;
26
+ createdAt: number;
27
+ updatedAt?: number;
28
+ memberCount?: number;
29
+ unreadCount?: number;
30
+ lastMessageTime?: number;
31
+ lastMessageText?: string;
32
+ /** When the current user joined this group locally (used to filter old events) */
33
+ localJoinedAt?: number;
34
+ }
35
+ interface GroupMessageData {
36
+ id?: string;
37
+ groupId: string;
38
+ content: string;
39
+ timestamp: number;
40
+ senderPubkey: string;
41
+ senderNametag?: string;
42
+ replyToId?: string;
43
+ previousIds?: string[];
44
+ }
45
+ interface GroupMemberData {
46
+ pubkey: string;
47
+ groupId: string;
48
+ role: GroupRole;
49
+ nametag?: string;
50
+ joinedAt: number;
51
+ }
52
+ interface GroupChatModuleConfig {
53
+ /** Override relay URLs (default: from network config) */
54
+ relays?: string[];
55
+ /** Default message fetch limit (default: 50) */
56
+ defaultMessageLimit?: number;
57
+ /** Max previous message IDs in ordering tags (default: 3) */
58
+ maxPreviousTags?: number;
59
+ /** Reconnect delay in ms (default: 3000) */
60
+ reconnectDelayMs?: number;
61
+ /** Max reconnect attempts (default: 5) */
62
+ maxReconnectAttempts?: number;
63
+ }
64
+ interface CreateGroupOptions {
65
+ name: string;
66
+ description?: string;
67
+ picture?: string;
68
+ visibility?: GroupVisibility;
69
+ }
70
+
4
71
  /**
5
72
  * Cryptographic utilities for SDK2
6
73
  *
@@ -618,7 +685,7 @@ interface TrackedAddress extends TrackedAddressEntry {
618
685
  /** Primary nametag (from nametag cache, without @ prefix) */
619
686
  readonly nametag?: string;
620
687
  }
621
- 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';
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';
622
689
  interface SphereEventMap {
623
690
  'transfer:incoming': IncomingTransfer;
624
691
  'transfer:confirmed': TransferResult;
@@ -685,6 +752,26 @@ interface SphereEventMap {
685
752
  added: number;
686
753
  removed: number;
687
754
  };
755
+ 'groupchat:message': GroupMessageData;
756
+ 'groupchat:joined': {
757
+ groupId: string;
758
+ groupName: string;
759
+ };
760
+ 'groupchat:left': {
761
+ groupId: string;
762
+ };
763
+ 'groupchat:kicked': {
764
+ groupId: string;
765
+ groupName: string;
766
+ };
767
+ 'groupchat:group_deleted': {
768
+ groupId: string;
769
+ groupName: string;
770
+ };
771
+ 'groupchat:updated': Record<string, never>;
772
+ 'groupchat:connection': {
773
+ connected: boolean;
774
+ };
688
775
  }
689
776
  type SphereEventHandler<T extends SphereEventType> = (data: SphereEventMap[T]) => void;
690
777
  /**
@@ -1326,22 +1413,6 @@ interface TokenStorageProvider<TData = unknown> extends BaseProvider {
1326
1413
  * Subscribe to storage events
1327
1414
  */
1328
1415
  onEvent?(callback: StorageEventCallback): () => void;
1329
- /**
1330
- * Save individual token (for file-based storage like lottery pattern)
1331
- */
1332
- saveToken?(tokenId: string, tokenData: unknown): Promise<void>;
1333
- /**
1334
- * Get individual token
1335
- */
1336
- getToken?(tokenId: string): Promise<unknown | null>;
1337
- /**
1338
- * List all token IDs
1339
- */
1340
- listTokenIds?(): Promise<string[]>;
1341
- /**
1342
- * Delete individual token
1343
- */
1344
- deleteToken?(tokenId: string): Promise<void>;
1345
1416
  }
1346
1417
  type StorageEventType = 'storage:saving' | 'storage:saved' | 'storage:loading' | 'storage:loaded' | 'storage:error' | 'storage:remote-updated' | 'sync:started' | 'sync:completed' | 'sync:conflict' | 'sync:error';
1347
1418
  interface StorageEvent {
@@ -1667,7 +1738,7 @@ declare class PaymentsModule {
1667
1738
  private archivedTokens;
1668
1739
  private forkedTokens;
1669
1740
  private transactionHistory;
1670
- private nametag;
1741
+ private nametags;
1671
1742
  private paymentRequests;
1672
1743
  private paymentRequestHandlers;
1673
1744
  private outgoingPaymentRequests;
@@ -2025,17 +2096,6 @@ declare class PaymentsModule {
2025
2096
  * @returns `true` if the token was added, `false` if rejected as duplicate or tombstoned.
2026
2097
  */
2027
2098
  addToken(token: Token, skipHistory?: boolean): Promise<boolean>;
2028
- /**
2029
- * Save token as individual file to token storage providers
2030
- * Similar to lottery's saveReceivedToken() pattern
2031
- */
2032
- private saveTokenToFileStorage;
2033
- /**
2034
- * Load tokens from file storage providers (lottery compatibility)
2035
- * This loads tokens from file-based storage that may have been saved
2036
- * by other applications using the same storage directory.
2037
- */
2038
- private loadTokensFromFileStorage;
2039
2099
  /**
2040
2100
  * Update an existing token or add it if not found.
2041
2101
  *
@@ -2057,11 +2117,6 @@ declare class PaymentsModule {
2057
2117
  * @param skipHistory - When `true`, skip creating a transaction history entry (default `false`).
2058
2118
  */
2059
2119
  removeToken(tokenId: string, recipientNametag?: string, skipHistory?: boolean): Promise<void>;
2060
- /**
2061
- * Delete physical token file(s) from all storage providers.
2062
- * Finds files by matching the SDK token ID prefix in the filename.
2063
- */
2064
- private deleteTokenFiles;
2065
2120
  /**
2066
2121
  * Get all tombstone entries.
2067
2122
  *
@@ -2189,11 +2244,17 @@ declare class PaymentsModule {
2189
2244
  */
2190
2245
  setNametag(nametag: NametagData): Promise<void>;
2191
2246
  /**
2192
- * Get the current nametag data.
2247
+ * Get the current (first) nametag data.
2193
2248
  *
2194
2249
  * @returns The nametag data, or `null` if no nametag is set.
2195
2250
  */
2196
2251
  getNametag(): NametagData | null;
2252
+ /**
2253
+ * Get all nametag data entries.
2254
+ *
2255
+ * @returns A copy of the nametags array.
2256
+ */
2257
+ getNametags(): NametagData[];
2197
2258
  /**
2198
2259
  * Check whether a nametag is currently set.
2199
2260
  *
@@ -2201,19 +2262,9 @@ declare class PaymentsModule {
2201
2262
  */
2202
2263
  hasNametag(): boolean;
2203
2264
  /**
2204
- * Remove the current nametag data from memory and storage.
2265
+ * Remove all nametag data from memory and storage.
2205
2266
  */
2206
2267
  clearNametag(): Promise<void>;
2207
- /**
2208
- * Save nametag to file storage for lottery compatibility
2209
- * Creates file: nametag-{name}.json
2210
- */
2211
- private saveNametagToFileStorage;
2212
- /**
2213
- * Load nametag from file storage (lottery compatibility)
2214
- * Looks for file: nametag-{name}.json
2215
- */
2216
- private loadNametagFromFileStorage;
2217
2268
  /**
2218
2269
  * Mint a nametag token on-chain (like Sphere wallet and lottery)
2219
2270
  * This creates the nametag token required for receiving tokens via PROXY addresses
@@ -2446,6 +2497,126 @@ declare class CommunicationsModule {
2446
2497
  private ensureInitialized;
2447
2498
  }
2448
2499
 
2500
+ /**
2501
+ * Group Chat Module (NIP-29)
2502
+ *
2503
+ * Relay-based group chat using NIP-29 protocol on a dedicated Nostr relay.
2504
+ * Embeds its own NostrClient — does NOT share the wallet's TransportProvider.
2505
+ */
2506
+
2507
+ interface GroupChatModuleDependencies {
2508
+ identity: FullIdentity;
2509
+ storage: StorageProvider;
2510
+ emitEvent: <T extends SphereEventType>(type: T, data: SphereEventMap[T]) => void;
2511
+ }
2512
+ declare class GroupChatModule {
2513
+ private config;
2514
+ private deps;
2515
+ private client;
2516
+ private keyManager;
2517
+ private connected;
2518
+ private connecting;
2519
+ private connectPromise;
2520
+ private reconnectAttempts;
2521
+ private reconnectTimer;
2522
+ private subscriptionIds;
2523
+ private groups;
2524
+ private messages;
2525
+ private members;
2526
+ private processedEventIds;
2527
+ private pendingLeaves;
2528
+ private persistTimer;
2529
+ private persistPromise;
2530
+ private relayAdminPubkeys;
2531
+ private relayAdminFetchPromise;
2532
+ private messageHandlers;
2533
+ constructor(config?: GroupChatModuleConfig);
2534
+ initialize(deps: GroupChatModuleDependencies): void;
2535
+ load(): Promise<void>;
2536
+ destroy(): void;
2537
+ private destroyConnection;
2538
+ connect(): Promise<void>;
2539
+ getConnectionStatus(): boolean;
2540
+ private doConnect;
2541
+ private scheduleReconnect;
2542
+ private subscribeToJoinedGroups;
2543
+ private subscribeToGroup;
2544
+ private handleGroupEvent;
2545
+ private handleMetadataEvent;
2546
+ private handleModerationEvent;
2547
+ private updateMembersFromEvent;
2548
+ private updateAdminsFromEvent;
2549
+ private restoreJoinedGroups;
2550
+ fetchAvailableGroups(): Promise<GroupData[]>;
2551
+ joinGroup(groupId: string, inviteCode?: string): Promise<boolean>;
2552
+ leaveGroup(groupId: string): Promise<boolean>;
2553
+ createGroup(options: CreateGroupOptions): Promise<GroupData | null>;
2554
+ deleteGroup(groupId: string): Promise<boolean>;
2555
+ createInvite(groupId: string): Promise<string | null>;
2556
+ sendMessage(groupId: string, content: string, replyToId?: string): Promise<GroupMessageData | null>;
2557
+ fetchMessages(groupId: string, since?: number, limit?: number): Promise<GroupMessageData[]>;
2558
+ getGroups(): GroupData[];
2559
+ getGroup(groupId: string): GroupData | null;
2560
+ getMessages(groupId: string): GroupMessageData[];
2561
+ getMembers(groupId: string): GroupMemberData[];
2562
+ getMember(groupId: string, pubkey: string): GroupMemberData | null;
2563
+ getTotalUnreadCount(): number;
2564
+ markGroupAsRead(groupId: string): void;
2565
+ kickUser(groupId: string, userPubkey: string, reason?: string): Promise<boolean>;
2566
+ deleteMessage(groupId: string, messageId: string): Promise<boolean>;
2567
+ isCurrentUserAdmin(groupId: string): boolean;
2568
+ isCurrentUserModerator(groupId: string): boolean;
2569
+ /**
2570
+ * Check if current user can moderate a group:
2571
+ * - Group admin/moderator can always moderate their group
2572
+ * - Relay admins can moderate public groups
2573
+ */
2574
+ canModerateGroup(groupId: string): Promise<boolean>;
2575
+ isCurrentUserRelayAdmin(): Promise<boolean>;
2576
+ getCurrentUserRole(groupId: string): GroupRole | null;
2577
+ onMessage(handler: (message: GroupMessageData) => void): () => void;
2578
+ getRelayUrls(): string[];
2579
+ getMyPublicKey(): string | null;
2580
+ private fetchRelayAdmins;
2581
+ private doFetchRelayAdmins;
2582
+ private fetchGroupMetadataInternal;
2583
+ private fetchAndSaveMembers;
2584
+ private fetchGroupMembersInternal;
2585
+ private fetchGroupAdminsInternal;
2586
+ private saveMessageToMemory;
2587
+ private deleteMessageFromMemory;
2588
+ private saveMemberToMemory;
2589
+ private removeMemberFromMemory;
2590
+ private removeGroupFromMemory;
2591
+ private updateGroupLastMessage;
2592
+ private updateMemberNametag;
2593
+ private addProcessedEventId;
2594
+ /** Schedule a debounced persist (coalesces rapid event bursts). */
2595
+ private schedulePersist;
2596
+ /** Persist immediately (for explicit flush points). */
2597
+ private persistAll;
2598
+ private doPersistAll;
2599
+ private persistGroups;
2600
+ private persistMessages;
2601
+ private persistMembers;
2602
+ private persistProcessedEvents;
2603
+ private checkAndClearOnRelayChange;
2604
+ private wrapMessageContent;
2605
+ private unwrapMessageContent;
2606
+ private getGroupIdFromEvent;
2607
+ private getGroupIdFromMetadataEvent;
2608
+ private extractReplyTo;
2609
+ private extractPreviousIds;
2610
+ private parseGroupMetadata;
2611
+ /** Subscribe and track the subscription ID for cleanup. */
2612
+ private trackSubscription;
2613
+ /** Subscribe for a one-shot fetch, auto-unsubscribe on EOSE or timeout. */
2614
+ private oneshotSubscription;
2615
+ private ensureInitialized;
2616
+ private ensureConnected;
2617
+ private randomId;
2618
+ }
2619
+
2449
2620
  /** Network configurations */
2450
2621
  declare const NETWORKS: {
2451
2622
  readonly mainnet: {
@@ -2454,6 +2625,7 @@ declare const NETWORKS: {
2454
2625
  readonly nostrRelays: readonly ["wss://relay.unicity.network", "wss://relay.damus.io", "wss://nos.lol", "wss://relay.nostr.band"];
2455
2626
  readonly ipfsGateways: readonly ["https://ipfs.unicity.network", "https://dweb.link", "https://ipfs.io"];
2456
2627
  readonly electrumUrl: "wss://fulcrum.alpha.unicity.network:50004";
2628
+ readonly groupRelays: readonly ["wss://sphere-relay.unicity.network"];
2457
2629
  };
2458
2630
  readonly testnet: {
2459
2631
  readonly name: "Testnet";
@@ -2461,6 +2633,7 @@ declare const NETWORKS: {
2461
2633
  readonly nostrRelays: readonly ["wss://nostr-relay.testnet.unicity.network"];
2462
2634
  readonly ipfsGateways: readonly ["https://ipfs.unicity.network", "https://dweb.link", "https://ipfs.io"];
2463
2635
  readonly electrumUrl: "wss://fulcrum.alpha.testnet.unicity.network:50004";
2636
+ readonly groupRelays: readonly ["wss://sphere-relay.unicity.network"];
2464
2637
  };
2465
2638
  readonly dev: {
2466
2639
  readonly name: "Development";
@@ -2468,6 +2641,7 @@ declare const NETWORKS: {
2468
2641
  readonly nostrRelays: readonly ["wss://nostr-relay.testnet.unicity.network"];
2469
2642
  readonly ipfsGateways: readonly ["https://ipfs.unicity.network", "https://dweb.link", "https://ipfs.io"];
2470
2643
  readonly electrumUrl: "wss://fulcrum.alpha.testnet.unicity.network:50004";
2644
+ readonly groupRelays: readonly ["wss://sphere-relay.unicity.network"];
2471
2645
  };
2472
2646
  };
2473
2647
  type NetworkType = keyof typeof NETWORKS;
@@ -2581,6 +2755,8 @@ interface SphereCreateOptions {
2581
2755
  * Use createBrowserProviders({ network: 'testnet' }) to set up testnet providers.
2582
2756
  */
2583
2757
  network?: NetworkType;
2758
+ /** Group chat configuration (NIP-29). Omit to disable groupchat. */
2759
+ groupChat?: GroupChatModuleConfig | boolean;
2584
2760
  }
2585
2761
  /** Options for loading existing wallet */
2586
2762
  interface SphereLoadOptions {
@@ -2602,6 +2778,8 @@ interface SphereLoadOptions {
2602
2778
  * Use createBrowserProviders({ network: 'testnet' }) to set up testnet providers.
2603
2779
  */
2604
2780
  network?: NetworkType;
2781
+ /** Group chat configuration (NIP-29). Omit to disable groupchat. */
2782
+ groupChat?: GroupChatModuleConfig | boolean;
2605
2783
  }
2606
2784
  /** Options for importing a wallet */
2607
2785
  interface SphereImportOptions {
@@ -2631,6 +2809,8 @@ interface SphereImportOptions {
2631
2809
  l1?: L1Config;
2632
2810
  /** Optional price provider for fiat conversion */
2633
2811
  price?: PriceProvider;
2812
+ /** Group chat configuration (NIP-29). Omit to disable groupchat. */
2813
+ groupChat?: GroupChatModuleConfig | boolean;
2634
2814
  }
2635
2815
  /** L1 (ALPHA blockchain) configuration */
2636
2816
  interface L1Config {
@@ -2669,6 +2849,13 @@ interface SphereInitOptions {
2669
2849
  * Use createBrowserProviders({ network: 'testnet' }) to set up testnet providers.
2670
2850
  */
2671
2851
  network?: NetworkType;
2852
+ /**
2853
+ * Group chat configuration (NIP-29).
2854
+ * - `true`: Enable with network-default relays
2855
+ * - `GroupChatModuleConfig`: Enable with custom config
2856
+ * - Omit/undefined: No groupchat module
2857
+ */
2858
+ groupChat?: GroupChatModuleConfig | boolean;
2672
2859
  }
2673
2860
  /** Result of init operation */
2674
2861
  interface SphereInitResult {
@@ -2704,6 +2891,7 @@ declare class Sphere {
2704
2891
  private _priceProvider;
2705
2892
  private _payments;
2706
2893
  private _communications;
2894
+ private _groupChat;
2707
2895
  private eventHandlers;
2708
2896
  private constructor();
2709
2897
  /**
@@ -2736,6 +2924,18 @@ declare class Sphere {
2736
2924
  * ```
2737
2925
  */
2738
2926
  static init(options: SphereInitOptions): Promise<SphereInitResult>;
2927
+ /**
2928
+ * Resolve groupChat config from init/create/load options.
2929
+ * - `true` → use network-default relays
2930
+ * - `GroupChatModuleConfig` → pass through
2931
+ * - `undefined` → no groupchat
2932
+ */
2933
+ /**
2934
+ * Resolve GroupChat config from Sphere.init() options.
2935
+ * Note: impl/shared/resolvers.ts has a similar resolver for provider-level config
2936
+ * (different input shape: { enabled?, relays? }). Both fill relay URLs from network defaults.
2937
+ */
2938
+ private static resolveGroupChatConfig;
2739
2939
  /**
2740
2940
  * Create new wallet with mnemonic
2741
2941
  */
@@ -2792,6 +2992,8 @@ declare class Sphere {
2792
2992
  get payments(): PaymentsModule;
2793
2993
  /** Communications module */
2794
2994
  get communications(): CommunicationsModule;
2995
+ /** Group chat module (NIP-29). Null if not configured. */
2996
+ get groupChat(): GroupChatModule | null;
2795
2997
  /** Current identity (public info only) */
2796
2998
  get identity(): Identity | null;
2797
2999
  /** Is ready */