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/esm/index.js CHANGED
@@ -3900,26 +3900,14 @@ var ensureIsLocalAttachment = (attachment) => {
3900
3900
 
3901
3901
  // src/store.ts
3902
3902
  var isPatch = (value) => typeof value === "function";
3903
+ var noop2 = () => {
3904
+ };
3903
3905
  var StateStore = class {
3904
3906
  constructor(value) {
3905
3907
  this.value = value;
3906
- this.handlerSet = /* @__PURE__ */ new Set();
3907
- this.next = (newValueOrPatch) => {
3908
- const newValue = isPatch(newValueOrPatch) ? newValueOrPatch(this.value) : newValueOrPatch;
3909
- if (newValue === this.value) return;
3910
- const oldValue = this.value;
3911
- this.value = newValue;
3912
- this.handlerSet.forEach((handler) => handler(this.value, oldValue));
3913
- };
3908
+ this.handlers = /* @__PURE__ */ new Set();
3909
+ this.preprocessors = /* @__PURE__ */ new Set();
3914
3910
  this.partialNext = (partial) => this.next((current) => ({ ...current, ...partial }));
3915
- this.getLatestValue = () => this.value;
3916
- this.subscribe = (handler) => {
3917
- handler(this.value, void 0);
3918
- this.handlerSet.add(handler);
3919
- return () => {
3920
- this.handlerSet.delete(handler);
3921
- };
3922
- };
3923
3911
  this.subscribeWithSelector = (selector, handler) => {
3924
3912
  let previouslySelectedValues;
3925
3913
  const wrappedHandler = (nextValue) => {
@@ -3938,6 +3926,183 @@ var StateStore = class {
3938
3926
  return this.subscribe(wrappedHandler);
3939
3927
  };
3940
3928
  }
3929
+ /**
3930
+ * Allows merging two stores only if their keys differ otherwise there's no way to ensure the data type stability.
3931
+ * @experimental
3932
+ * This method is experimental and may change in future versions.
3933
+ */
3934
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
3935
+ merge(stateStore) {
3936
+ return new MergedStateStore({
3937
+ original: this,
3938
+ merged: stateStore
3939
+ });
3940
+ }
3941
+ next(newValueOrPatch) {
3942
+ const newValue = isPatch(newValueOrPatch) ? newValueOrPatch(this.value) : newValueOrPatch;
3943
+ if (newValue === this.value) return;
3944
+ this.preprocessors.forEach((preprocessor) => preprocessor(newValue, this.value));
3945
+ const oldValue = this.value;
3946
+ this.value = newValue;
3947
+ this.handlers.forEach((handler) => handler(this.value, oldValue));
3948
+ }
3949
+ getLatestValue() {
3950
+ return this.value;
3951
+ }
3952
+ subscribe(handler) {
3953
+ handler(this.value, void 0);
3954
+ this.handlers.add(handler);
3955
+ return () => {
3956
+ this.handlers.delete(handler);
3957
+ };
3958
+ }
3959
+ /**
3960
+ * Registers a preprocessor function that will be called before the state is updated.
3961
+ *
3962
+ * Preprocessors are invoked with the new and previous values whenever `next` or `partialNext` methods
3963
+ * are called, allowing you to mutate or react to the new value before it is set. Preprocessors run in the
3964
+ * order they were registered.
3965
+ *
3966
+ * @example
3967
+ * ```ts
3968
+ * const store = new StateStore<{ count: number; isMaxValue: bool; }>({ count: 0, isMaxValue: false });
3969
+ *
3970
+ * store.addPreprocessor((nextValue, prevValue) => {
3971
+ * if (nextValue.count > 10) {
3972
+ * nextValue.count = 10; // Clamp the value to a maximum of 10
3973
+ * }
3974
+ *
3975
+ * if (nextValue.count === 10) {
3976
+ * nextValue.isMaxValue = true; // Set isMaxValue to true if count is 10
3977
+ * } else {
3978
+ * nextValue.isMaxValue = false; // Reset isMaxValue otherwise
3979
+ * }
3980
+ * });
3981
+ *
3982
+ * store.partialNext({ count: 15 });
3983
+ *
3984
+ * store.getLatestValue(); // { count: 10, isMaxValue: true }
3985
+ *
3986
+ * store.partialNext({ count: 5 });
3987
+ *
3988
+ * store.getLatestValue(); // { count: 5, isMaxValue: false }
3989
+ * ```
3990
+ *
3991
+ * @param preprocessor - The function to be called with the next and previous values before the state is updated.
3992
+ * @returns A `RemovePreprocessor` function that removes the preprocessor when called.
3993
+ */
3994
+ addPreprocessor(preprocessor) {
3995
+ this.preprocessors.add(preprocessor);
3996
+ return () => {
3997
+ this.preprocessors.delete(preprocessor);
3998
+ };
3999
+ }
4000
+ };
4001
+ var MergedStateStore = class _MergedStateStore extends StateStore {
4002
+ constructor({ original, merged }) {
4003
+ const originalValue = original.getLatestValue();
4004
+ const mergedValue = merged.getLatestValue();
4005
+ super({
4006
+ ...originalValue,
4007
+ ...mergedValue
4008
+ });
4009
+ // override original methods and "disable" them
4010
+ this.next = () => {
4011
+ console.warn(
4012
+ `${_MergedStateStore.name}.next is disabled, call original.next or merged.next instead`
4013
+ );
4014
+ };
4015
+ this.partialNext = () => {
4016
+ console.warn(
4017
+ `${_MergedStateStore.name}.partialNext is disabled, call original.partialNext or merged.partialNext instead`
4018
+ );
4019
+ };
4020
+ this.cachedOriginalValue = originalValue;
4021
+ this.cachedMergedValue = mergedValue;
4022
+ this.original = original;
4023
+ this.merged = merged;
4024
+ }
4025
+ /**
4026
+ * Subscribes to changes in the merged state store.
4027
+ *
4028
+ * This method extends the base subscribe functionality to handle the merged nature of this store:
4029
+ * 1. The first subscriber triggers registration of helper subscribers that listen to both source stores
4030
+ * 2. Changes from either source store are propagated to this merged store
4031
+ * 3. Source store values are cached to prevent unnecessary updates
4032
+ *
4033
+ * When the first subscriber is added, the method sets up listeners on both original and merged stores.
4034
+ * These listeners update the combined store value whenever either source store changes.
4035
+ * All subscriptions (helpers and the actual handler) are tracked so they can be properly cleaned up.
4036
+ *
4037
+ * @param handler - The callback function that will be executed when the state changes
4038
+ * @returns An unsubscribe function that, when called, removes the subscription and any helper subscriptions
4039
+ */
4040
+ subscribe(handler) {
4041
+ const unsubscribeFunctions = [];
4042
+ if (!this.handlers.size) {
4043
+ const base = (nextValue) => {
4044
+ super.next((currentValue) => ({
4045
+ ...currentValue,
4046
+ ...nextValue
4047
+ }));
4048
+ };
4049
+ unsubscribeFunctions.push(
4050
+ this.original.subscribe((nextValue) => {
4051
+ if (nextValue === this.cachedOriginalValue) return;
4052
+ this.cachedOriginalValue = nextValue;
4053
+ base(nextValue);
4054
+ }),
4055
+ this.merged.subscribe((nextValue) => {
4056
+ if (nextValue === this.cachedMergedValue) return;
4057
+ this.cachedMergedValue = nextValue;
4058
+ base(nextValue);
4059
+ })
4060
+ );
4061
+ }
4062
+ unsubscribeFunctions.push(super.subscribe(handler));
4063
+ return () => {
4064
+ unsubscribeFunctions.forEach((unsubscribe) => unsubscribe());
4065
+ };
4066
+ }
4067
+ /**
4068
+ * Retrieves the latest combined state from both original and merged stores.
4069
+ *
4070
+ * This method extends the base getLatestValue functionality to ensure the merged store
4071
+ * remains in sync with its source stores even when there are no active subscribers.
4072
+ *
4073
+ * When there are no handlers registered, the method:
4074
+ * 1. Fetches the latest values from both source stores
4075
+ * 2. Compares them with the cached values to detect changes
4076
+ * 3. If changes are detected, updates the internal value and caches
4077
+ * the new source values to maintain consistency
4078
+ *
4079
+ * This approach ensures that calling getLatestValue() always returns the most
4080
+ * up-to-date combined state, even if the merged store hasn't been actively
4081
+ * receiving updates through subscriptions.
4082
+ *
4083
+ * @returns The latest combined state from both original and merged stores
4084
+ */
4085
+ getLatestValue() {
4086
+ if (!this.handlers.size) {
4087
+ const originalValue = this.original.getLatestValue();
4088
+ const mergedValue = this.merged.getLatestValue();
4089
+ if (originalValue !== this.cachedOriginalValue || mergedValue !== this.cachedMergedValue) {
4090
+ this.value = {
4091
+ ...originalValue,
4092
+ ...mergedValue
4093
+ };
4094
+ this.cachedMergedValue = mergedValue;
4095
+ this.cachedOriginalValue = originalValue;
4096
+ }
4097
+ }
4098
+ return super.getLatestValue();
4099
+ }
4100
+ addPreprocessor() {
4101
+ console.warn(
4102
+ `${_MergedStateStore.name}.addPreprocessor is disabled, call original.addPreprocessor or merged.addPreprocessor instead`
4103
+ );
4104
+ return noop2;
4105
+ }
3941
4106
  };
3942
4107
 
3943
4108
  // src/utils/mergeWith/mergeWithCore.ts
@@ -4182,6 +4347,12 @@ function createMergeCore(options = {}) {
4182
4347
  return false;
4183
4348
  }
4184
4349
  function createNewTarget(targetValue, srcValue) {
4350
+ if (targetValue === null || typeof targetValue === "undefined") {
4351
+ return srcValue;
4352
+ }
4353
+ if (!Array.isArray(targetValue) && typeof targetValue !== "object") {
4354
+ return srcValue;
4355
+ }
4185
4356
  if (targetValue && typeof targetValue === "object") {
4186
4357
  const isTargetClassInstance = isClassInstance(targetValue);
4187
4358
  const isSourceClassInstance = isClassInstance(srcValue);
@@ -5283,8 +5454,8 @@ function decodeTlds(encoded) {
5283
5454
  var defaults2 = {
5284
5455
  defaultProtocol: "http",
5285
5456
  events: null,
5286
- format: noop2,
5287
- formatHref: noop2,
5457
+ format: noop3,
5458
+ formatHref: noop3,
5288
5459
  nl2br: false,
5289
5460
  tagName: "a",
5290
5461
  target: null,
@@ -5389,7 +5560,7 @@ Options.prototype = {
5389
5560
  return renderFn(ir, token.t, token);
5390
5561
  }
5391
5562
  };
5392
- function noop2(val) {
5563
+ function noop3(val) {
5393
5564
  return val;
5394
5565
  }
5395
5566
  function MultiToken(value, tokens) {
@@ -6480,7 +6651,9 @@ var ErrorFromResponse = class extends Error {
6480
6651
  return {
6481
6652
  message: `(${joinable.join(", ")}) - ${this.message}`,
6482
6653
  stack: this.stack,
6483
- name: this.name
6654
+ name: this.name,
6655
+ code: this.code,
6656
+ status: this.status
6484
6657
  };
6485
6658
  }
6486
6659
  };
@@ -8613,7 +8786,7 @@ var initState5 = (composition) => {
8613
8786
  pollId: message.poll_id ?? null
8614
8787
  };
8615
8788
  };
8616
- var noop3 = () => void 0;
8789
+ var noop4 = () => void 0;
8617
8790
  var _MessageComposer = class _MessageComposer extends WithSubscriptions {
8618
8791
  // todo: mediaRecorder: MediaRecorderController;
8619
8792
  constructor({
@@ -8641,7 +8814,7 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
8641
8814
  this.initEditingAuditState = (composition) => initEditingAuditState(composition);
8642
8815
  this.registerSubscriptions = () => {
8643
8816
  if (this.hasSubscriptions) {
8644
- return noop3;
8817
+ return noop4;
8645
8818
  }
8646
8819
  this.addUnsubscribeFunction(this.subscribeMessageComposerSetupStateChange());
8647
8820
  this.addUnsubscribeFunction(this.subscribeMessageUpdated());
@@ -13894,7 +14067,24 @@ var StreamChat = class _StreamChat {
13894
14067
  'data_template': 'data handlebars template',
13895
14068
  'apn_template': 'apn notification handlebars template under v2'
13896
14069
  },
13897
- 'webhook_url': 'https://acme.com/my/awesome/webhook/'
14070
+ 'webhook_url': 'https://acme.com/my/awesome/webhook/',
14071
+ 'event_hooks': [
14072
+ {
14073
+ 'hook_type': 'webhook',
14074
+ 'enabled': true,
14075
+ 'event_types': ['message.new'],
14076
+ 'webhook_url': 'https://acme.com/my/awesome/webhook/'
14077
+ },
14078
+ {
14079
+ 'hook_type': 'sqs',
14080
+ 'enabled': true,
14081
+ 'event_types': ['message.new'],
14082
+ 'sqs_url': 'https://sqs.us-east-1.amazonaws.com/1234567890/my-queue',
14083
+ 'sqs_auth_type': 'key',
14084
+ 'sqs_key': 'my-access-key',
14085
+ 'sqs_secret': 'my-secret-key'
14086
+ }
14087
+ ]
13898
14088
  }
13899
14089
  */
13900
14090
  async updateAppSettings(options) {
@@ -15238,13 +15428,15 @@ var StreamChat = class _StreamChat {
15238
15428
  } else {
15239
15429
  await this.offlineDb.softDeleteMessage({ id: messageID });
15240
15430
  }
15241
- return await this.offlineDb.queueTask({
15242
- task: {
15243
- messageId: messageID,
15244
- payload: [messageID, hardDelete],
15245
- type: "delete-message"
15431
+ return await this.offlineDb.queueTask(
15432
+ {
15433
+ task: {
15434
+ messageId: messageID,
15435
+ payload: [messageID, hardDelete],
15436
+ type: "delete-message"
15437
+ }
15246
15438
  }
15247
- });
15439
+ );
15248
15440
  }
15249
15441
  } catch (error) {
15250
15442
  this.logger("error", `offlineDb:deleteMessage`, {
@@ -15398,7 +15590,7 @@ var StreamChat = class _StreamChat {
15398
15590
  if (this.userAgent) {
15399
15591
  return this.userAgent;
15400
15592
  }
15401
- const version = "9.2.0";
15593
+ const version = "9.4.0";
15402
15594
  const clientBundle = "browser-esm";
15403
15595
  let userAgentString = "";
15404
15596
  if (this.sdkIdentifier) {
@@ -16574,6 +16766,26 @@ var BuiltinPermissions = {
16574
16766
  UseFrozenChannel: "Send messages and reactions to frozen channels"
16575
16767
  };
16576
16768
 
16769
+ // src/offline-support/types.ts
16770
+ var OfflineError = class extends Error {
16771
+ constructor(message, {
16772
+ type
16773
+ }) {
16774
+ super(message);
16775
+ this.name = "OfflineError";
16776
+ this.type = type;
16777
+ }
16778
+ // Vitest helper (serialized errors are too large to read)
16779
+ // https://github.com/vitest-dev/vitest/blob/v3.1.3/packages/utils/src/error.ts#L60-L62
16780
+ toJSON() {
16781
+ return {
16782
+ message: `${this.type} - ${this.message}`,
16783
+ stack: this.stack,
16784
+ name: this.name
16785
+ };
16786
+ }
16787
+ };
16788
+
16577
16789
  // src/offline-support/offline_sync_manager.ts
16578
16790
  var OfflineDBSyncManager = class {
16579
16791
  constructor({
@@ -17179,20 +17391,23 @@ var AbstractOfflineDB = class {
17179
17391
  * @param task - the pending task we want to execute
17180
17392
  */
17181
17393
  this.queueTask = async ({ task }) => {
17182
- let response;
17183
- try {
17394
+ const attemptTaskExecution = async () => {
17184
17395
  if (!this.client.wsConnection?.isHealthy) {
17185
- await this.addPendingTask(task);
17186
- return;
17396
+ throw new OfflineError(
17397
+ "Cannot execute task because the connection has been lost.",
17398
+ { type: "connection:lost" }
17399
+ );
17187
17400
  }
17188
- response = await this.executeTask({ task });
17401
+ return await this.executeTask({ task });
17402
+ };
17403
+ try {
17404
+ return await attemptTaskExecution();
17189
17405
  } catch (e) {
17190
17406
  if (!this.shouldSkipQueueingTask(e)) {
17191
17407
  await this.addPendingTask(task);
17192
- throw e;
17193
17408
  }
17409
+ throw e;
17194
17410
  }
17195
- return response;
17196
17411
  };
17197
17412
  /**
17198
17413
  * A utility method that determines if a failed task should be added to the
@@ -17385,6 +17600,7 @@ export {
17385
17600
  MODERATION_ENTITY_TYPES,
17386
17601
  MaxPriority,
17387
17602
  MentionsSearchSource,
17603
+ MergedStateStore,
17388
17604
  MessageComposer,
17389
17605
  MessageComposerMiddlewareExecutor,
17390
17606
  MessageDraftComposerMiddlewareExecutor,
@@ -17393,6 +17609,7 @@ export {
17393
17609
  MinPriority,
17394
17610
  Moderation,
17395
17611
  OfflineDBSyncManager,
17612
+ OfflineError,
17396
17613
  Permission,
17397
17614
  Poll,
17398
17615
  PollComposer,