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
|
@@ -192,6 +192,7 @@ __export(index_exports, {
|
|
|
192
192
|
MODERATION_ENTITY_TYPES: () => MODERATION_ENTITY_TYPES,
|
|
193
193
|
MaxPriority: () => MaxPriority,
|
|
194
194
|
MentionsSearchSource: () => MentionsSearchSource,
|
|
195
|
+
MergedStateStore: () => MergedStateStore,
|
|
195
196
|
MessageComposer: () => MessageComposer,
|
|
196
197
|
MessageComposerMiddlewareExecutor: () => MessageComposerMiddlewareExecutor,
|
|
197
198
|
MessageDraftComposerMiddlewareExecutor: () => MessageDraftComposerMiddlewareExecutor,
|
|
@@ -200,6 +201,7 @@ __export(index_exports, {
|
|
|
200
201
|
MinPriority: () => MinPriority,
|
|
201
202
|
Moderation: () => Moderation,
|
|
202
203
|
OfflineDBSyncManager: () => OfflineDBSyncManager,
|
|
204
|
+
OfflineError: () => OfflineError,
|
|
203
205
|
Permission: () => Permission,
|
|
204
206
|
Poll: () => Poll,
|
|
205
207
|
PollComposer: () => PollComposer,
|
|
@@ -4047,26 +4049,14 @@ var ensureIsLocalAttachment = (attachment) => {
|
|
|
4047
4049
|
|
|
4048
4050
|
// src/store.ts
|
|
4049
4051
|
var isPatch = (value) => typeof value === "function";
|
|
4052
|
+
var noop2 = () => {
|
|
4053
|
+
};
|
|
4050
4054
|
var StateStore = class {
|
|
4051
4055
|
constructor(value) {
|
|
4052
4056
|
this.value = value;
|
|
4053
|
-
this.
|
|
4054
|
-
this.
|
|
4055
|
-
const newValue = isPatch(newValueOrPatch) ? newValueOrPatch(this.value) : newValueOrPatch;
|
|
4056
|
-
if (newValue === this.value) return;
|
|
4057
|
-
const oldValue = this.value;
|
|
4058
|
-
this.value = newValue;
|
|
4059
|
-
this.handlerSet.forEach((handler) => handler(this.value, oldValue));
|
|
4060
|
-
};
|
|
4057
|
+
this.handlers = /* @__PURE__ */ new Set();
|
|
4058
|
+
this.preprocessors = /* @__PURE__ */ new Set();
|
|
4061
4059
|
this.partialNext = (partial) => this.next((current) => ({ ...current, ...partial }));
|
|
4062
|
-
this.getLatestValue = () => this.value;
|
|
4063
|
-
this.subscribe = (handler) => {
|
|
4064
|
-
handler(this.value, void 0);
|
|
4065
|
-
this.handlerSet.add(handler);
|
|
4066
|
-
return () => {
|
|
4067
|
-
this.handlerSet.delete(handler);
|
|
4068
|
-
};
|
|
4069
|
-
};
|
|
4070
4060
|
this.subscribeWithSelector = (selector, handler) => {
|
|
4071
4061
|
let previouslySelectedValues;
|
|
4072
4062
|
const wrappedHandler = (nextValue) => {
|
|
@@ -4085,6 +4075,183 @@ var StateStore = class {
|
|
|
4085
4075
|
return this.subscribe(wrappedHandler);
|
|
4086
4076
|
};
|
|
4087
4077
|
}
|
|
4078
|
+
/**
|
|
4079
|
+
* Allows merging two stores only if their keys differ otherwise there's no way to ensure the data type stability.
|
|
4080
|
+
* @experimental
|
|
4081
|
+
* This method is experimental and may change in future versions.
|
|
4082
|
+
*/
|
|
4083
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4084
|
+
merge(stateStore) {
|
|
4085
|
+
return new MergedStateStore({
|
|
4086
|
+
original: this,
|
|
4087
|
+
merged: stateStore
|
|
4088
|
+
});
|
|
4089
|
+
}
|
|
4090
|
+
next(newValueOrPatch) {
|
|
4091
|
+
const newValue = isPatch(newValueOrPatch) ? newValueOrPatch(this.value) : newValueOrPatch;
|
|
4092
|
+
if (newValue === this.value) return;
|
|
4093
|
+
this.preprocessors.forEach((preprocessor) => preprocessor(newValue, this.value));
|
|
4094
|
+
const oldValue = this.value;
|
|
4095
|
+
this.value = newValue;
|
|
4096
|
+
this.handlers.forEach((handler) => handler(this.value, oldValue));
|
|
4097
|
+
}
|
|
4098
|
+
getLatestValue() {
|
|
4099
|
+
return this.value;
|
|
4100
|
+
}
|
|
4101
|
+
subscribe(handler) {
|
|
4102
|
+
handler(this.value, void 0);
|
|
4103
|
+
this.handlers.add(handler);
|
|
4104
|
+
return () => {
|
|
4105
|
+
this.handlers.delete(handler);
|
|
4106
|
+
};
|
|
4107
|
+
}
|
|
4108
|
+
/**
|
|
4109
|
+
* Registers a preprocessor function that will be called before the state is updated.
|
|
4110
|
+
*
|
|
4111
|
+
* Preprocessors are invoked with the new and previous values whenever `next` or `partialNext` methods
|
|
4112
|
+
* are called, allowing you to mutate or react to the new value before it is set. Preprocessors run in the
|
|
4113
|
+
* order they were registered.
|
|
4114
|
+
*
|
|
4115
|
+
* @example
|
|
4116
|
+
* ```ts
|
|
4117
|
+
* const store = new StateStore<{ count: number; isMaxValue: bool; }>({ count: 0, isMaxValue: false });
|
|
4118
|
+
*
|
|
4119
|
+
* store.addPreprocessor((nextValue, prevValue) => {
|
|
4120
|
+
* if (nextValue.count > 10) {
|
|
4121
|
+
* nextValue.count = 10; // Clamp the value to a maximum of 10
|
|
4122
|
+
* }
|
|
4123
|
+
*
|
|
4124
|
+
* if (nextValue.count === 10) {
|
|
4125
|
+
* nextValue.isMaxValue = true; // Set isMaxValue to true if count is 10
|
|
4126
|
+
* } else {
|
|
4127
|
+
* nextValue.isMaxValue = false; // Reset isMaxValue otherwise
|
|
4128
|
+
* }
|
|
4129
|
+
* });
|
|
4130
|
+
*
|
|
4131
|
+
* store.partialNext({ count: 15 });
|
|
4132
|
+
*
|
|
4133
|
+
* store.getLatestValue(); // { count: 10, isMaxValue: true }
|
|
4134
|
+
*
|
|
4135
|
+
* store.partialNext({ count: 5 });
|
|
4136
|
+
*
|
|
4137
|
+
* store.getLatestValue(); // { count: 5, isMaxValue: false }
|
|
4138
|
+
* ```
|
|
4139
|
+
*
|
|
4140
|
+
* @param preprocessor - The function to be called with the next and previous values before the state is updated.
|
|
4141
|
+
* @returns A `RemovePreprocessor` function that removes the preprocessor when called.
|
|
4142
|
+
*/
|
|
4143
|
+
addPreprocessor(preprocessor) {
|
|
4144
|
+
this.preprocessors.add(preprocessor);
|
|
4145
|
+
return () => {
|
|
4146
|
+
this.preprocessors.delete(preprocessor);
|
|
4147
|
+
};
|
|
4148
|
+
}
|
|
4149
|
+
};
|
|
4150
|
+
var MergedStateStore = class _MergedStateStore extends StateStore {
|
|
4151
|
+
constructor({ original, merged }) {
|
|
4152
|
+
const originalValue = original.getLatestValue();
|
|
4153
|
+
const mergedValue = merged.getLatestValue();
|
|
4154
|
+
super({
|
|
4155
|
+
...originalValue,
|
|
4156
|
+
...mergedValue
|
|
4157
|
+
});
|
|
4158
|
+
// override original methods and "disable" them
|
|
4159
|
+
this.next = () => {
|
|
4160
|
+
console.warn(
|
|
4161
|
+
`${_MergedStateStore.name}.next is disabled, call original.next or merged.next instead`
|
|
4162
|
+
);
|
|
4163
|
+
};
|
|
4164
|
+
this.partialNext = () => {
|
|
4165
|
+
console.warn(
|
|
4166
|
+
`${_MergedStateStore.name}.partialNext is disabled, call original.partialNext or merged.partialNext instead`
|
|
4167
|
+
);
|
|
4168
|
+
};
|
|
4169
|
+
this.cachedOriginalValue = originalValue;
|
|
4170
|
+
this.cachedMergedValue = mergedValue;
|
|
4171
|
+
this.original = original;
|
|
4172
|
+
this.merged = merged;
|
|
4173
|
+
}
|
|
4174
|
+
/**
|
|
4175
|
+
* Subscribes to changes in the merged state store.
|
|
4176
|
+
*
|
|
4177
|
+
* This method extends the base subscribe functionality to handle the merged nature of this store:
|
|
4178
|
+
* 1. The first subscriber triggers registration of helper subscribers that listen to both source stores
|
|
4179
|
+
* 2. Changes from either source store are propagated to this merged store
|
|
4180
|
+
* 3. Source store values are cached to prevent unnecessary updates
|
|
4181
|
+
*
|
|
4182
|
+
* When the first subscriber is added, the method sets up listeners on both original and merged stores.
|
|
4183
|
+
* These listeners update the combined store value whenever either source store changes.
|
|
4184
|
+
* All subscriptions (helpers and the actual handler) are tracked so they can be properly cleaned up.
|
|
4185
|
+
*
|
|
4186
|
+
* @param handler - The callback function that will be executed when the state changes
|
|
4187
|
+
* @returns An unsubscribe function that, when called, removes the subscription and any helper subscriptions
|
|
4188
|
+
*/
|
|
4189
|
+
subscribe(handler) {
|
|
4190
|
+
const unsubscribeFunctions = [];
|
|
4191
|
+
if (!this.handlers.size) {
|
|
4192
|
+
const base = (nextValue) => {
|
|
4193
|
+
super.next((currentValue) => ({
|
|
4194
|
+
...currentValue,
|
|
4195
|
+
...nextValue
|
|
4196
|
+
}));
|
|
4197
|
+
};
|
|
4198
|
+
unsubscribeFunctions.push(
|
|
4199
|
+
this.original.subscribe((nextValue) => {
|
|
4200
|
+
if (nextValue === this.cachedOriginalValue) return;
|
|
4201
|
+
this.cachedOriginalValue = nextValue;
|
|
4202
|
+
base(nextValue);
|
|
4203
|
+
}),
|
|
4204
|
+
this.merged.subscribe((nextValue) => {
|
|
4205
|
+
if (nextValue === this.cachedMergedValue) return;
|
|
4206
|
+
this.cachedMergedValue = nextValue;
|
|
4207
|
+
base(nextValue);
|
|
4208
|
+
})
|
|
4209
|
+
);
|
|
4210
|
+
}
|
|
4211
|
+
unsubscribeFunctions.push(super.subscribe(handler));
|
|
4212
|
+
return () => {
|
|
4213
|
+
unsubscribeFunctions.forEach((unsubscribe) => unsubscribe());
|
|
4214
|
+
};
|
|
4215
|
+
}
|
|
4216
|
+
/**
|
|
4217
|
+
* Retrieves the latest combined state from both original and merged stores.
|
|
4218
|
+
*
|
|
4219
|
+
* This method extends the base getLatestValue functionality to ensure the merged store
|
|
4220
|
+
* remains in sync with its source stores even when there are no active subscribers.
|
|
4221
|
+
*
|
|
4222
|
+
* When there are no handlers registered, the method:
|
|
4223
|
+
* 1. Fetches the latest values from both source stores
|
|
4224
|
+
* 2. Compares them with the cached values to detect changes
|
|
4225
|
+
* 3. If changes are detected, updates the internal value and caches
|
|
4226
|
+
* the new source values to maintain consistency
|
|
4227
|
+
*
|
|
4228
|
+
* This approach ensures that calling getLatestValue() always returns the most
|
|
4229
|
+
* up-to-date combined state, even if the merged store hasn't been actively
|
|
4230
|
+
* receiving updates through subscriptions.
|
|
4231
|
+
*
|
|
4232
|
+
* @returns The latest combined state from both original and merged stores
|
|
4233
|
+
*/
|
|
4234
|
+
getLatestValue() {
|
|
4235
|
+
if (!this.handlers.size) {
|
|
4236
|
+
const originalValue = this.original.getLatestValue();
|
|
4237
|
+
const mergedValue = this.merged.getLatestValue();
|
|
4238
|
+
if (originalValue !== this.cachedOriginalValue || mergedValue !== this.cachedMergedValue) {
|
|
4239
|
+
this.value = {
|
|
4240
|
+
...originalValue,
|
|
4241
|
+
...mergedValue
|
|
4242
|
+
};
|
|
4243
|
+
this.cachedMergedValue = mergedValue;
|
|
4244
|
+
this.cachedOriginalValue = originalValue;
|
|
4245
|
+
}
|
|
4246
|
+
}
|
|
4247
|
+
return super.getLatestValue();
|
|
4248
|
+
}
|
|
4249
|
+
addPreprocessor() {
|
|
4250
|
+
console.warn(
|
|
4251
|
+
`${_MergedStateStore.name}.addPreprocessor is disabled, call original.addPreprocessor or merged.addPreprocessor instead`
|
|
4252
|
+
);
|
|
4253
|
+
return noop2;
|
|
4254
|
+
}
|
|
4088
4255
|
};
|
|
4089
4256
|
|
|
4090
4257
|
// src/utils/mergeWith/mergeWithCore.ts
|
|
@@ -4329,6 +4496,12 @@ function createMergeCore(options = {}) {
|
|
|
4329
4496
|
return false;
|
|
4330
4497
|
}
|
|
4331
4498
|
function createNewTarget(targetValue, srcValue) {
|
|
4499
|
+
if (targetValue === null || typeof targetValue === "undefined") {
|
|
4500
|
+
return srcValue;
|
|
4501
|
+
}
|
|
4502
|
+
if (!Array.isArray(targetValue) && typeof targetValue !== "object") {
|
|
4503
|
+
return srcValue;
|
|
4504
|
+
}
|
|
4332
4505
|
if (targetValue && typeof targetValue === "object") {
|
|
4333
4506
|
const isTargetClassInstance = isClassInstance(targetValue);
|
|
4334
4507
|
const isSourceClassInstance = isClassInstance(srcValue);
|
|
@@ -5498,7 +5671,9 @@ var ErrorFromResponse = class extends Error {
|
|
|
5498
5671
|
return {
|
|
5499
5672
|
message: `(${joinable.join(", ")}) - ${this.message}`,
|
|
5500
5673
|
stack: this.stack,
|
|
5501
|
-
name: this.name
|
|
5674
|
+
name: this.name,
|
|
5675
|
+
code: this.code,
|
|
5676
|
+
status: this.status
|
|
5502
5677
|
};
|
|
5503
5678
|
}
|
|
5504
5679
|
};
|
|
@@ -7631,7 +7806,7 @@ var initState5 = (composition) => {
|
|
|
7631
7806
|
pollId: message.poll_id ?? null
|
|
7632
7807
|
};
|
|
7633
7808
|
};
|
|
7634
|
-
var
|
|
7809
|
+
var noop3 = () => void 0;
|
|
7635
7810
|
var _MessageComposer = class _MessageComposer extends WithSubscriptions {
|
|
7636
7811
|
// todo: mediaRecorder: MediaRecorderController;
|
|
7637
7812
|
constructor({
|
|
@@ -7659,7 +7834,7 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
|
|
|
7659
7834
|
this.initEditingAuditState = (composition) => initEditingAuditState(composition);
|
|
7660
7835
|
this.registerSubscriptions = () => {
|
|
7661
7836
|
if (this.hasSubscriptions) {
|
|
7662
|
-
return
|
|
7837
|
+
return noop3;
|
|
7663
7838
|
}
|
|
7664
7839
|
this.addUnsubscribeFunction(this.subscribeMessageComposerSetupStateChange());
|
|
7665
7840
|
this.addUnsubscribeFunction(this.subscribeMessageUpdated());
|
|
@@ -12912,7 +13087,24 @@ var StreamChat = class _StreamChat {
|
|
|
12912
13087
|
'data_template': 'data handlebars template',
|
|
12913
13088
|
'apn_template': 'apn notification handlebars template under v2'
|
|
12914
13089
|
},
|
|
12915
|
-
'webhook_url': 'https://acme.com/my/awesome/webhook/'
|
|
13090
|
+
'webhook_url': 'https://acme.com/my/awesome/webhook/',
|
|
13091
|
+
'event_hooks': [
|
|
13092
|
+
{
|
|
13093
|
+
'hook_type': 'webhook',
|
|
13094
|
+
'enabled': true,
|
|
13095
|
+
'event_types': ['message.new'],
|
|
13096
|
+
'webhook_url': 'https://acme.com/my/awesome/webhook/'
|
|
13097
|
+
},
|
|
13098
|
+
{
|
|
13099
|
+
'hook_type': 'sqs',
|
|
13100
|
+
'enabled': true,
|
|
13101
|
+
'event_types': ['message.new'],
|
|
13102
|
+
'sqs_url': 'https://sqs.us-east-1.amazonaws.com/1234567890/my-queue',
|
|
13103
|
+
'sqs_auth_type': 'key',
|
|
13104
|
+
'sqs_key': 'my-access-key',
|
|
13105
|
+
'sqs_secret': 'my-secret-key'
|
|
13106
|
+
}
|
|
13107
|
+
]
|
|
12916
13108
|
}
|
|
12917
13109
|
*/
|
|
12918
13110
|
async updateAppSettings(options) {
|
|
@@ -14256,13 +14448,15 @@ var StreamChat = class _StreamChat {
|
|
|
14256
14448
|
} else {
|
|
14257
14449
|
await this.offlineDb.softDeleteMessage({ id: messageID });
|
|
14258
14450
|
}
|
|
14259
|
-
return await this.offlineDb.queueTask(
|
|
14260
|
-
|
|
14261
|
-
|
|
14262
|
-
|
|
14263
|
-
|
|
14451
|
+
return await this.offlineDb.queueTask(
|
|
14452
|
+
{
|
|
14453
|
+
task: {
|
|
14454
|
+
messageId: messageID,
|
|
14455
|
+
payload: [messageID, hardDelete],
|
|
14456
|
+
type: "delete-message"
|
|
14457
|
+
}
|
|
14264
14458
|
}
|
|
14265
|
-
|
|
14459
|
+
);
|
|
14266
14460
|
}
|
|
14267
14461
|
} catch (error) {
|
|
14268
14462
|
this.logger("error", `offlineDb:deleteMessage`, {
|
|
@@ -14416,7 +14610,7 @@ var StreamChat = class _StreamChat {
|
|
|
14416
14610
|
if (this.userAgent) {
|
|
14417
14611
|
return this.userAgent;
|
|
14418
14612
|
}
|
|
14419
|
-
const version = "9.
|
|
14613
|
+
const version = "9.4.0";
|
|
14420
14614
|
const clientBundle = "browser-cjs";
|
|
14421
14615
|
let userAgentString = "";
|
|
14422
14616
|
if (this.sdkIdentifier) {
|
|
@@ -15592,6 +15786,26 @@ var BuiltinPermissions = {
|
|
|
15592
15786
|
UseFrozenChannel: "Send messages and reactions to frozen channels"
|
|
15593
15787
|
};
|
|
15594
15788
|
|
|
15789
|
+
// src/offline-support/types.ts
|
|
15790
|
+
var OfflineError = class extends Error {
|
|
15791
|
+
constructor(message, {
|
|
15792
|
+
type
|
|
15793
|
+
}) {
|
|
15794
|
+
super(message);
|
|
15795
|
+
this.name = "OfflineError";
|
|
15796
|
+
this.type = type;
|
|
15797
|
+
}
|
|
15798
|
+
// Vitest helper (serialized errors are too large to read)
|
|
15799
|
+
// https://github.com/vitest-dev/vitest/blob/v3.1.3/packages/utils/src/error.ts#L60-L62
|
|
15800
|
+
toJSON() {
|
|
15801
|
+
return {
|
|
15802
|
+
message: `${this.type} - ${this.message}`,
|
|
15803
|
+
stack: this.stack,
|
|
15804
|
+
name: this.name
|
|
15805
|
+
};
|
|
15806
|
+
}
|
|
15807
|
+
};
|
|
15808
|
+
|
|
15595
15809
|
// src/offline-support/offline_sync_manager.ts
|
|
15596
15810
|
var OfflineDBSyncManager = class {
|
|
15597
15811
|
constructor({
|
|
@@ -16197,20 +16411,23 @@ var AbstractOfflineDB = class {
|
|
|
16197
16411
|
* @param task - the pending task we want to execute
|
|
16198
16412
|
*/
|
|
16199
16413
|
this.queueTask = async ({ task }) => {
|
|
16200
|
-
|
|
16201
|
-
try {
|
|
16414
|
+
const attemptTaskExecution = async () => {
|
|
16202
16415
|
if (!this.client.wsConnection?.isHealthy) {
|
|
16203
|
-
|
|
16204
|
-
|
|
16416
|
+
throw new OfflineError(
|
|
16417
|
+
"Cannot execute task because the connection has been lost.",
|
|
16418
|
+
{ type: "connection:lost" }
|
|
16419
|
+
);
|
|
16205
16420
|
}
|
|
16206
|
-
|
|
16421
|
+
return await this.executeTask({ task });
|
|
16422
|
+
};
|
|
16423
|
+
try {
|
|
16424
|
+
return await attemptTaskExecution();
|
|
16207
16425
|
} catch (e) {
|
|
16208
16426
|
if (!this.shouldSkipQueueingTask(e)) {
|
|
16209
16427
|
await this.addPendingTask(task);
|
|
16210
|
-
throw e;
|
|
16211
16428
|
}
|
|
16429
|
+
throw e;
|
|
16212
16430
|
}
|
|
16213
|
-
return response;
|
|
16214
16431
|
};
|
|
16215
16432
|
/**
|
|
16216
16433
|
* A utility method that determines if a failed task should be added to the
|