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/dist/index.js CHANGED
@@ -9,6 +9,8 @@ function bufferToBase64(buffer) {
9
9
  return buffer.toString("base64");
10
10
  }
11
11
  function base64ToBuffer(data) {
12
+ if (Buffer.isBuffer(data)) return data;
13
+ if (!data) return Buffer.alloc(0);
12
14
  return Buffer.from(data, "base64");
13
15
  }
14
16
 
@@ -45,6 +47,11 @@ function deriveSharedSecret(local, remotePublicKey) {
45
47
  const derivedKey = pbkdf2Sync(sharedSecret, salt, PBKDF2_ITERATIONS, KEY_LENGTH, "sha256");
46
48
  return derivedKey;
47
49
  }
50
+ function deriveLegacySharedSecret(local, remotePublicKey) {
51
+ const ecdh = createECDH2(SUPPORTED_CURVE);
52
+ ecdh.setPrivateKey(base64ToBuffer(local.privateKey));
53
+ return ecdh.computeSecret(base64ToBuffer(remotePublicKey));
54
+ }
48
55
  function encryptMessage(plaintext, secret) {
49
56
  const iv = randomBytes2(IV_LENGTH);
50
57
  const cipher = createCipheriv(ALGORITHM, secret, iv);
@@ -95,12 +102,78 @@ function generateUUID() {
95
102
  });
96
103
  }
97
104
 
105
+ // src/utils/logger.ts
106
+ var LogLevel = /* @__PURE__ */ ((LogLevel3) => {
107
+ LogLevel3[LogLevel3["DEBUG"] = 0] = "DEBUG";
108
+ LogLevel3[LogLevel3["INFO"] = 1] = "INFO";
109
+ LogLevel3[LogLevel3["WARN"] = 2] = "WARN";
110
+ LogLevel3[LogLevel3["ERROR"] = 3] = "ERROR";
111
+ LogLevel3[LogLevel3["NONE"] = 4] = "NONE";
112
+ return LogLevel3;
113
+ })(LogLevel || {});
114
+ var Logger = class {
115
+ config;
116
+ constructor(config = {}) {
117
+ this.config = {
118
+ level: config.level ?? 1 /* INFO */,
119
+ prefix: config.prefix ?? "[ChatSDK]",
120
+ timestamp: config.timestamp ?? true
121
+ };
122
+ }
123
+ shouldLog(level) {
124
+ return level >= this.config.level;
125
+ }
126
+ formatMessage(level, message, data) {
127
+ const parts = [];
128
+ if (this.config.timestamp) {
129
+ parts.push((/* @__PURE__ */ new Date()).toISOString());
130
+ }
131
+ parts.push(this.config.prefix);
132
+ parts.push(`[${level}]`);
133
+ parts.push(message);
134
+ let formatted = parts.join(" ");
135
+ if (data !== void 0) {
136
+ formatted += " " + JSON.stringify(data, null, 2);
137
+ }
138
+ return formatted;
139
+ }
140
+ debug(message, data) {
141
+ if (this.shouldLog(0 /* DEBUG */)) {
142
+ console.debug(this.formatMessage("DEBUG", message, data));
143
+ }
144
+ }
145
+ info(message, data) {
146
+ if (this.shouldLog(1 /* INFO */)) {
147
+ console.info(this.formatMessage("INFO", message, data));
148
+ }
149
+ }
150
+ warn(message, data) {
151
+ if (this.shouldLog(2 /* WARN */)) {
152
+ console.warn(this.formatMessage("WARN", message, data));
153
+ }
154
+ }
155
+ error(message, error) {
156
+ if (this.shouldLog(3 /* ERROR */)) {
157
+ const errorData = error instanceof Error ? { message: error.message, stack: error.stack } : error;
158
+ console.error(this.formatMessage("ERROR", message, errorData));
159
+ }
160
+ }
161
+ setLevel(level) {
162
+ this.config.level = level;
163
+ }
164
+ getLevel() {
165
+ return this.config.level;
166
+ }
167
+ };
168
+ var logger = new Logger();
169
+
98
170
  // src/chat/ChatSession.ts
