@quilibrium/quorum-shared 2.1.0-1

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 (51) hide show
  1. package/dist/index.d.mts +2414 -0
  2. package/dist/index.d.ts +2414 -0
  3. package/dist/index.js +2788 -0
  4. package/dist/index.mjs +2678 -0
  5. package/package.json +49 -0
  6. package/src/api/client.ts +86 -0
  7. package/src/api/endpoints.ts +87 -0
  8. package/src/api/errors.ts +179 -0
  9. package/src/api/index.ts +35 -0
  10. package/src/crypto/encryption-state.ts +249 -0
  11. package/src/crypto/index.ts +55 -0
  12. package/src/crypto/types.ts +307 -0
  13. package/src/crypto/wasm-provider.ts +298 -0
  14. package/src/hooks/index.ts +31 -0
  15. package/src/hooks/keys.ts +62 -0
  16. package/src/hooks/mutations/index.ts +15 -0
  17. package/src/hooks/mutations/useDeleteMessage.ts +67 -0
  18. package/src/hooks/mutations/useEditMessage.ts +87 -0
  19. package/src/hooks/mutations/useReaction.ts +163 -0
  20. package/src/hooks/mutations/useSendMessage.ts +131 -0
  21. package/src/hooks/useChannels.ts +49 -0
  22. package/src/hooks/useMessages.ts +77 -0
  23. package/src/hooks/useSpaces.ts +60 -0
  24. package/src/index.ts +32 -0
  25. package/src/signing/index.ts +10 -0
  26. package/src/signing/types.ts +83 -0
  27. package/src/signing/wasm-provider.ts +75 -0
  28. package/src/storage/adapter.ts +118 -0
  29. package/src/storage/index.ts +9 -0
  30. package/src/sync/index.ts +83 -0
  31. package/src/sync/service.test.ts +822 -0
  32. package/src/sync/service.ts +947 -0
  33. package/src/sync/types.ts +267 -0
  34. package/src/sync/utils.ts +588 -0
  35. package/src/transport/browser-websocket.ts +299 -0
  36. package/src/transport/index.ts +34 -0
  37. package/src/transport/rn-websocket.ts +321 -0
  38. package/src/transport/types.ts +56 -0
  39. package/src/transport/websocket.ts +212 -0
  40. package/src/types/bookmark.ts +29 -0
  41. package/src/types/conversation.ts +25 -0
  42. package/src/types/index.ts +57 -0
  43. package/src/types/message.ts +178 -0
  44. package/src/types/space.ts +75 -0
  45. package/src/types/user.ts +72 -0
  46. package/src/utils/encoding.ts +106 -0
  47. package/src/utils/formatting.ts +139 -0
  48. package/src/utils/index.ts +9 -0
  49. package/src/utils/logger.ts +141 -0
  50. package/src/utils/mentions.ts +135 -0
  51. package/src/utils/validation.ts +84 -0
