stream-chat 9.50.2 → 9.51.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.
Files changed (33) hide show
  1. package/dist/cjs/index.browser.js +252 -94
  2. package/dist/cjs/index.browser.js.map +3 -3
  3. package/dist/cjs/index.node.js +252 -94
  4. package/dist/cjs/index.node.js.map +3 -3
  5. package/dist/esm/index.mjs +252 -94
  6. package/dist/esm/index.mjs.map +3 -3
  7. package/dist/types/channel.d.ts +3 -2
  8. package/dist/types/client.d.ts +13 -9
  9. package/dist/types/messageComposer/middleware/textComposer/mentions.d.ts +15 -9
  10. package/dist/types/offline-support/offline_support_api.d.ts +7 -1
  11. package/dist/types/offline-support/offline_sync_manager.d.ts +19 -1
  12. package/dist/types/search/BaseSearchSource.d.ts +42 -24
  13. package/dist/types/search/ChannelMemberSearchSource.d.ts +9 -4
  14. package/dist/types/search/ChannelSearchSource.d.ts +9 -3
  15. package/dist/types/search/MessageSearchSource.d.ts +14 -3
  16. package/dist/types/search/UserSearchSource.d.ts +9 -3
  17. package/dist/types/search/types.d.ts +14 -1
  18. package/dist/types/types.d.ts +20 -1
  19. package/dist/types/utils.d.ts +1 -1
  20. package/package.json +7 -7
  21. package/src/channel.ts +4 -0
  22. package/src/client.ts +54 -12
  23. package/src/messageComposer/middleware/textComposer/mentions.ts +75 -23
  24. package/src/offline-support/offline_support_api.ts +17 -2
  25. package/src/offline-support/offline_sync_manager.ts +73 -14
  26. package/src/search/BaseSearchSource.ts +123 -39
  27. package/src/search/ChannelMemberSearchSource.ts +22 -11
  28. package/src/search/ChannelSearchSource.ts +16 -5
  29. package/src/search/MessageSearchSource.ts +36 -4
  30. package/src/search/UserSearchSource.ts +16 -5
  31. package/src/search/types.ts +14 -1
  32. package/src/types.ts +21 -1
  33. package/src/utils.ts +6 -3
@@ -967,7 +967,8 @@ var debounce = (fn, timeout = 0, { leading = false, trailing = true } = {}) => {
967
967
  }
968
968
  runningTimeout = null;
969
969
  };
970
- runningTimeout = setTimeout(timeoutHandler, timeout);
970
+ const delay = typeof timeout === "function" ? timeout(...args) : timeout;
971
+ runningTimeout = setTimeout(timeoutHandler, delay);
971
972
  return lastResult;
972
973
  };
