@antzsoft/chat-core 1.2.5 → 1.2.6

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.cjs CHANGED
@@ -99,7 +99,13 @@ var init_chat_store = __esm({
99
99
  // src/index.ts
100
100
  var src_exports = {};
101
101
  __export(src_exports, {
102
+ AntzChatAuthError: () => AntzChatAuthError,
102
103
  AntzChatClient: () => AntzChatClient,
104
+ AntzChatError: () => AntzChatError,
105
+ AntzChatNetworkError: () => AntzChatNetworkError,
106
+ AntzChatPermissionError: () => AntzChatPermissionError,
107
+ AntzChatServerError: () => AntzChatServerError,
108
+ AntzChatValidationError: () => AntzChatValidationError,
103
109
  appConfigApi: () => appConfigApi,
104
110
  authApi: () => authApi,
105
111
  connectSocket: () => connectSocket,
@@ -123,6 +129,7 @@ __export(src_exports, {
123
129
  initAuthStore: () => initAuthStore,
124
130
  isTransitEnvelope: () => isTransitEnvelope,
125
131
  messagesApi: () => messagesApi,
132
+ normalizeAxiosError: () => normalizeAxiosError,
126
133
  normalizeConversation: () => normalizeConversation,
127
134
  onSocketStatus: () => onSocketStatus,
128
135
  performHandshake: () => performHandshake,
@@ -265,7 +272,7 @@ async function compressFile(file, platformCompressFn, config) {
265
272
  }
266
273
 
267
274
  // src/api/client.ts
268
- var import_axios = __toESM(require("axios"), 1);
275
+ var import_axios2 = __toESM(require("axios"), 1);
269
276
 
270
277
  // src/crypto/transit.ts
271
278
  function hasWebCrypto() {
@@ -561,6 +568,94 @@ function base64ToUint82(b64) {
561
568
  return buf;
562
569
  }
563
570
 
571
+ // src/errors.ts
572
+ var import_axios = require("axios");
573
+ var AntzChatError = class extends Error {
574
+ constructor(code, message, retryable = false, context) {
575
+ super(message);
576
+ this.name = "AntzChatError";
577
+ this.code = code;
578
+ this.retryable = retryable;
579
+ this.context = context;
580
+ Object.setPrototypeOf(this, new.target.prototype);
581
+ }
582
+ };
583
+ var AntzChatAuthError = class extends AntzChatError {
584
+ constructor(message, code = "AUTH_FAILED", context) {
585
+ super(code, message, false, context);
586
+ this.name = "AntzChatAuthError";
587
+ Object.setPrototypeOf(this, new.target.prototype);
588
+ }
589
+ };
590
+ var AntzChatValidationError = class extends AntzChatError {
591
+ constructor(message, context) {
592
+ const msg = Array.isArray(message) ? message.join("; ") : message;
593
+ super("VALIDATION_ERROR", msg, false, context);
594
+ this.name = "AntzChatValidationError";
595
+ this.fields = Array.isArray(message) ? message : void 0;
596
+ Object.setPrototypeOf(this, new.target.prototype);
597
+ }
598
+ };
599
+ var AntzChatNetworkError = class extends AntzChatError {
600
+ constructor(message, code = "NETWORK_ERROR", context) {
601
+ super(code, message, true, context);
602
+ this.name = "AntzChatNetworkError";
603
+ Object.setPrototypeOf(this, new.target.prototype);
604
+ }
605
+ };
606
+ var AntzChatPermissionError = class extends AntzChatError {
607
+ constructor(message, context) {
608
+ super("PERMISSION_DENIED", message, false, context);
609
+ this.name = "AntzChatPermissionError";
610
+ Object.setPrototypeOf(this, new.target.prototype);
611
+ }
612
+ };
613
+ var AntzChatServerError = class extends AntzChatError {
614
+ constructor(message, httpStatus, context) {
615
+ super("SERVER_ERROR", message, true, context);
616
+ this.name = "AntzChatServerError";
617
+ this.httpStatus = httpStatus;
618
+ Object.setPrototypeOf(this, new.target.prototype);
619
+ }
620
+ };
621
+ function normalizeAxiosError(error) {
622
+ if (error instanceof AntzChatError) return error;
623
+ if ((0, import_axios.isAxiosError)(error)) {
624
+ const status = error.response?.status;
625
+ const body = error.response?.data;
626
+ const decryptionFailed = body != null && isTransitEnvelope(body);
627
+ const rawMessage = decryptionFailed ? void 0 : body?.message ?? void 0;
628
+ const message = (Array.isArray(rawMessage) ? rawMessage.join("; ") : rawMessage) || error.message || "Request failed";
629
+ const ctx = {
630
+ ...status != null && { httpStatus: status },
631
+ ...body?.path != null && { path: body.path },
632
+ ...body?.error != null && { serverError: body.error },
633
+ ...error.code != null && { axiosCode: error.code },
634
+ ...decryptionFailed && { decryptionFailed: true, note: "Transit decryption failed \u2014 server error body is an encrypted envelope" }
635
+ };
636
+ if (!error.response) {
637
+ return new AntzChatNetworkError(message || "Network error", "NETWORK_ERROR", ctx);
638
+ }
639
+ if (status === 401) {
640
+ const code = error.config?._retry ? "AUTH_FAILED" : "SESSION_EXPIRED";
641
+ return new AntzChatAuthError(message, code, ctx);
642
+ }
643
+ if (status === 403) return new AntzChatPermissionError(message, ctx);
644
+ if (status === 400 || status === 422) {
645
+ return new AntzChatValidationError(
646
+ Array.isArray(rawMessage) ? rawMessage : message,
647
+ ctx
648
+ );
649
+ }
650
+ if (status === 404) return new AntzChatServerError(message, 404, ctx);
651
+ if (status === 429) return new AntzChatNetworkError(message, "RATE_LIMITED", ctx);
652
+ if (status != null && status >= 500) return new AntzChatServerError(message, status, ctx);
653
+ return new AntzChatServerError(message, status, ctx);
654
+ }
655
+ const msg = error instanceof Error ? error.message : String(error);
656
+ return new AntzChatError("UNKNOWN_ERROR", msg, false);
657
+ }
658
+
564
659
  // src/api/client.ts
565
660
  var _tokenStore = null;
566
661
  var _config = null;
@@ -573,7 +668,7 @@ function initApiClient(config, tokenStore) {
573
668
  _config = config;
574
669
  _tokenStore = tokenStore;
575
670
  _avatarSent = false;
576
- const client = import_axios.default.create({
671
+ const client = import_axios2.default.create({
577
672
  baseURL: config.apiUrl,
578
673
  headers: { "Content-Type": "application/json" }
579
674
  });
@@ -658,7 +753,7 @@ function initApiClient(config, tokenStore) {
658
753
  const refreshToken = _tokenStore?.getRefreshToken();
659
754
  if (!refreshToken) {
660
755
  _tokenStore?.clearTokens();
661
- return Promise.reject(error);
756
+ return Promise.reject(normalizeAxiosError(error));
662
757
  }
663
758
  if (isRefreshing) {
664
759
  return new Promise((resolve) => {
@@ -671,7 +766,7 @@ function initApiClient(config, tokenStore) {
671
766
  original._retry = true;
672
767
  isRefreshing = true;
673
768
  try {
674
- const { data } = await import_axios.default.post(
769
+ const { data } = await import_axios2.default.post(
675
770
  `${_config.apiUrl}/auth/refresh`,
676
771
  { refreshToken }
677
772
  );
@@ -683,12 +778,12 @@ function initApiClient(config, tokenStore) {
683
778
  return client(original);
684
779
  } catch {
685
780
  _tokenStore?.clearTokens();
686
- return Promise.reject(error);
781
+ return Promise.reject(normalizeAxiosError(error));
687
782
  } finally {
688
783
  isRefreshing = false;
689
784
  }
690
785
  }
691
- return Promise.reject(error);
786
+ return Promise.reject(normalizeAxiosError(error));
692
787
  }
693
788
  );
694
789
  return client;
@@ -1116,6 +1211,9 @@ var conversationsApi = {
1116
1211
  async removeIcon(conversationId) {
1117
1212
  const { data } = await getApiClient().post(`/conversations/${conversationId}/icon/remove`);
1118
1213
  return normalizeConversation(data);
1214
+ },
1215
+ async clearChat(conversationId) {
1216
+ await getApiClient().post(`/conversations/${conversationId}/clear-for-me`);
1119
1217
  }
1120
1218
  };
1121
1219
 
@@ -1483,8 +1581,11 @@ async function _doConnect(config, getToken) {
1483
1581
  try {
1484
1582
  const serverKeys = await fetchServerKeys(config.apiUrl);
1485
1583
  if (!serverKeys.enabled) {
1486
- throw new Error(
1487
- "[AntzChat] Transit encryption mismatch: SDK has transitEncryption=true but server has TRANSIT_ENCRYPTION_ENABLED=false. Align the config on both sides."
1584
+ throw new AntzChatError(
1585
+ "TRANSIT_MISMATCH",
1586
+ "[AntzChat] Transit encryption mismatch: SDK has transitEncryption=true but server has TRANSIT_ENCRYPTION_ENABLED=false. Align the config on both sides.",
1587
+ false,
1588
+ { sdkEnabled: true, serverEnabled: false }
1488
1589
  );
1489
1590
  }
1490
1591
  const algo = globalThis.crypto?.subtle ? await detectTransitAlgo() : "x25519";
@@ -1497,12 +1598,15 @@ async function _doConnect(config, getToken) {
1497
1598
  try {
1498
1599
  const serverKeys = await fetchServerKeys(config.apiUrl);
1499
1600
  if (serverKeys.enabled) {
1500
- throw new Error(
1501
- "[AntzChat] Transit encryption mismatch: SDK has transitEncryption=false but server has TRANSIT_ENCRYPTION_ENABLED=true. Align the config on both sides."
1601
+ throw new AntzChatError(
1602
+ "TRANSIT_MISMATCH",
1603
+ "[AntzChat] Transit encryption mismatch: SDK has transitEncryption=false but server has TRANSIT_ENCRYPTION_ENABLED=true. Align the config on both sides.",
1604
+ false,
1605
+ { sdkEnabled: false, serverEnabled: true }
1502
1606
  );
1503
1607
  }
1504
1608
  } catch (err) {
1505
- if (err.message?.startsWith("[AntzChat] Transit encryption mismatch")) throw err;
1609
+ if (err?.code === "TRANSIT_MISMATCH") throw err;
1506
1610
  }
1507
1611
  }
1508
1612
  _socket = (0, import_socket.io)(`${config.socketOrigin}/chat`, {
@@ -1646,7 +1750,7 @@ async function drainSendQueue(conversationId) {
1646
1750
  while (queue.length > 0) {
1647
1751
  const entry = queue.shift();
1648
1752
  if (Date.now() - entry.enqueuedAt > QUEUE_ENTRY_TTL) {
1649
- entry.reject(new Error("[AntzChat] Message dropped: queued too long"));
1753
+ entry.reject(new AntzChatNetworkError("Message dropped: queued too long", "MESSAGE_DROPPED"));
1650
1754
  continue;
1651
1755
  }
1652
1756
  entry.run().then(entry.resolve).catch(entry.reject);
@@ -1659,7 +1763,7 @@ function queueSendMessage(payload) {
1659
1763
  if (!sendQueues.has(conversationId)) sendQueues.set(conversationId, []);
1660
1764
  const queue = sendQueues.get(conversationId);
1661
1765
  if (queue.length >= QUEUE_MAX_SIZE) {
1662
- return Promise.reject(new Error("[AntzChat] Send queue full: too many messages in flight"));
1766
+ return Promise.reject(new AntzChatNetworkError("Send queue full: too many messages in flight", "SEND_QUEUE_FULL", { conversationId }));
1663
1767
  }
1664
1768
  return new Promise((resolve, reject) => {
1665
1769
  queue.push({ run: () => withAck("send_message", payload), resolve, reject, enqueuedAt: Date.now() });
@@ -1670,7 +1774,7 @@ function waitForReconnect() {
1670
1774
  return new Promise((resolve, reject) => {
1671
1775
  const timer = setTimeout(() => {
1672
1776
  unsubscribe();
1673
- reject(new Error("[AntzChat] Socket reconnect timeout"));
1777
+ reject(new AntzChatNetworkError("Socket reconnect timeout", "SOCKET_TIMEOUT"));
1674
1778
  }, RECONNECT_WAIT_TIMEOUT);
1675
1779
  const unsubscribe = onSocketStatus((status) => {
1676
1780
  if (status === "connected") {
@@ -1680,7 +1784,7 @@ function waitForReconnect() {
1680
1784
  } else if (status === "error") {
1681
1785
  clearTimeout(timer);
1682
1786
  unsubscribe();
1683
- reject(new Error("[AntzChat] Socket reconnect failed"));
1787
+ reject(new AntzChatNetworkError("Socket reconnect failed", "SOCKET_NOT_CONNECTED"));
1684
1788
  }
1685
1789
  });
1686
1790
  });
@@ -1691,14 +1795,14 @@ async function withAck(event, payload) {
1691
1795
  await waitForReconnect();
1692
1796
  socket = tryGetSocket();
1693
1797
  }
1694
- if (!socket) return Promise.reject(new Error(`[AntzChat] Socket not connected (event: ${event})`));
1798
+ if (!socket) return Promise.reject(new AntzChatNetworkError(`Socket not connected (event: ${event})`, "SOCKET_NOT_CONNECTED", { event }));
1695
1799
  return new Promise((resolve, reject) => {
1696
1800
  let timer;
1697
1801
  secureEmit(socket, event, payload, (response) => {
1698
1802
  clearTimeout(timer);
1699
1803
  resolve(response);
1700
1804
  }).then(() => {
1701
- timer = setTimeout(() => reject(new Error(`Socket ack timeout: ${event}`)), ACK_TIMEOUT);
1805
+ timer = setTimeout(() => reject(new AntzChatNetworkError(`Socket ack timeout: ${event}`, "SOCKET_TIMEOUT", { event })), ACK_TIMEOUT);
1702
1806
  }).catch(reject);
1703
1807
  });
1704
1808
  }
@@ -1726,6 +1830,9 @@ var socketEmit = {
1726
1830
  deleteMessageForMe(messageId) {
1727
1831
  return withAck("delete_message_for_me", { messageId });
1728
1832
  },
1833
+ clearChat(conversationId) {
1834
+ return withAck("clear_chat_for_me", { conversationId });
1835
+ },
1729
1836
  addReaction(messageId, emoji) {
1730
1837
  return withAck("add_reaction", { messageId, emoji });
1731
1838
  },
@@ -1761,7 +1868,7 @@ var socketEmit = {
1761
1868
  resolve([]);
1762
1869
  }
1763
1870
  }).then(() => {
1764
- timer = setTimeout(() => reject(new Error("get_online_users timeout")), ACK_TIMEOUT);
1871
+ timer = setTimeout(() => reject(new AntzChatNetworkError("Socket ack timeout: get_online_users", "SOCKET_TIMEOUT", { event: "get_online_users" })), ACK_TIMEOUT);
1765
1872
  }).catch(reject);
1766
1873
  });
1767
1874
  },
@@ -1824,7 +1931,13 @@ var AntzChatClient = class {
1824
1931
  };
1825
1932
  // Annotate the CommonJS export names for ESM import in node:
1826
1933
  0 && (module.exports = {
1934
+ AntzChatAuthError,
1827
1935
  AntzChatClient,
1936
+ AntzChatError,
1937
+ AntzChatNetworkError,
1938
+ AntzChatPermissionError,
1939
+ AntzChatServerError,
1940
+ AntzChatValidationError,
1828
1941
  appConfigApi,
1829
1942
  authApi,
1830
1943
  connectSocket,
@@ -1848,6 +1961,7 @@ var AntzChatClient = class {
1848
1961
  initAuthStore,
1849
1962
  isTransitEnvelope,
1850
1963
  messagesApi,
1964
+ normalizeAxiosError,
1851
1965
  normalizeConversation,
1852
1966
  onSocketStatus,
1853
1967
  performHandshake,