stream-chat 9.2.0 → 9.4.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.cjs +251 -34
- package/dist/cjs/index.browser.cjs.map +4 -4
- package/dist/cjs/index.node.cjs +258 -39
- package/dist/cjs/index.node.cjs.map +4 -4
- package/dist/esm/index.js +254 -37
- package/dist/esm/index.js.map +4 -4
- package/dist/types/channel.d.ts +3 -5
- package/dist/types/client.d.ts +20 -3
- package/dist/types/offline-support/offline_support_api.d.ts +2 -4
- package/dist/types/offline-support/types.d.ts +14 -4
- package/dist/types/store.d.ts +114 -5
- package/dist/types/types.d.ts +29 -3
- package/package.json +4 -4
- package/src/channel.ts +5 -5
- package/src/client.ts +26 -7
- package/src/offline-support/offline_support_api.ts +12 -9
- package/src/offline-support/types.ts +28 -4
- package/src/store.ts +237 -11
- package/src/types.ts +29 -1
- package/src/utils/mergeWith/mergeWithCore.ts +7 -1
package/dist/types/channel.d.ts
CHANGED
|
@@ -139,7 +139,7 @@ export declare class Channel {
|
|
|
139
139
|
sendReaction(messageID: string, reaction: Reaction, options?: {
|
|
140
140
|
enforce_unique?: boolean;
|
|
141
141
|
skip_push?: boolean;
|
|
142
|
-
}): Promise<ReactionAPIResponse
|
|
142
|
+
}): Promise<ReactionAPIResponse>;
|
|
143
143
|
/**
|
|
144
144
|
* sendReaction - Send a reaction about a message
|
|
145
145
|
*
|
|
@@ -152,10 +152,8 @@ export declare class Channel {
|
|
|
152
152
|
_sendReaction(messageID: string, reaction: Reaction, options?: {
|
|
153
153
|
enforce_unique?: boolean;
|
|
154
154
|
skip_push?: boolean;
|
|
155
|
-
}): Promise<ReactionAPIResponse
|
|
156
|
-
deleteReaction(messageID: string, reactionType: string, user_id?: string): Promise<
|
|
157
|
-
message: MessageResponse;
|
|
158
|
-
}) | undefined>;
|
|
155
|
+
}): Promise<ReactionAPIResponse>;
|
|
156
|
+
deleteReaction(messageID: string, reactionType: string, user_id?: string): Promise<ReactionAPIResponse>;
|
|
159
157
|
/**
|
|
160
158
|
* deleteReaction - Delete a reaction by user and type
|
|
161
159
|
*
|
package/dist/types/client.d.ts
CHANGED
|
@@ -227,7 +227,24 @@ export declare class StreamChat {
|
|
|
227
227
|
'data_template': 'data handlebars template',
|
|
228
228
|
'apn_template': 'apn notification handlebars template under v2'
|
|
229
229
|
},
|
|
230
|
-
'webhook_url': 'https://acme.com/my/awesome/webhook/'
|
|
230
|
+
'webhook_url': 'https://acme.com/my/awesome/webhook/',
|
|
231
|
+
'event_hooks': [
|
|
232
|
+
{
|
|
233
|
+
'hook_type': 'webhook',
|
|
234
|
+
'enabled': true,
|
|
235
|
+
'event_types': ['message.new'],
|
|
236
|
+
'webhook_url': 'https://acme.com/my/awesome/webhook/'
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
'hook_type': 'sqs',
|
|
240
|
+
'enabled': true,
|
|
241
|
+
'event_types': ['message.new'],
|
|
242
|
+
'sqs_url': 'https://sqs.us-east-1.amazonaws.com/1234567890/my-queue',
|
|
243
|
+
'sqs_auth_type': 'key',
|
|
244
|
+
'sqs_key': 'my-access-key',
|
|
245
|
+
'sqs_secret': 'my-secret-key'
|
|
246
|
+
}
|
|
247
|
+
]
|
|
231
248
|
}
|
|
232
249
|
*/
|
|
233
250
|
updateAppSettings(options: AppSettings): Promise<APIResponse>;
|
|
@@ -1103,9 +1120,9 @@ export declare class StreamChat {
|
|
|
1103
1120
|
partialUpdateMessage(id: string, partialMessageObject: PartialMessageUpdate, partialUserOrUserId?: string | {
|
|
1104
1121
|
id: string;
|
|
1105
1122
|
}, options?: UpdateMessageOptions): Promise<UpdateMessageAPIResponse>;
|
|
1106
|
-
deleteMessage(messageID: string, hardDelete?: boolean): Promise<
|
|
1123
|
+
deleteMessage(messageID: string, hardDelete?: boolean): Promise<APIResponse & {
|
|
1107
1124
|
message: MessageResponse;
|
|
1108
|
-
}
|
|
1125
|
+
}>;
|
|
1109
1126
|
_deleteMessage(messageID: string, hardDelete?: boolean): Promise<APIResponse & {
|
|
1110
1127
|
message: MessageResponse;
|
|
1111
1128
|
}>;
|
|
@@ -522,11 +522,9 @@ export declare abstract class AbstractOfflineDB implements OfflineDBApi {
|
|
|
522
522
|
* It will return the response from the execution if it succeeded.
|
|
523
523
|
* @param task - the pending task we want to execute
|
|
524
524
|
*/
|
|
525
|
-
queueTask: ({ task }: {
|
|
525
|
+
queueTask: <T>({ task }: {
|
|
526
526
|
task: PendingTask;
|
|
527
|
-
}) => Promise<
|
|
528
|
-
message: import("..").MessageResponse;
|
|
529
|
-
}) | undefined>;
|
|
527
|
+
}) => Promise<T>;
|
|
530
528
|
/**
|
|
531
529
|
* A utility method that determines if a failed task should be added to the
|
|
532
530
|
* queue based on its error.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AppSettingsAPIResponse, ChannelAPIResponse, ChannelFilters, ChannelMemberResponse, ChannelResponse, ChannelSort, LocalMessage,
|
|
1
|
+
import type { AppSettingsAPIResponse, ChannelAPIResponse, ChannelFilters, ChannelMemberResponse, ChannelResponse, ChannelSort, LocalMessage, MessageResponse, PollResponse, ReactionFilters, ReactionResponse, ReactionSort, ReadResponse } from '../types';
|
|
2
2
|
import type { Channel } from '../channel';
|
|
3
3
|
import type { StreamChat } from '../client';
|
|
4
4
|
export type PrepareBatchDBQueries = [string] | [string, Array<unknown> | Array<Array<unknown>>];
|
|
@@ -322,6 +322,16 @@ export type PendingTask = {
|
|
|
322
322
|
payload: Parameters<Channel['sendMessage']>;
|
|
323
323
|
type: PendingTaskTypes['sendMessage'];
|
|
324
324
|
});
|
|
325
|
-
export type
|
|
326
|
-
|
|
327
|
-
|
|
325
|
+
export type OfflineErrorType = 'connection:lost';
|
|
326
|
+
export declare class OfflineError extends Error {
|
|
327
|
+
type: OfflineErrorType;
|
|
328
|
+
name: string;
|
|
329
|
+
constructor(message: string, { type, }: {
|
|
330
|
+
type: OfflineError['type'];
|
|
331
|
+
});
|
|
332
|
+
toJSON(): {
|
|
333
|
+
message: string;
|
|
334
|
+
stack: string | undefined;
|
|
335
|
+
name: string;
|
|
336
|
+
};
|
|
337
|
+
}
|
package/dist/types/store.d.ts
CHANGED
|
@@ -2,14 +2,123 @@ export type Patch<T> = (value: T) => T;
|
|
|
2
2
|
export type ValueOrPatch<T> = T | Patch<T>;
|
|
3
3
|
export type Handler<T> = (nextValue: T, previousValue: T | undefined) => void;
|
|
4
4
|
export type Unsubscribe = () => void;
|
|
5
|
+
export type RemovePreprocessor = Unsubscribe;
|
|
6
|
+
export type Preprocessor<T> = Handler<T>;
|
|
5
7
|
export declare const isPatch: <T>(value: ValueOrPatch<T>) => value is Patch<T>;
|
|
6
8
|
export declare class StateStore<T extends Record<string, unknown>> {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
+
protected value: T;
|
|
10
|
+
protected handlers: Set<Handler<T>>;
|
|
11
|
+
protected preprocessors: Set<Preprocessor<T>>;
|
|
9
12
|
constructor(value: T);
|
|
10
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Allows merging two stores only if their keys differ otherwise there's no way to ensure the data type stability.
|
|
15
|
+
* @experimental
|
|
16
|
+
* This method is experimental and may change in future versions.
|
|
17
|
+
*/
|
|
18
|
+
merge<Q extends StateStore<any>>(stateStore: Q extends StateStore<infer L> ? Extract<keyof T, keyof L> extends never ? Q : never : never): MergedStateStore<T, Q extends StateStore<infer L extends Record<string, unknown>> ? L : never>;
|
|
19
|
+
next(newValueOrPatch: ValueOrPatch<T>): void;
|
|
11
20
|
partialNext: (partial: Partial<T>) => void;
|
|
12
|
-
getLatestValue
|
|
13
|
-
subscribe
|
|
21
|
+
getLatestValue(): T;
|
|
22
|
+
subscribe(handler: Handler<T>): Unsubscribe;
|
|
14
23
|
subscribeWithSelector: <O extends Readonly<Record<string, unknown>> | Readonly<unknown[]>>(selector: (nextValue: T) => O, handler: Handler<O>) => Unsubscribe;
|
|
24
|
+
/**
|
|
25
|
+
* Registers a preprocessor function that will be called before the state is updated.
|
|
26
|
+
*
|
|
27
|
+
* Preprocessors are invoked with the new and previous values whenever `next` or `partialNext` methods
|
|
28
|
+
* are called, allowing you to mutate or react to the new value before it is set. Preprocessors run in the
|
|
29
|
+
* order they were registered.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* const store = new StateStore<{ count: number; isMaxValue: bool; }>({ count: 0, isMaxValue: false });
|
|
34
|
+
*
|
|
35
|
+
* store.addPreprocessor((nextValue, prevValue) => {
|
|
36
|
+
* if (nextValue.count > 10) {
|
|
37
|
+
* nextValue.count = 10; // Clamp the value to a maximum of 10
|
|
38
|
+
* }
|
|
39
|
+
*
|
|
40
|
+
* if (nextValue.count === 10) {
|
|
41
|
+
* nextValue.isMaxValue = true; // Set isMaxValue to true if count is 10
|
|
42
|
+
* } else {
|
|
43
|
+
* nextValue.isMaxValue = false; // Reset isMaxValue otherwise
|
|
44
|
+
* }
|
|
45
|
+
* });
|
|
46
|
+
*
|
|
47
|
+
* store.partialNext({ count: 15 });
|
|
48
|
+
*
|
|
49
|
+
* store.getLatestValue(); // { count: 10, isMaxValue: true }
|
|
50
|
+
*
|
|
51
|
+
* store.partialNext({ count: 5 });
|
|
52
|
+
*
|
|
53
|
+
* store.getLatestValue(); // { count: 5, isMaxValue: false }
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* @param preprocessor - The function to be called with the next and previous values before the state is updated.
|
|
57
|
+
* @returns A `RemovePreprocessor` function that removes the preprocessor when called.
|
|
58
|
+
*/
|
|
59
|
+
addPreprocessor(preprocessor: Preprocessor<T>): RemovePreprocessor;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Represents a merged state store that combines two separate state stores into one.
|
|
63
|
+
*
|
|
64
|
+
* The MergedStateStore allows combining two stores with non-overlapping keys.
|
|
65
|
+
* It extends StateStore with the combined type of both source stores.
|
|
66
|
+
* Changes to either the original or merged store will propagate to the combined store.
|
|
67
|
+
*
|
|
68
|
+
* Note: Direct mutations (next, partialNext, addPreprocessor) are disabled on the merged store.
|
|
69
|
+
* You should instead call these methods on the original or merged stores.
|
|
70
|
+
*
|
|
71
|
+
* @template O The type of the original state store
|
|
72
|
+
* @template M The type of the merged state store
|
|
73
|
+
*
|
|
74
|
+
* @experimental
|
|
75
|
+
* This class is experimental and may change in future versions.
|
|
76
|
+
*/
|
|
77
|
+
export declare class MergedStateStore<O extends Record<string, unknown>, M extends Record<string, unknown>> extends StateStore<O & M> {
|
|
78
|
+
readonly original: StateStore<O>;
|
|
79
|
+
readonly merged: StateStore<M>;
|
|
80
|
+
private cachedOriginalValue;
|
|
81
|
+
private cachedMergedValue;
|
|
82
|
+
constructor({ original, merged }: {
|
|
83
|
+
original: StateStore<O>;
|
|
84
|
+
merged: StateStore<M>;
|
|
85
|
+
});
|
|
86
|
+
/**
|
|
87
|
+
* Subscribes to changes in the merged state store.
|
|
88
|
+
*
|
|
89
|
+
* This method extends the base subscribe functionality to handle the merged nature of this store:
|
|
90
|
+
* 1. The first subscriber triggers registration of helper subscribers that listen to both source stores
|
|
91
|
+
* 2. Changes from either source store are propagated to this merged store
|
|
92
|
+
* 3. Source store values are cached to prevent unnecessary updates
|
|
93
|
+
*
|
|
94
|
+
* When the first subscriber is added, the method sets up listeners on both original and merged stores.
|
|
95
|
+
* These listeners update the combined store value whenever either source store changes.
|
|
96
|
+
* All subscriptions (helpers and the actual handler) are tracked so they can be properly cleaned up.
|
|
97
|
+
*
|
|
98
|
+
* @param handler - The callback function that will be executed when the state changes
|
|
99
|
+
* @returns An unsubscribe function that, when called, removes the subscription and any helper subscriptions
|
|
100
|
+
*/
|
|
101
|
+
subscribe(handler: Handler<O & M>): () => void;
|
|
102
|
+
/**
|
|
103
|
+
* Retrieves the latest combined state from both original and merged stores.
|
|
104
|
+
*
|
|
105
|
+
* This method extends the base getLatestValue functionality to ensure the merged store
|
|
106
|
+
* remains in sync with its source stores even when there are no active subscribers.
|
|
107
|
+
*
|
|
108
|
+
* When there are no handlers registered, the method:
|
|
109
|
+
* 1. Fetches the latest values from both source stores
|
|
110
|
+
* 2. Compares them with the cached values to detect changes
|
|
111
|
+
* 3. If changes are detected, updates the internal value and caches
|
|
112
|
+
* the new source values to maintain consistency
|
|
113
|
+
*
|
|
114
|
+
* This approach ensures that calling getLatestValue() always returns the most
|
|
115
|
+
* up-to-date combined state, even if the merged store hasn't been actively
|
|
116
|
+
* receiving updates through subscriptions.
|
|
117
|
+
*
|
|
118
|
+
* @returns The latest combined state from both original and merged stores
|
|
119
|
+
*/
|
|
120
|
+
getLatestValue(): O & M;
|
|
121
|
+
next: () => void;
|
|
122
|
+
partialNext: () => void;
|
|
123
|
+
addPreprocessor(): () => void;
|
|
15
124
|
}
|
package/dist/types/types.d.ts
CHANGED
|
@@ -90,6 +90,7 @@ export type AppSettingsAPIResponse = APIResponse & {
|
|
|
90
90
|
disable_auth_checks?: boolean;
|
|
91
91
|
disable_permissions_checks?: boolean;
|
|
92
92
|
enforce_unique_usernames?: 'no' | 'app' | 'team';
|
|
93
|
+
event_hooks?: Array<EventHook>;
|
|
93
94
|
file_upload_config?: FileUploadConfig;
|
|
94
95
|
geofences?: Array<{
|
|
95
96
|
country_codes: Array<string>;
|
|
@@ -1698,6 +1699,7 @@ export type AppSettings = {
|
|
|
1698
1699
|
disable_auth_checks?: boolean;
|
|
1699
1700
|
disable_permissions_checks?: boolean;
|
|
1700
1701
|
enforce_unique_usernames?: 'no' | 'app' | 'team';
|
|
1702
|
+
event_hooks?: Array<EventHook> | null;
|
|
1701
1703
|
file_upload_config?: FileUploadConfig;
|
|
1702
1704
|
firebase_config?: {
|
|
1703
1705
|
apn_template?: string;
|
|
@@ -2354,9 +2356,11 @@ export declare class ErrorFromResponse<T> extends Error {
|
|
|
2354
2356
|
status: ErrorFromResponse<T>['status'];
|
|
2355
2357
|
});
|
|
2356
2358
|
toJSON(): {
|
|
2357
|
-
message: string
|
|
2358
|
-
stack: string | undefined;
|
|
2359
|
-
name: string;
|
|
2359
|
+
readonly message: `(${string}) - ${string}`;
|
|
2360
|
+
readonly stack: string | undefined;
|
|
2361
|
+
readonly name: string;
|
|
2362
|
+
readonly code: number | null;
|
|
2363
|
+
readonly status: number;
|
|
2360
2364
|
};
|
|
2361
2365
|
}
|
|
2362
2366
|
export type QueryPollsResponse = {
|
|
@@ -2905,4 +2909,26 @@ export type ThreadFilters = QueryFilters<{
|
|
|
2905
2909
|
} & {
|
|
2906
2910
|
last_message_at?: RequireOnlyOne<Pick<QueryFilter<ThreadResponse['last_message_at']>, '$eq' | '$gt' | '$lt' | '$gte' | '$lte'>> | PrimitiveFilter<ThreadResponse['last_message_at']>;
|
|
2907
2911
|
}>;
|
|
2912
|
+
export type HookType = 'webhook' | 'sqs' | 'sns';
|
|
2913
|
+
export type EventHook = {
|
|
2914
|
+
id?: string;
|
|
2915
|
+
hook_type?: HookType;
|
|
2916
|
+
enabled?: boolean;
|
|
2917
|
+
event_types?: Array<string>;
|
|
2918
|
+
webhook_url?: string;
|
|
2919
|
+
sqs_queue_url?: string;
|
|
2920
|
+
sqs_region?: string;
|
|
2921
|
+
sqs_auth_type?: string;
|
|
2922
|
+
sqs_key?: string;
|
|
2923
|
+
sqs_secret?: string;
|
|
2924
|
+
sqs_role_arn?: string;
|
|
2925
|
+
sns_topic_arn?: string;
|
|
2926
|
+
sns_region?: string;
|
|
2927
|
+
sns_auth_type?: string;
|
|
2928
|
+
sns_key?: string;
|
|
2929
|
+
sns_secret?: string;
|
|
2930
|
+
sns_role_arn?: string;
|
|
2931
|
+
created_at?: string;
|
|
2932
|
+
updated_at?: string;
|
|
2933
|
+
};
|
|
2908
2934
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stream-chat",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.4.0",
|
|
4
4
|
"description": "JS SDK for the Stream Chat API",
|
|
5
5
|
"homepage": "https://getstream.io/chat/",
|
|
6
6
|
"author": {
|
|
@@ -68,6 +68,7 @@
|
|
|
68
68
|
"@types/base64-js": "^1.3.0",
|
|
69
69
|
"@types/node": "^22.15.21",
|
|
70
70
|
"@types/sinon": "^10.0.6",
|
|
71
|
+
"@vitest/coverage-v8": "3.1.4",
|
|
71
72
|
"concurrently": "^9.1.2",
|
|
72
73
|
"conventional-changelog-conventionalcommits": "^8.0.0",
|
|
73
74
|
"dotenv": "^8.2.0",
|
|
@@ -77,7 +78,6 @@
|
|
|
77
78
|
"globals": "^16.0.0",
|
|
78
79
|
"husky": "^9.1.7",
|
|
79
80
|
"lint-staged": "^15.2.2",
|
|
80
|
-
"nyc": "^15.1.0",
|
|
81
81
|
"prettier": "^3.5.3",
|
|
82
82
|
"semantic-release": "^24.2.3",
|
|
83
83
|
"sinon": "^12.0.1",
|
|
@@ -98,8 +98,8 @@
|
|
|
98
98
|
"test": "yarn test-unit",
|
|
99
99
|
"testwatch": "NODE_ENV=test nodemon ./node_modules/.bin/mocha --timeout 20000 --require test-entry.js test/test.js",
|
|
100
100
|
"test-types": "node test/typescript/index.js && tsc --esModuleInterop true --noEmit true --strictNullChecks true --noImplicitAny true --strict true test/typescript/*.ts",
|
|
101
|
-
"test-unit": "vitest
|
|
102
|
-
"test-coverage": "
|
|
101
|
+
"test-unit": "vitest",
|
|
102
|
+
"test-coverage": "vitest run --coverage",
|
|
103
103
|
"fix-staged": "lint-staged --config .lintstagedrc.fix.json --concurrent 1",
|
|
104
104
|
"semantic-release": "semantic-release",
|
|
105
105
|
"prepare": "husky; yarn run build"
|
package/src/channel.ts
CHANGED
|
@@ -207,7 +207,7 @@ export class Channel {
|
|
|
207
207
|
if (offlineDb) {
|
|
208
208
|
const messageId = message.id;
|
|
209
209
|
if (messageId) {
|
|
210
|
-
return
|
|
210
|
+
return await offlineDb.queueTask<SendMessageAPIResponse>({
|
|
211
211
|
task: {
|
|
212
212
|
channelId: this.id as string,
|
|
213
213
|
channelType: this.type,
|
|
@@ -215,7 +215,7 @@ export class Channel {
|
|
|
215
215
|
payload: [message, options],
|
|
216
216
|
type: 'send-message',
|
|
217
217
|
},
|
|
218
|
-
})
|
|
218
|
+
});
|
|
219
219
|
}
|
|
220
220
|
}
|
|
221
221
|
} catch (error) {
|
|
@@ -410,7 +410,7 @@ export class Channel {
|
|
|
410
410
|
messageID: string,
|
|
411
411
|
reaction: Reaction,
|
|
412
412
|
options?: { enforce_unique?: boolean; skip_push?: boolean },
|
|
413
|
-
)
|
|
413
|
+
) {
|
|
414
414
|
if (!messageID) {
|
|
415
415
|
throw Error(`Message id is missing`);
|
|
416
416
|
}
|
|
@@ -454,7 +454,7 @@ export class Channel {
|
|
|
454
454
|
messageID: string,
|
|
455
455
|
reaction: Reaction,
|
|
456
456
|
options?: { enforce_unique?: boolean; skip_push?: boolean },
|
|
457
|
-
)
|
|
457
|
+
) {
|
|
458
458
|
if (!messageID) {
|
|
459
459
|
throw Error(`Message id is missing`);
|
|
460
460
|
}
|
|
@@ -498,7 +498,7 @@ export class Channel {
|
|
|
498
498
|
});
|
|
499
499
|
}
|
|
500
500
|
|
|
501
|
-
return await offlineDb.queueTask({
|
|
501
|
+
return await offlineDb.queueTask<ReactionAPIResponse>({
|
|
502
502
|
task: {
|
|
503
503
|
channelId: this.id as string,
|
|
504
504
|
channelType: this.type,
|
package/src/client.ts
CHANGED
|
@@ -777,7 +777,24 @@ export class StreamChat {
|
|
|
777
777
|
'data_template': 'data handlebars template',
|
|
778
778
|
'apn_template': 'apn notification handlebars template under v2'
|
|
779
779
|
},
|
|
780
|
-
'webhook_url': 'https://acme.com/my/awesome/webhook/'
|
|
780
|
+
'webhook_url': 'https://acme.com/my/awesome/webhook/',
|
|
781
|
+
'event_hooks': [
|
|
782
|
+
{
|
|
783
|
+
'hook_type': 'webhook',
|
|
784
|
+
'enabled': true,
|
|
785
|
+
'event_types': ['message.new'],
|
|
786
|
+
'webhook_url': 'https://acme.com/my/awesome/webhook/'
|
|
787
|
+
},
|
|
788
|
+
{
|
|
789
|
+
'hook_type': 'sqs',
|
|
790
|
+
'enabled': true,
|
|
791
|
+
'event_types': ['message.new'],
|
|
792
|
+
'sqs_url': 'https://sqs.us-east-1.amazonaws.com/1234567890/my-queue',
|
|
793
|
+
'sqs_auth_type': 'key',
|
|
794
|
+
'sqs_key': 'my-access-key',
|
|
795
|
+
'sqs_secret': 'my-secret-key'
|
|
796
|
+
}
|
|
797
|
+
]
|
|
781
798
|
}
|
|
782
799
|
*/
|
|
783
800
|
async updateAppSettings(options: AppSettings) {
|
|
@@ -3019,13 +3036,15 @@ export class StreamChat {
|
|
|
3019
3036
|
} else {
|
|
3020
3037
|
await this.offlineDb.softDeleteMessage({ id: messageID });
|
|
3021
3038
|
}
|
|
3022
|
-
return await this.offlineDb.queueTask(
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3039
|
+
return await this.offlineDb.queueTask<APIResponse & { message: MessageResponse }>(
|
|
3040
|
+
{
|
|
3041
|
+
task: {
|
|
3042
|
+
messageId: messageID,
|
|
3043
|
+
payload: [messageID, hardDelete],
|
|
3044
|
+
type: 'delete-message',
|
|
3045
|
+
},
|
|
3027
3046
|
},
|
|
3028
|
-
|
|
3047
|
+
);
|
|
3029
3048
|
}
|
|
3030
3049
|
} catch (error) {
|
|
3031
3050
|
this.logger('error', `offlineDb:deleteMessage`, {
|
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
PendingTask,
|
|
7
7
|
PrepareBatchDBQueries,
|
|
8
8
|
} from './types';
|
|
9
|
+
import { OfflineError } from './types';
|
|
9
10
|
import type { StreamChat } from '../client';
|
|
10
11
|
import type { AxiosError } from 'axios';
|
|
11
12
|
import { OfflineDBSyncManager } from './offline_sync_manager';
|
|
@@ -986,22 +987,24 @@ export abstract class AbstractOfflineDB implements OfflineDBApi {
|
|
|
986
987
|
* It will return the response from the execution if it succeeded.
|
|
987
988
|
* @param task - the pending task we want to execute
|
|
988
989
|
*/
|
|
989
|
-
public queueTask = async ({ task }: { task: PendingTask }) => {
|
|
990
|
-
|
|
991
|
-
try {
|
|
990
|
+
public queueTask = async <T>({ task }: { task: PendingTask }): Promise<T> => {
|
|
991
|
+
const attemptTaskExecution = async () => {
|
|
992
992
|
if (!this.client.wsConnection?.isHealthy) {
|
|
993
|
-
|
|
994
|
-
|
|
993
|
+
throw new OfflineError(
|
|
994
|
+
'Cannot execute task because the connection has been lost.',
|
|
995
|
+
{ type: 'connection:lost' },
|
|
996
|
+
);
|
|
995
997
|
}
|
|
996
|
-
|
|
998
|
+
return (await this.executeTask({ task })) as T;
|
|
999
|
+
};
|
|
1000
|
+
try {
|
|
1001
|
+
return await attemptTaskExecution();
|
|
997
1002
|
} catch (e) {
|
|
998
1003
|
if (!this.shouldSkipQueueingTask(e as AxiosError<APIErrorResponse>)) {
|
|
999
1004
|
await this.addPendingTask(task);
|
|
1000
|
-
throw e;
|
|
1001
1005
|
}
|
|
1006
|
+
throw e;
|
|
1002
1007
|
}
|
|
1003
|
-
|
|
1004
|
-
return response;
|
|
1005
1008
|
};
|
|
1006
1009
|
|
|
1007
1010
|
/**
|
|
@@ -6,7 +6,6 @@ import type {
|
|
|
6
6
|
ChannelResponse,
|
|
7
7
|
ChannelSort,
|
|
8
8
|
LocalMessage,
|
|
9
|
-
Message,
|
|
10
9
|
MessageResponse,
|
|
11
10
|
PollResponse,
|
|
12
11
|
ReactionFilters,
|
|
@@ -398,6 +397,31 @@ export type PendingTask = {
|
|
|
398
397
|
}
|
|
399
398
|
);
|
|
400
399
|
|
|
401
|
-
export type
|
|
402
|
-
|
|
403
|
-
|
|
400
|
+
export type OfflineErrorType = 'connection:lost';
|
|
401
|
+
|
|
402
|
+
export class OfflineError extends Error {
|
|
403
|
+
public type: OfflineErrorType;
|
|
404
|
+
public name = 'OfflineError';
|
|
405
|
+
|
|
406
|
+
constructor(
|
|
407
|
+
message: string,
|
|
408
|
+
{
|
|
409
|
+
type,
|
|
410
|
+
}: {
|
|
411
|
+
type: OfflineError['type'];
|
|
412
|
+
},
|
|
413
|
+
) {
|
|
414
|
+
super(message);
|
|
415
|
+
this.type = type;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// Vitest helper (serialized errors are too large to read)
|
|
419
|
+
// https://github.com/vitest-dev/vitest/blob/v3.1.3/packages/utils/src/error.ts#L60-L62
|
|
420
|
+
toJSON() {
|
|
421
|
+
return {
|
|
422
|
+
message: `${this.type} - ${this.message}`,
|
|
423
|
+
stack: this.stack,
|
|
424
|
+
name: this.name,
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
}
|