@@ -0,0 +1,2414 @@
1
+ import * as _tanstack_react_query from '@tanstack/react-query';
2
+ import * as _tanstack_query_core from '@tanstack/query-core';
3
+
4
+ /**
5
+ * Space-related types for Quorum
6
+ */
7
+ type Permission = 'message:delete' | 'message:pin' | 'mention:everyone' | 'user:mute';
8
+ type Role = {
9
+ roleId: string;
10
+ displayName: string;
11
+ roleTag: string;
12
+ color: string;
13
+ members: string[];
14
+ permissions: Permission[];
15
+ isPublic?: boolean;
16
+ };
17
+ type Emoji = {
18
+ name: string;
19
+ id: string;
20
+ imgUrl: string;
21
+ };
22
+ type Sticker = {
23
+ name: string;
24
+ id: string;
25
+ imgUrl: string;
26
+ };
27
+ type Group = {
28
+ groupName: string;
29
+ channels: Channel[];
30
+ icon?: string;
31
+ iconColor?: string;
32
+ iconVariant?: 'outline' | 'filled';
33
+ };
34
+ type Channel = {
35
+ channelId: string;
36
+ spaceId: string;
37
+ channelName: string;
38
+ channelTopic: string;
39
+ channelKey?: string;
40
+ createdDate: number;
41
+ modifiedDate: number;
42
+ mentionCount?: number;
43
+ mentions?: string;
44
+ isReadOnly?: boolean;
45
+ managerRoleIds?: string[];
46
+ isPinned?: boolean;
47
+ pinnedAt?: number;
48
+ icon?: string;
49
+ iconColor?: string;
50
+ iconVariant?: 'outline' | 'filled';
51
+ };
52
+ type Space = {
53
+ spaceId: string;
54
+ spaceName: string;
55
+ description?: string;
56
+ vanityUrl: string;
57
+ inviteUrl: string;
58
+ iconUrl: string;
59
+ bannerUrl: string;
60
+ defaultChannelId: string;
61
+ hubAddress: string;
62
+ createdDate: number;
63
+ modifiedDate: number;
64
+ isRepudiable: boolean;
65
+ isPublic: boolean;
66
+ saveEditHistory?: boolean;
67
+ groups: Group[];
68
+ roles: Role[];
69
+ emojis: Emoji[];
70
+ stickers: Sticker[];
71
+ };
72
+
73
+ /**
74
+ * Message-related types for Quorum
75
+ */
76
+ /** Client-side ephemeral status - NEVER persist to storage or include in network payload */
77
+ type MessageSendStatus = 'sending' | 'sent' | 'failed';
78
+ type PostMessage = {
79
+ senderId: string;
80
+ type: 'post';
81
+ text: string | string[];
82
+ repliesToMessageId?: string;
83
+ };
84
+ type UpdateProfileMessage = {
85
+ senderId: string;
86
+ type: 'update-profile';
87
+ displayName: string;
88
+ userIcon: string;
89
+ };
90
+ type RemoveMessage = {
91
+ senderId: string;
92
+ type: 'remove-message';
93
+ removeMessageId: string;
94
+ };
95
+ type EventMessage = {
96
+ senderId: string;
97
+ type: 'event';
98
+ text: string;
99
+ repliesToMessageId?: string;
100
+ };
101
+ type EmbedMessage = {
102
+ senderId: string;
103
+ type: 'embed';
104
+ imageUrl?: string;
105
+ thumbnailUrl?: string;
106
+ videoUrl?: string;
107
+ width?: string;
108
+ height?: string;
109
+ isLargeGif?: boolean;
110
+ repliesToMessageId?: string;
111
+ };
112
+ type ReactionMessage = {
113
+ senderId: string;
114
+ type: 'reaction';
115
+ reaction: string;
116
+ messageId: string;
117
+ };
118
+ type RemoveReactionMessage = {
119
+ senderId: string;
120
+ type: 'remove-reaction';
121
+ reaction: string;
122
+ messageId: string;
123
+ };
124
+ type JoinMessage = {
125
+ senderId: string;
126
+ type: 'join';
127
+ };
128
+ type LeaveMessage = {
129
+ senderId: string;
130
+ type: 'leave';
131
+ };
132
+ type KickMessage = {
133
+ senderId: string;
134
+ type: 'kick';
135
+ };
136
+ type MuteMessage = {
137
+ senderId: string;
138
+ type: 'mute';
139
+ targetUserId: string;
140
+ muteId: string;
141
+ timestamp: number;
142
+ action: 'mute' | 'unmute';
143
+ duration?: number;
144
+ };
145
+ type StickerMessage = {
146
+ senderId: string;
147
+ type: 'sticker';
148
+ stickerId: string;
149
+ repliesToMessageId?: string;
150
+ };
151
+ type PinMessage = {
152
+ senderId: string;
153
+ type: 'pin';
154
+ targetMessageId: string;
155
+ action: 'pin' | 'unpin';
156
+ };
157
+ type DeleteConversationMessage = {
158
+ senderId: string;
159
+ type: 'delete-conversation';
160
+ };
161
+ type EditMessage = {
162
+ senderId: string;
163
+ type: 'edit-message';
164
+ originalMessageId: string;
165
+ editedText: string | string[];
166
+ editedAt: number;
167
+ editNonce: string;
168
+ editSignature?: string;
169
+ };
170
+ type MessageContent = PostMessage | EventMessage | EmbedMessage | ReactionMessage | RemoveReactionMessage | RemoveMessage | JoinMessage | LeaveMessage | KickMessage | MuteMessage | UpdateProfileMessage | StickerMessage | PinMessage | DeleteConversationMessage | EditMessage;
171
+ type Reaction = {
172
+ emojiId: string;
173
+ spaceId: string;
174
+ emojiName: string;
175
+ count: number;
176
+ memberIds: string[];
177
+ };
178
+ type Mentions = {
179
+ memberIds: string[];
180
+ roleIds: string[];
181
+ channelIds: string[];
182
+ everyone?: boolean;
183
+ totalMentionCount?: number;
184
+ };
185
+ type Message = {
186
+ channelId: string;
187
+ spaceId: string;
188
+ messageId: string;
189
+ digestAlgorithm: string;
190
+ nonce: string;
191
+ createdDate: number;
192
+ modifiedDate: number;
193
+ lastModifiedHash: string;
194
+ content: MessageContent;
195
+ reactions: Reaction[];
196
+ mentions: Mentions;
197
+ replyMetadata?: {
198
+ parentAuthor: string;
199
+ parentChannelId: string;
200
+ };
201
+ publicKey?: string;
202
+ signature?: string;
203
+ isPinned?: boolean;
204
+ pinnedAt?: number;
205
+ pinnedBy?: string;
206
+ edits?: Array<{
207
+ text: string | string[];
208
+ modifiedDate: number;
209
+ lastModifiedHash: string;
210
+ }>;
211
+ /** Client-side ephemeral - NEVER persist or transmit */
212
+ sendStatus?: MessageSendStatus;
213
+ /** Client-side ephemeral - sanitized error message for display */
214
+ sendError?: string;
215
+ };
216
+
217
+ /**
218
+ * Conversation (DM) types for Quorum
219
+ */
220
+ type ConversationSource = 'quorum' | 'farcaster';
221
+ type Conversation = {
222
+ conversationId: string;
223
+ type: 'direct' | 'group';
224
+ timestamp: number;
225
+ address: string;
226
+ icon: string;
227
+ displayName: string;
228
+ lastReadTimestamp?: number;
229
+ isRepudiable?: boolean;
230
+ saveEditHistory?: boolean;
231
+ lastMessageId?: string;
232
+ source?: ConversationSource;
233
+ farcasterConversationId?: string;
234
+ farcasterFid?: number;
235
+ farcasterUsername?: string;
236
+ farcasterParticipantFids?: number[];
237
+ unreadCount?: number;
238
+ };
239
+
240
+ /**
241
+ * Bookmark types for Quorum
242
+ */
243
+ type Bookmark = {
244
+ bookmarkId: string;
245
+ messageId: string;
246
+ spaceId?: string;
247
+ channelId?: string;
248
+ conversationId?: string;
249
+ sourceType: 'channel' | 'dm';
250
+ createdAt: number;
251
+ cachedPreview: {
252
+ senderAddress: string;
253
+ senderName: string;
254
+ textSnippet: string;
255
+ messageDate: number;
256
+ sourceName: string;
257
+ contentType: 'text' | 'image' | 'sticker';
258
+ imageUrl?: string;
259
+ thumbnailUrl?: string;
260
+ stickerId?: string;
261
+ };
262
+ };
263
+ declare const BOOKMARKS_CONFIG: {
264
+ readonly MAX_BOOKMARKS: 200;
265
+ readonly PREVIEW_SNIPPET_LENGTH: 150;
266
+ };
267
+
268
+ /**
269
+ * User-related types for Quorum
270
+ */
271
+
272
+ type FolderColor = string;
273
+ type NavItem = {
274
+ type: 'space';
275
+ id: string;
276
+ } | {
277
+ type: 'folder';
278
+ id: string;
279
+ name: string;
280
+ spaceIds: string[];
281
+ icon?: string;
282
+ color?: FolderColor;
283
+ createdDate: number;
284
+ modifiedDate: number;
285
+ };
286
+ type NotificationSettings = {
287
+ enabled?: boolean;
288
+ mentions?: boolean;
289
+ replies?: boolean;
290
+ all?: boolean;
291
+ };
292
+ type UserConfig = {
293
+ address: string;
294
+ spaceIds: string[];
295
+ items?: NavItem[];
296
+ timestamp?: number;
297
+ nonRepudiable?: boolean;
298
+ allowSync?: boolean;
299
+ name?: string;
300
+ profile_image?: string;
301
+ spaceKeys?: {
302
+ spaceId: string;
303
+ encryptionState: {
304
+ conversationId: string;
305
+ inboxId: string;
306
+ state: string;
307
+ timestamp: number;
308
+ };
309
+ keys: {
310
+ keyId: string;
311
+ address?: string;
312
+ publicKey: string;
313
+ privateKey: string;
314
+ spaceId: string;
315
+ }[];
316
+ }[];
317
+ notificationSettings?: {
318
+ [spaceId: string]: NotificationSettings;
319
+ };
320
+ bookmarks?: Bookmark[];
321
+ deletedBookmarkIds?: string[];
322
+ };
323
+ type UserProfile = {
324
+ address: string;
325
+ name?: string;
326
+ display_name?: string;
327
+ profile_image?: string;
328
+ bio?: string;
329
+ };
330
+ type SpaceMember = UserProfile & {
331
+ inbox_address: string;
332
+ isKicked?: boolean;
333
+ };
334
+
335
+ /**
336
+ * Sync Protocol Types
337
+ *
338
+ * Hash-based delta synchronization for efficient data transfer.
339
+ * Reduces bandwidth by only sending data the recipient is missing.
340
+ */
341
+
342
+ /** Compact message reference for sync comparison */
343
+ interface MessageDigest {
344
+ /** Unique message identifier */
345
+ messageId: string;
346
+ /** Message creation timestamp */
347
+ createdDate: number;
348
+ /** SHA-256 hash of canonical message content */
349
+ contentHash: string;
350
+ /** Last modification timestamp (for detecting edits) */
351
+ modifiedDate?: number;
352
+ }
353
+ /** Compact reaction reference for sync comparison */
354
+ interface ReactionDigest {
355
+ /** Message this reaction is on */
356
+ messageId: string;
357
+ /** Emoji/reaction identifier */
358
+ emojiId: string;
359
+ /** Number of users who reacted */
360
+ count: number;
361
+ /** Hash of sorted member IDs who reacted */
362
+ membersHash: string;
363
+ }
364
+ /** Manifest of all messages in a channel */
365
+ interface SyncManifest {
366
+ spaceId: string;
367
+ channelId: string;
368
+ messageCount: number;
369
+ oldestTimestamp: number;
370
+ newestTimestamp: number;
371
+ /** Message digests sorted by createdDate ascending */
372
+ digests: MessageDigest[];
373
+ /** Reaction digests for quick comparison */
374
+ reactionDigests: ReactionDigest[];
375
+ }
376
+ /** Delta response containing only needed messages */
377
+ interface MessageDelta {
378
+ spaceId: string;
379
+ channelId: string;
380
+ /** Messages the recipient is missing */
381
+ newMessages: Message[];
382
+ /** Messages that have been edited since recipient's version */
383
+ updatedMessages: Message[];
384
+ /** Message IDs that have been deleted (tombstones) */
385
+ deletedMessageIds: string[];
386
+ }
387
+ /** Delta response for reactions only */
388
+ interface ReactionDelta {
389
+ spaceId: string;
390
+ channelId: string;
391
+ /** Reactions to add: { messageId, emojiId, memberIds[] } */
392
+ added: Array<{
393
+ messageId: string;
394
+ emojiId: string;
395
+ memberIds: string[];
396
+ }>;
397
+ /** Reactions to remove: { messageId, emojiId, memberIds[] } */
398
+ removed: Array<{
399
+ messageId: string;
400
+ emojiId: string;
401
+ memberIds: string[];
402
+ }>;
403
+ }
404
+ /** Compact member reference */
405
+ interface MemberDigest {
406
+ /** User's address */
407
+ address: string;
408
+ /** User's inbox address */
409
+ inboxAddress: string;
410
+ /** SHA-256 hash of display_name (or empty string) */
411
+ displayNameHash: string;
412
+ /** SHA-256 hash of icon URL/data (or empty string) */
413
+ iconHash: string;
414
+ }
415
+ /** Member delta containing only changes */
416
+ interface MemberDelta {
417
+ spaceId: string;
418
+ /** New or updated members (full data) */
419
+ members: SpaceMember[];
420
+ /** Addresses of removed/kicked members */
421
+ removedAddresses: string[];
422
+ }
423
+ /** Peer map entry for Triple Ratchet */
424
+ interface PeerEntry {
425
+ /** Peer's ID in the ratchet */
426
+ peerId: number;
427
+ /** Peer's public key (hex or base64) */
428
+ publicKey: string;
429
+ /** Peer's identity public key (hex or base64) - optional for minimal sync */
430
+ identityPublicKey?: string;
431
+ /** Peer's signed pre-key (hex or base64) - optional for minimal sync */
432
+ signedPrePublicKey?: string;
433
+ }
434
+ /** Peer map delta */
435
+ interface PeerMapDelta {
436
+ spaceId: string;
437
+ /** New peer entries */
438
+ added: PeerEntry[];
439
+ /** Updated peer entries (changed keys) */
440
+ updated: PeerEntry[];
441
+ /** Removed peer IDs */
442
+ removed: number[];
443
+ }
444
+ /** Tombstone for a deleted message */
445
+ interface DeletedMessageTombstone {
446
+ messageId: string;
447
+ spaceId: string;
448
+ channelId: string;
449
+ deletedAt: number;
450
+ }
451
+ /** Summary of sync data for comparison */
452
+ interface SyncSummary {
453
+ memberCount: number;
454
+ messageCount: number;
455
+ newestMessageTimestamp: number;
456
+ oldestMessageTimestamp: number;
457
+ /** Hash of all message IDs for quick comparison */
458
+ manifestHash?: string;
459
+ }
460
+ /** sync-request payload (broadcast via hub) */
461
+ interface SyncRequestPayload {
462
+ type: 'sync-request';
463
+ /** Our inbox address for responses */
464
+ inboxAddress: string;
465
+ /** Request expiry timestamp */
466
+ expiry: number;
467
+ /** Summary of our data */
468
+ summary: SyncSummary;
469
+ }
470
+ /** sync-info payload (direct to requester) */
471
+ interface SyncInfoPayload {
472
+ type: 'sync-info';
473
+ /** Our inbox address */
474
+ inboxAddress: string;
475
+ /** Summary of our data */
476
+ summary: SyncSummary;
477
+ }
478
+ /** sync-initiate payload (direct to best peer) */
479
+ interface SyncInitiatePayload {
480
+ type: 'sync-initiate';
481
+ /** Our inbox address (for peer to send data back to) */
482
+ inboxAddress: string;
483
+ /** Our manifest for comparison */
484
+ manifest?: SyncManifest;
485
+ /** Our member digests for comparison */
486
+ memberDigests?: MemberDigest[];
487
+ /** Our peer IDs for comparison */
488
+ peerIds?: number[];
489
+ }
490
+ /** sync-manifest payload (response to sync-initiate with our data summary) */
491
+ interface SyncManifestPayload {
492
+ type: 'sync-manifest';
493
+ /** Our inbox address (for peer to send data back to) */
494
+ inboxAddress: string;
495
+ /** Our full manifest */
496
+ manifest: SyncManifest;
497
+ /** Our member digests */
498
+ memberDigests: MemberDigest[];
499
+ /** Our peer IDs */
500
+ peerIds: number[];
501
+ }
502
+ /** sync-delta payload (actual data transfer) */
503
+ interface SyncDeltaPayload {
504
+ type: 'sync-delta';
505
+ /** Message changes */
506
+ messageDelta?: MessageDelta;
507
+ /** Reaction changes (synced independently) */
508
+ reactionDelta?: ReactionDelta;
509
+ /** Member changes */
510
+ memberDelta?: MemberDelta;
511
+ /** Peer map changes */
512
+ peerMapDelta?: PeerMapDelta;
513
+ /** Whether this is the final delta chunk */
514
+ isFinal?: boolean;
515
+ }
516
+ /** Candidate from sync-info response */
517
+ interface SyncCandidate {
518
+ inboxAddress: string;
519
+ summary: SyncSummary;
520
+ }
521
+ /** Active sync session state */
522
+ interface SyncSession {
523
+ spaceId: string;
524
+ channelId: string;
525
+ /** Request expiry timestamp */
526
+ expiry: number;
527
+ /** Candidates who responded to sync-request */
528
+ candidates: SyncCandidate[];
529
+ /** Timeout handle for initiating sync */
530
+ timeout?: ReturnType<typeof setTimeout>;
531
+ /** Whether sync is in progress */
532
+ inProgress: boolean;
533
+ /** Target inbox address when we've initiated sync */
534
+ syncTarget?: string;
535
+ }
536
+ /** All sync control message payloads */
537
+ type SyncControlPayload = SyncRequestPayload | SyncInfoPayload | SyncInitiatePayload | SyncManifestPayload | SyncDeltaPayload;
538
+ /** Type guard for sync-request */
539
+ declare function isSyncRequest(payload: SyncControlPayload): payload is SyncRequestPayload;
540
+ /** Type guard for sync-info */
541
+ declare function isSyncInfo(payload: SyncControlPayload): payload is SyncInfoPayload;
542
+ /** Type guard for sync-initiate */
543
+ declare function isSyncInitiate(payload: SyncControlPayload): payload is SyncInitiatePayload;
544
+ /** Type guard for sync-manifest */
545
+ declare function isSyncManifest(payload: SyncControlPayload): payload is SyncManifestPayload;
546
+ /** Type guard for sync-delta */
547
+ declare function isSyncDelta(payload: SyncControlPayload): payload is SyncDeltaPayload;
548
+
549
+ /**
550
+ * StorageAdapter interface
551
+ *
552
+ * Platform-agnostic storage interface that can be implemented by:
553
+ * - IndexedDB (desktop/web)
554
+ * - MMKV (React Native mobile)
555
+ */
556
+
557
+ interface GetMessagesParams {
558
+ spaceId: string;
559
+ channelId: string;
560
+ cursor?: number;
561
+ direction?: 'forward' | 'backward';
562
+ limit?: number;
563
+ }
564
+ interface GetMessagesResult {
565
+ messages: Message[];
566
+ nextCursor: number | null;
567
+ prevCursor: number | null;
568
+ }
569
+ interface StorageAdapter {
570
+ init(): Promise<void>;
571
+ getSpaces(): Promise<Space[]>;
572
+ getSpace(spaceId: string): Promise<Space | null>;
573
+ saveSpace(space: Space): Promise<void>;
574
+ deleteSpace(spaceId: string): Promise<void>;
575
+ getChannels(spaceId: string): Promise<Channel[]>;
576
+ getMessages(params: GetMessagesParams): Promise<GetMessagesResult>;
577
+ getMessage(params: {
578
+ spaceId: string;
579
+ channelId: string;
580
+ messageId: string;
581
+ }): Promise<Message | undefined>;
582
+ saveMessage(message: Message, lastMessageTimestamp: number, address: string, conversationType: string, icon: string, displayName: string): Promise<void>;
583
+ deleteMessage(messageId: string): Promise<void>;
584
+ getConversations(params: {
585
+ type: 'direct' | 'group';
586
+ cursor?: number;
587
+ limit?: number;
588
+ }): Promise<{
589
+ conversations: Conversation[];
590
+ nextCursor: number | null;
591
+ }>;
592
+ getConversation(conversationId: string): Promise<Conversation | undefined>;
593
+ saveConversation(conversation: Conversation): Promise<void>;
594
+ deleteConversation(conversationId: string): Promise<void>;
595
+ getUserConfig(address: string): Promise<UserConfig | undefined>;
596
+ saveUserConfig(userConfig: UserConfig): Promise<void>;
597
+ getSpaceMembers(spaceId: string): Promise<SpaceMember[]>;
598
+ getSpaceMember(spaceId: string, address: string): Promise<SpaceMember | undefined>;
599
+ saveSpaceMember(spaceId: string, member: SpaceMember): Promise<void>;
600
+ getLastSyncTime(key: string): Promise<number | undefined>;
601
+ setLastSyncTime(key: string, time: number): Promise<void>;
602
+ /**
603
+ * Get message digests for efficient sync comparison.
604
+ * If not implemented, returns undefined and SyncService computes from messages.
605
+ */
606
+ getMessageDigests?(spaceId: string, channelId: string): Promise<MessageDigest[] | undefined>;
607
+ /**
608
+ * Get messages by IDs for delta sync.
609
+ * If not implemented, returns undefined and caller fetches individually.
610
+ */
611
+ getMessagesByIds?(spaceId: string, channelId: string, ids: string[]): Promise<Message[] | undefined>;
612
+ /**
613
+ * Get member digests for efficient sync comparison.
614
+ * If not implemented, returns undefined and SyncService computes from members.
615
+ */
616
+ getMemberDigests?(spaceId: string): Promise<MemberDigest[] | undefined>;
617
+ /**
618
+ * Get deleted message tombstones for sync.
619
+ */
620
+ getTombstones?(spaceId: string, channelId: string): Promise<DeletedMessageTombstone[]>;
621
+ /**
622
+ * Save deleted message tombstone.
623
+ */
624
+ saveTombstone?(tombstone: DeletedMessageTombstone): Promise<void>;
625
+ /**
626
+ * Clean up old tombstones.
627
+ */
628
+ cleanupTombstones?(maxAgeMs: number): Promise<void>;
629
+ }
630
+
631
+ /**
632
+ * QuorumApiClient interface
633
+ *
634
+ * Platform-agnostic API client that can be implemented differently
635
+ * for mobile (fetch) and desktop (Electron IPC or fetch)
636
+ */
637
+
638
+ interface SendMessageParams {
639
+ spaceId: string;
640
+ channelId: string;
641
+ text: string;
642
+ repliesToMessageId?: string;
643
+ }
644
+ interface AddReactionParams {
645
+ spaceId: string;
646
+ channelId: string;
647
+ messageId: string;
648
+ reaction: string;
649
+ }
650
+ interface RemoveReactionParams {
651
+ spaceId: string;
652
+ channelId: string;
653
+ messageId: string;
654
+ reaction: string;
655
+ }
656
+ interface EditMessageParams {
657
+ spaceId: string;
658
+ channelId: string;
659
+ messageId: string;
660
+ text: string;
661
+ }
662
+ interface DeleteMessageParams {
663
+ spaceId: string;
664
+ channelId: string;
665
+ messageId: string;
666
+ }
667
+ interface SendDirectMessageParams {
668
+ conversationId: string;
669
+ text: string;
670
+ repliesToMessageId?: string;
671
+ }
672
+ interface QuorumApiClient {
673
+ fetchSpaces(): Promise<Space[]>;
674
+ fetchSpace(spaceId: string): Promise<Space>;
675
+ joinSpace(inviteCode: string): Promise<Space>;
676
+ fetchMessages(params: {
677
+ spaceId: string;
678
+ channelId: string;
679
+ cursor?: string;
680
+ limit?: number;
681
+ }): Promise<{
682
+ messages: Message[];
683
+ nextPageToken?: string;
684
+ }>;
685
+ sendMessage(params: SendMessageParams): Promise<Message>;
686
+ editMessage(params: EditMessageParams): Promise<Message>;
687
+ deleteMessage(params: DeleteMessageParams): Promise<void>;
688
+ addReaction(params: AddReactionParams): Promise<void>;
689
+ removeReaction(params: RemoveReactionParams): Promise<void>;
690
+ fetchConversations(): Promise<Conversation[]>;
691
+ createConversation(params: {
692
+ address: string;
693
+ }): Promise<Conversation>;
694
+ sendDirectMessage(params: SendDirectMessageParams): Promise<Message>;
695
+ fetchDirectMessages(params: {
696
+ conversationId: string;
697
+ cursor?: string;
698
+ limit?: number;
699
+ }): Promise<{
700
+ messages: Message[];
701
+ nextPageToken?: string;
702
+ }>;
703
+ pinMessage(params: {
704
+ spaceId: string;
705
+ channelId: string;
706
+ messageId: string;
707
+ }): Promise<void>;
708
+ unpinMessage(params: {
709
+ spaceId: string;
710
+ channelId: string;
711
+ messageId: string;
712
+ }): Promise<void>;
713
+ }
714
+
715
+ /**
716
+ * API error classes
717
+ */
718
+ /** API error codes */
719
+ declare enum ApiErrorCode {
720
+ NETWORK_ERROR = "NETWORK_ERROR",
721
+ TIMEOUT = "TIMEOUT",
722
+ UNAUTHORIZED = "UNAUTHORIZED",
723
+ FORBIDDEN = "FORBIDDEN",
724
+ TOKEN_EXPIRED = "TOKEN_EXPIRED",
725
+ VALIDATION_ERROR = "VALIDATION_ERROR",
726
+ BAD_REQUEST = "BAD_REQUEST",
727
+ NOT_FOUND = "NOT_FOUND",
728
+ CONFLICT = "CONFLICT",
729
+ RATE_LIMITED = "RATE_LIMITED",
730
+ SERVER_ERROR = "SERVER_ERROR",
731
+ SERVICE_UNAVAILABLE = "SERVICE_UNAVAILABLE",
732
+ UNKNOWN = "UNKNOWN"
733
+ }
734
+ /** API error details */
735
+ interface ApiErrorDetails {
736
+ code: ApiErrorCode;
737
+ message: string;
738
+ status?: number;
739
+ field?: string;
740
+ retryAfter?: number;
741
+ originalError?: Error;
742
+ }
743
+ /**
744
+ * Custom API error class
745
+ */
746
+ declare class ApiError extends Error {
747
+ readonly code: ApiErrorCode;
748
+ readonly status?: number;
749
+ readonly field?: string;
750
+ readonly retryAfter?: number;
751
+ readonly originalError?: Error;
752
+ constructor(details: ApiErrorDetails);
753
+ /** Check if error is retryable */
754
+ get isRetryable(): boolean;
755
+ /** Check if error requires re-authentication */
756
+ get requiresAuth(): boolean;
757
+ /** Convert to JSON-serializable object */
758
+ toJSON(): ApiErrorDetails;
759
+ }
760
+ /**
761
+ * Create ApiError from HTTP response
762
+ */
763
+ declare function createApiError(status: number, message?: string, field?: string): ApiError;
764
+ /**
765
+ * Create ApiError from network error
766
+ */
767
+ declare function createNetworkError(error: Error): ApiError;
768
+
769
+ /**
770
+ * API endpoint definitions
771
+ */
772
+ /** Base API configuration */
773
+ interface ApiConfig {
774
+ baseUrl: string;
775
+ timeout?: number;
776
+ }
777
+ /** API endpoint builders */
778
+ declare const endpoints: {
779
+ readonly spaces: {
780
+ readonly list: () => string;
781
+ readonly detail: (spaceId: string) => string;
782
+ readonly members: (spaceId: string) => string;
783
+ readonly join: (spaceId: string) => string;
784
+ readonly leave: (spaceId: string) => string;
785
+ };
786
+ readonly channels: {
787
+ readonly list: (spaceId: string) => string;
788
+ readonly detail: (spaceId: string, channelId: string) => string;
789
+ };
790
+ readonly messages: {
791
+ readonly list: (spaceId: string, channelId: string) => string;
792
+ readonly detail: (spaceId: string, channelId: string, messageId: string) => string;
793
+ readonly send: (spaceId: string, channelId: string) => string;
794
+ readonly react: (spaceId: string, channelId: string, messageId: string) => string;
795
+ readonly pin: (spaceId: string, channelId: string, messageId: string) => string;
796
+ };
797
+ readonly conversations: {
798
+ readonly list: () => string;
799
+ readonly detail: (conversationId: string) => string;
800
+ readonly messages: (conversationId: string) => string;
801
+ };
802
+ readonly user: {
803
+ readonly config: () => string;
804
+ readonly profile: () => string;
805
+ readonly notifications: () => string;
806
+ };
807
+ readonly search: {
808
+ readonly messages: (spaceId: string) => string;
809
+ readonly members: (spaceId: string) => string;
810
+ };
811
+ };
812
+ /** HTTP methods */
813
+ type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
814
+ /** Request options */
815
+ interface RequestOptions {
816
+ method?: HttpMethod;
817
+ headers?: Record<string, string>;
818
+ body?: unknown;
819
+ timeout?: number;
820
+ signal?: AbortSignal;
821
+ }
822
+ /** Pagination parameters */
823
+ interface PaginationParams {
824
+ cursor?: string;
825
+ limit?: number;
826
+ }
827
+ /** Message list parameters */
828
+ interface MessageListParams extends PaginationParams {
829
+ before?: string;
830
+ after?: string;
831
+ around?: string;
832
+ }
833
+
834
+ /**
835
+ * Query key factories for React Query
836
+ *
837
+ * Consistent key structure across platforms for cache management
838
+ */
839
+ declare const queryKeys: {
840
+ readonly spaces: {
841
+ readonly all: readonly ["spaces"];
842
+ readonly detail: (spaceId: string) => readonly ["spaces", string];
843
+ readonly members: (spaceId: string) => readonly ["spaces", string, "members"];
844
+ readonly member: (spaceId: string, address: string) => readonly ["spaces", string, "members", string];
845
+ };
846
+ readonly channels: {
847
+ readonly bySpace: (spaceId: string) => readonly ["channels", string];
848
+ readonly detail: (spaceId: string, channelId: string) => readonly ["channels", string, string];
849
+ };
850
+ readonly messages: {
851
+ readonly infinite: (spaceId: string, channelId: string) => readonly ["messages", "infinite", string, string];
852
+ readonly detail: (spaceId: string, channelId: string, messageId: string) => readonly ["messages", string, string, string];
853
+ readonly pinned: (spaceId: string, channelId: string) => readonly ["messages", "pinned", string, string];
854
+ };
855
+ readonly conversations: {
856
+ readonly all: (type: "direct" | "group") => readonly ["conversations", "direct" | "group"];
857
+ readonly detail: (conversationId: string) => readonly ["conversations", string];
858
+ readonly messages: (conversationId: string) => readonly ["conversations", string, "messages"];
859
+ };
860
+ readonly user: {
861
+ readonly config: (address: string) => readonly ["user", "config", string];
862
+ readonly profile: (address: string) => readonly ["user", "profile", string];
863
+ };
864
+ readonly bookmarks: {
865
+ readonly all: readonly ["bookmarks"];
866
+ readonly bySource: (sourceType: "channel" | "dm") => readonly ["bookmarks", "channel" | "dm"];
867
+ readonly bySpace: (spaceId: string) => readonly ["bookmarks", "space", string];
868
+ readonly check: (messageId: string) => readonly ["bookmarks", "check", string];
869
+ };
870
+ };
871
+ type SpacesKey = typeof queryKeys.spaces.all;
872
+ type SpaceDetailKey = ReturnType<typeof queryKeys.spaces.detail>;
873
+ type ChannelsKey = ReturnType<typeof queryKeys.channels.bySpace>;
874
+ type MessagesInfiniteKey = ReturnType<typeof queryKeys.messages.infinite>;
875
+
876
+ interface UseSpacesOptions {
877
+ storage: StorageAdapter;
878
+ enabled?: boolean;
879
+ }
880
+ declare function useSpaces({ storage, enabled }: UseSpacesOptions): _tanstack_react_query.UseQueryResult<Space[], Error>;
881
+ interface UseSpaceOptions {
882
+ storage: StorageAdapter;
883
+ spaceId: string | undefined;
884
+ enabled?: boolean;
885
+ }
886
+ declare function useSpace({ storage, spaceId, enabled }: UseSpaceOptions): _tanstack_react_query.UseQueryResult<Space | null, Error>;
887
+ declare function useSpaceMembers({ storage, spaceId, enabled, }: UseSpaceOptions): _tanstack_react_query.UseQueryResult<SpaceMember[], Error>;
888
+
889
+ interface UseChannelsOptions {
890
+ storage: StorageAdapter;
891
+ spaceId: string | undefined;
892
+ enabled?: boolean;
893
+ }
894
+ declare function useChannels({ storage, spaceId, enabled }: UseChannelsOptions): _tanstack_react_query.UseQueryResult<Channel[], Error>;
895
+ /**
896
+ * Helper to extract channels from a space's groups
897
+ */
898
+ declare function flattenChannels(groups: {
899
+ channels: Channel[];
900
+ }[]): Channel[];
901
+ /**
902
+ * Find a channel by ID within groups
903
+ */
904
+ declare function findChannel(groups: {
905
+ channels: Channel[];
906
+ }[], channelId: string): Channel | undefined;
907
+
908
+ interface UseMessagesOptions {
909
+ storage: StorageAdapter;
910
+ spaceId: string | undefined;
911
+ channelId: string | undefined;
912
+ enabled?: boolean;
913
+ limit?: number;
914
+ }
915
+ declare function useMessages({ storage, spaceId, channelId, enabled, limit, }: UseMessagesOptions): _tanstack_react_query.UseInfiniteQueryResult<_tanstack_query_core.InfiniteData<GetMessagesResult, unknown>, Error>;
916
+ /**
917
+ * Flatten paginated messages into a single array
918
+ */
919
+ declare function flattenMessages(pages: GetMessagesResult[] | undefined): Message[];
920
+ /**
921
+ * Hook to invalidate message cache
922
+ */
923
+ declare function useInvalidateMessages(): {
924
+ invalidateChannel: (spaceId: string, channelId: string) => void;
925
+ invalidateSpace: (spaceId: string) => void;
926
+ };
927
+
928
+ interface UseSendMessageOptions {
929
+ storage: StorageAdapter;
930
+ apiClient: QuorumApiClient;
931
+ currentUserId: string;
932
+ }
933
+ declare function useSendMessage({ storage, apiClient, currentUserId, }: UseSendMessageOptions): _tanstack_react_query.UseMutationResult<Message, Error, SendMessageParams, {
934
+ previousData: unknown;
935
+ optimisticMessage: Message;
936
+ }>;
937
+
938
+ interface UseReactionOptions {
939
+ storage: StorageAdapter;
940
+ apiClient: QuorumApiClient;
941
+ currentUserId: string;
942
+ }
943
+ declare function useAddReaction({ storage, apiClient, currentUserId, }: UseReactionOptions): _tanstack_react_query.UseMutationResult<void, Error, AddReactionParams, {
944
+ previousData: unknown;
945
+ }>;
946
+ declare function useRemoveReaction({ storage, apiClient, currentUserId, }: UseReactionOptions): _tanstack_react_query.UseMutationResult<void, Error, RemoveReactionParams, {
947
+ previousData: unknown;
948
+ }>;
949
+
950
+ interface UseEditMessageOptions {
951
+ storage: StorageAdapter;
952
+ apiClient: QuorumApiClient;
953
+ }
954
+ declare function useEditMessage({ storage, apiClient }: UseEditMessageOptions): _tanstack_react_query.UseMutationResult<Message, Error, EditMessageParams, {
955
+ previousData: unknown;
956
+ }>;
957
+
958
+ interface UseDeleteMessageOptions {
959
+ storage: StorageAdapter;
960
+ apiClient: QuorumApiClient;
961
+ }
962
+ declare function useDeleteMessage({ storage, apiClient }: UseDeleteMessageOptions): _tanstack_react_query.UseMutationResult<void, Error, DeleteMessageParams, {
963
+ previousData: unknown;
964
+ }>;
965
+
966
+ /**
967
+ * Message validation utilities
968
+ */
969
+
970
+ /** Maximum message length */
971
+ declare const MAX_MESSAGE_LENGTH = 4000;
972
+ /** Maximum number of mentions per message */
973
+ declare const MAX_MENTIONS = 50;
974
+ /** Validation result */
975
+ interface ValidationResult {
976
+ valid: boolean;
977
+ errors: string[];
978
+ }
979
+ /**
980
+ * Validate message content before sending
981
+ */
982
+ declare function validateMessageContent(content: string): ValidationResult;
983
+ /**
984
+ * Validate a message object
985
+ */
986
+ declare function validateMessage(message: Partial<Message>): ValidationResult;
987
+ /**
988
+ * Sanitize message content for display
989
+ */
990
+ declare function sanitizeContent(content: string): string;
991
+
992
+ /**
993
+ * Mention parsing utilities
994
+ */
995
+
996
+ /** Mention patterns */
997
+ declare const MENTION_PATTERNS: {
998
+ user: RegExp;
999
+ role: RegExp;
1000
+ channel: RegExp;
1001
+ everyone: RegExp;
1002
+ here: RegExp;
1003
+ };
1004
+ /** Parsed mention */
1005
+ interface ParsedMention {
1006
+ type: 'user' | 'role' | 'channel' | 'everyone' | 'here';
1007
+ id?: string;
1008
+ raw: string;
1009
+ start: number;
1010
+ end: number;
1011
+ }
1012
+ /**
1013
+ * Parse mentions from message text
1014
+ */
1015
+ declare function parseMentions(text: string): ParsedMention[];
1016
+ /**
1017
+ * Extract Mentions object from parsed mentions
1018
+ */
1019
+ declare function extractMentions(text: string): Mentions;
1020
+ /**
1021
+ * Format mention for display
1022
+ */
1023
+ declare function formatMention(type: 'user' | 'role' | 'channel', id: string): string;
1024
+
1025
+ /**
1026
+ * Date and text formatting utilities
1027
+ */
1028
+ /**
1029
+ * Format timestamp to time string (e.g., "2:30 PM")
1030
+ */
1031
+ declare function formatTime(timestamp: number): string;
1032
+ /**
1033
+ * Format timestamp to date string (e.g., "Dec 24, 2025")
1034
+ */
1035
+ declare function formatDate(timestamp: number): string;
1036
+ /**
1037
+ * Format timestamp to full datetime string (e.g., "Dec 24, 2025 at 2:30 PM")
1038
+ */
1039
+ declare function formatDateTime(timestamp: number): string;
1040
+ /**
1041
+ * Format timestamp to relative time (e.g., "5 minutes ago", "Yesterday")
1042
+ */
1043
+ declare function formatRelativeTime(timestamp: number): string;
1044
+ /**
1045
+ * Format message date header (e.g., "Today", "Yesterday", "December 24, 2025")
1046
+ */
1047
+ declare function formatMessageDate(timestamp: number): string;
1048
+ /**
1049
+ * Check if two dates are the same day
1050
+ */
1051
+ declare function isSameDay(d1: Date, d2: Date): boolean;
1052
+ /**
1053
+ * Truncate text to a maximum length with ellipsis
1054
+ */
1055
+ declare function truncateText(text: string, maxLength: number): string;
1056
+ /**
1057
+ * Format file size (e.g., "1.5 MB")
1058
+ */
1059
+ declare function formatFileSize(bytes: number): string;
1060
+ /**
1061
+ * Format member count (e.g., "1.2K members")
1062
+ */
1063
+ declare function formatMemberCount(count: number): string;
1064
+
1065
+ /**
1066
+ * Encoding utilities for hex and base64 conversions
1067
+ *
1068
+ * These are platform-agnostic utilities for converting between
1069
+ * byte arrays and string representations.
1070
+ */
1071
+ /**
1072
+ * Convert a hex string to a byte array
1073
+ * @param hex - Hexadecimal string (with or without 0x prefix)
1074
+ * @returns Array of bytes
1075
+ */
1076
+ declare function hexToBytes(hex: string): number[];
1077
+ /**
1078
+ * Convert a byte array to a hex string
1079
+ * @param bytes - Array of bytes (number[] or Uint8Array)
1080
+ * @returns Hexadecimal string (lowercase, no prefix)
1081
+ */
1082
+ declare function bytesToHex(bytes: number[] | Uint8Array): string;
1083
+ /**
1084
+ * Convert a base64 string to a Uint8Array
1085
+ * @param base64 - Base64-encoded string
1086
+ * @returns Uint8Array of bytes
1087
+ */
1088
+ declare function base64ToBytes(base64: string): Uint8Array;
1089
+ /**
1090
+ * Convert a byte array to a base64 string
1091
+ * @param bytes - Array of bytes (number[] or Uint8Array)
1092
+ * @returns Base64-encoded string
1093
+ */
1094
+ declare function bytesToBase64(bytes: number[] | Uint8Array): string;
1095
+ /**
1096
+ * Convert a UTF-8 string to a byte array
1097
+ * @param str - UTF-8 string
1098
+ * @returns Array of bytes
1099
+ */
1100
+ declare function stringToBytes(str: string): number[];
1101
+ /**
1102
+ * Convert a byte array to a UTF-8 string
1103
+ * @param bytes - Array of bytes (number[] or Uint8Array)
1104
+ * @returns UTF-8 string
1105
+ */
1106
+ declare function bytesToString(bytes: number[] | Uint8Array): string;
1107
+ /**
1108
+ * Convert a number to an 8-byte big-endian array (int64)
1109
+ * Used for timestamp serialization in signatures
1110
+ *
1111
+ * @param num - The number to convert (must be within safe integer range)
1112
+ * @returns 8-byte Uint8Array in big-endian format
1113
+ */
1114
+ declare function int64ToBytes(num: number): Uint8Array;
1115
+
1116
+ /**
1117
+ * Logger utility that respects build environment
1118
+ *
1119
+ * In development: logs to console
1120
+ * In production: no-ops for performance
1121
+ *
1122
+ * Usage:
1123
+ * import { logger } from '@quorum/shared';
1124
+ * logger.log('[MyModule]', 'some message', data);
1125
+ * logger.warn('[MyModule]', 'warning message');
1126
+ * logger.error('[MyModule]', 'error message', error);
1127
+ */
1128
+ type LogLevel = 'log' | 'info' | 'warn' | 'error' | 'debug';
1129
+ interface LoggerConfig {
1130
+ enabled: boolean;
1131
+ minLevel: LogLevel;
1132
+ }
1133
+ declare const logger: {
1134
+ /**
1135
+ * Configure the logger
1136
+ */
1137
+ configure(newConfig: Partial<LoggerConfig>): void;
1138
+ /**
1139
+ * Check if logging is enabled
1140
+ */
1141
+ isEnabled(): boolean;
1142
+ /**
1143
+ * Enable logging (useful for debugging production issues)
1144
+ */
1145
+ enable(): void;
1146
+ /**
1147
+ * Disable logging
1148
+ */
1149
+ disable(): void;
1150
+ /**
1151
+ * Log at debug level
1152
+ */
1153
+ debug: (...args: unknown[]) => void;
1154
+ /**
1155
+ * Log at default level
1156
+ */
1157
+ log: (...args: unknown[]) => void;
1158
+ /**
1159
+ * Log at info level
1160
+ */
1161
+ info: (...args: unknown[]) => void;
1162
+ /**
1163
+ * Log at warn level
1164
+ */
1165
+ warn: (...args: unknown[]) => void;
1166
+ /**
1167
+ * Log at error level (always logs unless explicitly disabled)
1168
+ */
1169
+ error: (...args: unknown[]) => void;
1170
+ /**
1171
+ * Create a scoped logger with a prefix
1172
+ */
1173
+ scope(prefix: string): {
1174
+ debug: (...args: unknown[]) => void;
1175
+ log: (...args: unknown[]) => void;
1176
+ info: (...args: unknown[]) => void;
1177
+ warn: (...args: unknown[]) => void;
1178
+ error: (...args: unknown[]) => void;
1179
+ };
1180
+ };
1181
+
1182
+ /**
1183
+ * Crypto types and CryptoProvider interface
1184
+ *
1185
+ * Platform-agnostic cryptographic operations for E2E encryption.
1186
+ * Implementations:
1187
+ * - WASM (desktop/web): Uses channel-wasm bindings
1188
+ * - Native (iOS/Android): Uses uniffi-generated bindings
1189
+ */
1190
+ interface Ed448Keypair {
1191
+ type: 'ed448';
1192
+ public_key: number[];
1193
+ private_key: number[];
1194
+ }
1195
+ interface X448Keypair {
1196
+ type: 'x448';
1197
+ public_key: number[];
1198
+ private_key: number[];
1199
+ }
1200
+ type Keypair = Ed448Keypair | X448Keypair;
1201
+ interface MessageCiphertext {
1202
+ ciphertext: string;
1203
+ initialization_vector: string;
1204
+ associated_data?: string;
1205
+ }
1206
+ interface P2PChannelEnvelope {
1207
+ protocol_identifier: number;
1208
+ message_header: MessageCiphertext;
1209
+ message_body: MessageCiphertext;
1210
+ }
1211
+ interface SenderX3DHParams {
1212
+ sending_identity_private_key: number[];
1213
+ sending_ephemeral_private_key: number[];
1214
+ receiving_identity_key: number[];
1215
+ receiving_signed_pre_key: number[];
1216
+ session_key_length: number;
1217
+ }
1218
+ interface ReceiverX3DHParams {
1219
+ sending_identity_private_key: number[];
1220
+ sending_signed_private_key: number[];
1221
+ receiving_identity_key: number[];
1222
+ receiving_ephemeral_key: number[];
1223
+ session_key_length: number;
1224
+ }
1225
+ interface NewDoubleRatchetParams {
1226
+ session_key: number[];
1227
+ sending_header_key: number[];
1228
+ next_receiving_header_key: number[];
1229
+ is_sender: boolean;
1230
+ sending_ephemeral_private_key: number[];
1231
+ receiving_ephemeral_key: number[];
1232
+ }
1233
+ interface DoubleRatchetStateAndMessage {
1234
+ ratchet_state: string;
1235
+ message: number[];
1236
+ }
1237
+ interface DoubleRatchetStateAndEnvelope {
1238
+ ratchet_state: string;
1239
+ envelope: string;
1240
+ }
1241
+ interface PeerInfo {
1242
+ public_key: number[];
1243
+ identity_public_key: number[];
1244
+ signed_pre_public_key: number[];
1245
+ }
1246
+ interface NewTripleRatchetParams {
1247
+ peers: number[][];
1248
+ peer_key: number[];
1249
+ identity_key: number[];
1250
+ signed_pre_key: number[];
1251
+ threshold: number;
1252
+ async_dkg_ratchet: boolean;
1253
+ }
1254
+ interface TripleRatchetStateAndMetadata {
1255
+ ratchet_state: string;
1256
+ metadata: Record<string, string>;
1257
+ }
1258
+ interface TripleRatchetStateAndMessage {
1259
+ ratchet_state: string;
1260
+ message: number[];
1261
+ }
1262
+ interface TripleRatchetStateAndEnvelope {
1263
+ ratchet_state: string;
1264
+ envelope: string;
1265
+ }
1266
+ /**
1267
+ * InitializationEnvelope - Contains sender info for first message in a session
1268
+ *
1269
+ * This is the format BEFORE sealing. The ephemeral_public_key is NOT included here -
1270
+ * it goes at the TOP LEVEL of the SealedMessage, and the SAME ephemeral key is used for both:
1271
+ * 1. Sealing the envelope (inbox encryption)
1272
+ * 2. X3DH session establishment
1273
+ *
1274
+ * After unsealing, the ephemeral_public_key is added back to create UnsealedEnvelope
1275
+ * (see transport/websocket.ts)
1276
+ */
1277
+ interface InitializationEnvelope {
1278
+ /** Sender's user address */
1279
+ user_address: string;
1280
+ /** Sender's display name */
1281
+ display_name?: string;
1282
+ /** Sender's icon URL */
1283
+ user_icon?: string;
1284
+ /** Inbox address for sending replies */
1285
+ return_inbox_address: string;
1286
+ /** X448 encryption key for the return inbox (hex) */
1287
+ return_inbox_encryption_key: string;
1288
+ /** Ed448 public key for the return inbox (hex) */
1289
+ return_inbox_public_key: string;
1290
+ /** Ed448 private key for the return inbox (hex) - shared so recipient can sign replies */
1291
+ return_inbox_private_key: string;
1292
+ /** Sender's identity public key for X3DH (hex) */
1293
+ identity_public_key: string;
1294
+ /** Session/conversation tag (typically the return inbox address) */
1295
+ tag: string;
1296
+ /** The Double Ratchet encrypted message envelope */
1297
+ message: string;
1298
+ /** Message type (e.g., 'direct') */
1299
+ type: string;
1300
+ }
1301
+ interface InboxMessageEncryptRequest {
1302
+ inbox_public_key: number[];
1303
+ ephemeral_private_key: number[];
1304
+ plaintext: number[];
1305
+ }
1306
+ interface InboxMessageDecryptRequest {
1307
+ inbox_private_key: number[];
1308
+ ephemeral_public_key: number[];
1309
+ ciphertext: MessageCiphertext;
1310
+ }
1311
+ /**
1312
+ * CryptoProvider - Platform-agnostic interface for cryptographic operations
1313
+ *
1314
+ * This interface abstracts the underlying crypto implementation,
1315
+ * allowing the same code to work with WASM (desktop) or native modules (mobile).
1316
+ *
1317
+ * All methods are async to support both synchronous WASM and async native bridges.
1318
+ * State is serialized as JSON strings for cross-platform compatibility.
1319
+ */
1320
+ interface CryptoProvider {
1321
+ /**
1322
+ * Generate an X448 keypair for encryption
1323
+ */
1324
+ generateX448(): Promise<X448Keypair>;
1325
+ /**
1326
+ * Generate an Ed448 keypair for signing
1327
+ */
1328
+ generateEd448(): Promise<Ed448Keypair>;
1329
+ /**
1330
+ * Derive public key from X448 private key
1331
+ * @param privateKey Base64-encoded private key
1332
+ * @returns Base64-encoded public key
1333
+ */
1334
+ getPublicKeyX448(privateKey: string): Promise<string>;
1335
+ /**
1336
+ * Derive public key from Ed448 private key
1337
+ * @param privateKey Base64-encoded private key
1338
+ * @returns Base64-encoded public key
1339
+ */
1340
+ getPublicKeyEd448(privateKey: string): Promise<string>;
1341
+ /**
1342
+ * Perform sender-side X3DH key agreement
1343
+ * @returns Base64-encoded session key (96 bytes: 32 session + 32 sending header + 32 receiving header)
1344
+ */
1345
+ senderX3DH(params: SenderX3DHParams): Promise<string>;
1346
+ /**
1347
+ * Perform receiver-side X3DH key agreement
1348
+ * @returns Base64-encoded session key (96 bytes)
1349
+ */
1350
+ receiverX3DH(params: ReceiverX3DHParams): Promise<string>;
1351
+ /**
1352
+ * Initialize a new double ratchet session
1353
+ * @returns JSON-serialized ratchet state
1354
+ */
1355
+ newDoubleRatchet(params: NewDoubleRatchetParams): Promise<string>;
1356
+ /**
1357
+ * Encrypt a message using double ratchet
1358
+ * @returns Updated state and encrypted envelope
1359
+ */
1360
+ doubleRatchetEncrypt(stateAndMessage: DoubleRatchetStateAndMessage): Promise<DoubleRatchetStateAndEnvelope>;
1361
+ /**
1362
+ * Decrypt a message using double ratchet
1363
+ * @returns Updated state and decrypted message
1364
+ */
1365
+ doubleRatchetDecrypt(stateAndEnvelope: DoubleRatchetStateAndEnvelope): Promise<DoubleRatchetStateAndMessage>;
1366
+ /**
1367
+ * Initialize a new triple ratchet session for group messaging
1368
+ * @returns Initial state and DKG metadata
1369
+ */
1370
+ newTripleRatchet(params: NewTripleRatchetParams): Promise<TripleRatchetStateAndMetadata>;
1371
+ /**
1372
+ * Triple ratchet DKG round 1 - Generate polynomial fragments
1373
+ */
1374
+ tripleRatchetInitRound1(state: TripleRatchetStateAndMetadata): Promise<TripleRatchetStateAndMetadata>;
1375
+ /**
1376
+ * Triple ratchet DKG round 2 - Receive fragments, compute ZK commitment
1377
+ */
1378
+ tripleRatchetInitRound2(state: TripleRatchetStateAndMetadata): Promise<TripleRatchetStateAndMetadata>;
1379
+ /**
1380
+ * Triple ratchet DKG round 3 - Receive commitments, compute ZK reveal
1381
+ */
1382
+ tripleRatchetInitRound3(state: TripleRatchetStateAndMetadata): Promise<TripleRatchetStateAndMetadata>;
1383
+ /**
1384
+ * Triple ratchet DKG round 4 - Verify proofs, reconstruct group key
1385
+ */
1386
+ tripleRatchetInitRound4(state: TripleRatchetStateAndMetadata): Promise<TripleRatchetStateAndMetadata>;
1387
+ /**
1388
+ * Encrypt a message for the group using triple ratchet
1389
+ */
1390
+ tripleRatchetEncrypt(stateAndMessage: TripleRatchetStateAndMessage): Promise<TripleRatchetStateAndEnvelope>;
1391
+ /**
1392
+ * Decrypt a group message using triple ratchet
1393
+ */
1394
+ tripleRatchetDecrypt(stateAndEnvelope: TripleRatchetStateAndEnvelope): Promise<TripleRatchetStateAndMessage>;
1395
+ /**
1396
+ * Handle group membership changes
1397
+ */
1398
+ tripleRatchetResize(state: TripleRatchetStateAndMetadata): Promise<TripleRatchetStateAndMetadata>;
1399
+ /**
1400
+ * Encrypt a message for an inbox (sealed sender / ECIES-style)
1401
+ * @returns Base64-encoded sealed message
1402
+ */
1403
+ encryptInboxMessage(request: InboxMessageEncryptRequest): Promise<string>;
1404
+ /**
1405
+ * Decrypt a sealed inbox message
1406
+ * @returns Decrypted plaintext as byte array
1407
+ */
1408
+ decryptInboxMessage(request: InboxMessageDecryptRequest): Promise<number[]>;
1409
+ }
1410
+
1411
+ /**
1412
+ * Encryption State Types and Storage Interface
1413
+ *
1414
+ * Platform-agnostic types for managing Double Ratchet encryption states.
1415
+ * The actual storage implementation is platform-specific (MMKV for mobile, IndexedDB for desktop).
1416
+ */
1417
+ /**
1418
+ * DeviceKeys - Device's cryptographic keypairs for E2E encryption
1419
+ * Used for X3DH key exchange and inbox message unsealing
1420
+ */
1421
+ interface DeviceKeys {
1422
+ /** X448 identity key for X3DH - private key */
1423
+ identityPrivateKey: number[];
1424
+ /** X448 identity key for X3DH - public key */
1425
+ identityPublicKey: number[];
1426
+ /** X448 signed pre-key for X3DH - private key */
1427
+ preKeyPrivateKey: number[];
1428
+ /** X448 signed pre-key for X3DH - public key */
1429
+ preKeyPublicKey: number[];
1430
+ /** X448 inbox encryption key for unsealing - private key */
1431
+ inboxEncryptionPrivateKey: number[];
1432
+ /** X448 inbox encryption key for unsealing - public key */
1433
+ inboxEncryptionPublicKey: number[];
1434
+ }
1435
+ /**
1436
+ * RecipientInfo - Recipient's public keys needed for encryption
1437
+ * Used to establish X3DH session and seal messages
1438
+ */
1439
+ interface RecipientInfo {
1440
+ /** Recipient's address */
1441
+ address: string;
1442
+ /** X448 identity public key for X3DH */
1443
+ identityKey: number[];
1444
+ /** X448 signed pre-key for X3DH */
1445
+ signedPreKey: number[];
1446
+ /** Recipient's inbox address */
1447
+ inboxAddress: string;
1448
+ /** X448 public key for sealing envelopes to recipient's inbox */
1449
+ inboxEncryptionKey?: number[];
1450
+ }
1451
+ /**
1452
+ * EncryptedEnvelope - Result of encrypting a message
1453
+ */
1454
+ interface EncryptedEnvelope {
1455
+ /** The encrypted Double Ratchet envelope */
1456
+ envelope: string;
1457
+ /** Recipient's inbox address */
1458
+ inboxAddress: string;
1459
+ /** Ephemeral public key (only set for first message in new session) */
1460
+ ephemeralPublicKey?: number[];
1461
+ /** Ephemeral private key (only set for first message - needed for sealing) */
1462
+ ephemeralPrivateKey?: number[];
1463
+ }
1464
+ /**
1465
+ * KeyValueStorageProvider - Platform-agnostic key-value storage interface
1466
+ *
1467
+ * Implementations:
1468
+ * - MMKV (React Native)
1469
+ * - IndexedDB (Desktop/Web)
1470
+ * - AsyncStorage (React Native fallback)
1471
+ */
1472
+ interface KeyValueStorageProvider {
1473
+ /**
1474
+ * Get a string value by key
1475
+ * @returns The value or null if not found
1476
+ */
1477
+ getString(key: string): string | null;
1478
+ /**
1479
+ * Set a string value
1480
+ */
1481
+ set(key: string, value: string): void;
1482
+ /**
1483
+ * Remove a key
1484
+ */
1485
+ remove(key: string): void;
1486
+ /**
1487
+ * Get all keys in storage
1488
+ */
1489
+ getAllKeys(): string[];
1490
+ /**
1491
+ * Clear all data from storage
1492
+ */
1493
+ clearAll(): void;
1494
+ }
1495
+ /**
1496
+ * SendingInbox - Recipient's inbox info needed for sealing messages
1497
+ * Matches desktop SDK's SendingInbox type
1498
+ */
1499
+ interface SendingInbox {
1500
+ /** Recipient's inbox address where we send to */
1501
+ inbox_address: string;
1502
+ /** Recipient's X448 public key for sealing (hex) */
1503
+ inbox_encryption_key: string;
1504
+ /** Recipient's Ed448 public key (hex) - empty until confirmed */
1505
+ inbox_public_key: string;
1506
+ /** Always empty - we don't have their private key */
1507
+ inbox_private_key: string;
1508
+ }
1509
+ /**
1510
+ * ReceivingInbox - Our inbox info for receiving replies
1511
+ * Simplified version (full keypair stored separately in ConversationInboxKeypair)
1512
+ */
1513
+ interface ReceivingInbox {
1514
+ /** Our inbox address where we receive replies */
1515
+ inbox_address: string;
1516
+ }
1517
+ /**
1518
+ * EncryptionState - Double Ratchet state for a conversation+inbox pair
1519
+ * Matches desktop's DoubleRatchetStateAndInboxKeys structure
1520
+ */
1521
+ interface EncryptionState {
1522
+ /** JSON-serialized ratchet state */
1523
+ state: string;
1524
+ /** When state was created/updated */
1525
+ timestamp: number;
1526
+ /** Conversation identifier */
1527
+ conversationId: string;
1528
+ /** Associated inbox ID (our receiving inbox) */
1529
+ inboxId: string;
1530
+ /** Whether we've sent an accept message */
1531
+ sentAccept?: boolean;
1532
+ /** Recipient's inbox info for sealing messages */
1533
+ sendingInbox?: SendingInbox;
1534
+ /** Session tag (usually our inbox address) */
1535
+ tag?: string;
1536
+ /**
1537
+ * X3DH ephemeral public key (hex) used for session establishment.
1538
+ * MUST be reused for all init envelopes until session is confirmed.
1539
+ * This ensures the receiver can derive the same session key via X3DH.
1540
+ */
1541
+ x3dhEphemeralPublicKey?: string;
1542
+ /**
1543
+ * X3DH ephemeral private key (hex) used for session establishment.
1544
+ * MUST be reused for sealing init envelopes until session is confirmed.
1545
+ */
1546
+ x3dhEphemeralPrivateKey?: string;
1547
+ }
1548
+ /**
1549
+ * InboxMapping - Maps inbox address to conversation
1550
+ */
1551
+ interface InboxMapping {
1552
+ inboxId: string;
1553
+ conversationId: string;
1554
+ }
1555
+ /**
1556
+ * LatestState - Tracks the most recent state for a conversation
1557
+ */
1558
+ interface LatestState {
1559
+ conversationId: string;
1560
+ inboxId: string;
1561
+ timestamp: number;
1562
+ }
1563
+ /**
1564
+ * ConversationInboxKeypair - Per-conversation inbox keypair for receiving replies
1565
+ * Mirrors desktop's InboxKeyset structure with both Ed448 and X448 keys
1566
+ */
1567
+ interface ConversationInboxKeypair {
1568
+ conversationId: string;
1569
+ inboxAddress: string;
1570
+ /** X448 encryption public key (for sealing/unsealing messages) */
1571
+ encryptionPublicKey: number[];
1572
+ /** X448 encryption private key */
1573
+ encryptionPrivateKey: number[];
1574
+ /** Ed448 signing public key (for signing/verifying inbox messages) */
1575
+ signingPublicKey?: number[];
1576
+ /** Ed448 signing private key */
1577
+ signingPrivateKey?: number[];
1578
+ }
1579
+ /**
1580
+ * Storage key prefixes for encryption state data
1581
+ */
1582
+ declare const ENCRYPTION_STORAGE_KEYS: {
1583
+ /** enc_state:{conversationId}:{inboxId} */
1584
+ readonly ENCRYPTION_STATE: "enc_state:";
1585
+ /** inbox_map:{inboxId} */
1586
+ readonly INBOX_MAPPING: "inbox_map:";
1587
+ /** latest:{conversationId} */
1588
+ readonly LATEST_STATE: "latest:";
1589
+ /** conv_inboxes:{conversationId} -> inboxId[] */
1590
+ readonly CONVERSATION_INBOXES: "conv_inboxes:";
1591
+ /** conv_inbox_key:{conversationId} -> ConversationInboxKeypair */
1592
+ readonly CONVERSATION_INBOX_KEY: "conv_inbox_key:";
1593
+ };
1594
+ /**
1595
+ * EncryptionStateStorageInterface - Interface for managing encryption states
1596
+ *
1597
+ * This interface defines the contract for encryption state storage.
1598
+ * Implementations use platform-specific storage backends.
1599
+ */
1600
+ interface EncryptionStateStorageInterface {
1601
+ getEncryptionState(conversationId: string, inboxId: string): EncryptionState | null;
1602
+ getEncryptionStates(conversationId: string): EncryptionState[];
1603
+ saveEncryptionState(state: EncryptionState, updateLatest?: boolean): void;
1604
+ deleteEncryptionState(conversationId: string, inboxId: string): void;
1605
+ deleteAllEncryptionStates(conversationId: string): void;
1606
+ getInboxMapping(inboxId: string): InboxMapping | null;
1607
+ saveInboxMapping(inboxId: string, conversationId: string): void;
1608
+ deleteInboxMapping(inboxId: string): void;
1609
+ getLatestState(conversationId: string): LatestState | null;
1610
+ saveConversationInboxKeypair(keypair: ConversationInboxKeypair): void;
1611
+ getConversationInboxKeypair(conversationId: string): ConversationInboxKeypair | null;
1612
+ deleteConversationInboxKeypair(conversationId: string): void;
1613
+ getConversationInboxKeypairByAddress(inboxAddress: string): ConversationInboxKeypair | null;
1614
+ getAllConversationInboxAddresses(): string[];
1615
+ clearAll(): void;
1616
+ hasEncryptionState(conversationId: string): boolean;
1617
+ getStatesByInboxId(inboxId: string): Array<{
1618
+ conversationId: string;
1619
+ state: EncryptionState;
1620
+ }>;
1621
+ }
1622
+
1623
+ /**
1624
+ * WASM CryptoProvider implementation
1625
+ *
1626
+ * Wraps the channel-wasm WASM module to implement the CryptoProvider interface.
1627
+ * Used by desktop/web applications.
1628
+ *
1629
+ * Note: This file imports from the WASM module dynamically to avoid bundling
1630
+ * issues in environments that don't support WASM.
1631
+ */
1632
+
1633
+ /**
1634
+ * Interface for the WASM module functions
1635
+ * This matches the exports from channel-wasm
1636
+ */
1637
+ interface ChannelWasmModule {
1638
+ js_generate_x448(): string;
1639
+ js_generate_ed448(): string;
1640
+ js_get_pubkey_x448(key: string): string;
1641
+ js_get_pubkey_ed448(key: string): string;
1642
+ js_sign_ed448(key: string, message: string): string;
1643
+ js_verify_ed448(publicKey: string, message: string, signature: string): string;
1644
+ js_sender_x3dh(input: string): string;
1645
+ js_receiver_x3dh(input: string): string;
1646
+ js_new_double_ratchet(params: string): string;
1647
+ js_double_ratchet_encrypt(params: string): string;
1648
+ js_double_ratchet_decrypt(params: string): string;
1649
+ js_new_triple_ratchet(params: string): string;
1650
+ js_triple_ratchet_init_round_1(params: string): string;
1651
+ js_triple_ratchet_init_round_2(params: string): string;
1652
+ js_triple_ratchet_init_round_3(params: string): string;
1653
+ js_triple_ratchet_init_round_4(params: string): string;
1654
+ js_triple_ratchet_encrypt(params: string): string;
1655
+ js_triple_ratchet_decrypt(params: string): string;
1656
+ js_triple_ratchet_resize(params: string): string;
1657
+ js_encrypt_inbox_message(input: string): string;
1658
+ js_decrypt_inbox_message(input: string): string;
1659
+ }
1660
+ /**
1661
+ * WasmCryptoProvider - Implements CryptoProvider using channel-wasm
1662
+ */
1663
+ declare class WasmCryptoProvider implements CryptoProvider {
1664
+ private wasm;
1665
+ constructor(wasmModule: ChannelWasmModule);
1666
+ generateX448(): Promise<X448Keypair>;
1667
+ generateEd448(): Promise<Ed448Keypair>;
1668
+ getPublicKeyX448(privateKey: string): Promise<string>;
1669
+ getPublicKeyEd448(privateKey: string): Promise<string>;
1670
+ senderX3DH(params: SenderX3DHParams): Promise<string>;
1671
+ receiverX3DH(params: ReceiverX3DHParams): Promise<string>;
1672
+ newDoubleRatchet(params: NewDoubleRatchetParams): Promise<string>;
1673
+ doubleRatchetEncrypt(stateAndMessage: DoubleRatchetStateAndMessage): Promise<DoubleRatchetStateAndEnvelope>;
1674
+ doubleRatchetDecrypt(stateAndEnvelope: DoubleRatchetStateAndEnvelope): Promise<DoubleRatchetStateAndMessage>;
1675
+ newTripleRatchet(params: NewTripleRatchetParams): Promise<TripleRatchetStateAndMetadata>;
1676
+ tripleRatchetInitRound1(state: TripleRatchetStateAndMetadata): Promise<TripleRatchetStateAndMetadata>;
1677
+ tripleRatchetInitRound2(state: TripleRatchetStateAndMetadata): Promise<TripleRatchetStateAndMetadata>;
1678
+ tripleRatchetInitRound3(state: TripleRatchetStateAndMetadata): Promise<TripleRatchetStateAndMetadata>;
1679
+ tripleRatchetInitRound4(state: TripleRatchetStateAndMetadata): Promise<TripleRatchetStateAndMetadata>;
1680
+ tripleRatchetEncrypt(stateAndMessage: TripleRatchetStateAndMessage): Promise<TripleRatchetStateAndEnvelope>;
1681
+ tripleRatchetDecrypt(stateAndEnvelope: TripleRatchetStateAndEnvelope): Promise<TripleRatchetStateAndMessage>;
1682
+ tripleRatchetResize(state: TripleRatchetStateAndMetadata): Promise<TripleRatchetStateAndMetadata>;
1683
+ encryptInboxMessage(request: InboxMessageEncryptRequest): Promise<string>;
1684
+ decryptInboxMessage(request: InboxMessageDecryptRequest): Promise<number[]>;
1685
+ }
1686
+
1687
+ /**
1688
+ * Signing types and SigningProvider interface
1689
+ *
1690
+ * Platform-agnostic message signing and verification.
1691
+ * Uses Ed448 for all signing operations.
1692
+ */
1693
+ /**
1694
+ * SigningProvider - Platform-agnostic interface for cryptographic signing
1695
+ *
1696
+ * Implementations:
1697
+ * - WASM (desktop/web): Uses channel-wasm js_sign_ed448/js_verify_ed448
1698
+ * - Native (iOS/Android): Uses uniffi-generated bindings
1699
+ */
1700
+ interface SigningProvider {
1701
+ /**
1702
+ * Sign a message using Ed448
1703
+ * @param privateKey Base64-encoded Ed448 private key (56 bytes)
1704
+ * @param message Base64-encoded message to sign
1705
+ * @returns Base64-encoded signature (114 bytes)
1706
+ */
1707
+ signEd448(privateKey: string, message: string): Promise<string>;
1708
+ /**
1709
+ * Verify an Ed448 signature
1710
+ * @param publicKey Base64-encoded Ed448 public key (57 bytes)
1711
+ * @param message Base64-encoded original message
1712
+ * @param signature Base64-encoded signature to verify
1713
+ * @returns true if signature is valid, false otherwise
1714
+ */
1715
+ verifyEd448(publicKey: string, message: string, signature: string): Promise<boolean>;
1716
+ }
1717
+ /**
1718
+ * Signed message structure
1719
+ */
1720
+ interface SignedMessage {
1721
+ /** The message content (may be encrypted) */
1722
+ content: string;
1723
+ /** Base64-encoded Ed448 signature */
1724
+ signature: string;
1725
+ /** Base64-encoded Ed448 public key of signer */
1726
+ publicKey: string;
1727
+ /** Timestamp when signed */
1728
+ timestamp: number;
1729
+ }
1730
+ /**
1731
+ * Verify a signed message structure
1732
+ */
1733
+ declare function verifySignedMessage(provider: SigningProvider, message: SignedMessage): Promise<boolean>;
1734
+ /**
1735
+ * Create a signed message
1736
+ */
1737
+ declare function createSignedMessage(provider: SigningProvider, privateKey: string, publicKey: string, content: string): Promise<SignedMessage>;
1738
+
1739
+ /**
1740
+ * WASM SigningProvider implementation
1741
+ *
1742
+ * Wraps the channel-wasm WASM module to implement the SigningProvider interface.
1743
+ * Used by desktop/web applications.
1744
+ */
1745
+
1746
+ /**
1747
+ * WasmSigningProvider - Implements SigningProvider using channel-wasm
1748
+ */
1749
+ declare class WasmSigningProvider implements SigningProvider {
1750
+ private wasm;
1751
+ constructor(wasmModule: ChannelWasmModule);
1752
+ signEd448(privateKey: string, message: string): Promise<string>;
1753
+ verifyEd448(publicKey: string, message: string, signature: string): Promise<boolean>;
1754
+ }
1755
+
1756
+ /**
1757
+ * Transport types and interfaces
1758
+ *
1759
+ * Platform-agnostic transport layer for HTTP and IPC communication.
1760
+ */
1761
+ interface TransportConfig {
1762
+ baseUrl: string;
1763
+ timeout?: number;
1764
+ defaultHeaders?: Record<string, string>;
1765
+ }
1766
+ interface TransportRequestOptions {
1767
+ method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
1768
+ headers?: Record<string, string>;
1769
+ body?: unknown;
1770
+ timeout?: number;
1771
+ signal?: AbortSignal;
1772
+ }
1773
+ interface TransportResponse<T> {
1774
+ data: T;
1775
+ status: number;
1776
+ headers: Record<string, string>;
1777
+ }
1778
+ /**
1779
+ * TransportClient - Platform-agnostic HTTP transport
1780
+ *
1781
+ * Implementations:
1782
+ * - Fetch (mobile/web): Uses native fetch API
1783
+ * - Electron IPC (desktop): Uses Electron IPC for main process requests
1784
+ */
1785
+ interface TransportClient {
1786
+ /**
1787
+ * Make an HTTP request
1788
+ */
1789
+ request<T>(endpoint: string, options?: TransportRequestOptions): Promise<TransportResponse<T>>;
1790
+ /**
1791
+ * Configure the transport client
1792
+ */
1793
+ configure(config: TransportConfig): void;
1794
+ /**
1795
+ * Set authorization header for all requests
1796
+ */
1797
+ setAuthToken(token: string): void;
1798
+ /**
1799
+ * Clear authorization
1800
+ */
1801
+ clearAuth(): void;
1802
+ }
1803
+
1804
+ /**
1805
+ * WebSocket client interface
1806
+ *
1807
+ * Platform-agnostic WebSocket connection management.
1808
+ * Handles connection, reconnection, and message routing.
1809
+ *
1810
+ * IMPORTANT: This handles transport only. Encryption is handled separately
1811
+ * by the CryptoProvider before/after message transmission.
1812
+ */
1813
+ type WebSocketConnectionState = 'connecting' | 'connected' | 'disconnected' | 'reconnecting';
1814
+ /**
1815
+ * Encrypted message received from WebSocket
1816
+ * The content is a SealedMessage containing the encrypted envelope.
1817
+ * All cryptographic material (ephemeral key, identity key) comes from the envelope itself.
1818
+ */
1819
+ interface EncryptedWebSocketMessage {
1820
+ /** The encrypted message content (JSON string of SealedMessage) */
1821
+ encryptedContent: string;
1822
+ /** The inbox address this message was sent to */
1823
+ inboxAddress: string;
1824
+ /** Server timestamp */
1825
+ timestamp: number;
1826
+ }
1827
+ /**
1828
+ * SealedMessage format from the network
1829
+ * Contains an encrypted envelope that can be unsealed with device keys.
1830
+ * The ephemeral_public_key is used along with the inbox private key to decrypt.
1831
+ */
1832
+ interface SealedMessage {
1833
+ /** The inbox address the message was sent to */
1834
+ inbox_address: string;
1835
+ /** Sender's ephemeral public key (used for decryption) */
1836
+ ephemeral_public_key: string;
1837
+ /** The encrypted envelope (contains identity key, message, return inbox info) */
1838
+ envelope: string;
1839
+ /** Optional inbox public key for verification */
1840
+ inbox_public_key?: string;
1841
+ /** Optional inbox signature for verification */
1842
+ inbox_signature?: string;
1843
+ }
1844
+ /**
1845
+ * UnsealedEnvelope - the decrypted content of a SealedMessage
1846
+ * Contains all the data needed to establish a session and decrypt the message.
1847
+ */
1848
+ interface UnsealedEnvelope {
1849
+ /** Sender's user address */
1850
+ user_address: string;
1851
+ /** Sender's display name */
1852
+ display_name?: string;
1853
+ /** Sender's icon URL */
1854
+ user_icon?: string;
1855
+ /** Inbox address for sending replies */
1856
+ return_inbox_address: string;
1857
+ /** Encryption key for the return inbox */
1858
+ return_inbox_encryption_key: string;
1859
+ /** Public key for the return inbox */
1860
+ return_inbox_public_key: string;
1861
+ /** Private key for the return inbox (only for sender's own use) */
1862
+ return_inbox_private_key: string;
1863
+ /** Sender's identity public key (for X3DH) */
1864
+ identity_public_key: string;
1865
+ /** Session/conversation tag */
1866
+ tag: string;
1867
+ /** The encrypted/signed message content */
1868
+ message: string;
1869
+ /** Message type */
1870
+ type: string;
1871
+ /** The ephemeral public key used to seal this message */
1872
+ ephemeral_public_key: string;
1873
+ }
1874
+ /**
1875
+ * Outbound message to send via WebSocket
1876
+ */
1877
+ interface OutboundWebSocketMessage {
1878
+ /** Message type (e.g., 'message', 'listen', 'unlisten') */
1879
+ type: string;
1880
+ /** Message payload */
1881
+ payload: unknown;
1882
+ }
1883
+ /**
1884
+ * Listen/subscribe message for inbox addresses
1885
+ */
1886
+ interface ListenMessage {
1887
+ type: 'listen';
1888
+ inbox_addresses: string[];
1889
+ }
1890
+ /**
1891
+ * Unlisten/unsubscribe message
1892
+ */
1893
+ interface UnlistenMessage {
1894
+ type: 'unlisten';
1895
+ inbox_addresses: string[];
1896
+ }
1897
+ interface WebSocketClientOptions {
1898
+ /** WebSocket server URL */
1899
+ url: string;
1900
+ /** Interval between reconnection attempts (ms). Default: 1000 */
1901
+ reconnectInterval?: number;
1902
+ /** Maximum reconnection attempts. Default: Infinity */
1903
+ maxReconnectAttempts?: number;
1904
+ /** Queue processing interval (ms). Default: 1000 */
1905
+ queueProcessInterval?: number;
1906
+ }
1907
+ type MessageHandler = (message: EncryptedWebSocketMessage) => Promise<void>;
1908
+ type StateChangeHandler = (state: WebSocketConnectionState) => void;
1909
+ type ErrorHandler = (error: Error) => void;
1910
+ /**
1911
+ * WebSocketClient - Platform-agnostic WebSocket interface
1912
+ *
1913
+ * Features:
1914
+ * - Automatic reconnection with configurable interval
1915
+ * - Dual queue system (inbound/outbound)
1916
+ * - Inbox address subscription management
1917
+ * - Message handler registration
1918
+ *
1919
+ * Implementations:
1920
+ * - Browser/Electron: Uses native WebSocket API
1921
+ * - React Native: Uses React Native WebSocket
1922
+ */
1923
+ interface WebSocketClient {
1924
+ /**
1925
+ * Current connection state
1926
+ */
1927
+ readonly state: WebSocketConnectionState;
1928
+ /**
1929
+ * Whether currently connected
1930
+ */
1931
+ readonly isConnected: boolean;
1932
+ /**
1933
+ * Connect to the WebSocket server
1934
+ */
1935
+ connect(): Promise<void>;
1936
+ /**
1937
+ * Disconnect from the WebSocket server
1938
+ */
1939
+ disconnect(): void;
1940
+ /**
1941
+ * Send a message (must be already serialized)
1942
+ * Messages are queued if not connected and sent when connection is established
1943
+ */
1944
+ send(message: string): Promise<void>;
1945
+ /**
1946
+ * Enqueue an outbound message with async preparation
1947
+ * Useful when message needs async work before sending
1948
+ */
1949
+ enqueueOutbound(prepareMessage: () => Promise<string[]>): void;
1950
+ /**
1951
+ * Subscribe to messages for inbox addresses
1952
+ */
1953
+ subscribe(inboxAddresses: string[]): Promise<void>;
1954
+ /**
1955
+ * Unsubscribe from inbox addresses
1956
+ */
1957
+ unsubscribe(inboxAddresses: string[]): Promise<void>;
1958
+ /**
1959
+ * Set the message handler for incoming messages
1960
+ * Only one handler can be set at a time
1961
+ */
1962
+ setMessageHandler(handler: MessageHandler): void;
1963
+ /**
1964
+ * Set the resubscribe callback (called after reconnection)
1965
+ */
1966
+ setResubscribeHandler(handler: () => Promise<void>): void;
1967
+ /**
1968
+ * Add a connection state change listener
1969
+ * @returns Unsubscribe function
1970
+ */
1971
+ onStateChange(handler: StateChangeHandler): () => void;
1972
+ /**
1973
+ * Add an error listener
1974
+ * @returns Unsubscribe function
1975
+ */
1976
+ onError(handler: ErrorHandler): () => void;
1977
+ }
1978
+ /**
1979
+ * Factory function to create a WebSocket client
1980
+ */
1981
+ type CreateWebSocketClient = (options: WebSocketClientOptions) => WebSocketClient;
1982
+
1983
+ /**
1984
+ * Browser/Electron WebSocket client implementation
1985
+ *
1986
+ * Uses the native WebSocket API available in browsers and Electron.
1987
+ * Implements automatic reconnection, dual queue system, and subscription management.
1988
+ */
1989
+
1990
+ /**
1991
+ * BrowserWebSocketClient - WebSocket implementation for browser/Electron
1992
+ *
1993
+ * Features:
1994
+ * - Automatic reconnection with configurable interval
1995
+ * - Dual queue system (inbound messages, outbound message generators)
1996
+ * - Inbox address subscription management
1997
+ * - Periodic queue processing
1998
+ */
1999
+ declare class BrowserWebSocketClient implements WebSocketClient {
2000
+ private url;
2001
+ private reconnectInterval;
2002
+ private maxReconnectAttempts;
2003
+ private queueProcessInterval;
2004
+ private ws;
2005
+ private _state;
2006
+ private reconnectAttempts;
2007
+ private shouldReconnect;
2008
+ private inboundQueue;
2009
+ private outboundQueue;
2010
+ private isProcessing;
2011
+ private processIntervalId;
2012
+ private messageHandler;
2013
+ private resubscribeHandler;
2014
+ private stateChangeHandlers;
2015
+ private errorHandlers;
2016
+ constructor(options: WebSocketClientOptions);
2017
+ get state(): WebSocketConnectionState;
2018
+ get isConnected(): boolean;
2019
+ private setState;
2020
+ private emitError;
2021
+ connect(): Promise<void>;
2022
+ private doConnect;
2023
+ disconnect(): void;
2024
+ send(message: string): Promise<void>;
2025
+ enqueueOutbound(prepareMessage: () => Promise<string[]>): void;
2026
+ subscribe(inboxAddresses: string[]): Promise<void>;
2027
+ unsubscribe(inboxAddresses: string[]): Promise<void>;
2028
+ setMessageHandler(handler: MessageHandler): void;
2029
+ setResubscribeHandler(handler: () => Promise<void>): void;
2030
+ onStateChange(handler: StateChangeHandler): () => void;
2031
+ onError(handler: ErrorHandler): () => void;
2032
+ private startQueueProcessing;
2033
+ private stopQueueProcessing;
2034
+ private processQueues;
2035
+ }
2036
+ /**
2037
+ * Factory function to create a browser WebSocket client
2038
+ */
2039
+ declare function createBrowserWebSocketClient(options: WebSocketClientOptions): WebSocketClient;
2040
+
2041
+ /**
2042
+ * React Native WebSocket client implementation
2043
+ *
2044
+ * Uses the React Native WebSocket API which is similar to the browser's.
2045
+ * Implements automatic reconnection, dual queue system, and subscription management.
2046
+ *
2047
+ * Note: This implementation is nearly identical to browser-websocket.ts
2048
+ * but kept separate to allow for React Native-specific optimizations if needed.
2049
+ */
2050
+
2051
+ /**
2052
+ * RNWebSocketClient - WebSocket implementation for React Native
2053
+ *
2054
+ * Features:
2055
+ * - Automatic reconnection with configurable interval
2056
+ * - Dual queue system (inbound messages, outbound message generators)
2057
+ * - Inbox address subscription management
2058
+ * - Periodic queue processing
2059
+ */
2060
+ declare class RNWebSocketClient implements WebSocketClient {
2061
+ private url;
2062
+ private reconnectInterval;
2063
+ private maxReconnectAttempts;
2064
+ private queueProcessInterval;
2065
+ private ws;
2066
+ private _state;
2067
+ private reconnectAttempts;
2068
+ private shouldReconnect;
2069
+ private inboundQueue;
2070
+ private outboundQueue;
2071
+ private isProcessing;
2072
+ private processIntervalId;
2073
+ private messageHandler;
2074
+ private resubscribeHandler;
2075
+ private stateChangeHandlers;
2076
+ private errorHandlers;
2077
+ constructor(options: WebSocketClientOptions);
2078
+ get state(): WebSocketConnectionState;
2079
+ get isConnected(): boolean;
2080
+ private setState;
2081
+ private emitError;
2082
+ connect(): Promise<void>;
2083
+ private doConnect;
2084
+ disconnect(): void;
2085
+ send(message: string): Promise<void>;
2086
+ enqueueOutbound(prepareMessage: () => Promise<string[]>): void;
2087
+ subscribe(inboxAddresses: string[]): Promise<void>;
2088
+ unsubscribe(inboxAddresses: string[]): Promise<void>;
2089
+ setMessageHandler(handler: MessageHandler): void;
2090
+ setResubscribeHandler(handler: () => Promise<void>): void;
2091
+ onStateChange(handler: StateChangeHandler): () => void;
2092
+ onError(handler: ErrorHandler): () => void;
2093
+ private startQueueProcessing;
2094
+ private stopQueueProcessing;
2095
+ private processQueues;
2096
+ }
2097
+ /**
2098
+ * Factory function to create a React Native WebSocket client
2099
+ */
2100
+ declare function createRNWebSocketClient(options: WebSocketClientOptions): WebSocketClient;
2101
+
2102
+ /**
2103
+ * Sync Utility Functions
2104
+ *
2105
+ * Hash computation, digest creation, and delta calculation for sync protocol.
2106
+ */
2107
+
2108
+ /** Maximum chunk size for message transmission (5MB) */
2109
+ declare const MAX_CHUNK_SIZE: number;
2110
+ /** Default sync request expiry (30 seconds) */
2111
+ declare const DEFAULT_SYNC_EXPIRY_MS = 30000;
2112
+ /** Aggressive sync timeout after receiving first response (1 second) */
2113
+ declare const AGGRESSIVE_SYNC_TIMEOUT_MS = 1000;
2114
+ /**
2115
+ * Compute SHA-256 hash of a string
2116
+ */
2117
+ declare function computeHash(data: string): string;
2118
+ /**
2119
+ * Compute hash of message content for comparison.
2120
+ * Uses canonical representation: senderId + type + content-specific fields
2121
+ */
2122
+ declare function computeContentHash(message: Message): string;
2123
+ /**
2124
+ * Compute hash of reaction state for a message
2125
+ */
2126
+ declare function computeReactionHash(reactions: Reaction[]): string;
2127
+ /**
2128
+ * Compute hash of member's mutable fields
2129
+ */
2130
+ declare function computeMemberHash(member: SpaceMember): {
2131
+ displayNameHash: string;
2132
+ iconHash: string;
2133
+ };
2134
+ /**
2135
+ * Compute manifest hash for quick comparison.
2136
+ * Hash of sorted message IDs.
2137
+ */
2138
+ declare function computeManifestHash(digests: MessageDigest[]): string;
2139
+ /**
2140
+ * Create digest from message
2141
+ */
2142
+ declare function createMessageDigest(message: Message): MessageDigest;
2143
+ /**
2144
+ * Create reaction digest for a message
2145
+ */
2146
+ declare function createReactionDigest(messageId: string, reactions: Reaction[]): ReactionDigest[];
2147
+ /**
2148
+ * Create manifest from messages
2149
+ */
2150
+ declare function createManifest(spaceId: string, channelId: string, messages: Message[]): SyncManifest;
2151
+ /**
2152
+ * Create member digest
2153
+ */
2154
+ declare function createMemberDigest(member: SpaceMember): MemberDigest;
2155
+ interface MessageDiffResult {
2156
+ /** Message IDs we don't have */
2157
+ missingIds: string[];
2158
+ /** Message IDs we have but are outdated (content changed) */
2159
+ outdatedIds: string[];
2160
+ /** Message IDs we have but they don't (we should send to them) */
2161
+ extraIds: string[];
2162
+ }
2163
+ /**
2164
+ * Compare manifests and determine what messages differ
2165
+ */
2166
+ declare function computeMessageDiff(ourManifest: SyncManifest, theirManifest: SyncManifest): MessageDiffResult;
2167
+ interface ReactionDiffResult {
2168
+ /** Reactions to add: { messageId, emojiId, memberIds to add } */
2169
+ toAdd: Array<{
2170
+ messageId: string;
2171
+ emojiId: string;
2172
+ memberIds: string[];
2173
+ }>;
2174
+ /** Reactions to remove: { messageId, emojiId, memberIds to remove } */
2175
+ toRemove: Array<{
2176
+ messageId: string;
2177
+ emojiId: string;
2178
+ memberIds: string[];
2179
+ }>;
2180
+ }
2181
+ /**
2182
+ * Compare reaction digests and determine differences.
2183
+ * This requires the full reaction data to compute actual member diffs.
2184
+ */
2185
+ declare function computeReactionDiff(ourReactions: Map<string, Reaction[]>, // messageId -> reactions
2186
+ theirDigests: ReactionDigest[]): ReactionDiffResult;
2187
+ interface MemberDiffResult {
2188
+ /** Member addresses we don't have */
2189
+ missingAddresses: string[];
2190
+ /** Member addresses where our data is outdated */
2191
+ outdatedAddresses: string[];
2192
+ /** Member addresses we have that they don't */
2193
+ extraAddresses: string[];
2194
+ }
2195
+ /**
2196
+ * Compare member digests and determine differences
2197
+ */
2198
+ declare function computeMemberDiff(ourDigests: MemberDigest[], theirDigests: MemberDigest[]): MemberDiffResult;
2199
+ interface PeerDiffResult {
2200
+ /** Peer IDs they have that we don't */
2201
+ missingPeerIds: number[];
2202
+ /** Peer IDs we have that they don't */
2203
+ extraPeerIds: number[];
2204
+ }
2205
+ /**
2206
+ * Compare peer ID sets
2207
+ */
2208
+ declare function computePeerDiff(ourPeerIds: number[], theirPeerIds: number[]): PeerDiffResult;
2209
+ /**
2210
+ * Build message delta from diff result and message lookup
2211
+ */
2212
+ declare function buildMessageDelta(spaceId: string, channelId: string, diff: MessageDiffResult, messageMap: Map<string, Message>, tombstones: DeletedMessageTombstone[]): MessageDelta;
2213
+ /**
2214
+ * Build reaction delta for messages
2215
+ */
2216
+ declare function buildReactionDelta(spaceId: string, channelId: string, messageMap: Map<string, Message>, messageIds: string[]): ReactionDelta;
2217
+ /**
2218
+ * Build member delta from diff result and member lookup
2219
+ */
2220
+ declare function buildMemberDelta(spaceId: string, diff: MemberDiffResult, memberMap: Map<string, SpaceMember>): MemberDelta;
2221
+ /**
2222
+ * Chunk messages for transmission to stay under size limit
2223
+ */
2224
+ declare function chunkMessages(messages: Message[]): Message[][];
2225
+ /**
2226
+ * Chunk members for transmission
2227
+ */
2228
+ declare function chunkMembers(members: SpaceMember[]): SpaceMember[][];
2229
+ /**
2230
+ * Create sync summary from messages and members
2231
+ */
2232
+ declare function createSyncSummary(messages: Message[], memberCount: number): {
2233
+ memberCount: number;
2234
+ messageCount: number;
2235
+ newestMessageTimestamp: number;
2236
+ oldestMessageTimestamp: number;
2237
+ manifestHash: string;
2238
+ };
2239
+
2240
+ /**
2241
+ * SyncService
2242
+ *
2243
+ * Platform-agnostic sync orchestration logic.
2244
+ * Handles sync protocol without encryption (that's platform-specific).
2245
+ *
2246
+ * Usage:
2247
+ * 1. Platform creates SyncService with storage adapter
2248
+ * 2. Platform calls build* methods to create payloads
2249
+ * 3. Platform handles encryption and transmission
2250
+ * 4. Platform calls apply* methods to process received data
2251
+ */
2252
+
2253
+ interface SyncServiceConfig {
2254
+ storage: StorageAdapter;
2255
+ /** Maximum messages to include in sync (default: 1000) */
2256
+ maxMessages?: number;
2257
+ /** Sync request expiry in ms (default: 30000) */
2258
+ requestExpiry?: number;
2259
+ /** Callback when sync should be initiated */
2260
+ onInitiateSync?: (spaceId: string, target: string) => void;
2261
+ /** Cache TTL in ms (default: 5000) */
2262
+ cacheTtl?: number;
2263
+ }
2264
+ declare class SyncService {
2265
+ private storage;
2266
+ private maxMessages;
2267
+ private requestExpiry;
2268
+ private onInitiateSync?;
2269
+ /** Active sync sessions by spaceId */
2270
+ private sessions;
2271
+ /** Deleted message tombstones (caller must persist these) */
2272
+ private tombstones;
2273
+ /** Pre-computed sync payload cache per space:channel - always ready to use */
2274
+ private payloadCache;
2275
+ constructor(config: SyncServiceConfig);
2276
+ /**
2277
+ * Get cache key for space/channel
2278
+ */
2279
+ private getCacheKey;
2280
+ /**
2281
+ * Get or initialize the payload cache for a space/channel.
2282
+ * If not cached, loads from storage and builds the payload once.
2283
+ */
2284
+ private getPayloadCache;
2285
+ /**
2286
+ * Build the payload cache from messages and members
2287
+ */
2288
+ private buildPayloadCache;
2289
+ /**
2290
+ * Rebuild derived fields (manifest, digests, summary) from the maps
2291
+ */
2292
+ private rebuildPayloadCache;
2293
+ /**
2294
+ * Invalidate cache for a space/channel (forces reload from storage on next access)
2295
+ */
2296
+ invalidateCache(spaceId: string, channelId?: string): void;
2297
+ /**
2298
+ * Update cache with a new/updated message (incremental update - no storage query)
2299
+ */
2300
+ updateCacheWithMessage(spaceId: string, channelId: string, message: Message): void;
2301
+ /**
2302
+ * Remove a message from cache (incremental update - no storage query)
2303
+ */
2304
+ removeCacheMessage(spaceId: string, channelId: string, messageId: string): void;
2305
+ /**
2306
+ * Update cache with a new/updated member (incremental update - no storage query)
2307
+ */
2308
+ updateCacheWithMember(spaceId: string, channelId: string, member: SpaceMember): void;
2309
+ /**
2310
+ * Check if cache exists for a space/channel
2311
+ */
2312
+ hasCachedPayload(spaceId: string, channelId: string): boolean;
2313
+ /**
2314
+ * Check if a sync session is active for a space
2315
+ */
2316
+ hasActiveSession(spaceId: string): boolean;
2317
+ /**
2318
+ * Check if sync is in progress for a space
2319
+ */
2320
+ isSyncInProgress(spaceId: string): boolean;
2321
+ /**
2322
+ * Mark sync as in progress
2323
+ */
2324
+ setSyncInProgress(spaceId: string, inProgress: boolean): void;
2325
+ /**
2326
+ * Set the sync target (who we're syncing with)
2327
+ */
2328
+ setSyncTarget(spaceId: string, targetInbox: string): void;
2329
+ /**
2330
+ * Get the sync target for a space
2331
+ */
2332
+ getSyncTarget(spaceId: string): string | undefined;
2333
+ /**
2334
+ * Build sync-request payload to broadcast via hub
2335
+ */
2336
+ buildSyncRequest(spaceId: string, channelId: string, inboxAddress: string): Promise<SyncRequestPayload>;
2337
+ /**
2338
+ * Schedule sync initiation after timeout
2339
+ */
2340
+ scheduleSyncInitiation(spaceId: string, callback: () => void, timeoutMs?: number): void;
2341
+ /**
2342
+ * Build sync-info response if we have useful data.
2343
+ * Returns null if we have nothing to offer or are already in sync.
2344
+ */
2345
+ buildSyncInfo(spaceId: string, channelId: string, inboxAddress: string, theirSummary: SyncSummary): Promise<SyncInfoPayload | null>;
2346
+ /**
2347
+ * Add candidate from sync-info response
2348
+ */
2349
+ addCandidate(spaceId: string, candidate: SyncCandidate): void;
2350
+ /**
2351
+ * Select best candidate based on data availability
2352
+ */
2353
+ selectBestCandidate(spaceId: string): SyncCandidate | null;
2354
+ /**
2355
+ * Build sync-initiate payload for selected peer
2356
+ */
2357
+ buildSyncInitiate(spaceId: string, channelId: string, inboxAddress: string, peerIds: number[]): Promise<{
2358
+ target: string;
2359
+ payload: SyncInitiatePayload;
2360
+ } | null>;
2361
+ /**
2362
+ * Build sync-manifest response to sync-initiate
2363
+ */
2364
+ buildSyncManifest(spaceId: string, channelId: string, peerIds: number[], inboxAddress: string): Promise<SyncManifestPayload>;
2365
+ /**
2366
+ * Build sync-delta payloads based on manifest comparison.
2367
+ * May return multiple payloads for chunking.
2368
+ */
2369
+ buildSyncDelta(spaceId: string, channelId: string, theirManifest: SyncManifest, theirMemberDigests: MemberDigest[], theirPeerIds: number[], ourPeerEntries: Map<number, PeerEntry>): Promise<SyncDeltaPayload[]>;
2370
+ /**
2371
+ * Apply received message delta to local storage
2372
+ */
2373
+ applyMessageDelta(delta: MessageDelta): Promise<void>;
2374
+ /**
2375
+ * Apply received reaction delta to local storage.
2376
+ * This updates the reactions on existing messages.
2377
+ */
2378
+ applyReactionDelta(delta: ReactionDelta): Promise<void>;
2379
+ /**
2380
+ * Apply received member delta to local storage
2381
+ */
2382
+ applyMemberDelta(delta: MemberDelta): Promise<void>;
2383
+ /**
2384
+ * Apply full sync delta
2385
+ */
2386
+ applySyncDelta(delta: SyncDeltaPayload): Promise<void>;
2387
+ /**
2388
+ * Record a deleted message tombstone
2389
+ */
2390
+ addTombstone(tombstone: DeletedMessageTombstone): void;
2391
+ /**
2392
+ * Get all tombstones (for persistence by caller)
2393
+ */
2394
+ getTombstones(): DeletedMessageTombstone[];
2395
+ /**
2396
+ * Load tombstones (from caller's persistence)
2397
+ */
2398
+ loadTombstones(tombstones: DeletedMessageTombstone[]): void;
2399
+ /**
2400
+ * Clean up old tombstones (older than 30 days)
2401
+ */
2402
+ cleanupTombstones(maxAgeMs?: number): void;
2403
+ private getChannelMessages;
2404
+ /**
2405
+ * Clean up expired sessions
2406
+ */
2407
+ cleanupSessions(): void;
2408
+ /**
2409
+ * Cancel active sync for a space
2410
+ */
2411
+ cancelSync(spaceId: string): void;
2412
+ }
2413
+
2414
+ export { AGGRESSIVE_SYNC_TIMEOUT_MS, type AddReactionParams, type ApiConfig, ApiError, ApiErrorCode, type ApiErrorDetails, BOOKMARKS_CONFIG, type Bookmark, BrowserWebSocketClient, type Channel, type ChannelWasmModule, type ChannelsKey, type Conversation, type ConversationInboxKeypair, type ConversationSource, type CreateWebSocketClient, type CryptoProvider, DEFAULT_SYNC_EXPIRY_MS, type DeleteConversationMessage, type DeleteMessageParams, type DeletedMessageTombstone, type DeviceKeys, type DoubleRatchetStateAndEnvelope, type DoubleRatchetStateAndMessage, ENCRYPTION_STORAGE_KEYS, type Ed448Keypair, type EditMessage, type EditMessageParams, type EmbedMessage, type Emoji, type EncryptedEnvelope, type EncryptedWebSocketMessage, type EncryptionState, type EncryptionStateStorageInterface, type ErrorHandler, type EventMessage, type FolderColor, type GetMessagesParams, type GetMessagesResult, type Group, type HttpMethod, type InboxMapping, type InboxMessageDecryptRequest, type InboxMessageEncryptRequest, type InitializationEnvelope, type JoinMessage, type KeyValueStorageProvider, type Keypair, type KickMessage, type LatestState, type LeaveMessage, type ListenMessage, MAX_CHUNK_SIZE, MAX_MENTIONS, MAX_MESSAGE_LENGTH, MENTION_PATTERNS, type MemberDelta, type MemberDiffResult, type MemberDigest, type Mentions, type Message, type MessageCiphertext, type MessageContent, type MessageDelta, type MessageDiffResult, type MessageDigest, type MessageHandler, type MessageListParams, type MessageSendStatus, type MessagesInfiniteKey, type MuteMessage, type NavItem, type NewDoubleRatchetParams, type NewTripleRatchetParams, type NotificationSettings, type OutboundWebSocketMessage, type P2PChannelEnvelope, type PaginationParams, type ParsedMention, type PeerDiffResult, type PeerEntry, type PeerInfo, type PeerMapDelta, type Permission, type PinMessage, type PostMessage, type QuorumApiClient, RNWebSocketClient, type Reaction, type ReactionDelta, type ReactionDiffResult, type ReactionDigest, type ReactionMessage, type ReceiverX3DHParams, type ReceivingInbox, type RecipientInfo, type RemoveMessage, type RemoveReactionMessage, type RemoveReactionParams, type RequestOptions, type Role, type SealedMessage, type SendDirectMessageParams, type SendMessageParams, type SenderX3DHParams, type SendingInbox, type SignedMessage, type SigningProvider, type Space, type SpaceDetailKey, type SpaceMember, type SpacesKey, type StateChangeHandler, type Sticker, type StickerMessage, type StorageAdapter, type SyncCandidate, type SyncControlPayload, type SyncDeltaPayload, type SyncInfoPayload, type SyncInitiatePayload, type SyncManifest, type SyncManifestPayload, type SyncRequestPayload, SyncService, type SyncServiceConfig, type SyncSession, type SyncSummary, type TransportClient, type TransportConfig, type TransportRequestOptions, type TransportResponse, type TripleRatchetStateAndEnvelope, type TripleRatchetStateAndMessage, type TripleRatchetStateAndMetadata, type UnlistenMessage, type UnsealedEnvelope, type UpdateProfileMessage, type UseChannelsOptions, type UseDeleteMessageOptions, type UseEditMessageOptions, type UseMessagesOptions, type UseReactionOptions, type UseSendMessageOptions, type UseSpaceOptions, type UseSpacesOptions, type UserConfig, type UserProfile, type ValidationResult, WasmCryptoProvider, WasmSigningProvider, type WebSocketClient, type WebSocketClientOptions, type WebSocketConnectionState, type X448Keypair, base64ToBytes, buildMemberDelta, buildMessageDelta, buildReactionDelta, bytesToBase64, bytesToHex, bytesToString, chunkMembers, chunkMessages, computeContentHash, computeHash, computeManifestHash, computeMemberDiff, computeMemberHash, computeMessageDiff, computePeerDiff, computeReactionDiff, computeReactionHash, createApiError, createBrowserWebSocketClient, createManifest, createMemberDigest, createMessageDigest, createNetworkError, createRNWebSocketClient, createReactionDigest, createSignedMessage, createSyncSummary, endpoints, extractMentions, findChannel, flattenChannels, flattenMessages, formatDate, formatDateTime, formatFileSize, formatMemberCount, formatMention, formatMessageDate, formatRelativeTime, formatTime, hexToBytes, int64ToBytes, isSameDay, isSyncDelta, isSyncInfo, isSyncInitiate, isSyncManifest, isSyncRequest, logger, parseMentions, queryKeys, sanitizeContent, stringToBytes, truncateText, useAddReaction, useChannels, useDeleteMessage, useEditMessage, useInvalidateMessages, useMessages, useRemoveReaction, useSendMessage, useSpace, useSpaceMembers, useSpaces, validateMessage, validateMessageContent, verifySignedMessage };