stream-chat 9.50.3 → 9.52.0
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/cjs/index.browser.js +601 -275
- package/dist/cjs/index.browser.js.map +4 -4
- package/dist/cjs/index.node.js +610 -276
- package/dist/cjs/index.node.js.map +4 -4
- package/dist/esm/index.mjs +599 -273
- package/dist/esm/index.mjs.map +4 -4
- package/dist/types/channel.d.ts +4 -3
- package/dist/types/client.d.ts +23 -9
- package/dist/types/index.d.ts +7 -0
- package/dist/types/messageComposer/attachmentIdentity.d.ts +4 -0
- package/dist/types/messageComposer/attachmentManager.d.ts +8 -0
- package/dist/types/messageComposer/messageComposer.d.ts +13 -0
- package/dist/types/messageComposer/middleware/attachmentManager/postUpload/attachmentEnrichment.d.ts +20 -1
- package/dist/types/messageComposer/middleware/messageComposer/MessageComposerMiddlewareExecutor.d.ts +6 -1
- package/dist/types/messageComposer/middleware/messageComposer/attachments.d.ts +24 -0
- package/dist/types/messageComposer/middleware/messageComposer/types.d.ts +22 -1
- package/dist/types/messageComposer/middleware/textComposer/mentions.d.ts +15 -9
- package/dist/types/messageComposer/types.d.ts +5 -0
- package/dist/types/middleware.d.ts +8 -0
- package/dist/types/offline-support/offline_support_api.d.ts +7 -1
- package/dist/types/offline-support/offline_sync_manager.d.ts +12 -1
- package/dist/types/search/BaseSearchSource.d.ts +42 -24
- package/dist/types/search/ChannelMemberSearchSource.d.ts +9 -4
- package/dist/types/search/ChannelSearchSource.d.ts +9 -3
- package/dist/types/search/MessageSearchSource.d.ts +14 -3
- package/dist/types/search/UserSearchSource.d.ts +9 -3
- package/dist/types/search/types.d.ts +14 -1
- package/dist/types/types.d.ts +12 -1
- package/dist/types/uploadManager.d.ts +30 -0
- package/dist/types/utils.d.ts +21 -2
- package/package.json +7 -7
- package/src/channel.ts +20 -8
- package/src/client.ts +71 -13
- package/src/index.ts +12 -0
- package/src/messageComposer/attachmentIdentity.ts +27 -0
- package/src/messageComposer/attachmentManager.ts +61 -12
- package/src/messageComposer/messageComposer.ts +43 -8
- package/src/messageComposer/middleware/attachmentManager/postUpload/AttachmentPostUploadMiddlewareExecutor.ts +1 -1
- package/src/messageComposer/middleware/attachmentManager/postUpload/attachmentEnrichment.ts +58 -33
- package/src/messageComposer/middleware/attachmentManager/postUpload/uploadErrorHandler.ts +4 -0
- package/src/messageComposer/middleware/messageComposer/MessageComposerMiddlewareExecutor.ts +9 -0
- package/src/messageComposer/middleware/messageComposer/attachments.ts +109 -0
- package/src/messageComposer/middleware/messageComposer/types.ts +22 -1
- package/src/messageComposer/middleware/textComposer/mentions.ts +75 -23
- package/src/messageComposer/types.ts +5 -0
- package/src/middleware.ts +11 -0
- package/src/offline-support/offline_support_api.ts +21 -2
- package/src/offline-support/offline_sync_manager.ts +36 -8
- package/src/search/BaseSearchSource.ts +123 -39
- package/src/search/ChannelMemberSearchSource.ts +22 -11
- package/src/search/ChannelSearchSource.ts +16 -5
- package/src/search/MessageSearchSource.ts +36 -4
- package/src/search/UserSearchSource.ts +16 -5
- package/src/search/types.ts +14 -1
- package/src/types.ts +13 -1
- package/src/uploadManager.ts +45 -0
- package/src/utils.ts +78 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseSearchSource } from './BaseSearchSource';
|
|
1
|
+
import { BaseSearchSource, type SearchQueryOptions } from './BaseSearchSource';
|
|
2
2
|
import type { ChannelFilters, ChannelOptions, ChannelSort, MessageFilters, MessageResponse, SearchMessageSort } from '../types';
|
|
3
3
|
import type { StreamChat } from '../client';
|
|
4
4
|
import type { SearchSourceOptions } from './types';
|
|
@@ -26,6 +26,17 @@ export type MessageSearchSourceFilterBuilderOptions<TContexts extends MessageSea
|
|
|
26
26
|
messageSearch: FilterBuilderOptions<MessageFilters, MergeContext<BuiltInContexts['messageSearch'], TContexts['messageSearchContext']>>;
|
|
27
27
|
channelQuery: FilterBuilderOptions<ChannelFilters, MergeContext<BuiltInContexts['channelQuery'], TContexts['channelQueryContext']>>;
|
|
28
28
|
}>;
|
|
29
|
+
export type MessageSearchSourceOptions = SearchSourceOptions & {
|
|
30
|
+
/** Static base filters for the channel scope of the message search. */
|
|
31
|
+
messageSearchChannelFilters?: ChannelFilters;
|
|
32
|
+
/** Static base filters for the message search itself. */
|
|
33
|
+
messageSearchFilters?: MessageFilters;
|
|
34
|
+
messageSearchSort?: SearchMessageSort;
|
|
35
|
+
/** Static base filters for the follow-up query that hydrates unknown channels. */
|
|
36
|
+
channelQueryFilters?: ChannelFilters;
|
|
37
|
+
channelQuerySort?: ChannelSort;
|
|
38
|
+
channelQueryOptions?: Omit<ChannelOptions, 'limit' | 'offset'>;
|
|
39
|
+
};
|
|
29
40
|
export declare class MessageSearchSource<TContexts extends MessageSearchSourceContexts = {}> extends BaseSearchSource<MessageResponse> {
|
|
30
41
|
readonly type = "messages";
|
|
31
42
|
private client;
|
|
@@ -38,8 +49,8 @@ export declare class MessageSearchSource<TContexts extends MessageSearchSourceCo
|
|
|
38
49
|
messageSearchChannelFilterBuilder: FilterBuilder<ChannelFilters, MergeContext<BuiltInContexts['messageSearchChannel'], TContexts['messageSearchChannelContext']>>;
|
|
39
50
|
messageSearchFilterBuilder: FilterBuilder<MessageFilters, MergeContext<BuiltInContexts['messageSearch'], TContexts['messageSearchContext']>>;
|
|
40
51
|
channelQueryFilterBuilder: FilterBuilder<ChannelFilters, MergeContext<BuiltInContexts['channelQuery'], TContexts['channelQueryContext']>>;
|
|
41
|
-
constructor(client: StreamChat, options?:
|
|
42
|
-
protected query(searchQuery: string): Promise<{
|
|
52
|
+
constructor(client: StreamChat, options?: MessageSearchSourceOptions, filterBuilderOptions?: MessageSearchSourceFilterBuilderOptions<TContexts>);
|
|
53
|
+
protected query(searchQuery: string, queryOptions?: SearchQueryOptions): Promise<{
|
|
43
54
|
items: never[];
|
|
44
55
|
next?: undefined;
|
|
45
56
|
} | {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseSearchSource } from './BaseSearchSource';
|
|
1
|
+
import { BaseSearchSource, type SearchQueryOptions } from './BaseSearchSource';
|
|
2
2
|
import { FilterBuilder, type FilterBuilderOptions } from '../pagination';
|
|
3
3
|
import type { StreamChat } from '../client';
|
|
4
4
|
import type { UserFilters, UserOptions, UserResponse, UserSort } from '../types';
|
|
@@ -7,6 +7,12 @@ type CustomContext = Record<string, unknown>;
|
|
|
7
7
|
export type UserSearchSourceFilterBuilderContext<C extends CustomContext = CustomContext> = {
|
|
8
8
|
searchQuery?: string;
|
|
9
9
|
} & C;
|
|
10
|
+
export type UserSearchSourceOptions = SearchSourceOptions & {
|
|
11
|
+
/** Static base filters merged under the dynamically generated ones. */
|
|
12
|
+
filters?: UserFilters;
|
|
13
|
+
sort?: UserSort;
|
|
14
|
+
searchOptions?: Omit<UserOptions, 'limit' | 'offset'>;
|
|
15
|
+
};
|
|
10
16
|
export declare class UserSearchSource<TFilterContext extends CustomContext = CustomContext> extends BaseSearchSource<UserResponse> {
|
|
11
17
|
readonly type = "users";
|
|
12
18
|
client: StreamChat;
|
|
@@ -14,8 +20,8 @@ export declare class UserSearchSource<TFilterContext extends CustomContext = Cus
|
|
|
14
20
|
sort: UserSort | undefined;
|
|
15
21
|
searchOptions: Omit<UserOptions, 'limit' | 'offset'> | undefined;
|
|
16
22
|
filterBuilder: FilterBuilder<UserFilters, UserSearchSourceFilterBuilderContext<TFilterContext>>;
|
|
17
|
-
constructor(client: StreamChat, options?:
|
|
18
|
-
protected query(searchQuery: string): Promise<{
|
|
23
|
+
constructor(client: StreamChat, options?: UserSearchSourceOptions, filterBuilderOptions?: FilterBuilderOptions<UserFilters, UserSearchSourceFilterBuilderContext<TFilterContext>>);
|
|
24
|
+
protected query(searchQuery: string, queryOptions?: SearchQueryOptions): Promise<{
|
|
19
25
|
items: UserResponse[];
|
|
20
26
|
}>;
|
|
21
27
|
protected filterQueryResults(items: UserResponse[]): UserResponse[];
|
|
@@ -9,8 +9,21 @@ export type SearchSourceState<T = any> = {
|
|
|
9
9
|
offset?: number;
|
|
10
10
|
};
|
|
11
11
|
export type SearchSourceOptions = {
|
|
12
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* Legacy single debounce interval. When set, it applies to both short and long queries,
|
|
14
|
+
* unless overridden by `shortQueryDebounceMs` / `longQueryDebounceMs`.
|
|
15
|
+
*/
|
|
13
16
|
debounceMs?: number;
|
|
17
|
+
/**
|
|
18
|
+
* Debounce interval for queries no longer than `shortQueryMaxLength`. Such queries have
|
|
19
|
+
* low selectivity and are expensive server-side, so they are debounced harder.
|
|
20
|
+
* Defaults to 500ms.
|
|
21
|
+
*/
|
|
22
|
+
shortQueryDebounceMs?: number;
|
|
23
|
+
/** Debounce interval for queries longer than `shortQueryMaxLength`. Defaults to 300ms. */
|
|
24
|
+
longQueryDebounceMs?: number;
|
|
25
|
+
/** Query length (inclusive) that still counts as short. Defaults to 2. */
|
|
26
|
+
shortQueryMaxLength?: number;
|
|
14
27
|
pageSize?: number;
|
|
15
28
|
/** When true, the source can execute queries with an empty search string. Defaults to false. */
|
|
16
29
|
allowEmptySearchString?: boolean;
|
package/dist/types/types.d.ts
CHANGED
|
@@ -1008,7 +1008,13 @@ export type ChannelQueryOptions = {
|
|
|
1008
1008
|
watch?: boolean;
|
|
1009
1009
|
watchers?: PaginationOptions;
|
|
1010
1010
|
};
|
|
1011
|
-
|
|
1011
|
+
/**
|
|
1012
|
+
* Composed with RequestOptions because `queryChannels` carries the abort signal in this
|
|
1013
|
+
* bag; neither the state flags nor the signal are serialized into the request. Declaring
|
|
1014
|
+
* the composition keeps passing a RequestOptions here intentional rather than incidental
|
|
1015
|
+
* structural compatibility.
|
|
1016
|
+
*/
|
|
1017
|
+
export type ChannelStateOptions = RequestOptions & {
|
|
1012
1018
|
offlineMode?: boolean;
|
|
1013
1019
|
skipInitialization?: string[];
|
|
1014
1020
|
skipHydration?: boolean;
|
|
@@ -1353,6 +1359,11 @@ export type SearchOptions = {
|
|
|
1353
1359
|
offset?: number;
|
|
1354
1360
|
sort?: SearchMessageSort;
|
|
1355
1361
|
};
|
|
1362
|
+
/** Per-request options that are never part of the serialized request payload. */
|
|
1363
|
+
export type RequestOptions = {
|
|
1364
|
+
/** Aborts the request. See AbortController. */
|
|
1365
|
+
signal?: AbortSignal;
|
|
1366
|
+
};
|
|
1356
1367
|
export type StreamChatOptions = AxiosRequestConfig & {
|
|
1357
1368
|
/**
|
|
1358
1369
|
* Used to disable warnings that are triggered by using connectUser or connectAnonymousUser server-side.
|
|
@@ -1,8 +1,38 @@
|
|
|
1
1
|
import type { StreamChat } from './client';
|
|
2
2
|
import { StateStore } from './store';
|
|
3
3
|
import type { AttachmentManager } from '.';
|
|
4
|
+
/**
|
|
5
|
+
* Whether an upload ended because it was aborted rather than because it failed.
|
|
6
|
+
*
|
|
7
|
+
* {@link UploadManager.deleteUploadRecord} and {@link UploadManager.cancelAllUploads} abort the
|
|
8
|
+
* request through its `AbortController`, which is what happens when an attachment is removed
|
|
9
|
+
* from the composer mid-upload or the client disconnects. Axios reports that as a
|
|
10
|
+
* `CanceledError` (`axios.isCancel`); a custom `doUploadRequest` on another transport
|
|
11
|
+
* conventionally throws a `DOMException` named `AbortError`.
|
|
12
|
+
*
|
|
13
|
+
* Callers use this to keep a deliberate cancellation from being reported as an error.
|
|
14
|
+
*/
|
|
15
|
+
export declare const isUploadCancellation: (error: unknown) => boolean;
|
|
4
16
|
export type UploadRecord = {
|
|
5
17
|
id: string;
|
|
18
|
+
/**
|
|
19
|
+
* `true` once every byte has been handed to the transport but the server has not responded
|
|
20
|
+
* yet.
|
|
21
|
+
*
|
|
22
|
+
* Upload progress measures bytes *written to the connection*, not bytes the server
|
|
23
|
+
* acknowledged, so it reaches 100% the moment the request body is flushed — then the
|
|
24
|
+
* connection sits idle while the CDN ingests the file and the response travels back. On a
|
|
25
|
+
* large file over a slow link that window is long, and a UI that renders 100% as "done"
|
|
26
|
+
* claims the upload is confirmed while it is not.
|
|
27
|
+
*
|
|
28
|
+
* Nothing measurable happens during that window, so it is the point at which a determinate
|
|
29
|
+
* progress bar should hand over to an indeterminate one. Confirmation is the record being
|
|
30
|
+
* removed, not progress reaching 100.
|
|
31
|
+
*
|
|
32
|
+
* Requires `AttachmentManagerConfig.trackUploadProgress`; without progress reporting there is
|
|
33
|
+
* no way to tell when the flush happened, and this stays `false`.
|
|
34
|
+
*/
|
|
35
|
+
uploadConfirmationPending?: boolean;
|
|
6
36
|
uploadProgress?: number;
|
|
7
37
|
};
|
|
8
38
|
export type UploadManagerState = {
|
package/dist/types/utils.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import FormData from 'form-data';
|
|
2
|
-
import type { AscDesc, ChannelFilters, ChannelQueryOptions, ChannelSort, ChannelSortBase, LocalMessage, LocalMessageBase, Logger, Message, MessagePaginationOptions, MessageResponse, MessageResponseBase, MessageSet, OwnUserResponse, PromoteChannelParams, ReactionGroupResponse, UpdatedMessage, UserResponse } from './types';
|
|
2
|
+
import type { AscDesc, Attachment, ChannelFilters, ChannelQueryOptions, ChannelSort, ChannelSortBase, LocalMessage, LocalMessageBase, Logger, Message, MessagePaginationOptions, MessageResponse, MessageResponseBase, MessageSet, OwnUserResponse, PromoteChannelParams, ReactionGroupResponse, UpdatedMessage, UserResponse } from './types';
|
|
3
3
|
import type { StreamChat } from './client';
|
|
4
4
|
import type { Channel } from './channel';
|
|
5
5
|
import type { AxiosRequestConfig } from 'axios';
|
|
@@ -74,6 +74,25 @@ export declare function formatMessage(message: MessageResponse | MessageResponse
|
|
|
74
74
|
* @param {MessageResponse} message `MessageResponse` object
|
|
75
75
|
*/
|
|
76
76
|
export declare function unformatMessage(message: LocalMessage): MessageResponse;
|
|
77
|
+
/**
|
|
78
|
+
* Strips composer-internal state from a message's attachments, and drops any whose upload never
|
|
79
|
+
* resolved.
|
|
80
|
+
*
|
|
81
|
+
* `localMetadata` is how `AttachmentManager` tracks an upload (its id, the `File`, the local
|
|
82
|
+
* preview); it must never reach the API. An attachment that still carries it and has no remote
|
|
83
|
+
* URL was never settled — which is what happens when
|
|
84
|
+
* `createSendWithPendingUploadsAttachmentsMiddleware` is installed by a UI SDK that does not
|
|
85
|
+
* implement the rest of the flow (awaiting those uploads before sending). Sending it would store
|
|
86
|
+
* an attachment pointing at nothing, so it is dropped and logged instead.
|
|
87
|
+
*
|
|
88
|
+
* Returns the same message object when there was nothing to change.
|
|
89
|
+
*/
|
|
90
|
+
export declare const sanitizeOutgoingAttachments: <T extends {
|
|
91
|
+
attachments?: Attachment[];
|
|
92
|
+
}>({ client, message, }: {
|
|
93
|
+
client: StreamChat;
|
|
94
|
+
message: T;
|
|
95
|
+
}) => T;
|
|
77
96
|
export declare const localMessageToNewMessagePayload: (localMessage: LocalMessage) => Message;
|
|
78
97
|
export declare const toUpdatedMessagePayload: (message: LocalMessage | Partial<MessageResponse>) => UpdatedMessage;
|
|
79
98
|
export declare const toDeletedMessage: ({ message, deletedAt, hardDelete, }: {
|
|
@@ -227,7 +246,7 @@ export interface DebouncedFunc<T extends (...args: any[]) => any> {
|
|
|
227
246
|
*/
|
|
228
247
|
flush(): ReturnType<T> | undefined;
|
|
229
248
|
}
|
|
230
|
-
export declare const debounce: <T extends (...args: any[]) => any>(fn: T, timeout?: number, { leading, trailing }?: {
|
|
249
|
+
export declare const debounce: <T extends (...args: any[]) => any>(fn: T, timeout?: number | ((...args: Parameters<T>) => number), { leading, trailing }?: {
|
|
231
250
|
leading?: boolean;
|
|
232
251
|
trailing?: boolean;
|
|
233
252
|
}) => DebouncedFunc<T>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stream-chat",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.52.0",
|
|
4
4
|
"description": "JS SDK for the Stream Chat API",
|
|
5
5
|
"homepage": "https://getstream.io/chat/",
|
|
6
6
|
"author": {
|
|
@@ -52,9 +52,9 @@
|
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@types/jsonwebtoken": "^9.0.8",
|
|
54
54
|
"@types/ws": "^8.18.1",
|
|
55
|
-
"axios": "^1.
|
|
55
|
+
"axios": "^1.19.0",
|
|
56
56
|
"base64-js": "^1.5.1",
|
|
57
|
-
"form-data": "^4.0.
|
|
57
|
+
"form-data": "^4.0.6",
|
|
58
58
|
"isomorphic-ws": "^5.0.0",
|
|
59
59
|
"jsonwebtoken": "^9.0.3",
|
|
60
60
|
"linkifyjs": "^4.3.3",
|
|
@@ -68,22 +68,22 @@
|
|
|
68
68
|
"@semantic-release/git": "^10.0.1",
|
|
69
69
|
"@types/node": "^22.19.19",
|
|
70
70
|
"@types/sinon": "^10.0.6",
|
|
71
|
-
"@vitest/coverage-v8": "^4.1.
|
|
71
|
+
"@vitest/coverage-v8": "^4.1.10",
|
|
72
72
|
"concurrently": "^9.2.1",
|
|
73
73
|
"conventional-changelog-conventionalcommits": "^9.3.1",
|
|
74
74
|
"dotenv": "^17.4.2",
|
|
75
|
-
"esbuild": "^0.28.
|
|
75
|
+
"esbuild": "^0.28.2",
|
|
76
76
|
"eslint": "^9.39.4",
|
|
77
77
|
"eslint-plugin-import": "^2.32.0",
|
|
78
78
|
"globals": "^17.6.0",
|
|
79
79
|
"husky": "^9.1.7",
|
|
80
80
|
"lint-staged": "^17.0.5",
|
|
81
81
|
"prettier": "^3.8.3",
|
|
82
|
-
"semantic-release": "^25.0.
|
|
82
|
+
"semantic-release": "^25.0.9",
|
|
83
83
|
"sinon": "^12.0.1",
|
|
84
84
|
"typescript": "^6.0.3",
|
|
85
85
|
"typescript-eslint": "^8.59.4",
|
|
86
|
-
"vitest": "^4.1.
|
|
86
|
+
"vitest": "^4.1.10"
|
|
87
87
|
},
|
|
88
88
|
"scripts": {
|
|
89
89
|
"build": "rm -rf dist && concurrently 'tsc' './scripts/bundle.mjs'",
|
package/src/channel.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
logChatPromiseExecution,
|
|
10
10
|
messageSetPagination,
|
|
11
11
|
normalizeQuerySort,
|
|
12
|
+
sanitizeOutgoingAttachments,
|
|
12
13
|
} from './utils';
|
|
13
14
|
import type { StreamChat } from './client';
|
|
14
15
|
import { DEFAULT_QUERY_CHANNEL_MESSAGE_LIST_PAGE_SIZE } from './constants';
|
|
@@ -62,6 +63,7 @@ import type {
|
|
|
62
63
|
QueryMembersOptions,
|
|
63
64
|
Reaction,
|
|
64
65
|
ReactionAPIResponse,
|
|
66
|
+
RequestOptions,
|
|
65
67
|
SearchAPIResponse,
|
|
66
68
|
SearchMessageSortBase,
|
|
67
69
|
SearchOptions,
|
|
@@ -211,14 +213,17 @@ export class Channel {
|
|
|
211
213
|
*
|
|
212
214
|
* @return {Promise<SendMessageAPIResponse>} The Server Response
|
|
213
215
|
*/
|
|
214
|
-
async _sendMessage(
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
)
|
|
216
|
+
async _sendMessage(rawMessage: Message, options?: SendMessageOptions) {
|
|
217
|
+
const client = this.getClient();
|
|
218
|
+
const message = sanitizeOutgoingAttachments({
|
|
219
|
+
client,
|
|
220
|
+
message: rawMessage,
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
return await client.post<SendMessageAPIResponse>(this._channelURL() + '/message', {
|
|
224
|
+
message,
|
|
225
|
+
...options,
|
|
226
|
+
});
|
|
222
227
|
}
|
|
223
228
|
|
|
224
229
|
async sendMessage(message: Message, options?: SendMessageOptions) {
|
|
@@ -377,6 +382,7 @@ export class Channel {
|
|
|
377
382
|
* @param {MemberSort} [sort] Sort options, for instance [{created_at: -1}].
|
|
378
383
|
* When using multiple fields, make sure you use array of objects to guarantee field order, for instance [{name: -1}, {created_at: 1}]
|
|
379
384
|
* @param {{ limit?: number; offset?: number }} [options] Option object, {limit: 10, offset:10}
|
|
385
|
+
* @param {RequestOptions} [requestOptions] Carries an abort signal. Not sent in the request.
|
|
380
386
|
*
|
|
381
387
|
* @return {Promise<ChannelMemberAPIResponse>} Query Members response
|
|
382
388
|
*/
|
|
@@ -384,6 +390,7 @@ export class Channel {
|
|
|
384
390
|
filterConditions: MemberFilters,
|
|
385
391
|
sort: MemberSort = [],
|
|
386
392
|
options: QueryMembersOptions = {},
|
|
393
|
+
requestOptions: RequestOptions = {},
|
|
387
394
|
) {
|
|
388
395
|
let id: string | undefined;
|
|
389
396
|
const type = this.type;
|
|
@@ -406,6 +413,7 @@ export class Channel {
|
|
|
406
413
|
...options,
|
|
407
414
|
},
|
|
408
415
|
},
|
|
416
|
+
requestOptions,
|
|
409
417
|
);
|
|
410
418
|
}
|
|
411
419
|
|
|
@@ -2003,6 +2011,10 @@ export class Channel {
|
|
|
2003
2011
|
// delivery-report network sync below is skipped for it.
|
|
2004
2012
|
case 'message.read_locally':
|
|
2005
2013
|
case 'message.read':
|
|
2014
|
+
// Thread read events are handled indiscriminately within the reactive `thread` object, we can
|
|
2015
|
+
// skip them here in order to ensure the channel does not get read incidentally when it should
|
|
2016
|
+
// not.
|
|
2017
|
+
if (event.thread) break;
|
|
2006
2018
|
if (event.user?.id && event.created_at) {
|
|
2007
2019
|
const previousReadState = channelState.read[event.user.id];
|
|
2008
2020
|
channelState.read[event.user.id] = {
|
package/src/client.ts
CHANGED
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
normalizeQuerySort,
|
|
37
37
|
randomId,
|
|
38
38
|
retryInterval,
|
|
39
|
+
sanitizeOutgoingAttachments,
|
|
39
40
|
sleep,
|
|
40
41
|
toUpdatedMessagePayload,
|
|
41
42
|
} from './utils';
|
|
@@ -219,6 +220,7 @@ import type {
|
|
|
219
220
|
ReminderAPIResponse,
|
|
220
221
|
RemoveUserGroupMembersOptions,
|
|
221
222
|
RemoveUserGroupMembersResponse,
|
|
223
|
+
RequestOptions,
|
|
222
224
|
ReviewFlagReportOptions,
|
|
223
225
|
ReviewFlagReportResponse,
|
|
224
226
|
SdkIdentifier,
|
|
@@ -1312,6 +1314,9 @@ export class StreamChat {
|
|
|
1312
1314
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1313
1315
|
} catch (e: any /**TODO: generalize error types */) {
|
|
1314
1316
|
e.client_request_id = requestConfig.headers?.['x-client-request-id'];
|
|
1317
|
+
// An aborted request is a deliberate cancellation, not a failure - logging it
|
|
1318
|
+
// and counting it towards the token-refresh backoff would be misleading.
|
|
1319
|
+
if (axios.isCancel(e)) throw e;
|
|
1315
1320
|
this._logApiError(type, url, e);
|
|
1316
1321
|
this.consecutiveFailures += 1;
|
|
1317
1322
|
if (e.response) {
|
|
@@ -1333,16 +1338,22 @@ export class StreamChat {
|
|
|
1333
1338
|
}
|
|
1334
1339
|
};
|
|
1335
1340
|
|
|
1336
|
-
get<T>(
|
|
1337
|
-
|
|
1341
|
+
get<T>(
|
|
1342
|
+
url: string,
|
|
1343
|
+
params?: AxiosRequestConfig['params'],
|
|
1344
|
+
// `params` is omitted deliberately: _enrichAxiosOptions spreads `config` after the
|
|
1345
|
+
// enriched params, so a `config.params` would drop api_key/user_id/connection_id.
|
|
1346
|
+
config?: Omit<AxiosRequestConfig, 'params'>,
|
|
1347
|
+
) {
|
|
1348
|
+
return this.doAxiosRequest<T>('get', url, null, { params, config });
|
|
1338
1349
|
}
|
|
1339
1350
|
|
|
1340
1351
|
put<T>(url: string, data?: unknown) {
|
|
1341
1352
|
return this.doAxiosRequest<T>('put', url, data);
|
|
1342
1353
|
}
|
|
1343
1354
|
|
|
1344
|
-
post<T>(url: string, data?: unknown) {
|
|
1345
|
-
return this.doAxiosRequest<T>('post', url, data);
|
|
1355
|
+
post<T>(url: string, data?: unknown, config?: Omit<AxiosRequestConfig, 'params'>) {
|
|
1356
|
+
return this.doAxiosRequest<T>('post', url, data, { config });
|
|
1346
1357
|
}
|
|
1347
1358
|
|
|
1348
1359
|
patch<T>(url: string, data?: unknown) {
|
|
@@ -1837,6 +1848,7 @@ export class StreamChat {
|
|
|
1837
1848
|
* @param {UserSort} sort Sort options, for instance [{last_active: -1}].
|
|
1838
1849
|
* When using multiple fields, make sure you use array of objects to guarantee field order, for instance [{last_active: -1}, {created_at: 1}]
|
|
1839
1850
|
* @param {UserOptions} options Option object, {presence: true}
|
|
1851
|
+
* @param {RequestOptions} [requestOptions] Carries an abort signal. Not sent in the request.
|
|
1840
1852
|
*
|
|
1841
1853
|
* @return {Promise<{ users: Array<UserResponse> }>} User Query Response
|
|
1842
1854
|
*/
|
|
@@ -1844,6 +1856,7 @@ export class StreamChat {
|
|
|
1844
1856
|
filterConditions: UserFilters,
|
|
1845
1857
|
sort: UserSort = [],
|
|
1846
1858
|
options: UserOptions = {},
|
|
1859
|
+
requestOptions: RequestOptions = {},
|
|
1847
1860
|
) {
|
|
1848
1861
|
const defaultOptions = {
|
|
1849
1862
|
presence: false,
|
|
@@ -1867,6 +1880,7 @@ export class StreamChat {
|
|
|
1867
1880
|
...options,
|
|
1868
1881
|
},
|
|
1869
1882
|
},
|
|
1883
|
+
requestOptions,
|
|
1870
1884
|
);
|
|
1871
1885
|
|
|
1872
1886
|
this.state.updateUsers(data.users);
|
|
@@ -1918,13 +1932,17 @@ export class StreamChat {
|
|
|
1918
1932
|
* searchUserGroups - Search user groups by prefix for autocomplete
|
|
1919
1933
|
*
|
|
1920
1934
|
* @param {SearchUserGroupsOptions} options The search options
|
|
1921
|
-
*
|
|
1935
|
+
* @param {RequestOptions} [requestOptions] Carries an abort signal. Not sent in the request.
|
|
1922
1936
|
* @return {Promise<SearchUserGroupsResponse>} User Group Search Response
|
|
1923
1937
|
*/
|
|
1924
|
-
async searchUserGroups(
|
|
1938
|
+
async searchUserGroups(
|
|
1939
|
+
options: SearchUserGroupsOptions,
|
|
1940
|
+
requestOptions: RequestOptions = {},
|
|
1941
|
+
) {
|
|
1925
1942
|
return await this.get<SearchUserGroupsResponse>(
|
|
1926
1943
|
this.baseURL + '/usergroups/search',
|
|
1927
1944
|
options,
|
|
1945
|
+
requestOptions,
|
|
1928
1946
|
);
|
|
1929
1947
|
}
|
|
1930
1948
|
|
|
@@ -2061,6 +2079,7 @@ export class StreamChat {
|
|
|
2061
2079
|
* @param {ChannelSort} [sort] Sort options, for instance {created_at: -1}.
|
|
2062
2080
|
* When using multiple fields, make sure you use array of objects to guarantee field order, for instance [{last_updated: -1}, {created_at: 1}]
|
|
2063
2081
|
* @param {ChannelOptions} [options] Options object. Can include predefined_filter, filter_values, and sort_values for using predefined filters.
|
|
2082
|
+
* @param {RequestOptions} [requestOptions] Carries an abort signal. Not sent in the request.
|
|
2064
2083
|
*
|
|
2065
2084
|
* @return {Promise<QueryChannelsAPIResponse>} full search channels response
|
|
2066
2085
|
*/
|
|
@@ -2068,6 +2087,7 @@ export class StreamChat {
|
|
|
2068
2087
|
filterConditions: ChannelFilters,
|
|
2069
2088
|
sort: ChannelSort = [],
|
|
2070
2089
|
options: ChannelOptions = {},
|
|
2090
|
+
requestOptions: RequestOptions = {},
|
|
2071
2091
|
): Promise<QueryChannelsAPIResponse> {
|
|
2072
2092
|
const defaultOptions: ChannelOptions = {
|
|
2073
2093
|
state: true,
|
|
@@ -2101,7 +2121,11 @@ export class StreamChat {
|
|
|
2101
2121
|
...restOptions,
|
|
2102
2122
|
};
|
|
2103
2123
|
|
|
2104
|
-
return await this.post<QueryChannelsAPIResponse>(
|
|
2124
|
+
return await this.post<QueryChannelsAPIResponse>(
|
|
2125
|
+
this.baseURL + '/channels',
|
|
2126
|
+
payload,
|
|
2127
|
+
requestOptions,
|
|
2128
|
+
);
|
|
2105
2129
|
}
|
|
2106
2130
|
|
|
2107
2131
|
/**
|
|
@@ -2147,6 +2171,7 @@ export class StreamChat {
|
|
|
2147
2171
|
* - stateOptions.skipInitialization - Skips the initialization of the state for the channels matching the ids in the list.
|
|
2148
2172
|
* - stateOptions.skipHydration - Skips returning the channels as instances of the Channel class and rather returns the raw query response.
|
|
2149
2173
|
* - stateOptions.withResponse - Returns the full query response with hydrated channels. This is a compatibility bridge for internal callers that need response-level metadata while the default return value remains `Channel[]`.
|
|
2174
|
+
* - stateOptions.signal - Aborts the request. See AbortController.
|
|
2150
2175
|
*
|
|
2151
2176
|
* @return {Promise<Array<Channel>>} search channels response
|
|
2152
2177
|
*/
|
|
@@ -2172,6 +2197,8 @@ export class StreamChat {
|
|
|
2172
2197
|
filterConditions,
|
|
2173
2198
|
sort,
|
|
2174
2199
|
options,
|
|
2200
|
+
// stateOptions is never serialized, so it carries the abort signal
|
|
2201
|
+
{ signal: stateOptions.signal },
|
|
2175
2202
|
);
|
|
2176
2203
|
const channels = queryChannelsResponse.channels;
|
|
2177
2204
|
|
|
@@ -2317,6 +2344,7 @@ export class StreamChat {
|
|
|
2317
2344
|
* @param {ChannelFilters} filterConditions MongoDB style filter conditions
|
|
2318
2345
|
* @param {MessageFilters | string} query search query or object MongoDB style filters
|
|
2319
2346
|
* @param {SearchOptions} [options] Option object, {user_id: 'tommaso'}
|
|
2347
|
+
* @param {RequestOptions} [requestOptions] Carries an abort signal. Not sent in the request.
|
|
2320
2348
|
*
|
|
2321
2349
|
* @return {Promise<SearchAPIResponse>} search messages response
|
|
2322
2350
|
*/
|
|
@@ -2324,6 +2352,7 @@ export class StreamChat {
|
|
|
2324
2352
|
filterConditions: ChannelFilters,
|
|
2325
2353
|
query: string | MessageFilters,
|
|
2326
2354
|
options: SearchOptions = {},
|
|
2355
|
+
requestOptions: RequestOptions = {},
|
|
2327
2356
|
) {
|
|
2328
2357
|
if (options.offset && options.next) {
|
|
2329
2358
|
throw Error(`Cannot specify offset with next`);
|
|
@@ -2346,7 +2375,11 @@ export class StreamChat {
|
|
|
2346
2375
|
// Make sure we wait for the connect promise if there is a pending one
|
|
2347
2376
|
await this.wsPromise;
|
|
2348
2377
|
|
|
2349
|
-
return await this.get<SearchAPIResponse>(
|
|
2378
|
+
return await this.get<SearchAPIResponse>(
|
|
2379
|
+
this.baseURL + '/search',
|
|
2380
|
+
{ payload },
|
|
2381
|
+
requestOptions,
|
|
2382
|
+
);
|
|
2350
2383
|
}
|
|
2351
2384
|
|
|
2352
2385
|
/**
|
|
@@ -3019,6 +3052,11 @@ export class StreamChat {
|
|
|
3019
3052
|
|
|
3020
3053
|
/**
|
|
3021
3054
|
* unflagMessage - unflag a message
|
|
3055
|
+
*
|
|
3056
|
+
* @deprecated The `/moderation/unflag` endpoint is deprecated and is a no-op on
|
|
3057
|
+
* the backend - calling this resolves successfully but does not remove the flag.
|
|
3058
|
+
* Use `client.moderation.submitAction()` against the review queue item instead.
|
|
3059
|
+
*
|
|
3022
3060
|
* @param {string} targetMessageID
|
|
3023
3061
|
* @param {string} [options.user_id] currentUserID, only used with serverside auth
|
|
3024
3062
|
* @returns {Promise<APIResponse>}
|
|
@@ -3032,6 +3070,11 @@ export class StreamChat {
|
|
|
3032
3070
|
|
|
3033
3071
|
/**
|
|
3034
3072
|
* unflagUser - unflag a user
|
|
3073
|
+
*
|
|
3074
|
+
* @deprecated The `/moderation/unflag` endpoint is deprecated and is a no-op on
|
|
3075
|
+
* the backend - calling this resolves successfully but does not remove the flag.
|
|
3076
|
+
* Use `client.moderation.submitAction()` against the review queue item instead.
|
|
3077
|
+
*
|
|
3035
3078
|
* @param {string} targetID
|
|
3036
3079
|
* @param {string} [options.user_id] currentUserID, only used with serverside auth
|
|
3037
3080
|
* @returns {Promise<APIResponse>}
|
|
@@ -3388,7 +3431,12 @@ export class StreamChat {
|
|
|
3388
3431
|
}
|
|
3389
3432
|
|
|
3390
3433
|
// should not include user object
|
|
3391
|
-
|
|
3434
|
+
// Sanitized at the point of sending, which is the only place every path converges: the
|
|
3435
|
+
// offline replay of a queued `update-message` task calls this method directly.
|
|
3436
|
+
const payload = sanitizeOutgoingAttachments({
|
|
3437
|
+
client: this,
|
|
3438
|
+
message: toUpdatedMessagePayload(message),
|
|
3439
|
+
});
|
|
3392
3440
|
|
|
3393
3441
|
// add user_id (if exists)
|
|
3394
3442
|
if (typeof partialUserOrUserId === 'string') {
|
|
@@ -3806,6 +3854,12 @@ export class StreamChat {
|
|
|
3806
3854
|
...axiosRequestConfigRest
|
|
3807
3855
|
} = this.options.axiosRequestConfig || {};
|
|
3808
3856
|
|
|
3857
|
+
// Most specific wins, and it is spread last so that a config carrying an explicit
|
|
3858
|
+
// `signal: undefined` cannot clobber a client-wide one or an armed
|
|
3859
|
+
// nextRequestAbortController.
|
|
3860
|
+
const resolvedSignal =
|
|
3861
|
+
options.config?.signal ?? axiosRequestConfigRest.signal ?? signal;
|
|
3862
|
+
|
|
3809
3863
|
return {
|
|
3810
3864
|
params: {
|
|
3811
3865
|
user_id: this.userID,
|
|
@@ -3821,9 +3875,9 @@ export class StreamChat {
|
|
|
3821
3875
|
...options.headers,
|
|
3822
3876
|
...(axiosRequestConfigHeaders || {}),
|
|
3823
3877
|
},
|
|
3824
|
-
...(signal ? { signal } : {}),
|
|
3825
3878
|
...options.config,
|
|
3826
|
-
...
|
|
3879
|
+
...axiosRequestConfigRest,
|
|
3880
|
+
...(resolvedSignal ? { signal: resolvedSignal } : {}),
|
|
3827
3881
|
};
|
|
3828
3882
|
}
|
|
3829
3883
|
|
|
@@ -3993,8 +4047,12 @@ export class StreamChat {
|
|
|
3993
4047
|
*
|
|
3994
4048
|
* @returns {Promise<APIResponse>}
|
|
3995
4049
|
*/
|
|
3996
|
-
searchRoles(options: SearchRolesOptions) {
|
|
3997
|
-
return this.get<SearchRolesAPIResponse>(
|
|
4050
|
+
searchRoles(options: SearchRolesOptions, requestOptions: RequestOptions = {}) {
|
|
4051
|
+
return this.get<SearchRolesAPIResponse>(
|
|
4052
|
+
`${this.baseURL}/roles/search`,
|
|
4053
|
+
options,
|
|
4054
|
+
requestOptions,
|
|
4055
|
+
);
|
|
3998
4056
|
}
|
|
3999
4057
|
|
|
4000
4058
|
/** deleteRole - deletes a custom role
|
package/src/index.ts
CHANGED
|
@@ -62,3 +62,15 @@ export {
|
|
|
62
62
|
promoteChannel,
|
|
63
63
|
} from './utils';
|
|
64
64
|
export { FixedSizeQueueCache } from './utils/FixedSizeQueueCache';
|
|
65
|
+
/**
|
|
66
|
+
* Tag-keyed async runners. Exported so the UI SDKs (and integrators) serialize their own
|
|
67
|
+
* actions with the same primitive the SDK uses internally instead of hand-rolling promise
|
|
68
|
+
* chains. Tags share one process-wide map, so namespace yours (`my-app/thing/${id}`) to avoid
|
|
69
|
+
* colliding with another caller's.
|
|
70
|
+
*/
|
|
71
|
+
export {
|
|
72
|
+
hasPending,
|
|
73
|
+
settled,
|
|
74
|
+
withCancellation,
|
|
75
|
+
withoutConcurrency,
|
|
76
|
+
} from './utils/concurrency';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Attachment, SharedLocationResponse } from '../types';
|
|
2
2
|
import type {
|
|
3
|
+
AttachmentLoadingState,
|
|
3
4
|
AudioAttachment,
|
|
4
5
|
FileAttachment,
|
|
5
6
|
GiphyAttachment,
|
|
@@ -27,6 +28,32 @@ export const isLocalUploadAttachment = (
|
|
|
27
28
|
): attachment is LocalUploadAttachment =>
|
|
28
29
|
!!(attachment as LocalAttachment)?.localMetadata?.uploadState;
|
|
29
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Upload states meaning "the bytes are not on the CDN yet, but they are on their way".
|
|
33
|
+
* `blocked` and `failed` are excluded - those never resolve on their own.
|
|
34
|
+
*
|
|
35
|
+
* Typed against {@link AttachmentLoadingState} rather than `string`, so renaming a state fails
|
|
36
|
+
* the build instead of silently making this list match nothing - it decides what gets sent.
|
|
37
|
+
*/
|
|
38
|
+
const PENDING_UPLOAD_STATES: ReadonlyArray<AttachmentLoadingState> = [
|
|
39
|
+
'pending',
|
|
40
|
+
'uploading',
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
/** Whether an upload for this attachment is still expected to resolve. */
|
|
44
|
+
export const isPendingUpload = (
|
|
45
|
+
attachment: unknown,
|
|
46
|
+
): attachment is LocalUploadAttachment =>
|
|
47
|
+
isLocalUploadAttachment(attachment) &&
|
|
48
|
+
PENDING_UPLOAD_STATES.includes(attachment.localMetadata.uploadState);
|
|
49
|
+
|
|
50
|
+
/** Whether this attachment's upload already resolved to a URL. */
|
|
51
|
+
export const isFinishedUpload = (
|
|
52
|
+
attachment: unknown,
|
|
53
|
+
): attachment is LocalUploadAttachment =>
|
|
54
|
+
isLocalUploadAttachment(attachment) &&
|
|
55
|
+
attachment.localMetadata.uploadState === 'finished';
|
|
56
|
+
|
|
30
57
|
export const isFileAttachment = (
|
|
31
58
|
attachment: Attachment | LocalAttachment,
|
|
32
59
|
supportedVideoFormat: string[] = [],
|