@antzsoft/chat-core 1.4.3 → 1.4.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 +26 -3
- package/dist/index.cjs +668 -631
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +34 -5
- package/dist/index.d.ts +34 -5
- package/dist/index.js +448 -410
- package/dist/index.js.map +1 -1
- package/dist/internal.d.cts +1 -1
- package/dist/internal.d.ts +1 -1
- package/dist/{storage-D7GPq-Mm.d.cts → storage-C8V7aVum.d.cts} +23 -1
- package/dist/{storage-D7GPq-Mm.d.ts → storage-C8V7aVum.d.ts} +23 -1
- package/docs/integration-guide.html +187 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -747,7 +747,7 @@ import { messagesApi } from '@antzsoft/chat-core';
|
|
|
747
747
|
|---|---|---|
|
|
748
748
|
| `list` | `(conversationId: string, params?: ListMessagesParams) => Promise<CursorPaginatedResponse<Message>>` | Fetch messages with cursor pagination. |
|
|
749
749
|
| `get` | `(messageId: string) => Promise<Message>` | Fetch a single message. |
|
|
750
|
-
| `send` | `(conversationId: string, payload: SendData) => Promise<Message>` | Send a message via REST (use `socketEmit.sendMessage` for real-time delivery). |
|
|
750
|
+
| `send` | `(conversationId: string, payload: SendData) => Promise<Message>` | Send a message via REST (use `socketEmit.sendMessage` for real-time delivery). Pass `payload.tempId` and reuse the SAME value on retry to make a retry-after-timeout safe — see "Retry safety" below. |
|
|
751
751
|
| `update` | `(messageId: string, text: string) => Promise<Message>` | Edit message text. |
|
|
752
752
|
| `delete` | `(messageId: string) => Promise<void>` | Delete a message for everyone (own message within window, or admin). |
|
|
753
753
|
| `deleteForMe` | `(messageId: string) => Promise<void>` | Hide a message for the current user only — other participants are unaffected. |
|
|
@@ -779,7 +779,7 @@ interface SendData {
|
|
|
779
779
|
text?: string;
|
|
780
780
|
attachments?: SendMessageAttachment[];
|
|
781
781
|
replyTo?: string; // messageId of the message being replied to
|
|
782
|
-
tempId?: string; // Client-generated
|
|
782
|
+
tempId?: string; // Client-generated idempotency key — see "Retry safety" below. NOT auto-generated if omitted.
|
|
783
783
|
mentions?: string[]; // Mentioned userIds ('all' for @all); derived from @[name](id) tokens in text
|
|
784
784
|
}
|
|
785
785
|
|
|
@@ -813,13 +813,26 @@ await messagesApi.send('conv-abc', {
|
|
|
813
813
|
const results = await messagesApi.search({ query: 'deployment', conversationId: 'conv-abc' });
|
|
814
814
|
```
|
|
815
815
|
|
|
816
|
+
#### Message Send Retry Safety (v1.4.4+)
|
|
817
|
+
|
|
818
|
+
If `messagesApi.send()`/`socketEmit.sendMessage()` times out client-side, you don't know whether the server actually created the message before the response was lost. Retrying blind can create a duplicate. `tempId` fixes this — the server checks `(conversationId, senderId, tempId)` before creating a message, on both the REST send path and the socket path:
|
|
819
|
+
|
|
820
|
+
- Generate **one** `tempId` per send attempt (a real UUID — `generateUUID` from `'@antzsoft/chat-core/internal'`, or `crypto.randomUUID()`).
|
|
821
|
+
- Reuse the **exact same** `tempId` if you retry that same send. Never mint a new one for a retry — only for a genuinely new message.
|
|
822
|
+
- A retry with a matching `tempId` returns the already-created message instead of creating a duplicate. On the socket path this is fully silent to everyone else — the retrying client still gets its `message_ack` (so its optimistic bubble reconciles), but no second `new_message` is broadcast to the room and no second push notification fires, since the original successful attempt already delivered both.
|
|
823
|
+
- Unlike `forward()`, `send()` does **not** auto-generate a `tempId` if you omit it — omitting it, or generating a fresh one on every retry, gets no dedup protection and reproduces the exact pre-1.4.4 behavior.
|
|
824
|
+
- `socketEmit.sendMessage`'s `SendMessagePayload.tempId` was always a **required** field — every client already sends one on every call. This release is what makes the server actually act on it; no wire-format change.
|
|
825
|
+
|
|
826
|
+
**`useChat().retrySendMessage(failedMessageId)` (both UI SDKs)** does all of this correctly for you: it resends the exact original payload (same `tempId`, same already-uploaded attachment `fileId`s — no re-upload) for a message whose `deliveryStatus` is `'failed'`. The built-in `MessageItem` component renders a tappable "Retry" on the failed-delivery indicator and a "Retry" entry in the message action menu, both wired to this — no extra code needed if you're using the prebuilt UI. If you're driving `messagesApi`/`socketEmit` directly (headless usage), you own remembering the `tempId` per attempt yourself.
|
|
827
|
+
|
|
816
828
|
#### Forward Message (v1.4.2+)
|
|
817
829
|
|
|
818
830
|
Forwarding creates an **independent copy** of a message in each target conversation — never a shared row. Each copy gets its own ID, sequence number, read receipts, and delete lifecycle; deleting one copy never affects another, and none of them affect the original.
|
|
819
831
|
|
|
820
832
|
| Field / method | Type | Description |
|
|
821
833
|
|---|---|---|
|
|
822
|
-
| `messagesApi.forward(messageId, targetConversationIds, attachmentIds?)` | `Promise<ForwardResult[]>` | Forwards into up to `MAX_FORWARD_TARGETS` (5) conversations in one call — fewer if the source message is highly forwarded, see Limits below. Pass `attachmentIds` to forward only a subset of the source message's attachments (e.g. one image out of a multi-image message); omit it to forward the whole message unchanged. An ID not on the source message is ignored. |
|
|
834
|
+
| `messagesApi.forward(messageId, targetConversationIds, attachmentIds?, tempId?)` | `Promise<ForwardResult[]>` | Forwards into up to `MAX_FORWARD_TARGETS` (5) conversations in one call — fewer if the source message is highly forwarded, see Limits below. Pass `attachmentIds` to forward only a subset of the source message's attachments (e.g. one image out of a multi-image message); omit it to forward the whole message unchanged. An ID not on the source message is ignored. `tempId` makes retries safe — see "Retry safety" below; auto-generated if omitted. Uses the socket transport when connected, REST otherwise — see "Transport" below. |
|
|
835
|
+
| `socketEmit.forwardMessage(payload: ForwardMessagePayload)` | `Promise<ForwardAckPayload>` | Lower-level socket-only entry point that `messagesApi.forward()` uses internally when a socket is connected. Most consumers should call `messagesApi.forward()` instead so REST fallback is automatic. |
|
|
823
836
|
| `Message.forwardedFrom` | `MessageForwardReference \| undefined` | Present on a message created via forwarding. |
|
|
824
837
|
| `MAX_FORWARD_TARGETS` | `number` | `5` — matches the server's normal-case cap. |
|
|
825
838
|
| `HIGHLY_FORWARDED_DEPTH_THRESHOLD` | `number` | `5` — matches the server's `forwardDepth` value that triggers BOTH the "Forwarded many times" label and the reduced fan-out cap. Use this for the label instead of hardcoding a number. |
|
|
@@ -870,6 +883,16 @@ await messagesApi.forward(messageId, [convA], [message.content.attachments![1].i
|
|
|
870
883
|
|
|
871
884
|
**Rendering:** show a "Forwarded" (or "Forwarded many times" at `forwardDepth >= HIGHLY_FORWARDED_DEPTH_THRESHOLD`) label when `message.forwardedFrom` is present — do not render a sender name or content preview for it (unlike `replyTo`), since the message's own `content` already holds what was forwarded. The built-in `MessageItem` component in both `@antzsoft/chat-web-sdk` and `@antzsoft/chat-rn-sdk` already renders this label using the exported constant and exposes a "Forward" action in the message menu — no extra wiring needed if you're using the prebuilt UI.
|
|
872
885
|
|
|
886
|
+
**Transport (v1.4.4+).** `forward()` sends over the `forward_message` socket event (acked via the standard socket.io callback — the same request/response ack pattern as `update_message`/`pin_message`/etc., not a separate broadcast event) whenever a socket is connected, and transparently falls back to REST otherwise — you never call the socket path directly; `messagesApi.forward()` picks it for you. Either way it runs through the exact same server-side `MessagesService.forward()` code path, so broadcast/`conversation_updated`/push-notification behavior is identical regardless of which transport was actually used. There is still no built-in *automatic* retry on timeout — if a call times out client-side, you decide whether/when to retry — but unlike a plain REST call, a client that's connected gets the lower-latency socket path without any code change.
|
|
887
|
+
|
|
888
|
+
**Retry safety (v1.4.4+).** The `tempId` parameter is what makes a retry safe, on either transport:
|
|
889
|
+
|
|
890
|
+
- Generate **one** `tempId` per forward *action* (one user tap on "Forward," covering all its target conversations) — not one per target.
|
|
891
|
+
- Reuse the **exact same** `tempId` if you retry that same action (e.g. the user taps "Forward" again after a partial failure). Never mint a new one for a retry — only when the user starts a genuinely new forward.
|
|
892
|
+
- The server checks `(targetConversationId, senderId, tempId)` — scoped per target, since one forward action can create up to 5 messages, one per target — before creating anything. A retry with a matching `tempId` returns the message that already exists for that target instead of creating a duplicate; targets that failed the first time are retried normally.
|
|
893
|
+
- If you omit `tempId`, one is auto-generated per call — meaning a retry without passing the *same* value back gets **no dedup protection** and may create a duplicate for any target that actually succeeded before the failure was reported. The built-in `ForwardPicker` component in both `@antzsoft/chat-web-sdk` and `@antzsoft/chat-rn-sdk` already does this correctly (one `tempId` per picker session, reused across retries) — no extra wiring needed if you're using the prebuilt UI.
|
|
894
|
+
- Regular message sends (`socketEmit.sendMessage` / `messagesApi.send()`) get this same protection — see "Retry safety" above.
|
|
895
|
+
|
|
873
896
|
#### Jump to first unread message
|
|
874
897
|
|
|
875
898
|
Use `direction: 'after'` with the user's `lastReadMessageId` as the cursor to fetch only the unread messages. This powers a scroll-to-first-unread experience with an "↑ Unread messages" divider.
|