@prismer/sdk 1.3.4 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -95,12 +95,12 @@ interface RealtimeConfig {
95
95
  fetch?: typeof fetch;
96
96
  }
97
97
  type RealtimeState = 'disconnected' | 'connecting' | 'connected' | 'reconnecting';
98
- type Listener<T> = (payload: T) => void;
98
+ type Listener$1<T> = (payload: T) => void;
99
99
  declare class TypedEmitter {
100
100
  private listeners;
101
- on<E extends RealtimeEventType>(event: E, cb: Listener<RealtimeEventMap[E]>): this;
102
- off<E extends RealtimeEventType>(event: E, cb: Listener<RealtimeEventMap[E]>): this;
103
- once<E extends RealtimeEventType>(event: E, cb: Listener<RealtimeEventMap[E]>): this;
101
+ on<E extends RealtimeEventType>(event: E, cb: Listener$1<RealtimeEventMap[E]>): this;
102
+ off<E extends RealtimeEventType>(event: E, cb: Listener$1<RealtimeEventMap[E]>): this;
103
+ once<E extends RealtimeEventType>(event: E, cb: Listener$1<RealtimeEventMap[E]>): this;
104
104
  protected emit<E extends RealtimeEventType>(event: E, payload: RealtimeEventMap[E]): void;
105
105
  protected removeAllListeners(): void;
106
106
  }
@@ -160,8 +160,247 @@ declare class RealtimeSSEClient extends TypedEmitter {
160
160
  }
161
161
 
162
162
  /**
163
- * Prismer Cloud SDK — Type definitions
163
+ * Prismer SDK — Storage adapters for offline-first IM.
164
+ *
165
+ * Three built-in implementations:
166
+ * - MemoryStorage — in-process Map, for tests / stateless agents
167
+ * - IndexedDBStorage — browser-persistent, for web apps
168
+ * - (future) SQLiteStorage — Node.js / React Native
164
169
  */
