@antzsoft/chat-core 1.4.4 → 1.4.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/README.md +64 -1
- package/dist/{chunk-XTQYF5HU.js → chunk-U637W5MD.js} +147 -28
- package/dist/chunk-U637W5MD.js.map +1 -0
- package/dist/index.cjs +249 -72
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +60 -5
- package/dist/index.d.ts +60 -5
- package/dist/index.js +117 -48
- package/dist/index.js.map +1 -1
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.d.cts +1 -1
- package/dist/internal.d.ts +1 -1
- package/dist/internal.js +1 -1
- package/dist/{storage-C8V7aVum.d.cts → storage-DlxfLVqx.d.cts} +21 -1
- package/dist/{storage-C8V7aVum.d.ts → storage-DlxfLVqx.d.ts} +21 -1
- package/docs/integration-guide.html +151 -4
- package/package.json +1 -1
- package/dist/chunk-XTQYF5HU.js.map +0 -1
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, M as Message, e as CursorPaginatedResponse, 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, F as ForwardMessagePayload, o as ForwardAckPayload, p as PersistStorage, q as PresignedUrlRequest, r as PresignedUrlResponse, s as FileResponse, t as CompletedPart, u as FileType, v as AntzChatConfig, w as UploadableFile, B as BatchUploadResult } from './storage-
|
|
2
|
-
export { x as Attachment, y as CompressedFile, z as CompressionAlgorithm, D as CompressionConfig, E as ConversationType, G as ConversationUpdatedEvent, H as FileSizeLimits, I as ForwardResultPayload, J as LastReaction, K as MENTION_ALL_ID, N as MessageAckEvent, O as MessageContent, Q as MessageDeletedEvent, T as MessageDeletedForMeEvent, V as MessageDeliveredEvent, W as MessageForwardReference, X as MessageMetadata, Y as MessageReaction, Z as MessageReceiptEntry, _ as MessageReplyReference, $ as MessageStarUpdatedEvent, a0 as MessageUpdatedEvent, a1 as MessagesDeliveredEvent, a2 as MultipartPartUrl, a3 as MultipartUploadInfo, a4 as NewMessageEvent, a5 as OptimisticAttachment, a6 as PlatformCompressFn, a7 as PlatformUploadFn, a8 as PlatformUploadPartFn, a9 as QuietHours, aa as ReactionGroup, ab as ReactionUpdatedEvent, ac as ReactionUser, ad as ReadReceiptEvent, ae as ReplyAttachmentSnapshot, af as ResolvedFileSizeLimits, ag as
|
|
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, M as Message, e as CursorPaginatedResponse, 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, F as ForwardMessagePayload, o as ForwardAckPayload, p as PersistStorage, q as PresignedUrlRequest, r as PresignedUrlResponse, s as FileResponse, t as CompletedPart, u as FileType, v as AntzChatConfig, w as UploadableFile, B as BatchUploadResult } from './storage-DlxfLVqx.cjs';
|
|
2
|
+
export { x as Attachment, y as CompressedFile, z as CompressionAlgorithm, D as CompressionConfig, E as ConversationType, G as ConversationUpdatedEvent, H as FileSizeLimits, I as ForwardResultPayload, J as LastReaction, K as MENTION_ALL_ID, N as MessageAckEvent, O as MessageContent, Q as MessageDeletedEvent, T as MessageDeletedForMeEvent, V as MessageDeliveredEvent, W as MessageForwardReference, X as MessageMetadata, Y as MessageReaction, Z as MessageReceiptEntry, _ as MessageReplyReference, $ as MessageStarUpdatedEvent, a0 as MessageUpdatedEvent, a1 as MessagesDeliveredEvent, a2 as MultipartPartUrl, a3 as MultipartUploadInfo, a4 as NewMessageEvent, a5 as OptimisticAttachment, a6 as PlatformCompressFn, a7 as PlatformUploadFn, a8 as PlatformUploadPartFn, a9 as QuietHours, aa as ReactionGroup, ab as ReactionUpdatedEvent, ac as ReactionUser, ad as ReadReceiptEvent, ae as ReplyAttachmentSnapshot, af as ResolvedFileSizeLimits, ag as SendErrorContext, ah as SendMessageAttachment, ai as SyncDeletedForMe, aj as SyncDeliveredReceipt, ak as SyncParticipantChange, al as SyncReactionEntry, am as SyncReactions, an as SyncReadReceipt, ao as SyncStarEntry, ap as SystemMessageMetadata, aq as TypingIndicatorEvent, ar as UploadConfig, as as UploadProgress, at as UserStatusEvent, au as resolveConfig, av as storageApi, aw as uploadBatch } from './storage-DlxfLVqx.cjs';
|
|
3
3
|
import { AxiosInstance } from 'axios';
|
|
4
4
|
import { Socket } from 'socket.io-client';
|
|
5
5
|
import * as zustand_middleware from 'zustand/middleware';
|
|
@@ -325,6 +325,8 @@ type TokenStore = {
|
|
|
325
325
|
setTokens: (tokens: AuthTokens) => void;
|
|
326
326
|
clearTokens: () => void;
|
|
327
327
|
};
|
|
328
|
+
declare function setAuthReadyPromise(promise: Promise<unknown> | null): void;
|
|
329
|
+
declare function isApiClientConfigured(): boolean;
|
|
328
330
|
declare function initApiClient(config: ResolvedConfig, tokenStore: TokenStore): AxiosInstance;
|
|
329
331
|
declare function setApiClientInstance(instance: AxiosInstance): void;
|
|
330
332
|
declare function getApiClient(): AxiosInstance;
|
|
@@ -337,6 +339,10 @@ declare function getSocket(): Socket;
|
|
|
337
339
|
declare function tryGetSocket(): Socket | null;
|
|
338
340
|
declare function getSocketStatus(): SocketStatus;
|
|
339
341
|
declare function onSocketStatus(listener: StatusListener): () => void;
|
|
342
|
+
/** Drop all tracked rooms. Call on a genuine identity change (user/tenant
|
|
343
|
+
* switch) so the reconnect re-flush can't re-join the previous user's rooms.
|
|
344
|
+
* NOT called from disconnectSocket() — a token refresh is not an intent change. */
|
|
345
|
+
declare function resetTrackedRooms(): void;
|
|
340
346
|
declare function connectSocket(config: ResolvedConfig, getToken: () => string | null | undefined): Promise<Socket>;
|
|
341
347
|
declare function disconnectSocket(): void;
|
|
342
348
|
declare function reconnectSocket(token: string, userId?: string, tenantId?: string): void;
|
|
@@ -389,17 +395,66 @@ declare function encryptPayload(data: unknown, sessionKey: AnySessionKey): Promi
|
|
|
389
395
|
declare function decryptPayload(envelope: TransitEnvelope, sessionKey: AnySessionKey): Promise<unknown>;
|
|
390
396
|
declare function isTransitEnvelope(v: unknown): v is TransitEnvelope;
|
|
391
397
|
|
|
398
|
+
/**
|
|
399
|
+
* Caller identity attached to the pre-auth transit handshake requests.
|
|
400
|
+
*
|
|
401
|
+
* GET /crypto/pubkey and POST /crypto/session run BEFORE a transit session (and
|
|
402
|
+
* therefore before the authenticated axios client) exists, so they bypass the
|
|
403
|
+
* request interceptor that normally adds these headers. Without them the server
|
|
404
|
+
* can only rate-limit these two routes by client IP — which means every user
|
|
405
|
+
* behind one NAT, office proxy or ALB shares a single bucket, and one client's
|
|
406
|
+
* reload loop 429s everyone else.
|
|
407
|
+
*
|
|
408
|
+
* These values are NOT used for authentication: the endpoints are unauthenticated
|
|
409
|
+
* by design and the server treats the headers as a fairness hint only, with a
|
|
410
|
+
* per-IP ceiling underneath as the real abuse limit. Sending them is therefore
|
|
411
|
+
* safe, optional, and backward compatible — an older SDK that omits them simply
|
|
412
|
+
* falls back to the shared per-IP bucket.
|
|
413
|
+
*/
|
|
414
|
+
interface TransitIdentity {
|
|
415
|
+
/** External user id — same value sent as x-user-id on authenticated requests. */
|
|
416
|
+
userId?: string;
|
|
417
|
+
/** Tenant id — same value sent as X-Tenant-ID on authenticated requests. */
|
|
418
|
+
tenantId?: string;
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* How long the server asked us to wait, in ms, from a 429's Retry-After.
|
|
422
|
+
*
|
|
423
|
+
* The chat server runs TWO named rate-limit layers, and @nestjs/throttler
|
|
424
|
+
* suffixes its headers with the throttler name unless that name is literally
|
|
425
|
+
* "default". So a 429 carries `Retry-After-identity` or `Retry-After-ip`, not a
|
|
426
|
+
* bare `Retry-After`. Older servers and intermediary proxies may still send the
|
|
427
|
+
* bare name, so all three are read; when more than one is present the LONGER
|
|
428
|
+
* wait wins, since retrying before the slower bucket drains just earns another
|
|
429
|
+
* 429.
|
|
430
|
+
*
|
|
431
|
+
* Returns undefined when no variant is readable — in a browser that also
|
|
432
|
+
* happens when the server omits these from Access-Control-Expose-Headers, in
|
|
433
|
+
* which case callers must fall back to their own backoff.
|
|
434
|
+
*/
|
|
435
|
+
declare function readRetryAfterMs(headers: Headers): number | undefined;
|
|
436
|
+
/**
|
|
437
|
+
* Thrown when the transit handshake is rate-limited (HTTP 429).
|
|
438
|
+
*
|
|
439
|
+
* Distinct from a generic failure because the correct response differs: a 429
|
|
440
|
+
* means "wait", not "this is broken", so callers must not burn their retry
|
|
441
|
+
* budget on it and should honour `retryAfterMs` when the server supplied it.
|
|
442
|
+
*/
|
|
443
|
+
declare class TransitRateLimitedError extends Error {
|
|
444
|
+
readonly retryAfterMs?: number;
|
|
445
|
+
constructor(retryAfterMs?: number);
|
|
446
|
+
}
|
|
392
447
|
interface ServerPublicKeys {
|
|
393
448
|
x25519: string;
|
|
394
449
|
p256: string;
|
|
395
450
|
enabled: boolean;
|
|
396
451
|
}
|
|
397
|
-
declare function fetchServerKeys(apiUrl: string): Promise<ServerPublicKeys>;
|
|
452
|
+
declare function fetchServerKeys(apiUrl: string, identity?: TransitIdentity): Promise<ServerPublicKeys>;
|
|
398
453
|
declare function generateEphemeralKey(algo: TransitAlgo, serverKeys: ServerPublicKeys): Promise<{
|
|
399
454
|
ephemeralPubB64: string;
|
|
400
455
|
deriveSessionKey: (sessionId: string) => Promise<unknown>;
|
|
401
456
|
}>;
|
|
402
|
-
declare function createRestTransitSession(apiUrl: string): Promise<{
|
|
457
|
+
declare function createRestTransitSession(apiUrl: string, identity?: TransitIdentity): Promise<{
|
|
403
458
|
sessionId: string;
|
|
404
459
|
sessionKey: CryptoKey | Uint8Array;
|
|
405
460
|
} | null>;
|
|
@@ -793,4 +848,4 @@ declare class AntzChatClient {
|
|
|
793
848
|
uploadIcon(conversationId: string, file: UploadableFile): Promise<Conversation>;
|
|
794
849
|
}
|
|
795
850
|
|
|
796
|
-
export { AntzChatAuthError, AntzChatClient, AntzChatConfig, AntzChatError, AntzChatNetworkError, AntzChatPermissionError, AntzChatServerError, AntzChatValidationError, AppConfig, AuthResponse, AuthTokens, BatchUploadResult, CompletedPart, Conversation, ConversationListParams, ConversationSyncResponse, ConversationUnreadCount, type CreateDirectData, type CreateGroupData, CrossConversationSyncResponse, CursorPaginatedResponse, FileResponse, FileType, ForwardAckPayload, ForwardMessagePayload, type ForwardResult, HIGHLY_FORWARDED_DEPTH_THRESHOLD, type LastReadEntry, type ListMessagesParams, LoginCredentials, MAX_FORWARD_TARGETS, type MentionPart, type MentionSegment, Message, MessageReactionsResponse, MessageReceiptsResponse, type MobileDeviceToken, PaginatedResponse, type ParsedMention, Participant, PersistStorage, PresignedUrlRequest, PresignedUrlResponse, RegisterData, type RegisterDeviceTokenPayload, ResolvedCompressionConfig, ResolvedConfig, type SearchParams, type SendData, SendMessagePayload, type SocketStatus, type StatusListener, type TokenStore, UnreadSummary, type UpdateConversationData, type UpdateProfilePayload, UploadableFile, User, UserPreferences, type WebPushDeviceToken, appConfigApi, authApi, buildMentionText, connectSocket, conversationsApi, createAuthStore, createRestTransitSession, decryptPayload, devicesApi, disconnectSocket, encryptPayload, extractMentionIds, fetchServerKeys, generateEphemeralKey, getApiClient, getAuthStore, getCompressionStrategy, getSessionId, getSessionKey, getSocket, getSocketStatus, initApiClient, initAuthStore, isMentionAll, isTransitEnvelope, messagesApi, normalizeAxiosError, normalizeConversation, onSocketStatus, parseMentions, performHandshake, reconnectSocket, refreshSocketAuth, renderMentionParts, resetAuthStore, resolveSystemMessageText, setApiClientInstance, setTransitSession, socketEmit, syncApi, tryGetSocket, useChatStore, usersApi };
|
|
851
|
+
export { AntzChatAuthError, AntzChatClient, AntzChatConfig, AntzChatError, AntzChatNetworkError, AntzChatPermissionError, AntzChatServerError, AntzChatValidationError, AppConfig, AuthResponse, AuthTokens, BatchUploadResult, CompletedPart, Conversation, ConversationListParams, ConversationSyncResponse, ConversationUnreadCount, type CreateDirectData, type CreateGroupData, CrossConversationSyncResponse, CursorPaginatedResponse, FileResponse, FileType, ForwardAckPayload, ForwardMessagePayload, type ForwardResult, HIGHLY_FORWARDED_DEPTH_THRESHOLD, type LastReadEntry, type ListMessagesParams, LoginCredentials, MAX_FORWARD_TARGETS, type MentionPart, type MentionSegment, Message, MessageReactionsResponse, MessageReceiptsResponse, type MobileDeviceToken, PaginatedResponse, type ParsedMention, Participant, PersistStorage, PresignedUrlRequest, PresignedUrlResponse, RegisterData, type RegisterDeviceTokenPayload, ResolvedCompressionConfig, ResolvedConfig, type SearchParams, type SendData, SendMessagePayload, type SocketStatus, type StatusListener, type TokenStore, type TransitIdentity, TransitRateLimitedError, UnreadSummary, type UpdateConversationData, type UpdateProfilePayload, UploadableFile, User, UserPreferences, type WebPushDeviceToken, appConfigApi, authApi, buildMentionText, connectSocket, conversationsApi, createAuthStore, createRestTransitSession, decryptPayload, devicesApi, disconnectSocket, encryptPayload, extractMentionIds, fetchServerKeys, generateEphemeralKey, getApiClient, getAuthStore, getCompressionStrategy, getSessionId, getSessionKey, getSocket, getSocketStatus, initApiClient, initAuthStore, isApiClientConfigured, isMentionAll, isTransitEnvelope, messagesApi, normalizeAxiosError, normalizeConversation, onSocketStatus, parseMentions, performHandshake, readRetryAfterMs, reconnectSocket, refreshSocketAuth, renderMentionParts, resetAuthStore, resetTrackedRooms, resolveSystemMessageText, setApiClientInstance, setAuthReadyPromise, setTransitSession, socketEmit, syncApi, tryGetSocket, useChatStore, usersApi };
|
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, M as Message, e as CursorPaginatedResponse, 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, F as ForwardMessagePayload, o as ForwardAckPayload, p as PersistStorage, q as PresignedUrlRequest, r as PresignedUrlResponse, s as FileResponse, t as CompletedPart, u as FileType, v as AntzChatConfig, w as UploadableFile, B as BatchUploadResult } from './storage-
|
|
2
|
-
export { x as Attachment, y as CompressedFile, z as CompressionAlgorithm, D as CompressionConfig, E as ConversationType, G as ConversationUpdatedEvent, H as FileSizeLimits, I as ForwardResultPayload, J as LastReaction, K as MENTION_ALL_ID, N as MessageAckEvent, O as MessageContent, Q as MessageDeletedEvent, T as MessageDeletedForMeEvent, V as MessageDeliveredEvent, W as MessageForwardReference, X as MessageMetadata, Y as MessageReaction, Z as MessageReceiptEntry, _ as MessageReplyReference, $ as MessageStarUpdatedEvent, a0 as MessageUpdatedEvent, a1 as MessagesDeliveredEvent, a2 as MultipartPartUrl, a3 as MultipartUploadInfo, a4 as NewMessageEvent, a5 as OptimisticAttachment, a6 as PlatformCompressFn, a7 as PlatformUploadFn, a8 as PlatformUploadPartFn, a9 as QuietHours, aa as ReactionGroup, ab as ReactionUpdatedEvent, ac as ReactionUser, ad as ReadReceiptEvent, ae as ReplyAttachmentSnapshot, af as ResolvedFileSizeLimits, ag as
|
|
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, M as Message, e as CursorPaginatedResponse, 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, F as ForwardMessagePayload, o as ForwardAckPayload, p as PersistStorage, q as PresignedUrlRequest, r as PresignedUrlResponse, s as FileResponse, t as CompletedPart, u as FileType, v as AntzChatConfig, w as UploadableFile, B as BatchUploadResult } from './storage-DlxfLVqx.js';
|
|
2
|
+
export { x as Attachment, y as CompressedFile, z as CompressionAlgorithm, D as CompressionConfig, E as ConversationType, G as ConversationUpdatedEvent, H as FileSizeLimits, I as ForwardResultPayload, J as LastReaction, K as MENTION_ALL_ID, N as MessageAckEvent, O as MessageContent, Q as MessageDeletedEvent, T as MessageDeletedForMeEvent, V as MessageDeliveredEvent, W as MessageForwardReference, X as MessageMetadata, Y as MessageReaction, Z as MessageReceiptEntry, _ as MessageReplyReference, $ as MessageStarUpdatedEvent, a0 as MessageUpdatedEvent, a1 as MessagesDeliveredEvent, a2 as MultipartPartUrl, a3 as MultipartUploadInfo, a4 as NewMessageEvent, a5 as OptimisticAttachment, a6 as PlatformCompressFn, a7 as PlatformUploadFn, a8 as PlatformUploadPartFn, a9 as QuietHours, aa as ReactionGroup, ab as ReactionUpdatedEvent, ac as ReactionUser, ad as ReadReceiptEvent, ae as ReplyAttachmentSnapshot, af as ResolvedFileSizeLimits, ag as SendErrorContext, ah as SendMessageAttachment, ai as SyncDeletedForMe, aj as SyncDeliveredReceipt, ak as SyncParticipantChange, al as SyncReactionEntry, am as SyncReactions, an as SyncReadReceipt, ao as SyncStarEntry, ap as SystemMessageMetadata, aq as TypingIndicatorEvent, ar as UploadConfig, as as UploadProgress, at as UserStatusEvent, au as resolveConfig, av as storageApi, aw as uploadBatch } from './storage-DlxfLVqx.js';
|
|
3
3
|
import { AxiosInstance } from 'axios';
|
|
4
4
|
import { Socket } from 'socket.io-client';
|
|
5
5
|
import * as zustand_middleware from 'zustand/middleware';
|
|
@@ -325,6 +325,8 @@ type TokenStore = {
|
|
|
325
325
|
setTokens: (tokens: AuthTokens) => void;
|
|
326
326
|
clearTokens: () => void;
|
|
327
327
|
};
|
|
328
|
+
declare function setAuthReadyPromise(promise: Promise<unknown> | null): void;
|
|
329
|
+
declare function isApiClientConfigured(): boolean;
|
|
328
330
|
declare function initApiClient(config: ResolvedConfig, tokenStore: TokenStore): AxiosInstance;
|
|
329
331
|
declare function setApiClientInstance(instance: AxiosInstance): void;
|
|
330
332
|
declare function getApiClient(): AxiosInstance;
|
|
@@ -337,6 +339,10 @@ declare function getSocket(): Socket;
|
|
|
337
339
|
declare function tryGetSocket(): Socket | null;
|
|
338
340
|
declare function getSocketStatus(): SocketStatus;
|
|
339
341
|
declare function onSocketStatus(listener: StatusListener): () => void;
|
|
342
|
+
/** Drop all tracked rooms. Call on a genuine identity change (user/tenant
|
|
343
|
+
* switch) so the reconnect re-flush can't re-join the previous user's rooms.
|
|
344
|
+
* NOT called from disconnectSocket() — a token refresh is not an intent change. */
|
|
345
|
+
declare function resetTrackedRooms(): void;
|
|
340
346
|
declare function connectSocket(config: ResolvedConfig, getToken: () => string | null | undefined): Promise<Socket>;
|
|
341
347
|
declare function disconnectSocket(): void;
|
|
342
348
|
declare function reconnectSocket(token: string, userId?: string, tenantId?: string): void;
|
|
@@ -389,17 +395,66 @@ declare function encryptPayload(data: unknown, sessionKey: AnySessionKey): Promi
|
|
|
389
395
|
declare function decryptPayload(envelope: TransitEnvelope, sessionKey: AnySessionKey): Promise<unknown>;
|
|
390
396
|
declare function isTransitEnvelope(v: unknown): v is TransitEnvelope;
|
|
391
397
|
|
|
398
|
+
/**
|
|
399
|
+
* Caller identity attached to the pre-auth transit handshake requests.
|
|
400
|
+
*
|
|
401
|
+
* GET /crypto/pubkey and POST /crypto/session run BEFORE a transit session (and
|
|
402
|
+
* therefore before the authenticated axios client) exists, so they bypass the
|
|
403
|
+
* request interceptor that normally adds these headers. Without them the server
|
|
404
|
+
* can only rate-limit these two routes by client IP — which means every user
|
|
405
|
+
* behind one NAT, office proxy or ALB shares a single bucket, and one client's
|
|
406
|
+
* reload loop 429s everyone else.
|
|
407
|
+
*
|
|
408
|
+
* These values are NOT used for authentication: the endpoints are unauthenticated
|
|
409
|
+
* by design and the server treats the headers as a fairness hint only, with a
|
|
410
|
+
* per-IP ceiling underneath as the real abuse limit. Sending them is therefore
|
|
411
|
+
* safe, optional, and backward compatible — an older SDK that omits them simply
|
|
412
|
+
* falls back to the shared per-IP bucket.
|
|
413
|
+
*/
|
|
414
|
+
interface TransitIdentity {
|
|
415
|
+
/** External user id — same value sent as x-user-id on authenticated requests. */
|
|
416
|
+
userId?: string;
|
|
417
|
+
/** Tenant id — same value sent as X-Tenant-ID on authenticated requests. */
|
|
418
|
+
tenantId?: string;
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* How long the server asked us to wait, in ms, from a 429's Retry-After.
|
|
422
|
+
*
|
|
423
|
+
* The chat server runs TWO named rate-limit layers, and @nestjs/throttler
|
|
424
|
+
* suffixes its headers with the throttler name unless that name is literally
|
|
425
|
+
* "default". So a 429 carries `Retry-After-identity` or `Retry-After-ip`, not a
|
|
426
|
+
* bare `Retry-After`. Older servers and intermediary proxies may still send the
|
|
427
|
+
* bare name, so all three are read; when more than one is present the LONGER
|
|
428
|
+
* wait wins, since retrying before the slower bucket drains just earns another
|
|
429
|
+
* 429.
|
|
430
|
+
*
|
|
431
|
+
* Returns undefined when no variant is readable — in a browser that also
|
|
432
|
+
* happens when the server omits these from Access-Control-Expose-Headers, in
|
|
433
|
+
* which case callers must fall back to their own backoff.
|
|
434
|
+
*/
|
|
435
|
+
declare function readRetryAfterMs(headers: Headers): number | undefined;
|
|
436
|
+
/**
|
|
437
|
+
* Thrown when the transit handshake is rate-limited (HTTP 429).
|
|
438
|
+
*
|
|
439
|
+
* Distinct from a generic failure because the correct response differs: a 429
|
|
440
|
+
* means "wait", not "this is broken", so callers must not burn their retry
|
|
441
|
+
* budget on it and should honour `retryAfterMs` when the server supplied it.
|
|
442
|
+
*/
|
|
443
|
+
declare class TransitRateLimitedError extends Error {
|
|
444
|
+
readonly retryAfterMs?: number;
|
|
445
|
+
constructor(retryAfterMs?: number);
|
|
446
|
+
}
|
|
392
447
|
interface ServerPublicKeys {
|
|
393
448
|
x25519: string;
|
|
394
449
|
p256: string;
|
|
395
450
|
enabled: boolean;
|
|
396
451
|
}
|
|
397
|
-
declare function fetchServerKeys(apiUrl: string): Promise<ServerPublicKeys>;
|
|
452
|
+
declare function fetchServerKeys(apiUrl: string, identity?: TransitIdentity): Promise<ServerPublicKeys>;
|
|
398
453
|
declare function generateEphemeralKey(algo: TransitAlgo, serverKeys: ServerPublicKeys): Promise<{
|
|
399
454
|
ephemeralPubB64: string;
|
|
400
455
|
deriveSessionKey: (sessionId: string) => Promise<unknown>;
|
|
401
456
|
}>;
|
|
402
|
-
declare function createRestTransitSession(apiUrl: string): Promise<{
|
|
457
|
+
declare function createRestTransitSession(apiUrl: string, identity?: TransitIdentity): Promise<{
|
|
403
458
|
sessionId: string;
|
|
404
459
|
sessionKey: CryptoKey | Uint8Array;
|
|
405
460
|
} | null>;
|
|
@@ -793,4 +848,4 @@ declare class AntzChatClient {
|
|
|
793
848
|
uploadIcon(conversationId: string, file: UploadableFile): Promise<Conversation>;
|
|
794
849
|
}
|
|
795
850
|
|
|
796
|
-
export { AntzChatAuthError, AntzChatClient, AntzChatConfig, AntzChatError, AntzChatNetworkError, AntzChatPermissionError, AntzChatServerError, AntzChatValidationError, AppConfig, AuthResponse, AuthTokens, BatchUploadResult, CompletedPart, Conversation, ConversationListParams, ConversationSyncResponse, ConversationUnreadCount, type CreateDirectData, type CreateGroupData, CrossConversationSyncResponse, CursorPaginatedResponse, FileResponse, FileType, ForwardAckPayload, ForwardMessagePayload, type ForwardResult, HIGHLY_FORWARDED_DEPTH_THRESHOLD, type LastReadEntry, type ListMessagesParams, LoginCredentials, MAX_FORWARD_TARGETS, type MentionPart, type MentionSegment, Message, MessageReactionsResponse, MessageReceiptsResponse, type MobileDeviceToken, PaginatedResponse, type ParsedMention, Participant, PersistStorage, PresignedUrlRequest, PresignedUrlResponse, RegisterData, type RegisterDeviceTokenPayload, ResolvedCompressionConfig, ResolvedConfig, type SearchParams, type SendData, SendMessagePayload, type SocketStatus, type StatusListener, type TokenStore, UnreadSummary, type UpdateConversationData, type UpdateProfilePayload, UploadableFile, User, UserPreferences, type WebPushDeviceToken, appConfigApi, authApi, buildMentionText, connectSocket, conversationsApi, createAuthStore, createRestTransitSession, decryptPayload, devicesApi, disconnectSocket, encryptPayload, extractMentionIds, fetchServerKeys, generateEphemeralKey, getApiClient, getAuthStore, getCompressionStrategy, getSessionId, getSessionKey, getSocket, getSocketStatus, initApiClient, initAuthStore, isMentionAll, isTransitEnvelope, messagesApi, normalizeAxiosError, normalizeConversation, onSocketStatus, parseMentions, performHandshake, reconnectSocket, refreshSocketAuth, renderMentionParts, resetAuthStore, resolveSystemMessageText, setApiClientInstance, setTransitSession, socketEmit, syncApi, tryGetSocket, useChatStore, usersApi };
|
|
851
|
+
export { AntzChatAuthError, AntzChatClient, AntzChatConfig, AntzChatError, AntzChatNetworkError, AntzChatPermissionError, AntzChatServerError, AntzChatValidationError, AppConfig, AuthResponse, AuthTokens, BatchUploadResult, CompletedPart, Conversation, ConversationListParams, ConversationSyncResponse, ConversationUnreadCount, type CreateDirectData, type CreateGroupData, CrossConversationSyncResponse, CursorPaginatedResponse, FileResponse, FileType, ForwardAckPayload, ForwardMessagePayload, type ForwardResult, HIGHLY_FORWARDED_DEPTH_THRESHOLD, type LastReadEntry, type ListMessagesParams, LoginCredentials, MAX_FORWARD_TARGETS, type MentionPart, type MentionSegment, Message, MessageReactionsResponse, MessageReceiptsResponse, type MobileDeviceToken, PaginatedResponse, type ParsedMention, Participant, PersistStorage, PresignedUrlRequest, PresignedUrlResponse, RegisterData, type RegisterDeviceTokenPayload, ResolvedCompressionConfig, ResolvedConfig, type SearchParams, type SendData, SendMessagePayload, type SocketStatus, type StatusListener, type TokenStore, type TransitIdentity, TransitRateLimitedError, UnreadSummary, type UpdateConversationData, type UpdateProfilePayload, UploadableFile, User, UserPreferences, type WebPushDeviceToken, appConfigApi, authApi, buildMentionText, connectSocket, conversationsApi, createAuthStore, createRestTransitSession, decryptPayload, devicesApi, disconnectSocket, encryptPayload, extractMentionIds, fetchServerKeys, generateEphemeralKey, getApiClient, getAuthStore, getCompressionStrategy, getSessionId, getSessionKey, getSocket, getSocketStatus, initApiClient, initAuthStore, isApiClientConfigured, isMentionAll, isTransitEnvelope, messagesApi, normalizeAxiosError, normalizeConversation, onSocketStatus, parseMentions, performHandshake, readRetryAfterMs, reconnectSocket, refreshSocketAuth, renderMentionParts, resetAuthStore, resetTrackedRooms, resolveSystemMessageText, setApiClientInstance, setAuthReadyPromise, setTransitSession, socketEmit, syncApi, tryGetSocket, useChatStore, usersApi };
|
package/dist/index.js
CHANGED
|
@@ -5,12 +5,15 @@ import {
|
|
|
5
5
|
AntzChatPermissionError,
|
|
6
6
|
AntzChatServerError,
|
|
7
7
|
AntzChatValidationError,
|
|
8
|
+
TransitRateLimitedError,
|
|
9
|
+
awaitTransitReadyOr,
|
|
8
10
|
clearTransitSession,
|
|
9
|
-
|
|
11
|
+
configureTransitIfUnset,
|
|
10
12
|
createRestTransitSession,
|
|
11
13
|
decryptPayload,
|
|
12
14
|
detectTransitAlgo,
|
|
13
15
|
encryptPayload,
|
|
16
|
+
ensureRestTransitHandshake,
|
|
14
17
|
fetchServerKeys,
|
|
15
18
|
generateEphemeralKey,
|
|
16
19
|
generateUUID,
|
|
@@ -21,16 +24,21 @@ import {
|
|
|
21
24
|
getTransitHandshakePromise,
|
|
22
25
|
getTransitSession,
|
|
23
26
|
initApiClient,
|
|
27
|
+
isApiClientConfigured,
|
|
24
28
|
isTransitEnabled,
|
|
25
29
|
isTransitEnvelope,
|
|
30
|
+
isTransitRequired,
|
|
26
31
|
normalizeAxiosError,
|
|
32
|
+
onTransitReady,
|
|
27
33
|
performHandshake,
|
|
34
|
+
readRetryAfterMs,
|
|
28
35
|
resetAlgoCache,
|
|
29
36
|
setApiClientInstance,
|
|
37
|
+
setAuthReadyPromise,
|
|
30
38
|
setTransitSession,
|
|
31
39
|
storageApi,
|
|
32
40
|
uploadBatch
|
|
33
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-U637W5MD.js";
|
|
34
42
|
import {
|
|
35
43
|
useChatStore
|
|
36
44
|
} from "./chunk-UIYJAOGL.js";
|
|
@@ -89,6 +97,7 @@ function resolveConfig(config) {
|
|
|
89
97
|
compressDocuments: config.compression?.compressDocuments ?? true
|
|
90
98
|
},
|
|
91
99
|
persistStorage: config.persistStorage,
|
|
100
|
+
onSendError: config.onSendError,
|
|
92
101
|
messagePageSize: config.messagePageSize ?? 40,
|
|
93
102
|
starredMessagePageSize: config.starredMessagePageSize ?? 30,
|
|
94
103
|
searchPageSize: config.searchPageSize ?? 50
|
|
@@ -311,7 +320,6 @@ var _getToken = null;
|
|
|
311
320
|
var _userId;
|
|
312
321
|
var _tenantId;
|
|
313
322
|
var _config = null;
|
|
314
|
-
var _preservingSession = false;
|
|
315
323
|
function setStatus(s) {
|
|
316
324
|
_status = s;
|
|
317
325
|
_statusListeners.forEach((l) => l(s));
|
|
@@ -330,29 +338,48 @@ function onSocketStatus(listener) {
|
|
|
330
338
|
_statusListeners.add(listener);
|
|
331
339
|
return () => _statusListeners.delete(listener);
|
|
332
340
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
ack(decrypted);
|
|
344
|
-
} catch {
|
|
345
|
-
ack(encryptedResponse);
|
|
346
|
-
}
|
|
347
|
-
} else {
|
|
348
|
-
ack(encryptedResponse);
|
|
349
|
-
}
|
|
350
|
-
});
|
|
341
|
+
var SECURE_EMIT_WAIT_MS = 4e3;
|
|
342
|
+
function emitEncrypted(socket, event, envelope, key, ack) {
|
|
343
|
+
if (ack) {
|
|
344
|
+
socket.emit(event, envelope, async (encryptedResponse) => {
|
|
345
|
+
if (isTransitEnvelope(encryptedResponse)) {
|
|
346
|
+
try {
|
|
347
|
+
ack(await decryptPayload(encryptedResponse, key));
|
|
348
|
+
} catch {
|
|
349
|
+
ack(encryptedResponse);
|
|
350
|
+
}
|
|
351
351
|
} else {
|
|
352
|
-
|
|
352
|
+
ack(encryptedResponse);
|
|
353
353
|
}
|
|
354
|
+
});
|
|
355
|
+
} else {
|
|
356
|
+
socket.emit(event, envelope);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
async function secureEmit(socket, event, payload, ack) {
|
|
360
|
+
let key = getSessionKey();
|
|
361
|
+
if (key && isTransitEnabled()) {
|
|
362
|
+
emitEncrypted(socket, event, await encryptPayload(payload, key), key, ack);
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
if (isTransitRequired()) {
|
|
366
|
+
try {
|
|
367
|
+
ensureRestTransitHandshake();
|
|
368
|
+
} catch {
|
|
369
|
+
}
|
|
370
|
+
const ok = await awaitTransitReadyOr(SECURE_EMIT_WAIT_MS);
|
|
371
|
+
key = getSessionKey();
|
|
372
|
+
if (ok && key && isTransitEnabled()) {
|
|
373
|
+
emitEncrypted(socket, event, await encryptPayload(payload, key), key, ack);
|
|
354
374
|
return;
|
|
355
375
|
}
|
|
376
|
+
if (isTransitRequired()) {
|
|
377
|
+
throw new AntzChatNetworkError(
|
|
378
|
+
`Transit encryption required but no session established \u2014 "${event}" not sent`,
|
|
379
|
+
"TRANSIT_NOT_READY",
|
|
380
|
+
{ event }
|
|
381
|
+
);
|
|
382
|
+
}
|
|
356
383
|
}
|
|
357
384
|
if (ack) {
|
|
358
385
|
socket.emit(event, payload, ack);
|
|
@@ -378,6 +405,28 @@ function secureOn(socket, event, handler) {
|
|
|
378
405
|
handler(raw);
|
|
379
406
|
});
|
|
380
407
|
}
|
|
408
|
+
var _joinedRooms = /* @__PURE__ */ new Set();
|
|
409
|
+
function trackRoomJoin(conversationId) {
|
|
410
|
+
_joinedRooms.add(conversationId);
|
|
411
|
+
}
|
|
412
|
+
function trackRoomLeave(conversationId) {
|
|
413
|
+
_joinedRooms.delete(conversationId);
|
|
414
|
+
}
|
|
415
|
+
function resetTrackedRooms() {
|
|
416
|
+
_joinedRooms.clear();
|
|
417
|
+
}
|
|
418
|
+
function flushJoinedRooms() {
|
|
419
|
+
const socket = tryGetSocket();
|
|
420
|
+
if (!socket || _joinedRooms.size === 0) return;
|
|
421
|
+
for (const conversationId of _joinedRooms) {
|
|
422
|
+
void secureEmit(socket, "join_room", { conversationId }).catch(() => {
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
onTransitReady(flushJoinedRooms);
|
|
427
|
+
onSocketStatus((status) => {
|
|
428
|
+
if (status === "connected") flushJoinedRooms();
|
|
429
|
+
});
|
|
381
430
|
async function connectSocket(config, getToken) {
|
|
382
431
|
if (_socket && !_socket.disconnected) return _socket;
|
|
383
432
|
if (_connectingPromise) return _connectingPromise;
|
|
@@ -399,6 +448,7 @@ async function _doConnect(config, getToken) {
|
|
|
399
448
|
...config.avatar?.url && { avatarUrl: config.avatar.url },
|
|
400
449
|
...config.avatar?.base64 && { avatarBase64: config.avatar.base64 }
|
|
401
450
|
};
|
|
451
|
+
configureTransitIfUnset(config.transitEncryption);
|
|
402
452
|
let boundDeriveSessionKey = null;
|
|
403
453
|
if (config.transitEncryption) {
|
|
404
454
|
const inFlight = getTransitHandshakePromise();
|
|
@@ -408,7 +458,7 @@ async function _doConnect(config, getToken) {
|
|
|
408
458
|
if (existingSession?.enabled && existingSession.sessionId) {
|
|
409
459
|
httpsSession = { sessionId: existingSession.sessionId, sessionKey: existingSession.sessionKey };
|
|
410
460
|
} else {
|
|
411
|
-
httpsSession = await createRestTransitSession(config.apiUrl);
|
|
461
|
+
httpsSession = await createRestTransitSession(config.apiUrl, { userId: config.userId, tenantId: config.tenantId });
|
|
412
462
|
if (httpsSession) {
|
|
413
463
|
const algo = globalThis.crypto?.subtle ? await detectTransitAlgo() : "x25519";
|
|
414
464
|
setTransitSession({ sessionKey: httpsSession.sessionKey, algo, sessionId: httpsSession.sessionId, enabled: true });
|
|
@@ -419,7 +469,7 @@ async function _doConnect(config, getToken) {
|
|
|
419
469
|
boundDeriveSessionKey = null;
|
|
420
470
|
} else {
|
|
421
471
|
try {
|
|
422
|
-
const serverKeys = await fetchServerKeys(config.apiUrl);
|
|
472
|
+
const serverKeys = await fetchServerKeys(config.apiUrl, { userId: config.userId, tenantId: config.tenantId });
|
|
423
473
|
if (!serverKeys.enabled) {
|
|
424
474
|
throw new AntzChatError(
|
|
425
475
|
"TRANSIT_MISMATCH",
|
|
@@ -436,7 +486,7 @@ async function _doConnect(config, getToken) {
|
|
|
436
486
|
}
|
|
437
487
|
} else {
|
|
438
488
|
try {
|
|
439
|
-
const serverKeys = await fetchServerKeys(config.apiUrl);
|
|
489
|
+
const serverKeys = await fetchServerKeys(config.apiUrl, { userId: config.userId, tenantId: config.tenantId });
|
|
440
490
|
if (serverKeys.enabled) {
|
|
441
491
|
throw new AntzChatError(
|
|
442
492
|
"TRANSIT_MISMATCH",
|
|
@@ -463,7 +513,6 @@ async function _doConnect(config, getToken) {
|
|
|
463
513
|
_socket.on("connect", () => setStatus("connected"));
|
|
464
514
|
_socket.on("disconnect", () => {
|
|
465
515
|
setStatus("disconnected");
|
|
466
|
-
if (!_preservingSession) clearTransitSession();
|
|
467
516
|
});
|
|
468
517
|
_socket.on("connect_error", (err) => {
|
|
469
518
|
console.error("[AntzChat] Socket connect_error:", err?.message, err?.data);
|
|
@@ -478,8 +527,9 @@ async function _doConnect(config, getToken) {
|
|
|
478
527
|
resolve();
|
|
479
528
|
};
|
|
480
529
|
const timeout = setTimeout(() => {
|
|
481
|
-
console.
|
|
482
|
-
|
|
530
|
+
console.error(
|
|
531
|
+
"[AntzChat] transit_session did not arrive within 5s. Transit stays REQUIRED \u2014 chat requests are gated (not downgraded to plaintext) until the handshake succeeds via REST retry or socket reconnect. Persistent failure = check POST /crypto/session (rate limit / 5xx) and the socket transit middleware."
|
|
532
|
+
);
|
|
483
533
|
done();
|
|
484
534
|
}, 5e3);
|
|
485
535
|
_socket.on("transit_session", async ({ sessionId }) => {
|
|
@@ -566,13 +616,8 @@ function reconnectSocket(token, userId, tenantId) {
|
|
|
566
616
|
...tenantId && { tenantId },
|
|
567
617
|
transitSessionId: existing.sessionId
|
|
568
618
|
};
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
if (_socket.connected) _socket.disconnect();
|
|
572
|
-
_socket.connect();
|
|
573
|
-
} finally {
|
|
574
|
-
_preservingSession = false;
|
|
575
|
-
}
|
|
619
|
+
if (_socket.connected) _socket.disconnect();
|
|
620
|
+
_socket.connect();
|
|
576
621
|
return;
|
|
577
622
|
}
|
|
578
623
|
if (_getToken) {
|
|
@@ -617,7 +662,15 @@ async function drainSendQueue(conversationId) {
|
|
|
617
662
|
entry.reject(new AntzChatNetworkError("Message dropped: queued too long", "MESSAGE_DROPPED"));
|
|
618
663
|
continue;
|
|
619
664
|
}
|
|
620
|
-
|
|
665
|
+
let acked;
|
|
666
|
+
try {
|
|
667
|
+
({ acked } = await emitWithAck("send_message", entry.payload));
|
|
668
|
+
} catch (err) {
|
|
669
|
+
entry.reject(err);
|
|
670
|
+
while (queue.length > 0) queue.shift().reject(err);
|
|
671
|
+
break;
|
|
672
|
+
}
|
|
673
|
+
acked.then(entry.resolve).catch(entry.reject);
|
|
621
674
|
}
|
|
622
675
|
sendQueues.delete(conversationId);
|
|
623
676
|
sendQueueRunning.delete(conversationId);
|
|
@@ -630,7 +683,7 @@ function queueSendMessage(payload) {
|
|
|
630
683
|
return Promise.reject(new AntzChatNetworkError("Send queue full: too many messages in flight", "SEND_QUEUE_FULL", { conversationId }));
|
|
631
684
|
}
|
|
632
685
|
return new Promise((resolve, reject) => {
|
|
633
|
-
queue.push({
|
|
686
|
+
queue.push({ payload, resolve, reject, enqueuedAt: Date.now() });
|
|
634
687
|
drainSendQueue(conversationId);
|
|
635
688
|
});
|
|
636
689
|
}
|
|
@@ -653,33 +706,44 @@ function waitForReconnect() {
|
|
|
653
706
|
});
|
|
654
707
|
});
|
|
655
708
|
}
|
|
656
|
-
async function
|
|
709
|
+
async function emitWithAck(event, payload) {
|
|
657
710
|
let socket = tryGetSocket();
|
|
658
711
|
if (!socket) {
|
|
659
712
|
await waitForReconnect();
|
|
660
713
|
socket = tryGetSocket();
|
|
661
714
|
}
|
|
662
|
-
if (!socket)
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
}).then(() => {
|
|
669
|
-
timer = setTimeout(() => reject(new AntzChatNetworkError(`Socket ack timeout: ${event}`, "SOCKET_TIMEOUT", { event })), ACK_TIMEOUT);
|
|
670
|
-
}).catch(reject);
|
|
715
|
+
if (!socket) throw new AntzChatNetworkError(`Socket not connected (event: ${event})`, "SOCKET_NOT_CONNECTED", { event });
|
|
716
|
+
let resolveAck;
|
|
717
|
+
let rejectAck;
|
|
718
|
+
const acked = new Promise((res, rej) => {
|
|
719
|
+
resolveAck = res;
|
|
720
|
+
rejectAck = rej;
|
|
671
721
|
});
|
|
722
|
+
await secureEmit(socket, event, payload, (response) => resolveAck(response));
|
|
723
|
+
const timer = setTimeout(
|
|
724
|
+
() => rejectAck(new AntzChatNetworkError(`Socket ack timeout: ${event}`, "SOCKET_TIMEOUT", { event })),
|
|
725
|
+
ACK_TIMEOUT
|
|
726
|
+
);
|
|
727
|
+
void acked.then(() => clearTimeout(timer), () => clearTimeout(timer));
|
|
728
|
+
return { acked };
|
|
729
|
+
}
|
|
730
|
+
async function withAck(event, payload) {
|
|
731
|
+
const { acked } = await emitWithAck(event, payload);
|
|
732
|
+
return acked;
|
|
672
733
|
}
|
|
673
734
|
function fireAndForget(event, payload) {
|
|
674
735
|
const socket = tryGetSocket();
|
|
675
736
|
if (!socket) return;
|
|
676
|
-
secureEmit(socket, event, payload)
|
|
737
|
+
secureEmit(socket, event, payload).catch(() => {
|
|
738
|
+
});
|
|
677
739
|
}
|
|
678
740
|
var socketEmit = {
|
|
679
741
|
joinRoom(conversationId) {
|
|
742
|
+
trackRoomJoin(conversationId);
|
|
680
743
|
fireAndForget("join_room", { conversationId });
|
|
681
744
|
},
|
|
682
745
|
leaveRoom(conversationId) {
|
|
746
|
+
trackRoomLeave(conversationId);
|
|
683
747
|
fireAndForget("leave_room", { conversationId });
|
|
684
748
|
},
|
|
685
749
|
sendMessage(payload) {
|
|
@@ -1269,6 +1333,7 @@ export {
|
|
|
1269
1333
|
HIGHLY_FORWARDED_DEPTH_THRESHOLD,
|
|
1270
1334
|
MAX_FORWARD_TARGETS,
|
|
1271
1335
|
MENTION_ALL_ID,
|
|
1336
|
+
TransitRateLimitedError,
|
|
1272
1337
|
appConfigApi,
|
|
1273
1338
|
authApi,
|
|
1274
1339
|
buildMentionText,
|
|
@@ -1292,6 +1357,7 @@ export {
|
|
|
1292
1357
|
getSocketStatus,
|
|
1293
1358
|
initApiClient,
|
|
1294
1359
|
initAuthStore,
|
|
1360
|
+
isApiClientConfigured,
|
|
1295
1361
|
isMentionAll,
|
|
1296
1362
|
isTransitEnvelope,
|
|
1297
1363
|
messagesApi,
|
|
@@ -1300,13 +1366,16 @@ export {
|
|
|
1300
1366
|
onSocketStatus,
|
|
1301
1367
|
parseMentions,
|
|
1302
1368
|
performHandshake,
|
|
1369
|
+
readRetryAfterMs,
|
|
1303
1370
|
reconnectSocket,
|
|
1304
1371
|
refreshSocketAuth,
|
|
1305
1372
|
renderMentionParts,
|
|
1306
1373
|
resetAuthStore,
|
|
1374
|
+
resetTrackedRooms,
|
|
1307
1375
|
resolveConfig,
|
|
1308
1376
|
resolveSystemMessageText,
|
|
1309
1377
|
setApiClientInstance,
|
|
1378
|
+
setAuthReadyPromise,
|
|
1310
1379
|
setTransitSession,
|
|
1311
1380
|
socketEmit,
|
|
1312
1381
|
storageApi,
|