@antzsoft/chat-core 1.1.9 → 1.2.1

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/README.md CHANGED
@@ -2451,6 +2451,9 @@ document.querySelectorAll('[data-conv-id]').forEach((el) => {
2451
2451
 
2452
2452
  ## Changelog
2453
2453
 
2454
+ ### v1.2.0
2455
+ - **Fix: `getSocket()` and `tryGetSocket()` now return the transit-aware proxy** — Previously these functions returned the raw Socket.IO socket, bypassing the `secureOn` decryption wrapper. Any integrator calling `getSocket().on('new_message', ...)` or `tryGetSocket().on(...)` directly would receive raw encrypted envelopes (`{v:1, iv:..., ct:...}`) instead of decrypted payloads. Both functions now return the same secure proxy that `connectSocket()` returns — all `.on()` calls automatically decrypt via transit when enabled. **No integration changes required — existing code that uses `getSocket()` or `tryGetSocket()` will now transparently receive decrypted data.**
2456
+
2454
2457
  ### v1.1.9
2455
2458
  - **Fix: Transit 403 on `GET /users/me` and `POST /users/me/devices` in external / authToken mode** — In external and authToken auth modes, `AntzChatProvider` calls `GET /users/me` immediately on mount to refresh the user profile, and push token registration (`POST /users/me/devices`) fires on auth state change — both before the socket connects and the ECDH transit session is established. With transit enforcement enabled these requests were receiving `403 Transit encryption required`. Both endpoints are now marked `@PreTransit()` on the server so they are reachable during the bootstrap window. **No integration changes required.**
2456
2459
  - **Fix: React Native / Hermes support for transit encryption** — The core SDK (`@antzsoft/chat-core`) now works on React Native and Hermes out of the box. When `globalThis.crypto.subtle` is unavailable (Hermes does not expose it), the SDK automatically falls back to `@noble/curves` (X25519 ECDH) and `@noble/ciphers` (AES-256-GCM) for both the handshake and all payload encryption/decryption. Previously the SDK crashed with `TypeError: Cannot read property 'subtle' of undefined` on RN. **No integration changes required — no extra packages to install.**
@@ -0,0 +1,7 @@
1
+ import {
2
+ useChatStore
3
+ } from "./chunk-GUO5QQGK.js";
4
+ export {
5
+ useChatStore
6
+ };
7
+ //# sourceMappingURL=chat.store-JC6QYDDL.js.map
@@ -35,9 +35,13 @@ var useChatStore = create((set) => ({
35
35
  setLastRead: (conversationId, messageId, readAt) => set((state) => ({
36
36
  lastRead: { ...state.lastRead, [conversationId]: { messageId, readAt } }
37
37
  })),
38
- setLastSeen: (userId, lastSeenAt) => set((state) => ({
39
- lastSeen: { ...state.lastSeen, [userId]: lastSeenAt }
40
- })),
38
+ setLastSeen: (userId, lastSeenAt) => set((state) => {
39
+ if (lastSeenAt === null) {
40
+ const { [userId]: _, ...rest } = state.lastSeen;
41
+ return { lastSeen: rest };
42
+ }
43
+ return { lastSeen: { ...state.lastSeen, [userId]: lastSeenAt } };
44
+ }),
41
45
  setReplyingTo: (message) => set({ replyingTo: message, editingMessage: null }),
42
46
  setEditingMessage: (message) => set({ editingMessage: message, replyingTo: null }),
43
47
  toggleSidebar: () => set((state) => ({ isSidebarOpen: !state.isSidebarOpen })),
@@ -51,4 +55,4 @@ var useChatStore = create((set) => ({
51
55
  export {
52
56
  useChatStore
53
57
  };
54
- //# sourceMappingURL=chunk-TB52RCSF.js.map
58
+ //# sourceMappingURL=chunk-GUO5QQGK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/stores/chat.store.ts"],"sourcesContent":["import { create } from 'zustand';\nimport type { Message } from '../types/index.js';\n\ninterface TypingUser {\n userId: string;\n displayName: string;\n avatarUrl?: string;\n}\n\nexport interface LastReadEntry {\n messageId: string;\n readAt: string;\n}\n\ninterface ChatState {\n activeConversationId: string | null;\n pendingTarget: { conversationId: string; messageId: string } | null;\n typingUsers: Record<string, TypingUser[]>;\n onlineUsers: string[];\n /** keyed by conversationId — current user's last read pointer per conversation */\n lastRead: Record<string, LastReadEntry>;\n /** keyed by userId — last seen timestamp for each user */\n lastSeen: Record<string, string>;\n replyingTo: Message | null;\n editingMessage: Message | null;\n isSidebarOpen: boolean;\n isGroupInfoOpen: boolean;\n isStarredPanelOpen: boolean;\n\n setActiveConversation: (id: string | null) => void;\n setPendingTarget: (target: { conversationId: string; messageId: string } | null) => void;\n addTypingUser: (conversationId: string, user: TypingUser) => void;\n removeTypingUser: (conversationId: string, userId: string) => void;\n setUserOnline: (userId: string) => void;\n setUserOffline: (userId: string) => void;\n setOnlineUsers: (userIds: string[]) => void;\n setLastRead: (conversationId: string, messageId: string, readAt: string) => void;\n setLastSeen: (userId: string, lastSeenAt: string | null) => void;\n setReplyingTo: (message: Message | null) => void;\n setEditingMessage: (message: Message | null) => void;\n toggleSidebar: () => void;\n setSidebarOpen: (open: boolean) => void;\n toggleGroupInfo: () => void;\n setGroupInfoOpen: (open: boolean) => void;\n toggleStarredPanel: () => void;\n setStarredPanelOpen: (open: boolean) => void;\n}\n\nexport const useChatStore = create<ChatState>((set) => ({\n activeConversationId: null,\n pendingTarget: null,\n typingUsers: {},\n onlineUsers: [],\n lastRead: {},\n lastSeen: {},\n replyingTo: null,\n editingMessage: null,\n isSidebarOpen: true,\n isGroupInfoOpen: false,\n isStarredPanelOpen: false,\n\n setActiveConversation: (id) =>\n set({ activeConversationId: id, replyingTo: null, editingMessage: null }),\n\n setPendingTarget: (target) => set({ pendingTarget: target }),\n\n addTypingUser: (conversationId, user) =>\n set((state) => {\n const existing = state.typingUsers[conversationId] ?? [];\n const deduped = existing.filter((u) => u.userId !== user.userId);\n return { typingUsers: { ...state.typingUsers, [conversationId]: [...deduped, user] } };\n }),\n\n removeTypingUser: (conversationId, userId) =>\n set((state) => ({\n typingUsers: {\n ...state.typingUsers,\n [conversationId]: (state.typingUsers[conversationId] ?? []).filter(\n (u) => u.userId !== userId,\n ),\n },\n })),\n\n setUserOnline: (userId) =>\n set((state) => ({\n onlineUsers: state.onlineUsers.includes(userId)\n ? state.onlineUsers\n : [...state.onlineUsers, userId],\n })),\n\n setUserOffline: (userId) =>\n set((state) => ({ onlineUsers: state.onlineUsers.filter((id) => id !== userId) })),\n\n setOnlineUsers: (userIds) => set({ onlineUsers: userIds }),\n\n setLastRead: (conversationId, messageId, readAt) =>\n set((state) => ({\n lastRead: { ...state.lastRead, [conversationId]: { messageId, readAt } },\n })),\n\n setLastSeen: (userId, lastSeenAt) =>\n set((state) => {\n if (lastSeenAt === null) {\n const { [userId]: _, ...rest } = state.lastSeen;\n return { lastSeen: rest };\n }\n return { lastSeen: { ...state.lastSeen, [userId]: lastSeenAt } };\n }),\n\n setReplyingTo: (message) => set({ replyingTo: message, editingMessage: null }),\n\n setEditingMessage: (message) => set({ editingMessage: message, replyingTo: null }),\n\n toggleSidebar: () => set((state) => ({ isSidebarOpen: !state.isSidebarOpen })),\n setSidebarOpen: (open) => set({ isSidebarOpen: open }),\n\n toggleGroupInfo: () => set((state) => ({ isGroupInfoOpen: !state.isGroupInfoOpen })),\n setGroupInfoOpen: (open) => set({ isGroupInfoOpen: open }),\n\n toggleStarredPanel: () => set((state) => ({ isStarredPanelOpen: !state.isStarredPanelOpen })),\n setStarredPanelOpen: (open) => set({ isStarredPanelOpen: open }),\n}));\n"],"mappings":";AAAA,SAAS,cAAc;AAgDhB,IAAM,eAAe,OAAkB,CAAC,SAAS;AAAA,EACtD,sBAAsB;AAAA,EACtB,eAAe;AAAA,EACf,aAAa,CAAC;AAAA,EACd,aAAa,CAAC;AAAA,EACd,UAAU,CAAC;AAAA,EACX,UAAU,CAAC;AAAA,EACX,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EAEpB,uBAAuB,CAAC,OACtB,IAAI,EAAE,sBAAsB,IAAI,YAAY,MAAM,gBAAgB,KAAK,CAAC;AAAA,EAE1E,kBAAkB,CAAC,WAAW,IAAI,EAAE,eAAe,OAAO,CAAC;AAAA,EAE3D,eAAe,CAAC,gBAAgB,SAC9B,IAAI,CAAC,UAAU;AACb,UAAM,WAAW,MAAM,YAAY,cAAc,KAAK,CAAC;AACvD,UAAM,UAAU,SAAS,OAAO,CAAC,MAAM,EAAE,WAAW,KAAK,MAAM;AAC/D,WAAO,EAAE,aAAa,EAAE,GAAG,MAAM,aAAa,CAAC,cAAc,GAAG,CAAC,GAAG,SAAS,IAAI,EAAE,EAAE;AAAA,EACvF,CAAC;AAAA,EAEH,kBAAkB,CAAC,gBAAgB,WACjC,IAAI,CAAC,WAAW;AAAA,IACd,aAAa;AAAA,MACX,GAAG,MAAM;AAAA,MACT,CAAC,cAAc,IAAI,MAAM,YAAY,cAAc,KAAK,CAAC,GAAG;AAAA,QAC1D,CAAC,MAAM,EAAE,WAAW;AAAA,MACtB;AAAA,IACF;AAAA,EACF,EAAE;AAAA,EAEJ,eAAe,CAAC,WACd,IAAI,CAAC,WAAW;AAAA,IACd,aAAa,MAAM,YAAY,SAAS,MAAM,IAC1C,MAAM,cACN,CAAC,GAAG,MAAM,aAAa,MAAM;AAAA,EACnC,EAAE;AAAA,EAEJ,gBAAgB,CAAC,WACf,IAAI,CAAC,WAAW,EAAE,aAAa,MAAM,YAAY,OAAO,CAAC,OAAO,OAAO,MAAM,EAAE,EAAE;AAAA,EAEnF,gBAAgB,CAAC,YAAY,IAAI,EAAE,aAAa,QAAQ,CAAC;AAAA,EAEzD,aAAa,CAAC,gBAAgB,WAAW,WACvC,IAAI,CAAC,WAAW;AAAA,IACd,UAAU,EAAE,GAAG,MAAM,UAAU,CAAC,cAAc,GAAG,EAAE,WAAW,OAAO,EAAE;AAAA,EACzE,EAAE;AAAA,EAEJ,aAAa,CAAC,QAAQ,eACpB,IAAI,CAAC,UAAU;AACb,QAAI,eAAe,MAAM;AACvB,YAAM,EAAE,CAAC,MAAM,GAAG,GAAG,GAAG,KAAK,IAAI,MAAM;AACvC,aAAO,EAAE,UAAU,KAAK;AAAA,IAC1B;AACA,WAAO,EAAE,UAAU,EAAE,GAAG,MAAM,UAAU,CAAC,MAAM,GAAG,WAAW,EAAE;AAAA,EACjE,CAAC;AAAA,EAEH,eAAe,CAAC,YAAY,IAAI,EAAE,YAAY,SAAS,gBAAgB,KAAK,CAAC;AAAA,EAE7E,mBAAmB,CAAC,YAAY,IAAI,EAAE,gBAAgB,SAAS,YAAY,KAAK,CAAC;AAAA,EAEjF,eAAe,MAAM,IAAI,CAAC,WAAW,EAAE,eAAe,CAAC,MAAM,cAAc,EAAE;AAAA,EAC7E,gBAAgB,CAAC,SAAS,IAAI,EAAE,eAAe,KAAK,CAAC;AAAA,EAErD,iBAAiB,MAAM,IAAI,CAAC,WAAW,EAAE,iBAAiB,CAAC,MAAM,gBAAgB,EAAE;AAAA,EACnF,kBAAkB,CAAC,SAAS,IAAI,EAAE,iBAAiB,KAAK,CAAC;AAAA,EAEzD,oBAAoB,MAAM,IAAI,CAAC,WAAW,EAAE,oBAAoB,CAAC,MAAM,mBAAmB,EAAE;AAAA,EAC5F,qBAAqB,CAAC,SAAS,IAAI,EAAE,oBAAoB,KAAK,CAAC;AACjE,EAAE;","names":[]}
package/dist/index.cjs CHANGED
@@ -75,9 +75,13 @@ var init_chat_store = __esm({
75
75
  setLastRead: (conversationId, messageId, readAt) => set((state) => ({
76
76
  lastRead: { ...state.lastRead, [conversationId]: { messageId, readAt } }
77
77
  })),
78
- setLastSeen: (userId, lastSeenAt) => set((state) => ({
79
- lastSeen: { ...state.lastSeen, [userId]: lastSeenAt }
80
- })),
78
+ setLastSeen: (userId, lastSeenAt) => set((state) => {
79
+ if (lastSeenAt === null) {
80
+ const { [userId]: _, ...rest } = state.lastSeen;
81
+ return { lastSeen: rest };
82
+ }
83
+ return { lastSeen: { ...state.lastSeen, [userId]: lastSeenAt } };
84
+ }),
81
85
  setReplyingTo: (message) => set({ replyingTo: message, editingMessage: null }),
82
86
  setEditingMessage: (message) => set({ editingMessage: message, replyingTo: null }),
83
87
  toggleSidebar: () => set((state) => ({ isSidebarOpen: !state.isSidebarOpen })),
@@ -1112,6 +1116,7 @@ function base64ToUint82(b64) {
1112
1116
 
1113
1117
  // src/socket/socket.ts
1114
1118
  var _socket = null;
1119
+ var _socketProxy = null;
1115
1120
  var _connectingPromise = null;
1116
1121
  var _status = "disconnected";
1117
1122
  var _statusListeners = /* @__PURE__ */ new Set();
@@ -1123,11 +1128,11 @@ function setStatus(s) {
1123
1128
  _statusListeners.forEach((l) => l(s));
1124
1129
  }
1125
1130
  function getSocket() {
1126
- if (!_socket) throw new Error("[AntzChat] Socket not initialized. Call connectSocket first.");
1127
- return _socket;
1131
+ if (!_socketProxy) throw new Error("[AntzChat] Socket not initialized. Call connectSocket first.");
1132
+ return _socketProxy;
1128
1133
  }
1129
1134
  function tryGetSocket() {
1130
- return _socket?.connected ? _socket : null;
1135
+ return _socket?.connected ? _socketProxy : null;
1131
1136
  }
1132
1137
  function getSocketStatus() {
1133
1138
  return _status;
@@ -1286,7 +1291,10 @@ async function _doConnect(config, getToken) {
1286
1291
  });
1287
1292
  secureOn(_socket, "user_online", (event) => {
1288
1293
  Promise.resolve().then(() => (init_chat_store(), chat_store_exports)).then(({ useChatStore: useChatStore2 }) => {
1289
- useChatStore2.getState().setUserOnline(event.userId);
1294
+ const store = useChatStore2.getState();
1295
+ const e = event;
1296
+ store.setUserOnline(e.userId);
1297
+ store.setLastSeen(e.userId, null);
1290
1298
  });
1291
1299
  });
1292
1300
  secureOn(_socket, "user_offline", (event) => {
@@ -1297,7 +1305,8 @@ async function _doConnect(config, getToken) {
1297
1305
  if (e.lastSeenAt) store.setLastSeen(e.userId, e.lastSeenAt);
1298
1306
  });
1299
1307
  });
1300
- return createSecureSocketProxy(_socket);
1308
+ _socketProxy = createSecureSocketProxy(_socket);
1309
+ return _socketProxy;
1301
1310
  }
1302
1311
  function createSecureSocketProxy(socket) {
1303
1312
  return new Proxy(socket, {
@@ -1322,6 +1331,7 @@ function disconnectSocket() {
1322
1331
  if (_socket) {
1323
1332
  _socket.disconnect();
1324
1333
  _socket = null;
1334
+ _socketProxy = null;
1325
1335
  setStatus("disconnected");
1326
1336
  }
1327
1337
  clearTransitSession();