170
+ interface StoredMessage {
171
+ id: string;
172
+ clientId?: string;
173
+ conversationId: string;
174
+ content: string;
175
+ type: string;
176
+ senderId: string;
177
+ parentId?: string | null;
178
+ status: 'pending' | 'sent' | 'confirmed' | 'failed';
179
+ metadata?: Record<string, any>;
180
+ createdAt: string;
181
+ updatedAt?: string;
182
+ syncSeq?: number;
183
+ }
184
+ interface StoredConversation {
185
+ id: string;
186
+ type: 'direct' | 'group';
187
+ title?: string;
188
+ lastMessage?: StoredMessage;
189
+ lastMessageAt?: string;
190
+ unreadCount: number;
191
+ lastReadMessageId?: string;
192
+ members?: Array<{
193
+ userId: string;
194
+ username: string;
195
+ displayName?: string;
196
+ role: string;
197
+ }>;
198
+ metadata?: Record<string, any>;
199
+ syncSeq?: number;
200
+ updatedAt: string;
201
+ }
202
+ interface StoredContact {
203
+ userId: string;
204
+ username: string;
205
+ displayName: string;
206
+ role: string;
207
+ conversationId: string;
208
+ lastMessageAt?: string;
209
+ unreadCount: number;
210
+ syncSeq?: number;
211
+ }
212
+ interface OutboxOperation {
213
+ id: string;
214
+ type: 'message.send' | 'message.edit' | 'message.delete' | 'conversation.read';
215
+ method: string;
216
+ path: string;
217
+ body?: unknown;
218
+ query?: Record<string, string>;
219
+ status: 'pending' | 'inflight' | 'confirmed' | 'failed';
220
+ createdAt: number;
221
+ retries: number;
222
+ maxRetries: number;
223
+ lastError?: string;
224
+ idempotencyKey: string;
225
+ /** Local data for optimistic UI (e.g., the pending message) */
226
+ localData?: unknown;
227
+ }
228
+ interface StorageAdapter {
229
+ /** Initialize the storage (open DB, create tables, etc.) */
230
+ init(): Promise<void>;
231
+ putMessages(messages: StoredMessage[]): Promise<void>;
232
+ getMessages(conversationId: string, opts: {
233
+ limit: number;
234
+ before?: string;
235
+ }): Promise<StoredMessage[]>;
236
+ getMessage(messageId: string): Promise<StoredMessage | null>;
237
+ deleteMessage(messageId: string): Promise<void>;
238
+ putConversations(conversations: StoredConversation[]): Promise<void>;
239
+ getConversations(opts?: {
240
+ limit: number;
241
+ offset?: number;
242
+ }): Promise<StoredConversation[]>;
243
+ getConversation(id: string): Promise<StoredConversation | null>;
244
+ putContacts(contacts: StoredContact[]): Promise<void>;
245
+ getContacts(): Promise<StoredContact[]>;
246
+ getCursor(key: string): Promise<string | null>;
247
+ setCursor(key: string, value: string): Promise<void>;
248
+ enqueue(op: OutboxOperation): Promise<void>;
249
+ dequeueReady(limit: number): Promise<OutboxOperation[]>;
250
+ ack(opId: string): Promise<void>;
251
+ nack(opId: string, error: string, retries: number): Promise<void>;
252
+ getPendingCount(): Promise<number>;
253
+ clear(): Promise<void>;
254
+ /** Full-text search over message content. SQLiteStorage uses FTS5; others use basic contains. */
255
+ searchMessages?(query: string, opts?: {
256
+ conversationId?: string;
257
+ limit?: number;
258
+ }): Promise<StoredMessage[]>;
259
+ /** Approximate storage size in bytes by category. */
260
+ getStorageSize?(): Promise<{
261
+ messages: number;
262
+ conversations: number;
263
+ total: number;
264
+ }>;
265
+ /** Delete oldest messages in a conversation, keeping the newest `keepCount`. Returns deleted count. */
266
+ clearOldMessages?(conversationId: string, keepCount: number): Promise<number>;
267
+ }
268
+ declare class MemoryStorage implements StorageAdapter {
269
+ private messages;
270
+ private conversations;
271
+ private contacts;
272
+ private cursors;
273
+ private outbox;
274
+ init(): Promise<void>;
275
+ putMessages(messages: StoredMessage[]): Promise<void>;
276
+ getMessages(conversationId: string, opts: {
277
+ limit: number;
278
+ before?: string;
279
+ }): Promise<StoredMessage[]>;
280
+ getMessage(messageId: string): Promise<StoredMessage | null>;
281
+ deleteMessage(messageId: string): Promise<void>;
282
+ putConversations(conversations: StoredConversation[]): Promise<void>;
283
+ getConversations(opts?: {
284
+ limit: number;
285
+ offset?: number;
286
+ }): Promise<StoredConversation[]>;
287
+ getConversation(id: string): Promise<StoredConversation | null>;
288
+ putContacts(contacts: StoredContact[]): Promise<void>;
289
+ getContacts(): Promise<StoredContact[]>;
290
+ getCursor(key: string): Promise<string | null>;
291
+ setCursor(key: string, value: string): Promise<void>;
292
+ enqueue(op: OutboxOperation): Promise<void>;
293
+ dequeueReady(limit: number): Promise<OutboxOperation[]>;
294
+ ack(opId: string): Promise<void>;
295
+ nack(opId: string, error: string, retries: number): Promise<void>;
296
+ getPendingCount(): Promise<number>;
297
+ searchMessages(query: string, opts?: {
298
+ conversationId?: string;
299
+ limit?: number;
300
+ }): Promise<StoredMessage[]>;
301
+ getStorageSize(): Promise<{
302
+ messages: number;
303
+ conversations: number;
304
+ total: number;
305
+ }>;
306
+ clearOldMessages(conversationId: string, keepCount: number): Promise<number>;
307
+ clear(): Promise<void>;
308
+ }
309
+ declare class IndexedDBStorage implements StorageAdapter {
310
+ private dbName;
311
+ private version;
312
+ private db;
313
+ constructor(dbName?: string, version?: number);
314
+ init(): Promise<void>;
315
+ private tx;
316
+ private req;
317
+ putMessages(messages: StoredMessage[]): Promise<void>;
318
+ getMessages(conversationId: string, opts: {
319
+ limit: number;
320
+ before?: string;
321
+ }): Promise<StoredMessage[]>;
322
+ getMessage(messageId: string): Promise<StoredMessage | null>;
323
+ deleteMessage(messageId: string): Promise<void>;
324
+ putConversations(conversations: StoredConversation[]): Promise<void>;
325
+ getConversations(opts?: {
326
+ limit: number;
327
+ offset?: number;
328
+ }): Promise<StoredConversation[]>;
329
+ getConversation(id: string): Promise<StoredConversation | null>;
330
+ putContacts(contacts: StoredContact[]): Promise<void>;
331
+ getContacts(): Promise<StoredContact[]>;
332
+ getCursor(key: string): Promise<string | null>;
333
+ setCursor(key: string, value: string): Promise<void>;
334
+ enqueue(op: OutboxOperation): Promise<void>;
335
+ dequeueReady(limit: number): Promise<OutboxOperation[]>;
336
+ ack(opId: string): Promise<void>;
337
+ nack(opId: string, error: string, retries: number): Promise<void>;
338
+ getPendingCount(): Promise<number>;
339
+ searchMessages(query: string, opts?: {
340
+ conversationId?: string;
341
+ limit?: number;
342
+ }): Promise<StoredMessage[]>;
343
+ getStorageSize(): Promise<{
344
+ messages: number;
345
+ conversations: number;
346
+ total: number;
347
+ }>;
348
+ clearOldMessages(conversationId: string, keepCount: number): Promise<number>;
349
+ clear(): Promise<void>;
350
+ }
351
+ /**
352
+ * SQLiteStorage uses `better-sqlite3` for synchronous, fast local persistence.
353
+ * Includes FTS5 full-text search for message content.
354
+ *
355
+ * Usage:
356
+ * import { SQLiteStorage } from 'prismer/storage';
357
+ * const storage = new SQLiteStorage('./my-app.db');
358
+ * await storage.init();
359
+ */
360
+ declare class SQLiteStorage implements StorageAdapter {
361
+ private db;
362
+ private dbPath;
363
+ constructor(dbPath?: string);
364
+ init(): Promise<void>;
365
+ private ensureDb;
366
+ putMessages(messages: StoredMessage[]): Promise<void>;
367
+ getMessages(conversationId: string, opts: {
368
+ limit: number;
369
+ before?: string;
370
+ }): Promise<StoredMessage[]>;
371
+ getMessage(messageId: string): Promise<StoredMessage | null>;
372
+ deleteMessage(messageId: string): Promise<void>;
373
+ private rowToMessage;
374
+ putConversations(conversations: StoredConversation[]): Promise<void>;
375
+ getConversations(opts?: {
376
+ limit: number;
377
+ offset?: number;
378
+ }): Promise<StoredConversation[]>;
379
+ getConversation(id: string): Promise<StoredConversation | null>;
380
+ private rowToConversation;
381
+ putContacts(contacts: StoredContact[]): Promise<void>;
382
+ getContacts(): Promise<StoredContact[]>;
383
+ getCursor(key: string): Promise<string | null>;
384
+ setCursor(key: string, value: string): Promise<void>;
385
+ enqueue(op: OutboxOperation): Promise<void>;
386
+ dequeueReady(limit: number): Promise<OutboxOperation[]>;
387
+ ack(opId: string): Promise<void>;
388
+ nack(opId: string, error: string, retries: number): Promise<void>;
389
+ getPendingCount(): Promise<number>;
390
+ searchMessages(query: string, opts?: {
391
+ conversationId?: string;
392
+ limit?: number;
393
+ }): Promise<StoredMessage[]>;
394
+ getStorageSize(): Promise<{
395
+ messages: number;
396
+ conversations: number;
397
+ total: number;
398
+ }>;
399
+ clearOldMessages(conversationId: string, keepCount: number): Promise<number>;
400
+ clear(): Promise<void>;
401
+ private rowToOutbox;
402
+ }
403
+
165
404
  type Environment = 'production';
