@antzsoft/chat-core 1.3.3 → 1.3.4

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
@@ -1746,6 +1746,7 @@ interface CrossConversationSyncResponse {
1746
1746
  deliveredReceipts: SyncDeliveredReceipt[]; // delivery receipts since `since`
1747
1747
  reactions: SyncReactions; // v1.3.1+ — full current state for messages with reaction activity
1748
1748
  stars: SyncStarEntry[]; // v1.3.1+ — star/unstar records since `since`
1749
+ deletedConversationIds: string[]; // v1.3.4+ — conversations the user deleted from their list since `since`
1749
1750
  }
1750
1751
 
1751
1752
  interface SyncDeletedForMe {
@@ -1861,6 +1862,7 @@ The built-in `SyncService.syncConversation()` in the RN app uses the backward di
1861
1862
  | `participantChanges` | ✅ | ✅ | Role changes, mute/unmute, removals (`isActive: false`) |
1862
1863
  | `readReceipts` | ✅ | ✅ | Read receipts — merge into `msg.readBy[]`, recompute status |
1863
1864
  | `deliveredReceipts` | ✅ | ✅ | Delivery receipts — merge into `msg.deliveredTo[]`, recompute status |
1865
+ | `deletedConversationIds` | ✅ v1.3.4 | — | Conversations this user deleted from their list — remove from local DB. Cursor-safe: won't reappear on next `pull()` |
1864
1866
 
1865
1867
  #### Stale handling
1866
1868
 
@@ -3054,6 +3056,28 @@ document.querySelectorAll('[data-conv-id]').forEach((el) => {
3054
3056
 
3055
3057
  ## Changelog
3056
3058
 
3059
+ ### v1.3.4
3060
+
3061
+ - **New: `deletedConversationIds` in `CrossConversationSyncResponse`** — `syncApi.pull(since)` now returns `deletedConversationIds: string[]`. Contains the IDs of conversations this user explicitly deleted from their list (via the delete-conversation action) since the `since` cursor. The client should remove these from local SQLite and the UI conversation list.
3062
+
3063
+ ```typescript
3064
+ const result = await syncApi.pull(lastSyncedAt);
3065
+
3066
+ // Remove conversations the user deleted from their list
3067
+ for (const convId of result.deletedConversationIds) {
3068
+ await db.conversations.delete(convId);
3069
+ }
3070
+ ```
3071
+
3072
+ **Scoped to the requesting user only** — if another user deletes their copy of a shared DM, it does not appear in your `deletedConversationIds`. **Cursor-safe** — once the client saves the returned `syncedAt` as the new cursor, the same IDs will not reappear on the next `pull()` call. On the server side, this is detected via `participant.isHidden = true AND updatedAt > since` — the same flag set by `DELETE /conversations/:id`.
3073
+
3074
+ - **Fix: Sync access guard parity with message listing API** — All three sync paths now enforce the same access rules as `GET /messages`:
3075
+ - **`membershipPeriods` windows** — users who deleted and were re-added only see messages from their active membership periods; a deleted conversation with no periods returns no messages.
3076
+ - **`status` filter** — active messages and deleted-for-everyone tombstones (so clients can render "This message was deleted") are included; `failed`-status messages are excluded.
3077
+ - **`isHidden` group check** — hidden group participants (exit+delete) receive `403 Forbidden` on per-conversation sync, matching message listing behaviour. Hidden DM participants are still allowed.
3078
+ - **Removed-member read access** — removed members (`isActive: false`) can still call per-conversation sync to retrieve history, matching the message listing API's `validateConversationAccess` which does not require `isActive: true` for reads.
3079
+ - **No integration changes required.** These are server-side enforcement fixes — the response shape is unchanged, only the set of messages returned is now correctly filtered.
3080
+
3057
3081
  ### v1.3.3
3058
3082
 
3059
3083
  - **New: `metadata` and `sender` fields in all sync message responses** — Messages returned by all three sync paths (`crossConversationSync`, `conversationSync`, `conversationSyncBySeq`) now include `metadata` (system message metadata — `actorUserId`, `targetUserId`, `action`) and `sender` (displayName, avatarUrl snapshot at send time). Both fields were already optional on the core SDK `Message` type so no SDK type changes were required. Previously absent from sync responses, meaning system message text could not be rendered and sender avatars were blank after an offline sync. The server batch-fetches sender profiles in a single `findByIds` call per sync request — one extra DB round trip per sync, not one per message. **No integration changes required.**
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { R as ResolvedCompressionConfig, L as LoginCredentials, A as AuthResponse, a as RegisterData, b as AuthTokens, U as User, C as CrossConversationSyncResponse, c as ConversationSyncResponse, d as AppConfig, S as SendMessagePayload, e as CursorPaginatedResponse, M as Message, f as MessageReactionsResponse, P as PaginatedResponse, g as MessageReceiptsResponse, h as ConversationListParams, i as Conversation, j as Participant, k as ConversationUnreadCount, l as UnreadSummary, m as UserPreferences, n as ResolvedConfig, o as PersistStorage, p as PresignedUrlRequest, q as PresignedUrlResponse, F as FileResponse, r as CompletedPart, s as FileType, t as AntzChatConfig, u as UploadableFile, B as BatchUploadResult } from './storage-DdHsneZR.cjs';
2
- export { v as Attachment, w as CompressedFile, x as CompressionAlgorithm, y as CompressionConfig, z as ConversationType, D as ConversationUpdatedEvent, E as FileSizeLimits, G as LastReaction, H as MessageAckEvent, I as MessageContent, J as MessageDeletedEvent, K as MessageDeletedForMeEvent, N as MessageDeliveredEvent, O as MessageMetadata, Q as MessageReaction, T as MessageReceiptEntry, V as MessageReplyReference, W as MessageStarUpdatedEvent, X as MessageUpdatedEvent, Y as MessagesDeliveredEvent, Z as MultipartPartUrl, _ as MultipartUploadInfo, $ as NewMessageEvent, a0 as OptimisticAttachment, a1 as PlatformCompressFn, a2 as PlatformUploadFn, a3 as PlatformUploadPartFn, a4 as QuietHours, a5 as ReactionGroup, a6 as ReactionUpdatedEvent, a7 as ReactionUser, a8 as ReadReceiptEvent, a9 as ReplyAttachmentSnapshot, aa as ResolvedFileSizeLimits, ab as SendMessageAttachment, ac as SyncDeletedForMe, ad as SyncDeliveredReceipt, ae as SyncParticipantChange, af as SyncReactionEntry, ag as SyncReactions, ah as SyncReadReceipt, ai as SyncStarEntry, aj as SystemMessageMetadata, ak as TypingIndicatorEvent, al as UploadConfig, am as UploadProgress, an as UserStatusEvent, ao as resolveConfig, ap as storageApi, aq as uploadBatch } from './storage-DdHsneZR.cjs';
1
+ import { R as ResolvedCompressionConfig, L as LoginCredentials, A as AuthResponse, a as RegisterData, b as AuthTokens, U as User, C as CrossConversationSyncResponse, c as ConversationSyncResponse, d as AppConfig, S as SendMessagePayload, e as CursorPaginatedResponse, M as Message, f as MessageReactionsResponse, P as PaginatedResponse, g as MessageReceiptsResponse, h as ConversationListParams, i as Conversation, j as Participant, k as ConversationUnreadCount, l as UnreadSummary, m as UserPreferences, n as ResolvedConfig, o as PersistStorage, p as PresignedUrlRequest, q as PresignedUrlResponse, F as FileResponse, r as CompletedPart, s as FileType, t as AntzChatConfig, u as UploadableFile, B as BatchUploadResult } from './storage-DJh54pps.cjs';
2
+ export { v as Attachment, w as CompressedFile, x as CompressionAlgorithm, y as CompressionConfig, z as ConversationType, D as ConversationUpdatedEvent, E as FileSizeLimits, G as LastReaction, H as MessageAckEvent, I as MessageContent, J as MessageDeletedEvent, K as MessageDeletedForMeEvent, N as MessageDeliveredEvent, O as MessageMetadata, Q as MessageReaction, T as MessageReceiptEntry, V as MessageReplyReference, W as MessageStarUpdatedEvent, X as MessageUpdatedEvent, Y as MessagesDeliveredEvent, Z as MultipartPartUrl, _ as MultipartUploadInfo, $ as NewMessageEvent, a0 as OptimisticAttachment, a1 as PlatformCompressFn, a2 as PlatformUploadFn, a3 as PlatformUploadPartFn, a4 as QuietHours, a5 as ReactionGroup, a6 as ReactionUpdatedEvent, a7 as ReactionUser, a8 as ReadReceiptEvent, a9 as ReplyAttachmentSnapshot, aa as ResolvedFileSizeLimits, ab as SendMessageAttachment, ac as SyncDeletedForMe, ad as SyncDeliveredReceipt, ae as SyncParticipantChange, af as SyncReactionEntry, ag as SyncReactions, ah as SyncReadReceipt, ai as SyncStarEntry, aj as SystemMessageMetadata, ak as TypingIndicatorEvent, al as UploadConfig, am as UploadProgress, an as UserStatusEvent, ao as resolveConfig, ap as storageApi, aq as uploadBatch } from './storage-DJh54pps.cjs';
3
3
  import { AxiosInstance } from 'axios';
4
4
  import { Socket } from 'socket.io-client';
5
5
  import * as zustand_middleware from 'zustand/middleware';
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { R as ResolvedCompressionConfig, L as LoginCredentials, A as AuthResponse, a as RegisterData, b as AuthTokens, U as User, C as CrossConversationSyncResponse, c as ConversationSyncResponse, d as AppConfig, S as SendMessagePayload, e as CursorPaginatedResponse, M as Message, f as MessageReactionsResponse, P as PaginatedResponse, g as MessageReceiptsResponse, h as ConversationListParams, i as Conversation, j as Participant, k as ConversationUnreadCount, l as UnreadSummary, m as UserPreferences, n as ResolvedConfig, o as PersistStorage, p as PresignedUrlRequest, q as PresignedUrlResponse, F as FileResponse, r as CompletedPart, s as FileType, t as AntzChatConfig, u as UploadableFile, B as BatchUploadResult } from './storage-DdHsneZR.js';
2
- export { v as Attachment, w as CompressedFile, x as CompressionAlgorithm, y as CompressionConfig, z as ConversationType, D as ConversationUpdatedEvent, E as FileSizeLimits, G as LastReaction, H as MessageAckEvent, I as MessageContent, J as MessageDeletedEvent, K as MessageDeletedForMeEvent, N as MessageDeliveredEvent, O as MessageMetadata, Q as MessageReaction, T as MessageReceiptEntry, V as MessageReplyReference, W as MessageStarUpdatedEvent, X as MessageUpdatedEvent, Y as MessagesDeliveredEvent, Z as MultipartPartUrl, _ as MultipartUploadInfo, $ as NewMessageEvent, a0 as OptimisticAttachment, a1 as PlatformCompressFn, a2 as PlatformUploadFn, a3 as PlatformUploadPartFn, a4 as QuietHours, a5 as ReactionGroup, a6 as ReactionUpdatedEvent, a7 as ReactionUser, a8 as ReadReceiptEvent, a9 as ReplyAttachmentSnapshot, aa as ResolvedFileSizeLimits, ab as SendMessageAttachment, ac as SyncDeletedForMe, ad as SyncDeliveredReceipt, ae as SyncParticipantChange, af as SyncReactionEntry, ag as SyncReactions, ah as SyncReadReceipt, ai as SyncStarEntry, aj as SystemMessageMetadata, ak as TypingIndicatorEvent, al as UploadConfig, am as UploadProgress, an as UserStatusEvent, ao as resolveConfig, ap as storageApi, aq as uploadBatch } from './storage-DdHsneZR.js';
1
+ import { R as ResolvedCompressionConfig, L as LoginCredentials, A as AuthResponse, a as RegisterData, b as AuthTokens, U as User, C as CrossConversationSyncResponse, c as ConversationSyncResponse, d as AppConfig, S as SendMessagePayload, e as CursorPaginatedResponse, M as Message, f as MessageReactionsResponse, P as PaginatedResponse, g as MessageReceiptsResponse, h as ConversationListParams, i as Conversation, j as Participant, k as ConversationUnreadCount, l as UnreadSummary, m as UserPreferences, n as ResolvedConfig, o as PersistStorage, p as PresignedUrlRequest, q as PresignedUrlResponse, F as FileResponse, r as CompletedPart, s as FileType, t as AntzChatConfig, u as UploadableFile, B as BatchUploadResult } from './storage-DJh54pps.js';
2
+ export { v as Attachment, w as CompressedFile, x as CompressionAlgorithm, y as CompressionConfig, z as ConversationType, D as ConversationUpdatedEvent, E as FileSizeLimits, G as LastReaction, H as MessageAckEvent, I as MessageContent, J as MessageDeletedEvent, K as MessageDeletedForMeEvent, N as MessageDeliveredEvent, O as MessageMetadata, Q as MessageReaction, T as MessageReceiptEntry, V as MessageReplyReference, W as MessageStarUpdatedEvent, X as MessageUpdatedEvent, Y as MessagesDeliveredEvent, Z as MultipartPartUrl, _ as MultipartUploadInfo, $ as NewMessageEvent, a0 as OptimisticAttachment, a1 as PlatformCompressFn, a2 as PlatformUploadFn, a3 as PlatformUploadPartFn, a4 as QuietHours, a5 as ReactionGroup, a6 as ReactionUpdatedEvent, a7 as ReactionUser, a8 as ReadReceiptEvent, a9 as ReplyAttachmentSnapshot, aa as ResolvedFileSizeLimits, ab as SendMessageAttachment, ac as SyncDeletedForMe, ad as SyncDeliveredReceipt, ae as SyncParticipantChange, af as SyncReactionEntry, ag as SyncReactions, ah as SyncReadReceipt, ai as SyncStarEntry, aj as SystemMessageMetadata, ak as TypingIndicatorEvent, al as UploadConfig, am as UploadProgress, an as UserStatusEvent, ao as resolveConfig, ap as storageApi, aq as uploadBatch } from './storage-DJh54pps.js';
3
3
  import { AxiosInstance } from 'axios';
4
4
  import { Socket } from 'socket.io-client';
5
5
  import * as zustand_middleware from 'zustand/middleware';
@@ -1,4 +1,4 @@
1
- export { ar as uploadBatchWithSlots } from './storage-DdHsneZR.cjs';
1
+ export { ar as uploadBatchWithSlots } from './storage-DJh54pps.cjs';
2
2
 
3
3
  declare function generateUUID(): string;
4
4
 
@@ -1,4 +1,4 @@
1
- export { ar as uploadBatchWithSlots } from './storage-DdHsneZR.js';
1
+ export { ar as uploadBatchWithSlots } from './storage-DJh54pps.js';
2
2
 
3
3
  declare function generateUUID(): string;
4
4
 
@@ -484,6 +484,13 @@ interface CrossConversationSyncResponse {
484
484
  participantChanges: SyncParticipantChange[];
485
485
  readReceipts: SyncReadReceipt[];
486
486
  deliveredReceipts: SyncDeliveredReceipt[];
487
+ /**
488
+ * Conversation IDs the client should remove from its local list.
489
+ * Set when the user explicitly deleted their own copy of a conversation
490
+ * (DM or group) via the delete action. Scoped to this user only — other
491
+ * participants are not affected.
492
+ */
493
+ deletedConversationIds: string[];
487
494
  }
488
495
  interface ConversationSyncResponse {
489
496
  syncedAt: string;
@@ -484,6 +484,13 @@ interface CrossConversationSyncResponse {
484
484
  participantChanges: SyncParticipantChange[];
485
485
  readReceipts: SyncReadReceipt[];
486
486
  deliveredReceipts: SyncDeliveredReceipt[];
487
+ /**
488
+ * Conversation IDs the client should remove from its local list.
489
+ * Set when the user explicitly deleted their own copy of a conversation
490
+ * (DM or group) via the delete action. Scoped to this user only — other
491
+ * participants are not affected.
492
+ */
493
+ deletedConversationIds: string[];
487
494
  }
488
495
  interface ConversationSyncResponse {
489
496
  syncedAt: string;
@@ -155,7 +155,7 @@ section.sec>h2:hover{color:#fff}
155
155
 
156
156
  <div class="section-label">What's New</div>
157
157
  <ul>
158
- <li><a href="#whats-new">v1.3.3 Release Notes</a></li>
158
+ <li><a href="#whats-new">v1.3.4 Release Notes</a></li>
159
159
  </ul>
160
160
 
161
161
  <div class="section-label">Getting Started</div>
@@ -250,12 +250,72 @@ section.sec>h2:hover{color:#fff}
250
250
  <h2>What's New</h2>
251
251
  <p style="color:var(--muted);font-size:13px;margin-bottom:20px">Version history and release notes. Click a version to expand.</p>
252
252
 
253
- <!-- ── v1.3.3 (current) ── -->
253
+ <!-- ── v1.3.4 (current) ── -->
254
+ <div class="wn-version open" id="wn-134">
255
+ <div class="wn-header" onclick="toggleVersion('wn-134')">
256
+ <div class="wn-title">
257
+ <span class="wn-ver">v1.3.4</span>
258
+ <span class="wn-badge current">Current</span>
259
+ <span class="wn-date">July 2026</span>
260
+ </div>
261
+ <span class="wn-chevron">▲</span>
262
+ </div>
263
+ <div class="wn-body">
264
+
265
+ <div class="wn-item" id="wn-134-deleted-conv-ids">
266
+ <div class="wn-item-header" onclick="toggleItem('wn-134-deleted-conv-ids')">
267
+ <span class="wn-tag new">New</span>
268
+ <span class="wn-item-title"><code>deletedConversationIds</code> in <code>CrossConversationSyncResponse</code></span>
269
+ <span class="wn-chevron-sm">▾</span>
270
+ </div>
271
+ <div class="wn-item-body">
272
+ <p><code>syncApi.pull(since)</code> now returns <code>deletedConversationIds: string[]</code> — IDs of conversations this user explicitly deleted from their list (via the delete-conversation action) since the <code>since</code> cursor. The client should remove these from local storage and the UI conversation list.</p>
273
+ <table>
274
+ <thead><tr><th>Property</th><th>Type</th><th>Description</th></tr></thead>
275
+ <tbody>
276
+ <tr><td><code>deletedConversationIds</code></td><td><code>string[]</code></td><td>Conversation IDs to remove from local list. Empty array when none were deleted in the sync window.</td></tr>
277
+ </tbody>
278
+ </table>
279
+ <pre><code><span class="kw">const</span> result = <span class="kw">await</span> syncApi.<span class="fn">pull</span>(lastSyncedAt);
280
+
281
+ <span class="cm">// Remove conversations the user deleted from their list</span>
282
+ <span class="kw">for</span> (<span class="kw">const</span> convId <span class="kw">of</span> result.deletedConversationIds) {
283
+ <span class="kw">await</span> db.conversations.<span class="fn">delete</span>(convId);
284
+ }</code></pre>
285
+ <p><strong>Scoped to the requesting user only</strong> — if another user deletes their copy of a shared DM, it does not appear in your <code>deletedConversationIds</code>. <strong>Cursor-safe</strong> — once the client saves the returned <code>syncedAt</code> as the new cursor, the same IDs will not reappear on the next <code>pull()</code>. No acknowledged-receipt tracking needed.</p>
286
+ <p><strong>No integration changes required</strong> beyond consuming the new field. The field is always present (empty array when nothing was deleted).</p>
287
+ </div>
288
+ </div>
289
+
290
+ <div class="wn-item" id="wn-134-sync-guards">
291
+ <div class="wn-item-header" onclick="toggleItem('wn-134-sync-guards')">
292
+ <span class="wn-tag fix">Fix</span>
293
+ <span class="wn-item-title">Sync access guard parity with message listing API</span>
294
+ <span class="wn-chevron-sm">▾</span>
295
+ </div>
296
+ <div class="wn-item-body">
297
+ <p>All three sync paths now enforce the same access rules as <code>GET /messages</code>. Previously sync could return messages that message listing would not show.</p>
298
+ <table>
299
+ <thead><tr><th>Guard</th><th>Before</th><th>After</th></tr></thead>
300
+ <tbody>
301
+ <tr><td><code>membershipPeriods</code> windows</td><td>Not enforced — all messages returned regardless of when user was a member</td><td>Messages outside the user's active membership windows are excluded. Re-added users only see messages from their active periods.</td></tr>
302
+ <tr><td><code>status</code> filter</td><td>Only <code>deletedFor</code> checked — <code>failed</code>-status messages leaked through</td><td>Only <code>active</code> and <code>deleted</code> (tombstone) messages returned, matching message listing</td></tr>
303
+ <tr><td><code>isHidden</code> group check</td><td>Not checked — hidden group members could still sync</td><td>Hidden group participants (exit+delete) receive <code>403</code>. Hidden DM participants still allowed (consistent with message listing).</td></tr>
304
+ <tr><td>Removed-member read access</td><td><code>isActive: true</code> required — removed members blocked from syncing history</td><td>Removed members can sync read-only history, matching message listing <code>validateConversationAccess</code></td></tr>
305
+ </tbody>
306
+ </table>
307
+ <p><strong>No integration changes required.</strong> Response shape is unchanged — only the correctness of the returned message set improves.</p>
308
+ </div>
309
+ </div>
310
+
311
+ </div>
312
+ </div><!-- /.wn-version -->
313
+
314
+ <!-- ── v1.3.3 ── -->
254
315
  <div class="wn-version open" id="wn-133">
255
316
  <div class="wn-header" onclick="toggleVersion('wn-133')">
256
317
  <div class="wn-title">
257
318
  <span class="wn-ver">v1.3.3</span>
258
- <span class="wn-badge current">Current</span>
259
319
  <span class="wn-date">June 2026</span>
260
320
  </div>
261
321
  <span class="wn-chevron">▲</span>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antzsoft/chat-core",
3
- "version": "1.3.3",
3
+ "version": "1.3.4",
4
4
  "description": "Platform-agnostic core for Antz Chat — API, socket, stores, types. Works in browser, React Native (Expo or bare), and Node.js.",
5
5
  "author": "Antz",
6
6
  "license": "MIT",