973
974
  debouncedFn.cancel = () => {
@@ -4405,13 +4406,41 @@ function isErrorResponse(res) {
4405
4406
 
4406
4407
  // src/search/BaseSearchSource.ts
4407
4408
  var DEFAULT_SEARCH_SOURCE_OPTIONS = {
4408
- debounceMs: 300,
4409
4409
  pageSize: 10,
4410
4410
  allowEmptySearchString: false,
4411
4411
  resetOnNewSearchQuery: true
4412
4412
  };
4413
+ var resolveDebounceOptions = ({
4414
+ debounceMs,
4415
+ shortQueryDebounceMs,
4416
+ longQueryDebounceMs,
4417
+ shortQueryMaxLength
4418
+ }) => ({
4419
+ shortQueryDebounceMs: shortQueryDebounceMs ?? debounceMs ?? 500,
4420
+ longQueryDebounceMs: longQueryDebounceMs ?? debounceMs ?? 300,
4421
+ shortQueryMaxLength: shortQueryMaxLength ?? 2
4422
+ });
4413
4423
  var BaseSearchSourceBase = class {
4414
4424
  constructor(options) {
4425
+ this.setDebounceOptions = (options = {}) => {
4426
+ const resolved = resolveDebounceOptions(options);
4427
+ this.shortQueryDebounceMs = resolved.shortQueryDebounceMs;
4428
+ this.longQueryDebounceMs = resolved.longQueryDebounceMs;
4429
+ this.shortQueryMaxLength = resolved.shortQueryMaxLength;
4430
+ this.searchDebounced = debounce(
4431
+ this.executeQuery.bind(this),
4432
+ (searchString) => this.getDebounceMs(searchString)
4433
+ );
4434
+ };
4435
+ /**
4436
+ * Short queries match too much and are slow server-side, so they get a longer debounce.
4437
+ * Resolved per call, which is what lets the interval switch as the user keeps typing.
4438
+ */
4439
+ this.getDebounceMs = (searchString) => {
4440
+ const { length } = searchString ?? this.searchQuery;
4441
+ return length <= this.shortQueryMaxLength ? this.shortQueryDebounceMs : this.longQueryDebounceMs;
4442
+ };
4443
+ this.search = (searchQuery) => this.searchDebounced(searchQuery);
4415
4444
  this.activate = () => {
4416
4445
  if (this.isActive) return;
4417
4446
  this.state.partialNext({ isActive: true });
@@ -4423,7 +4452,7 @@ var BaseSearchSourceBase = class {
4423
4452
  this.canExecuteQuery = (newSearchString) => {
4424
4453
  const hasNewSearchQuery = typeof newSearchString !== "undefined";
4425
4454
  const searchString = newSearchString ?? this.searchQuery;
4426
- return !!(this.isActive && !this.isLoading && (this.hasNext || hasNewSearchQuery) && (this.allowEmptySearchString || searchString));
4455
+ return !!(this.isActive && this.canDispatchQuery(hasNewSearchQuery) && (this.allowEmptySearchString || searchString));
4427
4456
  };
4428
4457
  const { pageSize, allowEmptySearchString, resetOnNewSearchQuery } = {
4429
4458
  ...DEFAULT_SEARCH_SOURCE_OPTIONS,
@@ -4433,6 +4462,14 @@ var BaseSearchSourceBase = class {
4433
4462
  this.allowEmptySearchString = allowEmptySearchString;
4434
4463
  this.resetOnNewSearchQuery = resetOnNewSearchQuery;
4435
4464
  this.state = new StateStore(this.initialState);
4465
+ this.setDebounceOptions(options ?? {});
4466
+ }
4467
+ /**
4468
+ * A new search query preempts an in-flight one; pagination waits for it to finish
4469
+ * and needs a next page to fetch.
4470
+ */
4471
+ canDispatchQuery(hasNewSearchQuery) {
4472
+ return hasNewSearchQuery || !this.isLoading && this.hasNext;
4436
4473
  }
4437
4474
  get lastQueryError() {
4438
4475
  return this.state.getLatestValue().lastQueryError;
@@ -4527,21 +4564,30 @@ var BaseSearchSourceBase = class {
4527
4564
  }
4528
4565
  };
4529
4566
  var BaseSearchSource = class extends BaseSearchSourceBase {
4530
- constructor(options) {
4531
- const { debounceMs } = { ...DEFAULT_SEARCH_SOURCE_OPTIONS, ...options };
4532
- super(options);
4533
- this.setDebounceOptions = ({ debounceMs }) => {
4534
- this.searchDebounced = debounce(this.executeQuery.bind(this), debounceMs);
4535
- };
4536
- this.search = (searchQuery) => this.searchDebounced(searchQuery);
4537
- this.setDebounceOptions({ debounceMs });
4567
+ constructor() {
4568
+ super(...arguments);
4569
+ /** Aborts the in-flight request, if any, once a newer query is dispatched. */
4570
+ this.queryAbortController = null;
4571
+ }
4572
+ /** Aborts the in-flight request, if any. Returns true when one was aborted. */
4573
+ abortInFlightQuery() {
4574
+ if (!this.queryAbortController) return false;
4575
+ this.queryAbortController.abort();
4576
+ this.queryAbortController = null;
4577
+ return true;
4578
+ }
4579
+ resetState() {
4580
+ this.abortInFlightQuery();
4581
+ super.resetState();
4538
4582
  }
4539
4583
  async executeQuery(newSearchString) {
4540
4584
  if (!this.canExecuteQuery(newSearchString)) return;
4585
+ this.abortInFlightQuery();
4586
+ const { signal } = this.queryAbortController = new AbortController();
4541
4587
  const { hasNewSearchQuery, searchString } = this.prepareStateForQuery(newSearchString);
4542
4588
  let stateUpdate = {};
4543
4589
  try {
4544
- const results = await this.query(searchString);
4590
+ const results = await this.query(searchString, { signal });
4545
4591
  if (!results) return;
4546
4592
  const { items } = results;
4547
4593
  stateUpdate = this.updatePaginationStateFromQuery(results);
@@ -4552,23 +4598,19 @@ var BaseSearchSource = class extends BaseSearchSourceBase {
4552
4598
  stateUpdate.hasNext = false;
4553
4599
  }
4554
4600
  } finally {
4555
- this.state.next(this.getStateAfterQuery(stateUpdate, hasNewSearchQuery));
4601
+ if (!signal.aborted) {
4602
+ this.state.next(this.getStateAfterQuery(stateUpdate, hasNewSearchQuery));
4603
+ }
4556
4604
  }
4557
4605
  }
4558
4606
  cancelScheduledQuery() {
4559
4607
  this.searchDebounced.cancel();
4608
+ if (this.abortInFlightQuery() && this.isLoading) {
4609
+ this.state.partialNext({ isLoading: false });
4610
+ }
4560
4611
  }
4561
4612
  };
4562
4613
  var BaseSearchSourceSync = class extends BaseSearchSourceBase {
4563
- constructor(options) {
4564
- const { debounceMs } = { ...DEFAULT_SEARCH_SOURCE_OPTIONS, ...options };
4565
- super(options);
4566
- this.setDebounceOptions = ({ debounceMs }) => {
4567
- this.searchDebounced = debounce(this.executeQuery.bind(this), debounceMs);
4568
- };
4569
- this.search = (searchQuery) => this.searchDebounced(searchQuery);
4570
- this.setDebounceOptions({ debounceMs });
4571
- }
4572
4614
  executeQuery(newSearchString) {
4573
4615
  if (!this.canExecuteQuery(newSearchString)) return;
4574
4616
  const { hasNewSearchQuery, searchString } = this.prepareStateForQuery(newSearchString);
@@ -4864,13 +4906,13 @@ var UserGroupPaginator = class extends BasePaginator {
4864
4906
  // src/search/ChannelMemberSearchSource.ts
4865
4907
  var ChannelMemberSearchSource = class extends BaseSearchSource {
4866
4908
  constructor(channel, options, filterBuilderOptions = {}) {
4867
- super(options);
4909
+ const { filters, sort, searchOptions, ...restOptions } = options || {};
4910
+ super({ ...restOptions, allowEmptySearchString: true });
4868
4911
  this.type = "members";
4869
- this.canExecuteQuery = (newSearchString) => {
4870
- const hasNewSearchQuery = typeof newSearchString !== "undefined";
4871
- return this.isActive && !this.isLoading && (this.hasNext || hasNewSearchQuery);
4872
- };
4873
4912
  this.channel = channel;
4913
+ this.filters = filters;
4914
+ this.sort = sort;
4915
+ this.searchOptions = searchOptions;
4874
4916
  this.filterBuilder = new FilterBuilder({
4875
4917
  initialFilterConfig: {
4876
4918
  default: {
@@ -4886,7 +4928,7 @@ var ChannelMemberSearchSource = class extends BaseSearchSource {
4886
4928
  ...filterBuilderOptions
4887
4929
  });
4888
4930
  }
4889
- async query(searchQuery) {
4931
+ async query(searchQuery, queryOptions = {}) {
4890
4932
  const filters = this.filterBuilder.buildFilters({
4891
4933
  baseFilters: this.filters,
4892
4934
  context: {
@@ -4895,7 +4937,12 @@ var ChannelMemberSearchSource = class extends BaseSearchSource {
4895
4937
  });
4896
4938
  const sort = this.sort ?? [];
4897
4939
  const options = { ...this.searchOptions, limit: this.pageSize, offset: this.offset };
4898
- const { members } = await this.channel.queryMembers(filters ?? {}, sort, options);
4940
+ const { members } = await this.channel.queryMembers(
4941
+ filters ?? {},
4942
+ sort,
4943
+ options,
4944
+ queryOptions
4945
+ );
4899
4946
  return { items: members };
4900
4947
  }
4901
4948
  filterQueryResults(items) {
@@ -5010,9 +5057,13 @@ var SearchController = class {
5010
5057
  // src/search/UserSearchSource.ts
5011
5058
  var UserSearchSource = class extends BaseSearchSource {
5012
5059
  constructor(client, options, filterBuilderOptions = {}) {
5013
- super(options);
5060
+ const { filters, sort, searchOptions, ...restOptions } = options || {};
5061
+ super(restOptions);
5014
5062
  this.type = "users";
5015
5063
  this.client = client;
5064
+ this.filters = filters;
5065
+ this.sort = sort;
5066
+ this.searchOptions = searchOptions;
5016
5067
  this.filterBuilder = new FilterBuilder({
5017
5068
  initialFilterConfig: {
5018
5069
  $or: {
@@ -5028,7 +5079,7 @@ var UserSearchSource = class extends BaseSearchSource {
5028
5079
  ...filterBuilderOptions
5029
5080
  });
5030
5081
  }
5031
- async query(searchQuery) {
5082
+ async query(searchQuery, queryOptions = {}) {
5032
5083
  const filters = this.filterBuilder.buildFilters({
5033
5084
  baseFilters: this.filters,
5034
5085
  context: { searchQuery }
@@ -5041,7 +5092,7 @@ var UserSearchSource = class extends BaseSearchSource {
5041
5092
  sort = { id: 1, ...this.sort };
5042
5093
  }
5043
5094
  const options = { ...this.searchOptions, limit: this.pageSize, offset: this.offset };
5044
- const { users } = await this.client.queryUsers(filters, sort, options);
5095
+ const { users } = await this.client.queryUsers(filters, sort, options, queryOptions);
5045
5096
  return { items: users };
5046
5097
  }
5047
5098
  filterQueryResults(items) {
@@ -5052,9 +5103,13 @@ var UserSearchSource = class extends BaseSearchSource {
5052
5103
  // src/search/ChannelSearchSource.ts
5053
5104
  var ChannelSearchSource = class extends BaseSearchSource {
5054
5105
  constructor(client, options, filterBuilderOptions = {}) {
5055
- super(options);
5106
+ const { filters, sort, searchOptions, ...restOptions } = options || {};
5107
+ super(restOptions);
5056
5108
  this.type = "channels";
5057
5109
  this.client = client;
5110
+ this.filters = filters;
5111
+ this.sort = sort;
5112
+ this.searchOptions = searchOptions;
5058
5113
  this.filterBuilder = new FilterBuilder({
5059
5114
  ...filterBuilderOptions,
5060
5115
  initialFilterConfig: {
@@ -5066,7 +5121,7 @@ var ChannelSearchSource = class extends BaseSearchSource {
5066
5121
  }
5067
5122
  });
5068
5123
  }
5069
- async query(searchQuery) {
5124
+ async query(searchQuery, queryOptions = {}) {
5070
5125
  const filters = this.filterBuilder.buildFilters({
5071
5126
  baseFilters: {
5072
5127
  ...this.client.userID ? { members: { $in: [this.client.userID] } } : {},
@@ -5076,7 +5131,7 @@ var ChannelSearchSource = class extends BaseSearchSource {
5076
5131
  });
5077
5132
  const sort = this.sort ?? {};
5078
5133
  const options = { ...this.searchOptions, limit: this.pageSize, offset: this.offset };
5079
- const items = await this.client.queryChannels(filters, sort, options);
5134
+ const items = await this.client.queryChannels(filters, sort, options, queryOptions);
5080
5135
  return { items };
5081
5136
  }
5082
5137
  filterQueryResults(items) {
@@ -5087,9 +5142,24 @@ var ChannelSearchSource = class extends BaseSearchSource {
5087
5142
  // src/search/MessageSearchSource.ts
5088
5143
  var MessageSearchSource = class extends BaseSearchSource {
5089
5144
  constructor(client, options, filterBuilderOptions) {
5090
- super(options);
5145
+ const {
5146
+ messageSearchChannelFilters,
5147
+ messageSearchFilters,
5148
+ messageSearchSort,
5149
+ channelQueryFilters,
5150
+ channelQuerySort,
5151
+ channelQueryOptions,
5152
+ ...restOptions
5153
+ } = options || {};
5154
+ super(restOptions);
5091
5155
  this.type = "messages";
5092
5156
  this.client = client;
5157
+ this.messageSearchChannelFilters = messageSearchChannelFilters;
5158
+ this.messageSearchFilters = messageSearchFilters;
5159
+ this.messageSearchSort = messageSearchSort;
5160
+ this.channelQueryFilters = channelQueryFilters;
5161
+ this.channelQuerySort = channelQuerySort;
5162
+ this.channelQueryOptions = channelQueryOptions;
5093
5163
  this.messageSearchChannelFilterBuilder = new FilterBuilder(filterBuilderOptions?.messageSearchChannel);
5094
5164
  this.messageSearchFilterBuilder = new FilterBuilder({
5095
5165
  ...filterBuilderOptions?.messageSearch,
@@ -5112,7 +5182,7 @@ var MessageSearchSource = class extends BaseSearchSource {
5112
5182
  }
5113
5183
  });
5114
5184
  }
5115
- async query(searchQuery) {
5185
+ async query(searchQuery, queryOptions = {}) {
5116
5186
  if (!this.client.userID || this.next === null) return { items: [] };
5117
5187
  const channelFilters = this.messageSearchChannelFilterBuilder.buildFilters({
5118
5188
  baseFilters: {
@@ -5140,9 +5210,11 @@ var MessageSearchSource = class extends BaseSearchSource {
5140
5210
  const { next, results } = await this.client.search(
5141
5211
  channelFilters,
5142
5212
  messageFilters,
5143
- options
5213
+ options,
5214
+ queryOptions
5144
5215
  );
5145
5216
  const items = results.map(({ message }) => message);
5217
+ if (queryOptions.signal?.aborted) return { items, next };
5146
5218
  const cids = Array.from(
5147
5219
  items.reduce((acc, message) => {
5148
5220
  if (message.cid && !this.client.activeChannels[message.cid]) acc.add(message.cid);
@@ -5160,7 +5232,8 @@ var MessageSearchSource = class extends BaseSearchSource {
5160
5232
  last_message_at: -1,
5161
5233
  ...this.channelQuerySort
5162
5234
  },
5163
- this.channelQueryOptions
5235
+ this.channelQueryOptions,
5236
+ queryOptions
5164
5237
  );
5165
5238
  }
5166
5239
  return { items, next };
@@ -6454,13 +6527,18 @@ var MentionsSearchSource = class extends BaseSearchSource {
6454
6527
  constructor(channel, options) {
6455
6528
  const {
6456
6529
  mentionAllAppUsers,
6530
+ memberFilters,
6531
+ memberSort,
6532
+ searchOptions,
6457
6533
  suggestionFactoryMappers,
6458
6534
  textComposerText,
6459
6535
  transliterate,
6460
6536
  trigger,
6537
+ userFilters,
6538
+ userSort,
6461
6539
  ...restOptions
6462
6540
  } = options || {};
6463
- super(restOptions);
6541
+ super({ ...restOptions, allowEmptySearchString: true });
6464
6542
  this.type = "mentions";
6465
6543
  this.normalizeSearchValue = (value) => this.transliterate(removeDiacritics(value)).toLowerCase();
6466
6544
  this.matchesSearchQuery = (value, searchQuery) => {
@@ -6498,10 +6576,6 @@ var MentionsSearchSource = class extends BaseSearchSource {
6498
6576
  this.toHereMentionSuggestion = (searchToken = this.searchQuery) => this.mapMentionSuggestion("here", "here", searchToken);
6499
6577
  this.toRoleMentionSuggestion = (role, searchToken = this.searchQuery) => this.mapMentionSuggestion("role", role, searchToken);
6500
6578
  this.toUserGroupMentionSuggestion = (userGroup, searchToken = this.searchQuery) => this.mapMentionSuggestion("user_group", userGroup, searchToken);
6501
- this.canExecuteQuery = (newSearchString) => {
6502
- const hasNewSearchQuery = typeof newSearchString !== "undefined";
6503
- return this.isActive && !this.isLoading && (hasNewSearchQuery || this.hasNext);
6504
- };
6505
6579
  this.transliterate = (text) => text;
6506
6580
  this.getMembersAndWatchers = () => {
6507
6581
  const memberUsers = Object.values(this.channel.state.members ?? {}).map(
@@ -6521,10 +6595,10 @@ var MentionsSearchSource = class extends BaseSearchSource {
6521
6595
  ...this.isMentionTypeAllowed("channel") ? [this.toChannelMentionSuggestion(searchQuery)] : [],
6522
6596
  ...this.isMentionTypeAllowed("here") ? [this.toHereMentionSuggestion(searchQuery)] : []
6523
6597
  ].filter(({ name }) => this.matchesPrefixSearchQuery(name, searchQuery));
6524
- this.getRoleMentionSuggestions = async (query) => {
6598
+ this.getRoleMentionSuggestions = async (query, queryOptions = {}) => {
6525
6599
  if (!this.isMentionTypeAllowed("role")) return [];
6526
6600
  if (!query) return [];
6527
- const { roles } = await this.client.searchRoles({ query });
6601
+ const { roles } = await this.client.searchRoles({ query }, queryOptions);
6528
6602
  return [...roles?.map((role) => role.name) ?? []].sort((left, right) => left.localeCompare(right)).map((role) => this.toRoleMentionSuggestion(role, query));
6529
6603
  };
6530
6604
  this.searchMembersLocally = (searchQuery) => {
@@ -6586,20 +6660,25 @@ var MentionsSearchSource = class extends BaseSearchSource {
6586
6660
  options: { ...this.searchOptions, limit: this.pageSize, offset }
6587
6661
  };
6588
6662
  };
6589
- this.queryUsers = async (searchQuery, offset = 0) => {
6663
+ this.queryUsers = async (searchQuery, offset = 0, queryOptions = {}) => {
6590
6664
  const { filters, sort, options } = this.prepareQueryUsersParams(searchQuery, offset);
6591
- const { users } = await this.client.queryUsers(filters, sort, options);
6665
+ const { users } = await this.client.queryUsers(filters, sort, options, queryOptions);
6592
6666
  return users;
6593
6667
  };
6594
- this.queryMembers = async (searchQuery, offset = 0) => {
6668
+ this.queryMembers = async (searchQuery, offset = 0, queryOptions = {}) => {
6595
6669
  const { filters, sort, options } = this.prepareQueryMembersParams(
6596
6670
  searchQuery,
6597
6671
  offset
6598
6672
  );
6599
- const response = await this.channel.queryMembers(filters, sort, options);
6673
+ const response = await this.channel.queryMembers(
6674
+ filters,
6675
+ sort,
6676
+ options,
6677
+ queryOptions
6678
+ );
6600
6679
  return response.members.map((member) => member.user);
6601
6680
  };
6602
- this.getUserSuggestionsPage = async (searchQuery, userOffset = 0) => {
6681
+ this.getUserSuggestionsPage = async (searchQuery, userOffset = 0, queryOptions = {}) => {
6603
6682
  if (!this.isMentionTypeAllowed("user")) {
6604
6683
  return {
6605
6684
  items: [],
@@ -6609,7 +6688,7 @@ var MentionsSearchSource = class extends BaseSearchSource {
6609
6688
  let users;
6610
6689
  const shouldSearchLocally = this.allMembersLoadedWithInitialChannelQuery || !searchQuery;
6611
6690
  if (this.config.mentionAllAppUsers) {
6612
- users = await this.queryUsers(searchQuery, userOffset);
6691
+ users = await this.queryUsers(searchQuery, userOffset, queryOptions);
6613
6692
  } else if (shouldSearchLocally) {
6614
6693
  const localUsers = this.searchMembersLocally(searchQuery);
6615
6694
  const items2 = localUsers.slice(userOffset, userOffset + this.pageSize).map((user) => this.toUserSuggestion(user, searchQuery));
@@ -6618,7 +6697,7 @@ var MentionsSearchSource = class extends BaseSearchSource {
6618
6697
  nextOffset: localUsers.length > userOffset + this.pageSize ? userOffset + items2.length : void 0
6619
6698
  };
6620
6699
  } else {
6621
- users = await this.queryMembers(searchQuery, userOffset);
6700
+ users = await this.queryMembers(searchQuery, userOffset, queryOptions);
6622
6701
  }
6623
6702
  const items = users.map((user) => this.toUserSuggestion(user, searchQuery));
6624
6703
  return {
@@ -6635,7 +6714,7 @@ var MentionsSearchSource = class extends BaseSearchSource {
6635
6714
  name_gt: lastItem.name
6636
6715
  });
6637
6716
  };
6638
- this.getUserGroupSuggestionsPage = async (searchQuery, cursor) => {
6717
+ this.getUserGroupSuggestionsPage = async (searchQuery, cursor, queryOptions = {}) => {
6639
6718
  if (!this.isMentionTypeAllowed("user_group")) {
6640
6719
  return {
6641
6720
  items: [],
@@ -6657,7 +6736,7 @@ var MentionsSearchSource = class extends BaseSearchSource {
6657
6736
  ...userGroupCursor?.id_gt ? { id_gt: userGroupCursor.id_gt } : {},
6658
6737
  ...userGroupCursor?.name_gt ? { name_gt: userGroupCursor.name_gt } : {}
6659
6738
  };
6660
- const { user_groups } = await this.client.searchUserGroups(options);
6739
+ const { user_groups } = await this.client.searchUserGroups(options, queryOptions);
6661
6740
  return {
6662
6741
  items: user_groups.map(
6663
6742
  (userGroup) => this.toUserGroupMentionSuggestion(userGroup, searchQuery)
@@ -6667,6 +6746,11 @@ var MentionsSearchSource = class extends BaseSearchSource {
6667
6746
  };
6668
6747
  this.client = channel.getClient();
6669
6748
  this.channel = channel;
6749
+ this.userFilters = userFilters;
6750
+ this.userSort = userSort;
6751
+ this.memberFilters = memberFilters;
6752
+ this.memberSort = memberSort;
6753
+ this.searchOptions = searchOptions;
6670
6754
  this.config = {
6671
6755
  mentionAllAppUsers,
6672
6756
  suggestionFactoryMappers,
@@ -6699,16 +6783,21 @@ var MentionsSearchSource = class extends BaseSearchSource {
6699
6783
  offset: (this.offset ?? 0) + userPaginationState.itemCount
6700
6784
  };
6701
6785
  }
6702
- async query(searchQuery) {
6786
+ async query(searchQuery, queryOptions = {}) {
6703
6787
  const userOffset = this.offset ?? 0;
6704
6788
  const isFirstPage = userOffset === 0 && typeof this.userGroupCursor === "undefined";
6705
6789
  const previousUserPaginationState = this.latestUserPaginationState;
6706
6790
  const previousUserGroupCursor = this.userGroupCursor;
6707
6791
  const [userResultsState, userGroupResultsState, roleSuggestionsState] = await Promise.allSettled([
6708
- this.getUserSuggestionsPage(searchQuery, userOffset),
6709
- this.getUserGroupSuggestionsPage(searchQuery, previousUserGroupCursor),
6710
- isFirstPage ? this.getRoleMentionSuggestions(searchQuery) : Promise.resolve([])
6792
+ this.getUserSuggestionsPage(searchQuery, userOffset, queryOptions),
6793
+ this.getUserGroupSuggestionsPage(
6794
+ searchQuery,
6795
+ previousUserGroupCursor,
6796
+ queryOptions
6797
+ ),
6798
+ isFirstPage ? this.getRoleMentionSuggestions(searchQuery, queryOptions) : Promise.resolve([])
6711
6799
  ]);
6800
+ if (queryOptions.signal?.aborted) return { items: [] };
6712
6801
  const userResults = userResultsState.status === "fulfilled" ? userResultsState.value : {
6713
6802
  items: [],
6714
6803
  nextOffset: isFirstPage ? void 0 : previousUserPaginationState?.nextOffset
@@ -9384,10 +9473,11 @@ var Channel = class {
9384
9473
  * @param {MemberSort} [sort] Sort options, for instance [{created_at: -1}].
9385
9474
  * When using multiple fields, make sure you use array of objects to guarantee field order, for instance [{name: -1}, {created_at: 1}]
9386
9475
  * @param {{ limit?: number; offset?: number }} [options] Option object, {limit: 10, offset:10}
9476
+ * @param {RequestOptions} [requestOptions] Carries an abort signal. Not sent in the request.
9387
9477
  *
9388
9478
  * @return {Promise<ChannelMemberAPIResponse>} Query Members response
9389
9479
  */
9390
- async queryMembers(filterConditions, sort = [], options = {}) {
9480
+ async queryMembers(filterConditions, sort = [], options = {}, requestOptions = {}) {
9391
9481
  let id;
9392
9482
  const type = this.type;
9393
9483
  let members;
@@ -9407,7 +9497,8 @@ var Channel = class {
9407
9497
  filter_conditions: filterConditions,
9408
9498
  ...options
9409
9499
  }
9410
- }
9500
+ },
9501
+ requestOptions
9411
9502
  );
9412
9503
  }
9413
9504
  /**
@@ -14539,6 +14630,7 @@ var StreamChat = class _StreamChat {
14539
14630
  return this.handleResponse(response);
14540
14631
  } catch (e) {
14541
14632
  e.client_request_id = requestConfig.headers?.["x-client-request-id"];
14633
+ if (import_axios3.default.isCancel(e)) throw e;
14542
14634
  this._logApiError(type, url, e);
14543
14635
  this.consecutiveFailures += 1;
14544
14636
  if (e.response) {
@@ -14850,7 +14942,7 @@ var StreamChat = class _StreamChat {
14850
14942
  return this.userAgent;
14851
14943
  }
14852
14944
  if (!this.cachedUserAgent) {
14853
- const version = "9.50.2";
14945
+ const version = "9.51.0";
14854
14946
  const { name: sdkName, version: sdkVersion } = this.sdkIdentifier ?? {};
14855
14947
  const { name: appName, version: appVersion } = this.appIdentifier ?? {};
14856
14948
  const { os, model: deviceModel } = this.deviceIdentifier ?? {};
@@ -15226,14 +15318,14 @@ var StreamChat = class _StreamChat {
15226
15318
  error
15227
15319
  });
15228
15320
  }
15229
- get(url, params) {
15230
- return this.doAxiosRequest("get", url, null, { params });
15321
+ get(url, params, config) {
15322
+ return this.doAxiosRequest("get", url, null, { params, config });
15231
15323
  }
15232
15324
  put(url, data) {
15233
15325
  return this.doAxiosRequest("put", url, data);
15234
15326
  }
15235
- post(url, data) {
15236
- return this.doAxiosRequest("post", url, data);
15327
+ post(url, data, config) {
15328
+ return this.doAxiosRequest("post", url, data, { config });
15237
15329
  }
15238
15330
  patch(url, data) {
15239
15331
  return this.doAxiosRequest("patch", url, data);
@@ -15428,10 +15520,11 @@ var StreamChat = class _StreamChat {
15428
15520
  * @param {UserSort} sort Sort options, for instance [{last_active: -1}].
15429
15521
  * When using multiple fields, make sure you use array of objects to guarantee field order, for instance [{last_active: -1}, {created_at: 1}]
15430
15522
  * @param {UserOptions} options Option object, {presence: true}
15523
+ * @param {RequestOptions} [requestOptions] Carries an abort signal. Not sent in the request.
15431
15524
  *
15432
15525
  * @return {Promise<{ users: Array<UserResponse> }>} User Query Response
15433
15526
  */
15434
- async queryUsers(filterConditions, sort = [], options = {}) {
15527
+ async queryUsers(filterConditions, sort = [], options = {}, requestOptions = {}) {
15435
15528
  const defaultOptions = {
15436
15529
  presence: false
15437
15530
  };
@@ -15448,7 +15541,8 @@ var StreamChat = class _StreamChat {
15448
15541
  ...defaultOptions,
15449
15542
  ...options
15450
15543
  }
15451
- }
15544
+ },
15545
+ requestOptions
15452
15546
  );
15453
15547
  this.state.updateUsers(data.users);
15454
15548
  return data;
@@ -15494,13 +15588,14 @@ var StreamChat = class _StreamChat {
15494
15588
  * searchUserGroups - Search user groups by prefix for autocomplete
15495
15589
  *
15496
15590
  * @param {SearchUserGroupsOptions} options The search options
15497
- *
15591
+ * @param {RequestOptions} [requestOptions] Carries an abort signal. Not sent in the request.
15498
15592
  * @return {Promise<SearchUserGroupsResponse>} User Group Search Response
15499
15593
  */
15500
- async searchUserGroups(options) {
15594
+ async searchUserGroups(options, requestOptions = {}) {
15501
15595
  return await this.get(
15502
15596
  this.baseURL + "/usergroups/search",
15503
- options
15597
+ options,
15598
+ requestOptions
15504
15599
  );
15505
15600
  }
15506
15601
  /**
@@ -15620,10 +15715,11 @@ var StreamChat = class _StreamChat {
15620
15715
  * @param {ChannelSort} [sort] Sort options, for instance {created_at: -1}.
15621
15716
  * When using multiple fields, make sure you use array of objects to guarantee field order, for instance [{last_updated: -1}, {created_at: 1}]
15622
15717
  * @param {ChannelOptions} [options] Options object. Can include predefined_filter, filter_values, and sort_values for using predefined filters.
15718
+ * @param {RequestOptions} [requestOptions] Carries an abort signal. Not sent in the request.
15623
15719
  *
15624
15720
  * @return {Promise<QueryChannelsAPIResponse>} full search channels response
15625
15721
  */
15626
- async queryChannelsRequestWithResponse(filterConditions, sort = [], options = {}) {
15722
+ async queryChannelsRequestWithResponse(filterConditions, sort = [], options = {}, requestOptions = {}) {
15627
15723
  const defaultOptions = {
15628
15724
  state: true,
15629
15725
  watch: true,
@@ -15648,7 +15744,11 @@ var StreamChat = class _StreamChat {
15648
15744
  ...defaultOptions,
15649
15745
  ...restOptions
15650
15746
  };
15651
- return await this.post(this.baseURL + "/channels", payload);
15747
+ return await this.post(
15748
+ this.baseURL + "/channels",
15749
+ payload,
15750
+ requestOptions
15751
+ );
15652
15752
  }
15653
15753
  /**
15654
15754
  * queryChannelsRequest - Queries channels and returns the raw channel response list.
@@ -15677,7 +15777,9 @@ var StreamChat = class _StreamChat {
15677
15777
  const queryChannelsResponse = await this.queryChannelsRequestWithResponse(
15678
15778
  filterConditions,
15679
15779
  sort,
15680
- options
15780
+ options,
15781
+ // stateOptions is never serialized, so it carries the abort signal
15782
+ { signal: stateOptions.signal }
15681
15783
  );
15682
15784
  const channels = queryChannelsResponse.channels;
15683
15785
  this.dispatchEvent({
@@ -15794,10 +15896,11 @@ var StreamChat = class _StreamChat {
15794
15896
  * @param {ChannelFilters} filterConditions MongoDB style filter conditions
15795
15897
  * @param {MessageFilters | string} query search query or object MongoDB style filters
15796
15898
  * @param {SearchOptions} [options] Option object, {user_id: 'tommaso'}
15899
+ * @param {RequestOptions} [requestOptions] Carries an abort signal. Not sent in the request.
15797
15900
  *
15798
15901
  * @return {Promise<SearchAPIResponse>} search messages response
15799
15902
  */
15800
- async search(filterConditions, query, options = {}) {
15903
+ async search(filterConditions, query, options = {}, requestOptions = {}) {
15801
15904
  if (options.offset && options.next) {
15802
15905
  throw Error(`Cannot specify offset with next`);
15803
15906
  }
@@ -15814,7 +15917,11 @@ var StreamChat = class _StreamChat {
15814
15917
  throw Error(`Invalid type ${typeof query} for query parameter`);
15815
15918
  }
15816
15919
  await this.wsPromise;
15817
- return await this.get(this.baseURL + "/search", { payload });
15920
+ return await this.get(
15921
+ this.baseURL + "/search",
15922
+ { payload },
15923
+ requestOptions
15924
+ );
15818
15925
  }
15819
15926
  /**
15820
15927
  * setLocalDevice - Set the device info for the current client(device) that will be sent via WS connection automatically
@@ -16851,6 +16958,7 @@ var StreamChat = class _StreamChat {
16851
16958
  headers: axiosRequestConfigHeaders,
16852
16959
  ...axiosRequestConfigRest
16853
16960
  } = this.options.axiosRequestConfig || {};
16961
+ const resolvedSignal = options.config?.signal ?? axiosRequestConfigRest.signal ?? signal;
16854
16962
  return {
16855
16963
  params: {
16856
16964
  user_id: this.userID,
@@ -16866,9 +16974,9 @@ var StreamChat = class _StreamChat {
16866
16974
  ...options.headers,
16867
16975
  ...axiosRequestConfigHeaders || {}
16868
16976
  },
16869
- ...signal ? { signal } : {},
16870
16977
  ...options.config,
16871
- ...axiosRequestConfigRest || {}
16978
+ ...axiosRequestConfigRest,
16979
+ ...resolvedSignal ? { signal: resolvedSignal } : {}
16872
16980
  };
16873
16981
  }
16874
16982
  _getToken() {
@@ -17008,8 +17116,12 @@ var StreamChat = class _StreamChat {
17008
17116
  *
17009
17117
  * @returns {Promise<APIResponse>}
17010
17118
  */
17011
- searchRoles(options) {
17012
- return this.get(`${this.baseURL}/roles/search`, options);
17119
+ searchRoles(options, requestOptions = {}) {
17120
+ return this.get(
17121
+ `${this.baseURL}/roles/search`,
17122
+ options,
17123
+ requestOptions
17124
+ );
17013
17125
  }
17014
17126
  /** deleteRole - deletes a custom role
17015
17127
  *
@@ -18366,7 +18478,8 @@ var import_axios4 = require("axios");
18366
18478
  var OfflineDBSyncManager = class {
18367
18479
  constructor({
18368
18480
  client,
18369
- offlineDb
18481
+ offlineDb,
18482
+ syncMaxEventCount
18370
18483
  }) {
18371
18484
  this.syncStatus = false;
18372
18485
  this.connectionChangedListener = null;
@@ -18434,10 +18547,22 @@ var OfflineDBSyncManager = class {
18434
18547
  */
18435
18548
  this.invokeSyncStatusListeners = async (status) => {
18436
18549
  this.syncStatus = status;
18437
- this.syncStatusListeners.forEach((l) => l(status));
18550
+ this.syncStatusListeners.forEach((l) => {
18551
+ try {
18552
+ l(status);
18553
+ } catch (error) {
18554
+ console.log("Error in a sync status listener.", error);
18555
+ }
18556
+ });
18438
18557
  if (status) {
18439
18558
  const promises = Array.from(this.scheduledSyncStatusCallbacks.values()).map(
18440
- (cb) => cb()
18559
+ async (cb) => {
18560
+ try {
18561
+ await cb();
18562
+ } catch (error) {
18563
+ console.log("Error executing a scheduled sync status callback.", error);
18564
+ }
18565
+ }
18441
18566
  );
18442
18567
  await Promise.all(promises);
18443
18568
  this.scheduledSyncStatusCallbacks.clear();
@@ -18471,13 +18596,23 @@ var OfflineDBSyncManager = class {
18471
18596
  await this.offlineDb.resetDB();
18472
18597
  } else {
18473
18598
  const result = await this.client.sync(cids, lastSyncedAtDate.toISOString());
18474
- const queryPromises = result.events.map(
18475
- (event) => this.offlineDb.handleEvent({ event, execute: false })
18476
- );
18477
- const queriesArray = await Promise.all(queryPromises);
18478
- const queries = queriesArray.flat();
18479
- if (queries.length) {
18480
- await this.offlineDb.executeSqlBatch(queries);
18599
+ const { syncMaxEventCount } = this;
18600
+ const exceedsLimit = typeof syncMaxEventCount === "number" && syncMaxEventCount > 0 && result.events.length > syncMaxEventCount;
18601
+ if (exceedsLimit) {
18602
+ this.client.logger(
18603
+ "warn",
18604
+ `Skipping sync event replay: received ${result.events.length} events, which exceeds the configured limit of ${syncMaxEventCount}. Visible channels are refreshed on reconnect instead.`,
18605
+ { tags: ["sync", "offline"] }
18606
+ );
18607
+ } else {
18608
+ const queryPromises = result.events.map(
18609
+ (event) => this.offlineDb.handleEvent({ event, execute: false })
18610
+ );
18611
+ const queriesArray = await Promise.all(queryPromises);
18612
+ const queries = queriesArray.flat();
18613
+ if (queries.length) {
18614
+ await this.offlineDb.executeSqlBatch(queries);
18615
+ }
18481
18616
  }
18482
18617
  }
18483
18618
  }
@@ -18499,19 +18634,38 @@ var OfflineDBSyncManager = class {
18499
18634
  };
18500
18635
  /**
18501
18636
  * Executes any tasks that were queued while offline and then performs a sync.
18637
+ *
18638
+ * Each step is isolated so a failure in one does not prevent the other, and
18639
+ * neither can escape to the callers (init + the connection.changed handler).
18640
+ * This guarantees the subsequent invokeSyncStatusListeners(true) always runs,
18641
+ * so syncStatus recovers to true and gated channel queries are unblocked.
18642
+ * Failed syncs degrade to "possibly stale data until the next query" rather
18643
+ * than freezing all future queries. See issue #1816.
18502
18644
  */
18503
18645
  this.syncAndExecutePendingTasks = async () => {
18504
- await this.offlineDb.executePendingTasks();
18505
- await this.sync();
18646
+ try {
18647
+ await this.offlineDb.executePendingTasks();
18648
+ } catch (error) {
18649
+ console.log("Error executing pending tasks during sync.", error);
18650
+ }
18651
+ try {
18652
+ await this.sync();
18653
+ } catch (error) {
18654
+ console.log("Error while syncing the DB.", error);
18655
+ }
18506
18656
  };
18507
18657
  this.client = client;
18508
18658
  this.offlineDb = offlineDb;
18659
+ this.syncMaxEventCount = syncMaxEventCount;
18509
18660
  }
18510
18661
  };
18511
18662
 
18512
18663
  // src/offline-support/offline_support_api.ts
18513
18664
  var AbstractOfflineDB = class {
18514
- constructor({ client }) {
18665
+ constructor({
18666
+ client,
18667
+ syncMaxEventCount
18668
+ }) {
18515
18669
  /**
18516
18670
  * Initializes the DB as well as its syncManager for a given userId.
18517
18671
  * It will update the DBs reactive state with initialization values.
@@ -19201,7 +19355,11 @@ var AbstractOfflineDB = class {
19201
19355
  }
19202
19356
  };
19203
19357
  this.client = client;
19204
- this.syncManager = new OfflineDBSyncManager({ client, offlineDb: this });
19358
+ this.syncManager = new OfflineDBSyncManager({
19359
+ client,
19360
+ offlineDb: this,
19361
+ syncMaxEventCount
19362
+ });
19205
19363
  this.state = new StateStore({
19206
19364
  initialized: false,
19207
19365
  userId: this.client.userID