99
171
  var ChatSession = class {
100
- constructor(id, userA, userB) {
172
+ constructor(id, userA, userB, storageProvider) {
101
173
  this.id = id;
102
174
  this.userA = userA;
103
175
  this.userB = userB;
176
+ this.storageProvider = storageProvider;
104
177
  }
105
178
  sharedSecret = null;
106
179
  ephemeralKeyPair = null;
@@ -124,6 +197,12 @@ var ChatSession = class {
124
197
  publicKey: user.publicKey,
125
198
  privateKey: user.privateKey
126
199
  };
200
+ logger.debug(`[ChatSession] Initializing for user ${user.id}`, {
201
+ hasLocalPriv: !!user.privateKey,
202
+ privType: typeof user.privateKey,
203
+ hasRemotePub: !!otherUser.publicKey,
204
+ pubType: typeof otherUser.publicKey
205
+ });
127
206
  this.sharedSecret = deriveSharedSecret(localKeyPair, otherUser.publicKey);
128
207
  }
129
208
  /**
@@ -158,14 +237,27 @@ var ChatSession = class {
158
237
  throw new Error("Failed to initialize session");
159
238
  }
160
239
  const { ciphertext, iv } = encryptMessage(plaintext, this.sharedSecret);
161
- const { ciphertext: encryptedMediaData } = encryptMessage(
162
- media.data,
240
+ const { ciphertext: encryptedMediaData, iv: mediaIv } = encryptMessage(
241
+ media.data || "",
163
242
  this.sharedSecret
164
243
  );
165
244
  const encryptedMedia = {
166
245
  ...media,
167
- data: encryptedMediaData
246
+ data: encryptedMediaData,
247
+ iv: mediaIv
168
248
  };
249
+ if (this.storageProvider) {
250
+ const filename = `${this.id}/${generateUUID()}-${media.metadata.filename}`;
251
+ const uploadResult = await this.storageProvider.upload(
252
+ encryptedMediaData,
253
+ filename,
254
+ media.metadata.mimeType
255
+ );
256
+ encryptedMedia.storage = this.storageProvider.name;
257
+ encryptedMedia.storageKey = uploadResult.storageKey;
258
+ encryptedMedia.url = uploadResult.url;
259
+ encryptedMedia.data = void 0;
260
+ }
169
261
  return {
170
262
  id: generateUUID(),
171
263
  senderId,
@@ -187,7 +279,29 @@ var ChatSession = class {
187
279
  if (!this.sharedSecret) {
188
280
  throw new Error("Failed to initialize session");
189
281
  }
190
- return decryptMessage(message.ciphertext, message.iv, this.sharedSecret);
282
+ try {
283
+ return decryptMessage(message.ciphertext, message.iv, this.sharedSecret);
284
+ } catch (error) {
285
+ const legacySecret = this.deriveLegacySecret(user);
286
+ try {
287
+ return decryptMessage(message.ciphertext, message.iv, legacySecret);
288
+ } catch (innerError) {
289
+ throw error;
290
+ }
291
+ }
292
+ }
293
+ deriveLegacySecret(user) {
294
+ const otherUser = user.id === this.userA.id ? this.userB : this.userA;
295
+ logger.debug(`[ChatSession] Deriving legacy secret for user ${user.id}`, {
296
+ hasPriv: !!user.privateKey,
297
+ privType: typeof user.privateKey,
298
+ remotePubType: typeof otherUser.publicKey
299
+ });
300
+ const localKeyPair = {
301
+ publicKey: user.publicKey,
302
+ privateKey: user.privateKey
303
+ };
304
+ return deriveLegacySharedSecret(localKeyPair, otherUser.publicKey);
191
305
  }
192
306
  /**
193
307
  * Decrypt a media message in this session
@@ -203,11 +317,32 @@ var ChatSession = class {
203
317
  throw new Error("Failed to initialize session");
204
318
  }
205
319
  const text = decryptMessage(message.ciphertext, message.iv, this.sharedSecret);
206
- const decryptedMediaData = decryptMessage(
207
- message.media.data,
208
- message.iv,
209
- this.sharedSecret
210
- );
320
+ let encryptedMediaData = message.media.data;
321
+ if (!encryptedMediaData && message.media.storageKey && this.storageProvider) {
322
+ encryptedMediaData = await this.storageProvider.download(message.media.storageKey);
323
+ }
324
+ if (!message.media.iv && !encryptedMediaData) {
325
+ throw new Error("Media data or IV missing");
326
+ }
327
+ let decryptedMediaData;
328
+ try {
329
+ decryptedMediaData = decryptMessage(
330
+ encryptedMediaData || "",
331
+ message.media.iv || message.iv,
332
+ this.sharedSecret
333
+ );
334
+ } catch (error) {
335
+ const legacySecret = this.deriveLegacySecret(user);
336
+ try {
337
+ decryptedMediaData = decryptMessage(
338
+ encryptedMediaData || "",
339
+ message.media.iv || message.iv,
340
+ legacySecret
341
+ );
342
+ } catch (innerError) {
343
+ throw error;
344
+ }
345
+ }
211
346
  const decryptedMedia = {
212
347
  ...message.media,
213
348
  data: decryptedMediaData
@@ -231,8 +366,9 @@ function deriveGroupKey(groupId) {
231
366
 
232
367
  // src/chat/GroupSession.ts
233
368
  var GroupSession = class {
234
- constructor(group) {
369
+ constructor(group, storageProvider) {
235
370
  this.group = group;
371
+ this.storageProvider = storageProvider;
236
372
  }
237
373
  groupKey = null;
238
374
  /**
@@ -274,14 +410,27 @@ var GroupSession = class {
274
410
  throw new Error("Failed to initialize group session");
275
411
  }
276
412
  const { ciphertext, iv } = encryptMessage(plaintext, this.groupKey);
277
- const { ciphertext: encryptedMediaData } = encryptMessage(
278
- media.data,
413
+ const { ciphertext: encryptedMediaData, iv: mediaIv } = encryptMessage(
414
+ media.data || "",
279
415
  this.groupKey
280
416
  );
281
417
  const encryptedMedia = {
282
418
  ...media,
283
- data: encryptedMediaData
419
+ data: encryptedMediaData,
420
+ iv: mediaIv
284
421
  };
422
+ if (this.storageProvider) {
423
+ const filename = `groups/${this.group.id}/${generateUUID()}-${media.metadata.filename}`;
424
+ const uploadResult = await this.storageProvider.upload(
425
+ encryptedMediaData,
426
+ filename,
427
+ media.metadata.mimeType
428
+ );
429
+ encryptedMedia.storage = this.storageProvider.name;
430
+ encryptedMedia.storageKey = uploadResult.storageKey;
431
+ encryptedMedia.url = uploadResult.url;
432
+ encryptedMedia.data = void 0;
433
+ }
285
434
  return {
286
435
  id: generateUUID(),
287
436
  senderId,
@@ -319,9 +468,17 @@ var GroupSession = class {
319
468
  throw new Error("Failed to initialize group session");
320
469
  }
321
470
  const text = decryptMessage(message.ciphertext, message.iv, this.groupKey);
471
+ let encryptedMediaData = message.media.data;
472
+ if (!encryptedMediaData && message.media.storageKey && this.storageProvider) {
473
+ encryptedMediaData = await this.storageProvider.download(message.media.storageKey);
474
+ }
475
+ if (!message.media.iv && !encryptedMediaData) {
476
+ throw new Error("Media data or IV missing");
477
+ }
322
478
  const decryptedMediaData = decryptMessage(
323
- message.media.data,
324
- message.iv,
479
+ encryptedMediaData || "",
480
+ message.media.iv || message.iv,
481
+ // Fallback to message IV for backward compatibility
325
482
  this.groupKey
326
483
  );
327
484
  const decryptedMedia = {
@@ -332,71 +489,6 @@ var GroupSession = class {
332
489
  }
333
490
  };
334
491
 
335
- // src/utils/logger.ts
336
- var LogLevel = /* @__PURE__ */ ((LogLevel3) => {
337
- LogLevel3[LogLevel3["DEBUG"] = 0] = "DEBUG";
338
- LogLevel3[LogLevel3["INFO"] = 1] = "INFO";
339
- LogLevel3[LogLevel3["WARN"] = 2] = "WARN";
340
- LogLevel3[LogLevel3["ERROR"] = 3] = "ERROR";
341
- LogLevel3[LogLevel3["NONE"] = 4] = "NONE";
342
- return LogLevel3;
343
- })(LogLevel || {});
344
- var Logger = class {
345
- config;
346
- constructor(config = {}) {
347
- this.config = {
348
- level: config.level ?? 1 /* INFO */,
349
- prefix: config.prefix ?? "[ChatSDK]",
350
- timestamp: config.timestamp ?? true
351
- };
352
- }
353
- shouldLog(level) {
354
- return level >= this.config.level;
355
- }
356
- formatMessage(level, message, data) {
357
- const parts = [];
358
- if (this.config.timestamp) {
359
- parts.push((/* @__PURE__ */ new Date()).toISOString());
360
- }
361
- parts.push(this.config.prefix);
362
- parts.push(`[${level}]`);
363
- parts.push(message);
364
- let formatted = parts.join(" ");
365
- if (data !== void 0) {
366
- formatted += " " + JSON.stringify(data, null, 2);
367
- }
368
- return formatted;
369
- }
370
- debug(message, data) {
371
- if (this.shouldLog(0 /* DEBUG */)) {
372
- console.debug(this.formatMessage("DEBUG", message, data));
373
- }
374
- }
375
- info(message, data) {
376
- if (this.shouldLog(1 /* INFO */)) {
377
- console.info(this.formatMessage("INFO", message, data));
378
- }
379
- }
380
- warn(message, data) {
381
- if (this.shouldLog(2 /* WARN */)) {
382
- console.warn(this.formatMessage("WARN", message, data));
383
- }
384
- }
385
- error(message, error) {
386
- if (this.shouldLog(3 /* ERROR */)) {
387
- const errorData = error instanceof Error ? { message: error.message, stack: error.stack } : error;
388
- console.error(this.formatMessage("ERROR", message, errorData));
389
- }
390
- }
391
- setLevel(level) {
392
- this.config.level = level;
393
- }
394
- getLevel() {
395
- return this.config.level;
396
- }
397
- };
398
- var logger = new Logger();
399
-
400
492
  // src/utils/errors.ts
401
493
  var SDKError = class extends Error {
402
494
  constructor(message, code, retryable = false, details) {
@@ -719,6 +811,9 @@ var InMemoryMessageStore = class {
719
811
  this.messages.push(message);
720
812
  return message;
721
813
  }
814
+ async findById(id) {
815
+ return this.messages.find((msg) => msg.id === id);
816
+ }
722
817
  async listByUser(userId) {
723
818
  return this.messages.filter(
724
819
  (msg) => msg.senderId === userId || msg.receiverId === userId
@@ -727,6 +822,9 @@ var InMemoryMessageStore = class {
727
822
  async listByGroup(groupId) {
728
823
  return this.messages.filter((msg) => msg.groupId === groupId);
729
824
  }
825
+ async delete(id) {
826
+ this.messages = this.messages.filter((msg) => msg.id !== id);
827
+ }
730
828
  };
731
829
 
732
830
  // src/stores/memory/groupStore.ts
@@ -767,7 +865,7 @@ var InMemoryTransport = class {
767
865
  if (this.stateHandler) {
768
866
  this.stateHandler(this.connectionState);
769
867
  }
770
- await new Promise((resolve) => setTimeout(resolve, 100));
868
+ await new Promise((resolve2) => setTimeout(resolve2, 100));
771
869
  this.connectionState = "connected" /* CONNECTED */;
772
870
  if (this.stateHandler) {
773
871
  this.stateHandler(this.connectionState);
@@ -837,7 +935,7 @@ var WebSocketClient = class {
837
935
  }
838
936
  this.updateState("connecting" /* CONNECTING */);
839
937
  logger.info("Connecting to WebSocket", { url: this.url, userId: this.currentUserId });
840
- return new Promise((resolve, reject) => {
938
+ return new Promise((resolve2, reject) => {
841
939
  try {
842
940
  const wsUrl = this.currentUserId ? `${this.url}?userId=${this.currentUserId}` : this.url;
843
941
  this.ws = new WebSocket(wsUrl);
@@ -855,7 +953,7 @@ var WebSocketClient = class {
855
953
  this.updateState("connected" /* CONNECTED */);
856
954
  logger.info("WebSocket connected");
857
955
  this.startHeartbeat();
858
- resolve();
956
+ resolve2();
859
957
  };
860
958
  this.ws.onmessage = (event) => {
861
959
  try {
@@ -1070,7 +1168,7 @@ var FILE_SIZE_LIMITS = {
1070
1168
 
1071
1169
  // src/utils/mediaUtils.ts
1072
1170
  async function encodeFileToBase64(file) {
1073
- return new Promise((resolve, reject) => {
1171
+ return new Promise((resolve2, reject) => {
1074
1172
  const reader = new FileReader();
1075
1173
  reader.onload = () => {
1076
1174
  const result = reader.result;
@@ -1083,7 +1181,7 @@ async function encodeFileToBase64(file) {
1083
1181
  reject(new Error("Failed to extract base64 data"));
1084
1182
  return;
1085
1183
  }
1086
- resolve(base64);
1184
+ resolve2(base64);
1087
1185
  };
1088
1186
  reader.onerror = () => reject(new Error("Failed to read file"));
1089
1187
  reader.readAsDataURL(file);
@@ -1145,12 +1243,12 @@ async function createMediaMetadata(file, filename) {
1145
1243
  return metadata;
1146
1244
  }
1147
1245
  function getImageDimensions(file) {
1148
- return new Promise((resolve, reject) => {
1246
+ return new Promise((resolve2, reject) => {
1149
1247
  const img = new Image();
1150
1248
  const url = URL.createObjectURL(file);
1151
1249
  img.onload = () => {
1152
1250
  URL.revokeObjectURL(url);
1153
- resolve({ width: img.width, height: img.height });
1251
+ resolve2({ width: img.width, height: img.height });
1154
1252
  };
1155
1253
  img.onerror = () => {
1156
1254
  URL.revokeObjectURL(url);
@@ -1160,7 +1258,7 @@ function getImageDimensions(file) {
1160
1258
  });
1161
1259
  }
1162
1260
  async function generateThumbnail(file) {
1163
- return new Promise((resolve, reject) => {
1261
+ return new Promise((resolve2, reject) => {
1164
1262
  const img = new Image();
1165
1263
  const url = URL.createObjectURL(file);
1166
1264
  img.onload = () => {
@@ -1194,7 +1292,7 @@ async function generateThumbnail(file) {
1194
1292
  reject(new Error("Failed to generate thumbnail"));
1195
1293
  return;
1196
1294
  }
1197
- resolve(thumbnail);
1295
+ resolve2(thumbnail);
1198
1296
  };
1199
1297
  img.onerror = () => {
1200
1298
  URL.revokeObjectURL(url);
@@ -1220,6 +1318,102 @@ function formatFileSize(bytes) {
1220
1318
  return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
1221
1319
  }
1222
1320
 
1321
+ // src/storage/localStorage.ts
1322
+ import { promises as fs } from "fs";
1323
+ import * as path from "path";
1324
+ var LocalStorageProvider = class {
1325
+ name = "local";
1326
+ storageDir;
1327
+ constructor(storageDir = "./storage") {
1328
+ this.storageDir = path.resolve(storageDir);
1329
+ fs.mkdir(this.storageDir, { recursive: true }).catch(() => {
1330
+ });
1331
+ }
1332
+ async upload(data, filename, mimeType) {
1333
+ const buffer = typeof data === "string" ? Buffer.from(data, "base64") : data;
1334
+ const filePath = path.join(this.storageDir, filename);
1335
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
1336
+ await fs.writeFile(filePath, buffer);
1337
+ return {
1338
+ storageKey: filename,
1339
+ url: `file://${filePath}`
1340
+ };
1341
+ }
1342
+ async download(storageKey) {
1343
+ const filePath = path.join(this.storageDir, storageKey);
1344
+ const buffer = await fs.readFile(filePath);
1345
+ return buffer.toString("base64");
1346
+ }
1347
+ async delete(storageKey) {
1348
+ const filePath = path.join(this.storageDir, storageKey);
1349
+ try {
1350
+ await fs.unlink(filePath);
1351
+ } catch (error) {
1352
+ if (error.code !== "ENOENT") {
1353
+ throw error;
1354
+ }
1355
+ }
1356
+ }
1357
+ };
1358
+
1359
+ // src/storage/s3Storage.ts
1360
+ import { S3Client, PutObjectCommand, GetObjectCommand, DeleteObjectCommand } from "@aws-sdk/client-s3";
1361
+ var S3StorageProvider = class {
1362
+ name = "s3";
1363
+ client;
1364
+ bucket;
1365
+ constructor(config) {
1366
+ const s3Config = {
1367
+ region: config.region,
1368
+ forcePathStyle: config.forcePathStyle
1369
+ };
1370
+ if (config.credentials) {
1371
+ s3Config.credentials = config.credentials;
1372
+ }
1373
+ if (config.endpoint) {
1374
+ s3Config.endpoint = config.endpoint;
1375
+ }
1376
+ this.client = new S3Client(s3Config);
1377
+ this.bucket = config.bucket;
1378
+ }
1379
+ async upload(data, filename, mimeType) {
1380
+ const body = typeof data === "string" ? Buffer.from(data, "base64") : data;
1381
+ await this.client.send(
1382
+ new PutObjectCommand({
1383
+ Bucket: this.bucket,
1384
+ Key: filename,
1385
+ Body: body,
1386
+ ContentType: mimeType
1387
+ })
1388
+ );
1389
+ return {
1390
+ storageKey: filename,
1391
+ url: `https://${this.bucket}.s3.amazonaws.com/${filename}`
1392
+ };
1393
+ }
1394
+ async download(storageKey) {
1395
+ const response = await this.client.send(
1396
+ new GetObjectCommand({
1397
+ Bucket: this.bucket,
1398
+ Key: storageKey
1399
+ })
1400
+ );
1401
+ if (!response.Body) {
1402
+ throw new Error("S3 download failed: empty body");
1403
+ }
1404
+ const bytes = await response.Body.transformToByteArray();
1405
+ return Buffer.from(bytes).toString("base64");
1406
+ }
1407
+ async delete(storageKey) {
1408
+ await this.client.send(
1409
+ new DeleteObjectCommand({
1410
+ Bucket: this.bucket,
1411
+ Key: storageKey
1412
+ })
1413
+ );
1414
+ }
1415
+ };
1416
+
1223
1417
  // src/index.ts
1224
1418
  var ChatSDK = class extends EventEmitter {
1225
1419
  config;
@@ -1363,7 +1557,7 @@ var ChatSDK = class extends EventEmitter {
1363
1557
  const ids = [userA.id, userB.id].sort();
1364
1558
  const sessionId = `${ids[0]}-${ids[1]}`;
1365
1559
  try {
1366
- const session = new ChatSession(sessionId, userA, userB);
1560
+ const session = new ChatSession(sessionId, userA, userB, this.config.storageProvider);
1367
1561
  await session.initialize();
1368
1562
  logger.info("Chat session created", { sessionId, users: [userA.id, userB.id] });
1369
1563
  this.emit(EVENTS.SESSION_CREATED, session);
@@ -1392,7 +1586,7 @@ var ChatSDK = class extends EventEmitter {
1392
1586
  };
1393
1587
  try {
1394
1588
  await this.config.groupStore.create(group);
1395
- const session = new GroupSession(group);
1589
+ const session = new GroupSession(group, this.config.storageProvider);
1396
1590
  await session.initialize();
1397
1591
  logger.info("Group created", { groupId: group.id, name: group.name, memberCount: members.length });
1398
1592
  this.emit(EVENTS.GROUP_CREATED, session);
@@ -1417,7 +1611,7 @@ var ChatSDK = class extends EventEmitter {
1417
1611
  if (!group) {
1418
1612
  throw new SessionError(`Group not found: ${id}`, { groupId: id });
1419
1613
  }
1420
- const session = new GroupSession(group);
1614
+ const session = new GroupSession(group, this.config.storageProvider);
1421
1615
  await session.initialize();
1422
1616
  logger.debug("Group loaded", { groupId: id });
1423
1617
  return session;
@@ -1549,7 +1743,7 @@ var ChatSDK = class extends EventEmitter {
1549
1743
  }
1550
1744
  const ids = [user.id, otherUser.id].sort();
1551
1745
  const sessionId = `${ids[0]}-${ids[1]}`;
1552
- const session = new ChatSession(sessionId, user, otherUser);
1746
+ const session = new ChatSession(sessionId, user, otherUser, this.config.storageProvider);
1553
1747
  await session.initializeForUser(user);
1554
1748
  return await session.decrypt(message, user);
1555
1749
  }
@@ -1573,7 +1767,7 @@ var ChatSDK = class extends EventEmitter {
1573
1767
  if (!group) {
1574
1768
  throw new SessionError(`Group not found: ${message.groupId}`, { groupId: message.groupId });
1575
1769
  }
1576
- const session = new GroupSession(group);
1770
+ const session = new GroupSession(group, this.config.storageProvider);
1577
1771
  await session.initialize();
1578
1772
  return await session.decryptMedia(message);
1579
1773
  } else {
@@ -1587,7 +1781,7 @@ var ChatSDK = class extends EventEmitter {
1587
1781
  }
1588
1782
  const ids = [user.id, otherUser.id].sort();
1589
1783
  const sessionId = `${ids[0]}-${ids[1]}`;
1590
- const session = new ChatSession(sessionId, user, otherUser);
1784
+ const session = new ChatSession(sessionId, user, otherUser, this.config.storageProvider);
1591
1785
  await session.initializeForUser(user);
1592
1786
  return await session.decryptMedia(message, user);
1593
1787
  }
@@ -1759,6 +1953,7 @@ export {
1759
1953
  InMemoryTransport,
1760
1954
  InMemoryUserStore,
1761
1955
  KEY_LENGTH3 as KEY_LENGTH,
1956
+ LocalStorageProvider,
1762
1957
  LogLevel,
1763
1958
  Logger,
1764
1959
  MAX_QUEUE_SIZE,
@@ -1772,6 +1967,7 @@ export {
1772
1967
  RECONNECT_BASE_DELAY,
1773
1968
  RECONNECT_MAX_ATTEMPTS,
1774
1969
  RECONNECT_MAX_DELAY,
1970
+ S3StorageProvider,
1775
1971
  SALT_LENGTH2 as SALT_LENGTH,
1776
1972
  SDKError,
1777
1973
  SUPPORTED_CURVE2 as SUPPORTED_CURVE,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chatly-sdk",
3
- "version": "0.0.8",
3
+ "version": "2.0.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -30,6 +30,7 @@
30
30
  "license": "MIT",
31
31
  "description": "Production-ready end-to-end encrypted chat SDK with WhatsApp-style features",
32
32
  "dependencies": {
33
+ "@aws-sdk/client-s3": "^3.958.0",
33
34
  "buffer": "^6.0.3",
34
35
  "events": "^3.3.0"
35
36
  },