166
405
  declare const ENVIRONMENTS: Record<Environment, string>;
167
406
  interface PrismerConfig {
@@ -177,6 +416,8 @@ interface PrismerConfig {
177
416
  fetch?: typeof fetch;
178
417
  /** Default X-IM-Agent header for IM requests (select which agent identity to use) */
179
418
  imAgent?: string;
419
+ /** Enable offline-first mode for IM with local persistence and sync */
420
+ offline?: OfflineConfig;
180
421
  }
181
422
  interface LoadOptions {
182
423
  inputType?: 'auto' | 'url' | 'urls' | 'query';
@@ -271,6 +512,7 @@ interface SaveOptions {
271
512
  url: string;
272
513
  hqcc: string;
273
514
  raw?: string;
515
+ visibility?: 'public' | 'private' | 'unlisted';
274
516
  meta?: Record<string, any>;
275
517
  }
276
518
  interface SaveBatchOptions {
@@ -280,14 +522,19 @@ interface SaveResult {
280
522
  success: boolean;
281
523
  status?: string;
282
524
  url?: string;
525
+ content_uri?: string;
526
+ visibility?: string;
283
527
  results?: Array<{
284
528
  url: string;
285
529
  status: string;
530
+ content_uri?: string;
286
531
  }>;
287
532
  summary?: {
288
533
  total: number;
289
534
  created: number;
290
- exists: number;
535
+ updated?: number;
536
+ failed?: number;
537
+ exists?: number;
291
538
  };
292
539
  error?: {
293
540
  code: string;
@@ -555,6 +802,69 @@ interface IMDiscoverOptions {
555
802
  type?: string;
556
803
  capability?: string;
557
804
  }
805
+ interface IMPresignOptions {
806
+ fileName: string;
807
+ fileSize: number;
808
+ mimeType: string;
809
+ }
810
+ interface IMPresignResult {
811
+ uploadId: string;
812
+ url: string;
813
+ fields: Record<string, string>;
814
+ expiresAt: string;
815
+ }
816
+ interface IMConfirmResult {
817
+ uploadId: string;
818
+ cdnUrl: string;
819
+ fileName: string;
820
+ fileSize: number;
821
+ mimeType: string;
822
+ sha256: string | null;
823
+ cost: number;
824
+ }
825
+ interface IMFileQuota {
826
+ used: number;
827
+ limit: number;
828
+ tier: string;
829
+ fileCount: number;
830
+ }
831
+ /** Input source for upload() — polymorphic across Node.js and browser */
832
+ type FileInput = File | Blob | Buffer | Uint8Array | string;
833
+ interface UploadOptions {
834
+ /** File name (required if input is Buffer/Uint8Array/Blob without name) */
835
+ fileName?: string;
836
+ /** MIME type (auto-detected from fileName extension if not provided) */
837
+ mimeType?: string;
838
+ /** Progress callback */
839
+ onProgress?: (uploaded: number, total: number) => void;
840
+ }
841
+ interface UploadResult {
842
+ uploadId: string;
843
+ cdnUrl: string;
844
+ fileName: string;
845
+ fileSize: number;
846
+ mimeType: string;
847
+ sha256: string | null;
848
+ cost: number;
849
+ }
850
+ interface SendFileOptions extends UploadOptions {
851
+ /** Message content (defaults to fileName) */
852
+ content?: string;
853
+ /** Parent message ID for threading */
854
+ parentId?: string;
855
+ }
856
+ interface SendFileResult {
857
+ upload: UploadResult;
858
+ message: any;
859
+ }
860
+ interface IMMultipartInitResult {
861
+ uploadId: string;
862
+ parts: Array<{
863
+ partNumber: number;
864
+ url: string;
865
+ }>;
866
+ expiresAt: string;
867
+ }
558
868
  /** Generic IM API response wrapper */
559
869
  interface IMResult<T = any> {
560
870
  ok: boolean;
@@ -568,9 +878,373 @@ interface IMResult<T = any> {
568
878
  message: string;
569
879
  };
570
880
  }
881
+ interface OfflineConfig {
882
+ /** Storage adapter implementation (IndexedDBStorage, MemoryStorage, SQLiteStorage) */
883
+ storage: StorageAdapter;
884
+ /** Auto-sync on reconnect (default: true) */
885
+ syncOnConnect?: boolean;
886
+ /** Max retries per outbox operation (default: 5) */
887
+ outboxRetryLimit?: number;
888
+ /** Outbox flush interval in ms (default: 1000) */
889
+ outboxFlushInterval?: number;
890
+ /** Conflict strategy: 'server' = server wins, 'client' = client wins (default: 'server') */
891
+ conflictStrategy?: 'server' | 'client';
892
+ /** Custom conflict resolver — called when server and local message diverge */
893
+ onConflict?: (local: StoredMessage, remote: {
894
+ type: string;
895
+ data: any;
896
+ seq: number;
897
+ }) => 'keep_local' | 'accept_remote' | StoredMessage;
898
+ /** Sync mode: 'push' = SSE continuous stream, 'poll' = periodic polling (default: 'push') */
899
+ syncMode?: 'push' | 'poll';
900
+ /** Enable multi-tab coordination via BroadcastChannel (default: true in browser, false in Node.js) */
901
+ multiTab?: boolean;
902
+ /** E2E encryption config */
903
+ e2e?: {
904
+ enabled: boolean;
905
+ /** User passphrase for master key derivation (PBKDF2) */
906
+ passphrase: string;
907
+ };
908
+ /** Storage quota config */
909
+ quota?: {
910
+ /** Max storage size in bytes (default: 500MB) */
911
+ maxStorageBytes?: number;
912
+ /** Warning threshold 0-1 (default: 0.9 = 90%) */
913
+ warningThreshold?: number;
914
+ };
915
+ }
571
916
  /** Internal request function type */
572
917
  type RequestFn = <T>(method: string, path: string, body?: unknown, query?: Record<string, string>) => Promise<T>;
573
918
 
919
+ /**
920
+ * Prismer SDK — Offline Manager, Outbox Queue, and Sync Engine.
921
+ *
922
+ * Orchestrates local persistence, optimistic writes, and incremental sync.
923
+ */
924
+
925
+ interface SyncEvent {
926
+ seq: number;
927
+ type: string;
928
+ data: any;
929
+ conversationId?: string;
930
+ at: string;
931
+ }
932
+ interface SyncResult {
933
+ events: SyncEvent[];
934
+ cursor: number;
935
+ hasMore: boolean;
936
+ }
937
+ interface OfflineEventMap {
938
+ 'sync.start': undefined;
939
+ 'sync.progress': {
940
+ synced: number;
941
+ total: number;
942
+ };
943
+ 'sync.complete': {
944
+ newMessages: number;
945
+ updatedConversations: number;
946
+ };
947
+ 'sync.error': {
948
+ error: string;
949
+ willRetry: boolean;
950
+ };
951
+ 'outbox.sending': {
952
+ opId: string;
953
+ type: string;
954
+ };
955
+ 'outbox.confirmed': {
956
+ opId: string;
957
+ serverData: any;
958
+ };
959
+ 'outbox.failed': {
960
+ opId: string;
961
+ error: string;
962
+ retriesLeft: number;
963
+ };
964
+ 'message.local': StoredMessage;
965
+ 'message.confirmed': {
966
+ clientId: string;
967
+ serverMessage: any;
968
+ };
969
+ 'message.failed': {
970
+ clientId: string;
971
+ error: string;
972
+ };
973
+ 'network.online': undefined;
974
+ 'network.offline': undefined;
975
+ 'presence.changed': {
976
+ userId: string;
977
+ status: string;
978
+ lastSeen?: string;
979
+ };
980
+ 'quota.warning': {
981
+ used: number;
982
+ limit: number;
983
+ percentage: number;
984
+ };
985
+ 'quota.exceeded': {
986
+ used: number;
987
+ limit: number;
988
+ };
989
+ }
990
+ type OfflineEventType = keyof OfflineEventMap;
991
+ type Listener<T> = (payload: T) => void;
992
+ declare class OfflineEmitter {
993
+ private listeners;
994
+ on<E extends OfflineEventType>(event: E, cb: Listener<OfflineEventMap[E]>): this;
995
+ off<E extends OfflineEventType>(event: E, cb: Listener<OfflineEventMap[E]>): this;
996
+ emit<E extends OfflineEventType>(event: E, payload: OfflineEventMap[E]): void;
997
+ removeAllListeners(): void;
998
+ }
999
+ declare class OfflineManager extends OfflineEmitter {
1000
+ readonly storage: StorageAdapter;
1001
+ private networkRequest;
1002
+ private options;
1003
+ private flushTimer;
1004
+ private flushing;
1005
+ private _isOnline;
1006
+ private _syncState;
1007
+ private sseSource;
1008
+ private sseReconnectTimer;
1009
+ private sseReconnectAttempts;
1010
+ /** Presence cache for realtime presence events */
1011
+ private presenceCache;
1012
+ /** Auth token provider — set by PrismerClient for SSE auth */
1013
+ tokenProvider?: () => string | undefined;
1014
+ get isOnline(): boolean;
1015
+ get syncState(): string;
1016
+ constructor(storage: StorageAdapter, networkRequest: RequestFn, options?: Omit<OfflineConfig, 'storage'>);
1017
+ init(): Promise<void>;
1018
+ destroy(): Promise<void>;
1019
+ setOnline(online: boolean): void;
1020
+ /**
1021
+ * Dispatch an IM request. Write ops go through outbox; reads check local cache.
1022
+ */
1023
+ dispatch<T>(method: string, path: string, body?: unknown, query?: Record<string, string>): Promise<T>;
1024
+ private dispatchWrite;
1025
+ private startFlushTimer;
1026
+ private stopFlushTimer;
1027
+ flush(): Promise<void>;
1028
+ get outboxSize(): Promise<number>;
1029
+ sync(): Promise<void>;
1030
+ private applySyncEvent;
1031
+ /**
1032
+ * Handle a realtime event (from WS/SSE) and store locally.
1033
+ */
1034
+ handleRealtimeEvent(type: string, payload: any): Promise<void>;
1035
+ /**
1036
+ * Get cached presence status for a user.
1037
+ */
1038
+ getPresence(userId: string): {
1039
+ status: string;
1040
+ lastSeen: string;
1041
+ } | null;
1042
+ /**
1043
+ * Search messages in local storage.
1044
+ */
1045
+ searchMessages(query: string, opts?: {
1046
+ conversationId?: string;
1047
+ limit?: number;
1048
+ }): Promise<StoredMessage[]>;
1049
+ /**
1050
+ * Get storage size and quota info.
1051
+ */
1052
+ getQuotaStatus(): Promise<{
1053
+ used: number;
1054
+ limit: number;
1055
+ percentage: number;
1056
+ warning: boolean;
1057
+ exceeded: boolean;
1058
+ }>;
1059
+ /**
1060
+ * Clear old messages for a conversation (user-initiated quota management).
1061
+ */
1062
+ clearOldMessages(conversationId: string, keepCount: number): Promise<number>;
1063
+ private readFromCache;
1064
+ private cacheReadResult;
1065
+ /**
1066
+ * Start continuous sync via SSE (Server-Sent Events).
1067
+ * Replaces polling with real-time push when syncMode is 'push'.
1068
+ */
1069
+ startContinuousSync(): Promise<void>;
1070
+ /**
1071
+ * Stop the SSE continuous sync connection.
1072
+ */
1073
+ stopContinuousSync(): void;
1074
+ private scheduleSseReconnect;
1075
+ /** Get the base URL for SSE connections (strip /api/im prefix). */
1076
+ private getBaseUrl;
1077
+ private checkQuota;
1078
+ }
1079
+ interface QueuedAttachment {
1080
+ id: string;
1081
+ conversationId: string;
1082
+ file: {
1083
+ name: string;
1084
+ size: number;
1085
+ type: string;
1086
+ };
1087
+ /** File data — stored in memory for MemoryStorage, in IndexedDB/SQLite for persistent */
1088
+ data?: ArrayBuffer;
1089
+ status: 'pending' | 'uploading' | 'uploaded' | 'failed';
1090
+ progress: number;
1091
+ messageClientId: string;
1092
+ error?: string;
1093
+ createdAt: number;
1094
+ }
1095
+ /**
1096
+ * AttachmentQueue manages offline file uploads.
1097
+ * Files are queued locally and uploaded when online, then a message
1098
+ * is sent referencing the uploaded file.
1099
+ */
1100
+ declare class AttachmentQueue {
1101
+ private offline;
1102
+ private networkRequest;
1103
+ private queue;
1104
+ private uploading;
1105
+ constructor(offline: OfflineManager, networkRequest: RequestFn);
1106
+ /**
1107
+ * Queue a file attachment for offline upload.
1108
+ * Returns the queued attachment with a local ID.
1109
+ */
1110
+ queueAttachment(conversationId: string, file: {
1111
+ name: string;
1112
+ size: number;
1113
+ type: string;
1114
+ data: ArrayBuffer;
1115
+ }, messageContent?: string): Promise<QueuedAttachment>;
1116
+ /** Process pending uploads. */
1117
+ processQueue(): Promise<void>;
1118
+ /** Get all queued attachments. */
1119
+ getQueue(): QueuedAttachment[];
1120
+ /** Retry a failed attachment upload. */
1121
+ retry(attachmentId: string): Promise<void>;
1122
+ /** Cancel and remove a queued attachment. */
1123
+ cancel(attachmentId: string): Promise<void>;
1124
+ }
1125
+
1126
+ /**
1127
+ * Prismer SDK — Multi-Tab Coordination
1128
+ *
1129
+ * Uses BroadcastChannel API to coordinate multiple browser tabs.
1130
+ * Protocol: "Last login wins" — the most recently opened tab becomes leader.
1131
+ * Leader runs outbox flush + sync. Passive tabs receive events from leader.
1132
+ *
1133
+ * Fallback: In environments without BroadcastChannel (Node.js, old browsers),
1134
+ * this is a no-op — single-tab/single-process behavior.
1135
+ */
1136
+
1137
+ /**
1138
+ * TabCoordinator manages leadership election and event relay
1139
+ * between multiple browser tabs sharing the same IndexedDB.
1140
+ */
1141
+ declare class TabCoordinator {
1142
+ private offline;
1143
+ private channelName;
1144
+ private channel;
1145
+ private tabId;
1146
+ private _isLeader;
1147
+ private disposed;
1148
+ get isLeader(): boolean;
1149
+ constructor(offline: OfflineManager, channelName?: string);
1150
+ /**
1151
+ * Initialize tab coordination.
1152
+ * Claims leadership immediately (last-login-wins).
1153
+ */
1154
+ init(): void;
1155
+ /**
1156
+ * Release leadership and clean up.
1157
+ */
1158
+ destroy(): void;
1159
+ /**
1160
+ * Relay a sync event to passive tabs.
1161
+ * Called by the leader tab after processing a sync event.
1162
+ */
1163
+ relaySyncEvent(event: SyncEvent): void;
1164
+ private claimLeadership;
1165
+ private demoteToPassive;
1166
+ private handleMessage;
1167
+ private onBecomeLeader;
1168
+ private onBecomePassive;
1169
+ private broadcast;
1170
+ }
1171
+
1172
+ /**
1173
+ * Prismer SDK — E2E Encryption
1174
+ *
1175
+ * Industry-standard end-to-end encryption for IM messages.
1176
+ *
1177
+ * - Per-conversation symmetric key: AES-256-GCM
1178
+ * - Key exchange: ECDH P-256
1179
+ * - Master key derivation: PBKDF2-SHA256
1180
+ * - Key storage: Encrypted with master key
1181
+ * - Runtime: Web Crypto API (browser) / node:crypto (Node.js)
1182
+ *
1183
+ * The server only sees ciphertext — it cannot decrypt message content.
1184
+ */
1185
+ /**
1186
+ * E2EEncryption manages per-conversation symmetric keys and
1187
+ * encrypts/decrypts message content using AES-256-GCM.
1188
+ *
1189
+ * Usage:
1190
+ * const e2e = new E2EEncryption();
1191
+ * await e2e.init('user-passphrase');
1192
+ * const ciphertext = await e2e.encrypt('conv-123', 'Hello!');
1193
+ * const plaintext = await e2e.decrypt('conv-123', ciphertext);
1194
+ */
1195
+ declare class E2EEncryption {
1196
+ private masterKey;
1197
+ private keyPair;
1198
+ private sessionKeys;
1199
+ private salt;
1200
+ /**
1201
+ * Initialize encryption with user passphrase.
1202
+ * Derives a master key via PBKDF2 and generates an ECDH key pair.
1203
+ */
1204
+ init(passphrase: string): Promise<void>;
1205
+ /**
1206
+ * Export public key for sharing with conversation peers.
1207
+ */
1208
+ exportPublicKey(): Promise<JsonWebKey>;
1209
+ /**
1210
+ * Derive a shared session key for a conversation using ECDH.
1211
+ * Call this with each peer's public key.
1212
+ */
1213
+ deriveSessionKey(conversationId: string, peerPublicKey: JsonWebKey): Promise<void>;
1214
+ /**
1215
+ * Set a pre-shared session key for a conversation.
1216
+ * Useful when the key is exchanged out-of-band or derived from a group key.
1217
+ */
1218
+ setSessionKey(conversationId: string, rawKey: ArrayBuffer): Promise<void>;
1219
+ /**
1220
+ * Generate a random session key for a conversation.
1221
+ * Returns the raw key bytes for sharing with peers.
1222
+ */
1223
+ generateSessionKey(conversationId: string): Promise<ArrayBuffer>;
1224
+ /**
1225
+ * Encrypt plaintext for a conversation.
1226
+ * Returns base64-encoded ciphertext with prepended IV.
1227
+ */
1228
+ encrypt(conversationId: string, plaintext: string): Promise<string>;
1229
+ /**
1230
+ * Decrypt ciphertext from a conversation.
1231
+ * Expects base64-encoded data with prepended IV.
1232
+ */
1233
+ decrypt(conversationId: string, ciphertext: string): Promise<string>;
1234
+ /**
1235
+ * Check if a session key exists for a conversation.
1236
+ */
1237
+ hasSessionKey(conversationId: string): boolean;
1238
+ /**
1239
+ * Remove session key for a conversation.
1240
+ */
1241
+ removeSessionKey(conversationId: string): void;
1242
+ /**
1243
+ * Clear all keys and reset state.
1244
+ */
1245
+ destroy(): void;
1246
+ }
1247
+
574
1248
  /**
575
1249
  * Prismer Cloud SDK for TypeScript/JavaScript
576
1250
  *
@@ -705,6 +1379,55 @@ declare class WorkspaceClient {
705
1379
  /** @mention autocomplete */
706
1380
  mentionAutocomplete(conversationId: string, query?: string): Promise<IMResult<IMAutocompleteResult[]>>;
707
1381
  }
1382
+ /** File upload management (presign → upload → confirm) */
1383
+ declare class FilesClient {
1384
+ private _r;
1385
+ private _baseUrl;
1386
+ private _fetchFn;
1387
+ private _getAuthHeaders;
1388
+ constructor(_r: RequestFn, _baseUrl: string, _fetchFn: typeof fetch, _getAuthHeaders: () => Record<string, string>);
1389
+ /** Get a presigned upload URL */
1390
+ presign(options: IMPresignOptions): Promise<IMResult<IMPresignResult>>;
1391
+ /** Confirm an uploaded file (triggers validation + CDN activation) */
1392
+ confirm(uploadId: string): Promise<IMResult<IMConfirmResult>>;
1393
+ /** Get storage quota */
1394
+ quota(): Promise<IMResult<IMFileQuota>>;
1395
+ /** Delete a file */
1396
+ delete(uploadId: string): Promise<IMResult<void>>;
1397
+ /** List allowed MIME types */
1398
+ types(): Promise<IMResult<{
1399
+ allowedMimeTypes: string[];
1400
+ }>>;
1401
+ /** Initialize a multipart upload (for files > 10 MB) */
1402
+ initMultipart(opts: {
1403
+ fileName: string;
1404
+ fileSize: number;
1405
+ mimeType: string;
1406
+ }): Promise<IMResult<IMMultipartInitResult>>;
1407
+ /** Complete a multipart upload */
1408
+ completeMultipart(uploadId: string, parts: Array<{
1409
+ partNumber: number;
1410
+ etag: string;
1411
+ }>): Promise<IMResult<IMConfirmResult>>;
1412
+ /**
1413
+ * Upload a file (full lifecycle: presign → upload → confirm).
1414
+ *
1415
+ * @param input - File, Blob, Buffer, Uint8Array, or file path (Node.js string)
1416
+ * @param opts - Optional fileName, mimeType, onProgress
1417
+ * @returns Confirmed upload result with CDN URL
1418
+ */
1419
+ upload(input: FileInput, opts?: UploadOptions): Promise<UploadResult>;
1420
+ /**
1421
+ * Upload a file and send it as a message in one call.
1422
+ *
1423
+ * @param conversationId - Target conversation
1424
+ * @param input - File input (same as upload())
1425
+ * @param opts - Upload options + optional message content/parentId
1426
+ */
1427
+ sendFile(conversationId: string, input: FileInput, opts?: SendFileOptions): Promise<SendFileResult>;
1428
+ private _uploadSimple;
1429
+ private _uploadMultipart;
1430
+ }
708
1431
  /** Real-time connection factory (WebSocket & SSE) */
709
1432
  declare class IMRealtimeClient {
710
1433
  private _wsBase;
@@ -728,8 +1451,11 @@ declare class IMClient {
728
1451
  readonly bindings: BindingsClient;
729
1452
  readonly credits: CreditsClient;
730
1453
  readonly workspace: WorkspaceClient;
1454
+ readonly files: FilesClient;
731
1455
  readonly realtime: IMRealtimeClient;
732
- constructor(request: RequestFn, wsBase: string);
1456
+ /** Offline manager (null if offline mode not enabled) */
1457
+ readonly offline: OfflineManager | null;
1458
+ constructor(request: RequestFn, wsBase: string, fetchFn: typeof fetch, getAuthHeaders: () => Record<string, string>, offlineManager?: OfflineManager | null);
733
1459
  /** IM health check */
734
1460
  health(): Promise<IMResult<void>>;
735
1461
  }
@@ -739,14 +1465,19 @@ declare class PrismerClient {
739
1465
  private readonly timeout;
740
1466
  private readonly fetchFn;
741
1467
  private readonly imAgent?;
1468
+ private _offlineManager;
742
1469
  /** IM API sub-client */
743
1470
  readonly im: IMClient;
744
1471
  constructor(config?: PrismerConfig);
1472
+ /** Build auth headers for raw HTTP requests (used by file upload) */
1473
+ private _getAuthHeaders;
745
1474
  /**
746
1475
  * Set or update the auth token (API key or IM JWT).
747
1476
  * Useful after anonymous registration to set the returned JWT.
748
1477
  */
749
1478
  setToken(token: string): void;
1479
+ /** Cleanup resources (offline manager, timers). Call when disposing the client. */
1480
+ destroy(): Promise<void>;
750
1481
  private _request;
751
1482
  /** Load content from URL(s) or search query */
752
1483
  load(input: string | string[], options?: LoadOptions): Promise<LoadResult>;
@@ -773,4 +1504,4 @@ declare class PrismerClient {
773
1504
 
774
1505
  declare function createClient(config: PrismerConfig): PrismerClient;
775
1506
 
776
- export { AccountClient, type AuthenticatedPayload, type BatchSummary, type BatchUrlCost, BindingsClient, ContactsClient, ConversationsClient, CreditsClient, DirectClient, type DisconnectedPayload, ENVIRONMENTS, type Environment, type ErrorPayload, GroupsClient, type IMAgentCard, type IMAutocompleteResult, type IMBinding, type IMBindingData, IMClient, type IMContact, type IMConversation, type IMConversationsOptions, type IMCreateBindingOptions, type IMCreateGroupOptions, type IMCreditsData, type IMDiscoverAgent, type IMDiscoverOptions, type IMGroupData, type IMGroupMember, type IMMeData, type IMMessage, type IMMessageData, type IMPaginationOptions, IMRealtimeClient, type IMRegisterData, type IMRegisterOptions, type IMResult, type IMRouting, type IMSendOptions, type IMTokenData, type IMTransaction, type IMUser, type IMWorkspaceData, type IMWorkspaceInitGroupOptions, type IMWorkspaceInitOptions, type LoadOptions, type LoadResult, type LoadResultItem, type MessageNewPayload, MessagesClient, type ParseCost, type ParseCostBreakdown, type ParseDocument, type ParseDocumentImage, type ParseOptions, type ParseResult, type ParseUsage, type PongPayload, type PresenceChangedPayload, PrismerClient, type PrismerConfig, type QueryCost, type QuerySummary, type RankingFactors, type RealtimeCommand, type RealtimeConfig, type RealtimeEventMap, type RealtimeEventType, RealtimeSSEClient, type RealtimeState, RealtimeWSClient, type ReconnectingPayload, type RequestFn, type SaveBatchOptions, type SaveOptions, type SaveResult, type SingleUrlCost, type TypingIndicatorPayload, WorkspaceClient, createClient, PrismerClient as default };
1507
+ export { AccountClient, AttachmentQueue, type AuthenticatedPayload, type BatchSummary, type BatchUrlCost, BindingsClient, ContactsClient, ConversationsClient, CreditsClient, DirectClient, type DisconnectedPayload, E2EEncryption, ENVIRONMENTS, type Environment, type ErrorPayload, type FileInput, FilesClient, GroupsClient, type IMAgentCard, type IMAutocompleteResult, type IMBinding, type IMBindingData, IMClient, type IMConfirmResult, type IMContact, type IMConversation, type IMConversationsOptions, type IMCreateBindingOptions, type IMCreateGroupOptions, type IMCreditsData, type IMDiscoverAgent, type IMDiscoverOptions, type IMFileQuota, type IMGroupData, type IMGroupMember, type IMMeData, type IMMessage, type IMMessageData, type IMMultipartInitResult, type IMPaginationOptions, type IMPresignOptions, type IMPresignResult, IMRealtimeClient, type IMRegisterData, type IMRegisterOptions, type IMResult, type IMRouting, type IMSendOptions, type IMTokenData, type IMTransaction, type IMUser, type IMWorkspaceData, type IMWorkspaceInitGroupOptions, type IMWorkspaceInitOptions, IndexedDBStorage, type LoadOptions, type LoadResult, type LoadResultItem, MemoryStorage, type MessageNewPayload, MessagesClient, type OfflineConfig, type OfflineEventMap, type OfflineEventType, OfflineManager, type OutboxOperation, type ParseCost, type ParseCostBreakdown, type ParseDocument, type ParseDocumentImage, type ParseOptions, type ParseResult, type ParseUsage, type PongPayload, type PresenceChangedPayload, PrismerClient, type PrismerConfig, type QueryCost, type QuerySummary, type QueuedAttachment, type RankingFactors, type RealtimeCommand, type RealtimeConfig, type RealtimeEventMap, type RealtimeEventType, RealtimeSSEClient, type RealtimeState, RealtimeWSClient, type ReconnectingPayload, type RequestFn, SQLiteStorage, type SaveBatchOptions, type SaveOptions, type SaveResult, type SendFileOptions, type SendFileResult, type SingleUrlCost, type StorageAdapter, type StoredContact, type StoredConversation, type StoredMessage, type SyncEvent, type SyncResult, TabCoordinator, type TypingIndicatorPayload, type UploadOptions, type UploadResult, WorkspaceClient, createClient, PrismerClient as default };