@antzsoft/chat-core 1.0.7 → 1.0.9

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
@@ -232,6 +232,12 @@ interface CursorPaginatedResponse<T> {
232
232
  data: T[];
233
233
  meta: CursorPaginationMeta;
234
234
  }
235
+ type CompressionAlgorithm = 'webp' | 'jpeg' | 'gzip' | 'none';
236
+ interface CompressedFile extends UploadableFile {
237
+ originalSize: number;
238
+ compressed: boolean;
239
+ compressionAlgorithm: CompressionAlgorithm;
240
+ }
235
241
  interface PresignedUrlRequest {
236
242
  filename: string;
237
243
  mimeType: string;
@@ -348,6 +354,7 @@ interface SendMessageAttachment {
348
354
  filename: string;
349
355
  mimeType: string;
350
356
  size: number;
357
+ duration?: number;
351
358
  }
352
359
  interface OptimisticAttachment extends SendMessageAttachment {
353
360
  id: string;
@@ -425,6 +432,29 @@ interface PersistStorage {
425
432
  setItem(key: string, value: string): void | Promise<void>;
426
433
  removeItem(key: string): void | Promise<void>;
427
434
  }
435
+ /**
436
+ * Platform-provided compression function. Optional — if omitted, files are uploaded as-is.
437
+ * - Web: uses canvas (images) + CompressionStream (text/docs) — both browser-native
438
+ * - RN: uses expo-image-manipulator (images) + pako (text/docs)
439
+ * - Node: uses sharp (images) + zlib (text/docs)
440
+ */
441
+ type PlatformCompressFn = (file: UploadableFile, options: ResolvedCompressionConfig) => Promise<CompressedFile>;
442
+ interface CompressionConfig {
443
+ /** Master switch. Default: true when platformCompressFn is provided, false otherwise */
444
+ enabled?: boolean;
445
+ /** WebP quality for images, 0–1. Default: 0.85 */
446
+ imageQuality?: number;
447
+ /** Longest side cap in px before encoding. Default: 1920 */
448
+ imageMaxDimension?: number;
449
+ /** Gzip text-based documents (plain, csv, json, xml, yaml, svg). Default: true */
450
+ compressDocuments?: boolean;
451
+ }
452
+ interface ResolvedCompressionConfig {
453
+ enabled: boolean;
454
+ imageQuality: number;
455
+ imageMaxDimension: number;
456
+ compressDocuments: boolean;
457
+ }
428
458
  interface UploadConfig {
429
459
  /**
430
460
  * Per-type file size limits in MB. Can also pass a single number for all types.
@@ -479,6 +509,15 @@ interface AntzChatConfig {
479
509
  /** Must match server ENCRYPTION_MODE env var. Default: 'none' */
480
510
  encryptionMode?: 'none' | 'server';
481
511
  upload?: UploadConfig;
512
+ /**
513
+ * Optional compression config. Compression is disabled if platformCompressFn is not provided.
514
+ */
515
+ compression?: CompressionConfig;
516
+ /**
517
+ * Platform-specific compression implementation. Optional — omit to disable compression.
518
+ * Each SDK (web, RN) provides its own default; Node.js users wire in their own.
519
+ */
520
+ platformCompressFn?: PlatformCompressFn;
482
521
  /**
483
522
  * Number of messages fetched per page when loading chat history.
484
523
  * Default: 40
@@ -534,6 +573,8 @@ interface ResolvedConfig {
534
573
  onProgress?: (progress: number) => void;
535
574
  };
536
575
  platformUploadFn: PlatformUploadFn;
576
+ platformCompressFn?: PlatformCompressFn;
577
+ compression: ResolvedCompressionConfig;
537
578
  persistStorage: PersistStorage;
538
579
  messagePageSize: number;
539
580
  starredMessagePageSize: number;
@@ -541,6 +582,9 @@ interface ResolvedConfig {
541
582
  }
542
583
  declare function resolveConfig(config: AntzChatConfig): ResolvedConfig;
543
584
 
585
+ type CompressionStrategy = 'image' | 'gzip' | 'skip';
586
+ declare function getCompressionStrategy(mimeType: string, config: ResolvedCompressionConfig): CompressionStrategy;
587
+
544
588
  declare const authApi: {
545
589
  login(credentials: LoginCredentials): Promise<AuthResponse>;
546
590
  register(payload: RegisterData): Promise<AuthResponse>;
@@ -683,8 +727,11 @@ declare const storageApi: {
683
727
  * High-level batch upload.
684
728
  * The actual binary transfer is delegated to platformUploadFn so this
685
729
  * function is platform-agnostic (works on web and React Native).
730
+ * If platformCompressFn + compressionConfig are provided, each file is
731
+ * compressed before the presigned URL is requested (so the server receives
732
+ * the correct compressed size and MIME type).
686
733
  */
687
- declare function uploadBatch(files: UploadableFile[], platformUploadFn: PlatformUploadFn, conversationId?: string, onProgress?: (pct: number) => void): Promise<BatchUploadResult>;
734
+ declare function uploadBatch(files: UploadableFile[], platformUploadFn: PlatformUploadFn, conversationId?: string, onProgress?: (pct: number) => void, platformCompressFn?: PlatformCompressFn, compressionConfig?: ResolvedCompressionConfig): Promise<BatchUploadResult>;
688
735
 
689
736
  /** Shared fields for every device registration. */
690
737
  interface DeviceTokenBase {
@@ -1068,4 +1115,4 @@ declare class AntzChatClient {
1068
1115
  uploadIcon(conversationId: string, file: UploadableFile): Promise<Conversation>;
1069
1116
  }
1070
1117
 
1071
- export { AntzChatClient, type AntzChatConfig, type Attachment, type AuthResponse, type AuthTokens, type BatchUploadResult, type Conversation, type ConversationListParams, type ConversationUnreadCount, type CreateDirectData, type CreateGroupData, type CursorPaginatedResponse, type EncryptedContent, type EncryptionKeyInfo, type EncryptionMode, type FileResponse, type FileSizeLimits, type FileType, type LastReadEntry, type ListMessagesParams, type LoginCredentials, type Message, type MessageAckEvent, type MessageContent, type MessageDeletedEvent, type MessageDeletedForMeEvent, type MessageDeliveredEvent, type MessageReaction, type MessageReplyReference, type MessageUpdatedEvent, type MessagesDeliveredEvent, type MobileDeviceToken, type NewMessageEvent, type OptimisticAttachment, type PaginatedResponse, type Participant, type PersistStorage, type PlatformUploadFn, type PresignedUrlRequest, type PresignedUrlResponse, type QuietHours, type ReactionUpdatedEvent, type ReadReceiptEvent, type RegisterData, type RegisterDeviceTokenPayload, type ResolvedConfig, type ResolvedFileSizeLimits, type SearchParams, type SendData, type SendMessageAttachment, type SendMessagePayload, type SocketStatus, type StatusListener, type TokenStore, type TypingIndicatorEvent, type UnreadSummary, type UpdateConversationData, type UploadConfig, type UploadProgress, type UploadableFile, type User, type UserPreferences, type UserStatusEvent, type WebPushDeviceToken, authApi, connectSocket, conversationsApi, createAuthStore, devicesApi, disconnectSocket, getApiClient, getAuthStore, getSocket, getSocketStatus, initApiClient, initAuthStore, messagesApi, normalizeConversation, onSocketStatus, reconnectSocket, refreshSocketAuth, resetAuthStore, resolveConfig, setApiClientInstance, socketEmit, storageApi, tryGetSocket, uploadBatch, useChatStore, usersApi };
1118
+ export { AntzChatClient, type AntzChatConfig, type Attachment, type AuthResponse, type AuthTokens, type BatchUploadResult, type CompressedFile, type CompressionAlgorithm, type CompressionConfig, type Conversation, type ConversationListParams, type ConversationUnreadCount, type CreateDirectData, type CreateGroupData, type CursorPaginatedResponse, type EncryptedContent, type EncryptionKeyInfo, type EncryptionMode, type FileResponse, type FileSizeLimits, type FileType, type LastReadEntry, type ListMessagesParams, type LoginCredentials, type Message, type MessageAckEvent, type MessageContent, type MessageDeletedEvent, type MessageDeletedForMeEvent, type MessageDeliveredEvent, type MessageReaction, type MessageReplyReference, type MessageUpdatedEvent, type MessagesDeliveredEvent, type MobileDeviceToken, type NewMessageEvent, type OptimisticAttachment, type PaginatedResponse, type Participant, type PersistStorage, type PlatformCompressFn, type PlatformUploadFn, type PresignedUrlRequest, type PresignedUrlResponse, type QuietHours, type ReactionUpdatedEvent, type ReadReceiptEvent, type RegisterData, type RegisterDeviceTokenPayload, type ResolvedCompressionConfig, type ResolvedConfig, type ResolvedFileSizeLimits, type SearchParams, type SendData, type SendMessageAttachment, type SendMessagePayload, type SocketStatus, type StatusListener, type TokenStore, type TypingIndicatorEvent, type UnreadSummary, type UpdateConversationData, type UploadConfig, type UploadProgress, type UploadableFile, type User, type UserPreferences, type UserStatusEvent, type WebPushDeviceToken, authApi, connectSocket, conversationsApi, createAuthStore, devicesApi, disconnectSocket, getApiClient, getAuthStore, getCompressionStrategy, getSocket, getSocketStatus, initApiClient, initAuthStore, messagesApi, normalizeConversation, onSocketStatus, reconnectSocket, refreshSocketAuth, resetAuthStore, resolveConfig, setApiClientInstance, socketEmit, storageApi, tryGetSocket, uploadBatch, useChatStore, usersApi };
package/dist/index.js CHANGED
@@ -47,6 +47,13 @@ function resolveConfig(config) {
47
47
  onProgress: config.upload?.onProgress
48
48
  },
49
49
  platformUploadFn: config.platformUploadFn,
50
+ platformCompressFn: config.platformCompressFn,
51
+ compression: {
52
+ enabled: config.compression?.enabled ?? config.platformCompressFn != null,
53
+ imageQuality: config.compression?.imageQuality ?? 0.85,
54
+ imageMaxDimension: config.compression?.imageMaxDimension ?? 1920,
55
+ compressDocuments: config.compression?.compressDocuments ?? true
56
+ },
50
57
  persistStorage: config.persistStorage,
51
58
  messagePageSize: config.messagePageSize ?? 40,
52
59
  starredMessagePageSize: config.starredMessagePageSize ?? 30,
@@ -54,6 +61,68 @@ function resolveConfig(config) {
54
61
  };
55
62
  }
56
63
 
64
+ // src/compression/compress.ts
65
+ var GZIP_MIME_TYPES = /* @__PURE__ */ new Set([
66
+ "text/plain",
67
+ "text/csv",
68
+ "text/markdown",
69
+ "text/x-markdown",
70
+ "text/xml",
71
+ "application/xml",
72
+ "text/yaml",
73
+ "text/x-yaml",
74
+ "application/x-yaml",
75
+ "application/rtf",
76
+ "text/rtf",
77
+ "application/json",
78
+ "image/svg+xml"
79
+ ]);
80
+ var IMAGE_MIME_TYPES = /* @__PURE__ */ new Set([
81
+ "image/jpeg",
82
+ "image/png",
83
+ "image/gif",
84
+ "image/webp",
85
+ "image/bmp",
86
+ "image/tiff"
87
+ ]);
88
+ var SKIP_MIME_TYPES = /* @__PURE__ */ new Set([
89
+ "video/mp4",
90
+ "video/webm",
91
+ "video/quicktime",
92
+ "audio/mpeg",
93
+ "audio/wav",
94
+ "audio/ogg",
95
+ "audio/webm",
96
+ "audio/mp4",
97
+ "application/zip",
98
+ "application/pdf",
99
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
100
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
101
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation"
102
+ ]);
103
+ function getCompressionStrategy(mimeType, config) {
104
+ if (SKIP_MIME_TYPES.has(mimeType)) return "skip";
105
+ if (IMAGE_MIME_TYPES.has(mimeType)) return "image";
106
+ if (config.compressDocuments && GZIP_MIME_TYPES.has(mimeType)) return "gzip";
107
+ return "skip";
108
+ }
109
+ async function compressFile(file, platformCompressFn, config) {
110
+ const noop = {
111
+ ...file,
112
+ originalSize: file.size,
113
+ compressed: false,
114
+ compressionAlgorithm: "none"
115
+ };
116
+ if (!config.enabled || !platformCompressFn) return noop;
117
+ const strategy = getCompressionStrategy(file.type, config);
118
+ if (strategy === "skip") return noop;
119
+ try {
120
+ return await platformCompressFn(file, config);
121
+ } catch {
122
+ return noop;
123
+ }
124
+ }
125
+
57
126
  // src/api/client.ts
58
127
  import axios from "axios";
59
128
  var _tokenStore = null;
@@ -450,12 +519,22 @@ var storageApi = {
450
519
  return data;
451
520
  }
452
521
  };
453
- async function uploadBatch(files, platformUploadFn, conversationId, onProgress) {
454
- const requests = files.map((f) => ({
522
+ async function uploadBatch(files, platformUploadFn, conversationId, onProgress, platformCompressFn, compressionConfig) {
523
+ const compressedFiles = await Promise.all(
524
+ files.map((f) => compressFile(f, platformCompressFn, compressionConfig ?? { enabled: false, imageQuality: 0.85, imageMaxDimension: 1920, compressDocuments: true }))
525
+ );
526
+ const requests = compressedFiles.map((f) => ({
455
527
  filename: f.name,
456
528
  mimeType: f.type,
457
529
  size: f.size,
458
- conversationId
530
+ conversationId,
531
+ ...f.compressed && {
532
+ metadata: {
533
+ compressed: true,
534
+ originalSize: f.originalSize,
535
+ compressionAlgorithm: f.compressionAlgorithm
536
+ }
537
+ }
459
538
  }));
460
539
  const { urls, errors: requestErrors } = await storageApi.requestPresignedUrlBatch(requests);
461
540
  const progressMap = {};
@@ -469,7 +548,7 @@ async function uploadBatch(files, platformUploadFn, conversationId, onProgress)
469
548
  const failed = [...requestErrors];
470
549
  await Promise.all(
471
550
  urls.map(async (presigned, idx) => {
472
- const file = files[idx];
551
+ const file = compressedFiles[idx];
473
552
  progressMap[idx] = 0;
474
553
  try {
475
554
  await platformUploadFn(presigned, file, (pct) => {
@@ -664,8 +743,32 @@ function refreshSocketAuth() {
664
743
 
665
744
  // src/socket/emitters.ts
666
745
  var ACK_TIMEOUT = 5e3;
667
- function withAck(event, payload) {
668
- const socket = tryGetSocket();
746
+ var RECONNECT_WAIT_TIMEOUT = 15e3;
747
+ function waitForReconnect() {
748
+ return new Promise((resolve, reject) => {
749
+ const timer = setTimeout(() => {
750
+ unsubscribe();
751
+ reject(new Error("[AntzChat] Socket reconnect timeout"));
752
+ }, RECONNECT_WAIT_TIMEOUT);
753
+ const unsubscribe = onSocketStatus((status) => {
754
+ if (status === "connected") {
755
+ clearTimeout(timer);
756
+ unsubscribe();
757
+ resolve();
758
+ } else if (status === "error") {
759
+ clearTimeout(timer);
760
+ unsubscribe();
761
+ reject(new Error("[AntzChat] Socket reconnect failed"));
762
+ }
763
+ });
764
+ });
765
+ }
766
+ async function withAck(event, payload) {
767
+ let socket = tryGetSocket();
768
+ if (!socket) {
769
+ await waitForReconnect();
770
+ socket = tryGetSocket();
771
+ }
669
772
  if (!socket) return Promise.reject(new Error(`[AntzChat] Socket not connected (event: ${event})`));
670
773
  return new Promise((resolve, reject) => {
671
774
  const timer = setTimeout(() => reject(new Error(`Socket ack timeout: ${event}`)), ACK_TIMEOUT);
@@ -861,7 +964,14 @@ var AntzChatClient = class {
861
964
  disconnectSocket();
862
965
  }
863
966
  uploadFiles(files, conversationId) {
864
- return uploadBatch(files, this._config.platformUploadFn, conversationId, this._config.upload.onProgress);
967
+ return uploadBatch(
968
+ files,
969
+ this._config.platformUploadFn,
970
+ conversationId,
971
+ this._config.upload.onProgress,
972
+ this._config.platformCompressFn,
973
+ this._config.compression
974
+ );
865
975
  }
866
976
  async uploadIcon(conversationId, file) {
867
977
  const result = await this.uploadFiles([file], conversationId);
@@ -880,6 +990,7 @@ export {
880
990
  disconnectSocket,
881
991
  getApiClient,
882
992
  getAuthStore,
993
+ getCompressionStrategy,
883
994
  getSocket,
884
995
  getSocketStatus,
885
996
  initApiClient,