chatly-sdk 0.0.8 → 2.0.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/IMPROVEMENTS.md CHANGED
@@ -4,7 +4,7 @@ This document outlines the major improvements and production-ready features that
4
4
 
5
5
  ---
6
6
 
7
- ## 🎯 Production-Ready Features
7
+ ## 🎯 Production-Ready Features (Beta)
8
8
 
9
9
  ### 1. Message Queue with Automatic Retry
10
10
 
package/README.md CHANGED
@@ -1,6 +1,9 @@
1
1
  # 🔐 Chatly SDK
2
2
 
3
- Beta end-to-end encrypted chat SDK with WhatsApp-style features, event-driven architecture, and automatic reconnection.
3
+ Production-ready end-to-end encrypted chat SDK with WhatsApp-style features, event-driven architecture, and automatic reconnection.
4
+
5
+ You can find the sample project repository here: [Chatly SDK Sample Project](https://github.com/bharath-arch/chatly-sdk-demo.git)
6
+
4
7
 
5
8
  [![npm version](https://img.shields.io/npm/v/chatly-sdk.svg)](https://www.npmjs.com/package/chatly-sdk)
6
9
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
@@ -12,6 +15,7 @@ Beta end-to-end encrypted chat SDK with WhatsApp-style features, event-driven ar
12
15
  - **Per-User Identity Keys** - Unique cryptographic identity
13
16
  - **Session-Based Encryption** - Secure 1:1 and group messaging
14
17
  - **Input Validation** - Protection against injection attacks
18
+ - **Configurable Media Storage** - Offload encrypted files to S3 or Local storage
15
19
 
16
20
  ### 💬 Messaging
17
21
  - **1:1 Chat** - Secure direct messaging
@@ -181,6 +185,7 @@ const sdk = new ChatSDK({
181
185
  userStore: new InMemoryUserStore(),
182
186
  messageStore: new InMemoryMessageStore(),
183
187
  groupStore: new InMemoryGroupStore(),
188
+ storageProvider: new LocalStorageProvider('./uploads'), // Optional: S3 or Local
184
189
  logLevel: LogLevel.INFO, // Optional: DEBUG, INFO, WARN, ERROR, NONE
185
190
  });
186
191
 
@@ -376,24 +381,59 @@ console.log('Supported image types:', SUPPORTED_MIME_TYPES.image);
376
381
  console.log('Max video size:', FILE_SIZE_LIMITS.video); // 100 MB
377
382
  ```
378
383
 
384
+ ### 📦 Media Storage & Offloading
385
+
386
+ To prevent database bloat, large media files are automatically offloaded to a storage provider. The SDK encrypts the file locally, then uploads the ciphertext.
387
+
388
+ #### Local Storage
389
+ Store files on the server's local filesystem:
390
+
391
+ ```typescript
392
+ import { LocalStorageProvider } from 'chatly-sdk';
393
+
394
+ const storage = new LocalStorageProvider('./uploads');
395
+ const sdk = new ChatSDK({ storageProvider: storage, ... });
396
+ ```
397
+
398
+ #### AWS S3 Storage
399
+ Store files in an S3 bucket:
400
+
401
+ ```typescript
402
+ import { S3StorageProvider } from 'chatly-sdk';
403
+
404
+ const storage = new S3StorageProvider({
405
+ region: 'us-east-1',
406
+ bucket: 'my-chat-app-media',
407
+ credentials: {
408
+ accessKeyId: '...',
409
+ secretAccessKey: '...'
410
+ }
411
+ });
412
+ const sdk = new ChatSDK({ storageProvider: storage, ... });
413
+ ```
414
+
379
415
  ### Media Encryption
380
416
 
381
417
  All media files are **fully encrypted end-to-end**:
382
418
 
383
- 1. **File data** is encrypted with the session/group key
384
- 2. **Metadata** (filename, size, etc.) is stored in plaintext for efficiency
385
- 3. **Thumbnails** (for images/videos) are encrypted
386
- 4. **No URL-based approach** - all files sent directly through SDK
419
+ 1. **Double Encryption**: File data and captions are encrypted separately with unique IVs.
420
+ 2. **Offloading**: Encrypted data is sent to the `StorageProvider`, leaving only a small reference in the database.
421
+ 3. **Automatic Management**: The SDK automatically downloads and decrypts media when needed.
422
+ 4. **Cleanup**: Deleting a message automatically triggers deletion of the remote file.
387
423
 
388
424
  ```typescript
389
- // Media encryption happens automatically
390
- const media = await createMediaAttachment(file);
391
- const message = await sdk.sendMediaMessage(session, caption, media);
392
-
393
- // Message contains:
394
- // - Encrypted caption (ciphertext)
395
- // - Encrypted media data (media.data)
396
- // - Plaintext metadata (media.metadata)
425
+ // Message in database now looks like this:
426
+ {
427
+ "id": "msg_123",
428
+ "type": "media",
429
+ "media": {
430
+ "storage": "s3",
431
+ "storageKey": "session_id/unique-file-name.jpg",
432
+ "iv": "media_specific_iv",
433
+ "data": undefined, // Raw data is removed from DB
434
+ "metadata": { ... }
435
+ }
436
+ }
397
437
  ```
398
438
 
399
439
  ### Example: Sending an Image
package/dist/index.d.ts CHANGED
@@ -37,8 +37,12 @@ interface MediaMetadata {
37
37
  */
38
38
  interface MediaAttachment {
39
39
  type: MediaType;
40
- data: string;
40
+ data?: string | undefined;
41
+ iv?: string | undefined;
41
42
  metadata: MediaMetadata;
43
+ storage?: 'local' | 's3' | undefined;
44
+ storageKey?: string | undefined;
45
+ url?: string | undefined;
42
46
  }
43
47
  /**
44
48
  * Supported MIME types
@@ -87,8 +91,10 @@ interface UserStoreAdapter {
87
91
  }
88
92
  interface MessageStoreAdapter {
89
93
  create(message: Message): Promise<Message>;
94
+ findById(id: string): Promise<Message | undefined>;
90
95
  listByUser(userId: string): Promise<Message[]>;
91
96
  listByGroup(groupId: string): Promise<Message[]>;
97
+ delete(id: string): Promise<void>;
92
98
  }
93
99
  interface GroupStoreAdapter {
94
100
  create(group: Group): Promise<Group>;
@@ -191,13 +197,43 @@ interface TransportAdapter {
191
197
  isConnected(): boolean;
192
198
  }
193
199
 
200
+ interface StorageUploadResult {
201
+ storageKey: string;
202
+ url?: string;
203
+ }
204
+ interface StorageProvider {
205
+ /**
206
+ * Name of the storage provider (e.g., 'local', 's3')
207
+ */
208
+ readonly name: string;
209
+ /**
210
+ * Upload data to storage
211
+ * @param data Base64 encoded data or Buffer
212
+ * @param filename Desired filename or path
213
+ * @param mimeType MIME type of the file
214
+ */
215
+ upload(data: string | Buffer, filename: string, mimeType: string): Promise<StorageUploadResult>;
216
+ /**
217
+ * Download data from storage
218
+ * @param storageKey Key/path of the file in storage
219
+ * @returns Base64 encoded data
220
+ */
221
+ download(storageKey: string): Promise<string>;
222
+ /**
223
+ * Delete data from storage
224
+ * @param storageKey Key/path of the file in storage
225
+ */
226
+ delete(storageKey: string): Promise<void>;
227
+ }
228
+
194
229
  declare class ChatSession {
195
230
  readonly id: string;
196
231
  readonly userA: User;
197
232
  readonly userB: User;
233
+ private storageProvider?;
198
234
  private sharedSecret;
199
235
  private ephemeralKeyPair;
200
- constructor(id: string, userA: User, userB: User);
236
+ constructor(id: string, userA: User, userB: User, storageProvider?: StorageProvider | undefined);
201
237
  /**
202
238
  * Initialize the session by deriving the shared secret
203
239
  * ECDH is commutative, so we can use either user's keys
@@ -219,6 +255,7 @@ declare class ChatSession {
219
255
  * Decrypt a message in this session
220
256
  */
221
257
  decrypt(message: Message, user: User): Promise<string>;
258
+ private deriveLegacySecret;
222
259
  /**
223
260
  * Decrypt a media message in this session
224
261
  */
@@ -230,8 +267,9 @@ declare class ChatSession {
230
267
 
231
268
  declare class GroupSession {
232
269
  readonly group: Group;
270
+ private storageProvider?;
233
271
  private groupKey;
234
- constructor(group: Group);
272
+ constructor(group: Group, storageProvider?: StorageProvider | undefined);
235
273
  /**
236
274
  * Initialize the session by deriving the group key
237
275
  */
@@ -303,8 +341,10 @@ declare class InMemoryUserStore implements UserStoreAdapter {
303
341
  declare class InMemoryMessageStore implements MessageStoreAdapter {
304
342
  private messages;
305
343
  create(message: Message): Promise<Message>;
344
+ findById(id: string): Promise<Message | undefined>;
306
345
  listByUser(userId: string): Promise<Message[]>;
307
346
  listByGroup(groupId: string): Promise<Message[]>;
347
+ delete(id: string): Promise<void>;
308
348
  }
309
349
 
310
350
  declare class InMemoryGroupStore implements GroupStoreAdapter {
@@ -481,10 +521,40 @@ declare function createMediaAttachment(file: File | Blob, filename?: string): Pr
481
521
  */
482
522
  declare function formatFileSize(bytes: number): string;
483
523
 
524
+ declare class LocalStorageProvider implements StorageProvider {
525
+ readonly name = "local";
526
+ private storageDir;
527
+ constructor(storageDir?: string);
528
+ upload(data: string | Buffer, filename: string, mimeType: string): Promise<StorageUploadResult>;
529
+ download(storageKey: string): Promise<string>;
530
+ delete(storageKey: string): Promise<void>;
531
+ }
532
+
533
+ interface S3Config {
534
+ region: string;
535
+ bucket: string;
536
+ credentials?: {
537
+ accessKeyId: string;
538
+ secretAccessKey: string;
539
+ };
540
+ endpoint?: string;
541
+ forcePathStyle?: boolean;
542
+ }
543
+ declare class S3StorageProvider implements StorageProvider {
544
+ readonly name = "s3";
545
+ private client;
546
+ private bucket;
547
+ constructor(config: S3Config);
548
+ upload(data: string | Buffer, filename: string, mimeType: string): Promise<StorageUploadResult>;
549
+ download(storageKey: string): Promise<string>;
550
+ delete(storageKey: string): Promise<void>;
551
+ }
552
+
484
553
  interface ChatSDKConfig {
485
554
  userStore: UserStoreAdapter;
486
555
  messageStore: MessageStoreAdapter;
487
556
  groupStore: GroupStoreAdapter;
557
+ storageProvider?: StorageProvider;
488
558
  transport?: TransportAdapter;
489
559
  logLevel?: LogLevel;
490
560
  }
@@ -606,4 +676,4 @@ declare class ChatSDK extends EventEmitter {
606
676
  };
607
677
  }
608
678
 
609
- export { ALGORITHM, AuthError, CONNECTION_TIMEOUT, ChatSDK, type ChatSDKConfig, ChatSession, ConfigError, ConnectionState, EVENTS, EncryptionError, FILE_SIZE_LIMITS, GROUP_MAX_MEMBERS, GROUP_MIN_MEMBERS, GROUP_NAME_MAX_LENGTH, type Group, GroupSession, type GroupStoreAdapter, HEARTBEAT_INTERVAL, IV_LENGTH, InMemoryGroupStore, InMemoryMessageStore, InMemoryTransport, InMemoryUserStore, KEY_LENGTH, LogLevel, Logger, type LoggerConfig, MAX_QUEUE_SIZE, MESSAGE_MAX_LENGTH, MESSAGE_RETRY_ATTEMPTS, MESSAGE_RETRY_DELAY, type MediaAttachment, type MediaMetadata, MediaType, type Message, MessageStatus, type MessageStoreAdapter, type MessageType, NetworkError, PBKDF2_ITERATIONS, RECONNECT_BASE_DELAY, RECONNECT_MAX_ATTEMPTS, RECONNECT_MAX_DELAY, SALT_LENGTH, SDKError, SUPPORTED_CURVE, SUPPORTED_MIME_TYPES, SessionError, StorageError, type StoredUser, TAG_LENGTH, type TransportAdapter, TransportError, USERNAME_MAX_LENGTH, USERNAME_MIN_LENGTH, type User, type UserStoreAdapter, ValidationError, WebSocketClient, createMediaAttachment, createMediaMetadata, decodeBase64ToBlob, encodeFileToBase64, formatFileSize, getMediaType, logger, validateGroupMembers, validateGroupName, validateMediaFile, validateMessage, validateUserId, validateUsername };
679
+ export { ALGORITHM, AuthError, CONNECTION_TIMEOUT, ChatSDK, type ChatSDKConfig, ChatSession, ConfigError, ConnectionState, EVENTS, EncryptionError, FILE_SIZE_LIMITS, GROUP_MAX_MEMBERS, GROUP_MIN_MEMBERS, GROUP_NAME_MAX_LENGTH, type Group, GroupSession, type GroupStoreAdapter, HEARTBEAT_INTERVAL, IV_LENGTH, InMemoryGroupStore, InMemoryMessageStore, InMemoryTransport, InMemoryUserStore, KEY_LENGTH, LocalStorageProvider, LogLevel, Logger, type LoggerConfig, MAX_QUEUE_SIZE, MESSAGE_MAX_LENGTH, MESSAGE_RETRY_ATTEMPTS, MESSAGE_RETRY_DELAY, type MediaAttachment, type MediaMetadata, MediaType, type Message, MessageStatus, type MessageStoreAdapter, type MessageType, NetworkError, PBKDF2_ITERATIONS, RECONNECT_BASE_DELAY, RECONNECT_MAX_ATTEMPTS, RECONNECT_MAX_DELAY, type S3Config, S3StorageProvider, SALT_LENGTH, SDKError, SUPPORTED_CURVE, SUPPORTED_MIME_TYPES, SessionError, StorageError, type StorageProvider, type StorageUploadResult, type StoredUser, TAG_LENGTH, type TransportAdapter, TransportError, USERNAME_MAX_LENGTH, USERNAME_MIN_LENGTH, type User, type UserStoreAdapter, ValidationError, WebSocketClient, createMediaAttachment, createMediaMetadata, decodeBase64ToBlob, encodeFileToBase64, formatFileSize, getMediaType, logger, validateGroupMembers, validateGroupName, validateMediaFile, validateMessage, validateUserId, validateUsername };