stream-chat 9.51.0 → 9.52.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/cjs/index.browser.js +382 -187
  2. package/dist/cjs/index.browser.js.map +4 -4
  3. package/dist/cjs/index.node.js +391 -188
  4. package/dist/cjs/index.node.js.map +4 -4
  5. package/dist/esm/index.mjs +380 -185
  6. package/dist/esm/index.mjs.map +4 -4
  7. package/dist/types/channel.d.ts +1 -1
  8. package/dist/types/client.d.ts +10 -0
  9. package/dist/types/index.d.ts +7 -0
  10. package/dist/types/messageComposer/attachmentIdentity.d.ts +4 -0
  11. package/dist/types/messageComposer/attachmentManager.d.ts +8 -0
  12. package/dist/types/messageComposer/messageComposer.d.ts +13 -0
  13. package/dist/types/messageComposer/middleware/attachmentManager/postUpload/attachmentEnrichment.d.ts +20 -1
  14. package/dist/types/messageComposer/middleware/messageComposer/MessageComposerMiddlewareExecutor.d.ts +6 -1
  15. package/dist/types/messageComposer/middleware/messageComposer/attachments.d.ts +24 -0
  16. package/dist/types/messageComposer/middleware/messageComposer/types.d.ts +22 -1
  17. package/dist/types/messageComposer/types.d.ts +5 -0
  18. package/dist/types/middleware.d.ts +8 -0
  19. package/dist/types/uploadManager.d.ts +30 -0
  20. package/dist/types/utils.d.ts +20 -1
  21. package/package.json +1 -1
  22. package/src/channel.ts +16 -8
  23. package/src/client.ts +17 -1
  24. package/src/index.ts +12 -0
  25. package/src/messageComposer/attachmentIdentity.ts +27 -0
  26. package/src/messageComposer/attachmentManager.ts +61 -12
  27. package/src/messageComposer/messageComposer.ts +43 -8
  28. package/src/messageComposer/middleware/attachmentManager/postUpload/AttachmentPostUploadMiddlewareExecutor.ts +1 -1
  29. package/src/messageComposer/middleware/attachmentManager/postUpload/attachmentEnrichment.ts +58 -33
  30. package/src/messageComposer/middleware/attachmentManager/postUpload/uploadErrorHandler.ts +4 -0
  31. package/src/messageComposer/middleware/messageComposer/MessageComposerMiddlewareExecutor.ts +9 -0
  32. package/src/messageComposer/middleware/messageComposer/attachments.ts +109 -0
  33. package/src/messageComposer/middleware/messageComposer/types.ts +22 -1
  34. package/src/messageComposer/types.ts +5 -0
  35. package/src/middleware.ts +11 -0
  36. package/src/offline-support/offline_support_api.ts +4 -0
  37. package/src/uploadManager.ts +45 -0
  38. package/src/utils.ts +72 -0
@@ -158,6 +158,7 @@ __export(index_exports, {
158
158
  createMessageComposerStateCompositionMiddleware: () => createMessageComposerStateCompositionMiddleware,
159
159
  createPollComposerStateMiddleware: () => createPollComposerStateMiddleware,
160
160
  createPostUploadAttachmentEnrichmentMiddleware: () => createPostUploadAttachmentEnrichmentMiddleware,
161
+ createSendWithPendingUploadsAttachmentsMiddleware: () => createSendWithPendingUploadsAttachmentsMiddleware,
161
162
  createSharedLocationCompositionMiddleware: () => createSharedLocationCompositionMiddleware,
162
163
  createTextComposerCompositionMiddleware: () => createTextComposerCompositionMiddleware,
163
164
  createTextComposerPreValidationMiddleware: () => createTextComposerPreValidationMiddleware,
@@ -186,6 +187,7 @@ __export(index_exports, {
186
187
  getTokenizedSuggestionDisplayName: () => getTokenizedSuggestionDisplayName,
187
188
  getTriggerCharWithToken: () => getTriggerCharWithToken,
188
189
  gunzipPayload: () => gunzipPayload,
190
+ hasPending: () => hasPending,
189
191
  insertItemWithTrigger: () => insertItemWithTrigger,
190
192
  isAudioAttachment: () => isAudioAttachment,
191
193
  isBlobButNotFile: () => isBlobButNotFile,
@@ -193,6 +195,7 @@ __export(index_exports, {
193
195
  isFileAttachment: () => isFileAttachment,
194
196
  isFileList: () => isFileList,
195
197
  isFileReference: () => isFileReference,
198
+ isFinishedUpload: () => isFinishedUpload,
196
199
  isGiphyAttachment: () => isGiphyAttachment,
197
200
  isImageAttachment: () => isImageAttachment,
198
201
  isImageFile: () => isImageFile,
@@ -205,9 +208,11 @@ __export(index_exports, {
205
208
  isLocalVoiceRecordingAttachment: () => isLocalVoiceRecordingAttachment,
206
209
  isOwnUser: () => isOwnUser,
207
210
  isPatch: () => isPatch,
211
+ isPendingUpload: () => isPendingUpload,
208
212
  isScrapedContent: () => isScrapedContent,
209
213
  isSharedLocationResponse: () => isSharedLocationResponse,
210
214
  isTargetedOptionTextUpdate: () => isTargetedOptionTextUpdate,
215
+ isUploadCancellation: () => isUploadCancellation,
211
216
  isUploadedAttachment: () => isUploadedAttachment,
212
217
  isVideoAttachment: () => isVideoAttachment,
213
218
  isVoiceRecordingAttachment: () => isVoiceRecordingAttachment,
@@ -227,12 +232,15 @@ __export(index_exports, {
227
232
  readFileAsArrayBuffer: () => readFileAsArrayBuffer,
228
233
  removeDiacritics: () => removeDiacritics,
229
234
  replaceWordWithEntity: () => replaceWordWithEntity,
235
+ settled: () => settled,
230
236
  stripCommandFromText: () => stripCommandFromText,
231
237
  stripMentionTokens: () => stripMentionTokens,
232
238
  textIsEmpty: () => textIsEmpty,
233
239
  timeLeftMs: () => timeLeftMs,
234
240
  verifyAndParseWebhook: () => verifyAndParseWebhook,
235
- verifySignature: () => verifySignature
241
+ verifySignature: () => verifySignature,
242
+ withCancellation: () => withCancellation,
243
+ withoutConcurrency: () => withoutConcurrency
236
244
  });
237
245
  module.exports = __toCommonJS(index_exports);
238
246
 
@@ -499,7 +507,7 @@ var ChannelBatchUpdater = class {
499
507
  };
500
508
 
501
509
  // src/client.ts
502
- var import_axios3 = __toESM(require("axios"));
510
+ var import_axios4 = __toESM(require("axios"));
503
511
  var import_https = __toESM(require("https"));
504
512
 
505
513
  // src/utils.ts
@@ -759,6 +767,38 @@ function unformatMessage(message) {
759
767
  quoted_message: toMessageResponseBase(message.quoted_message)
760
768
  };
761
769
  }
770
+ var isRemoteUrl = (url) => !!url && /^https?:\/\//i.test(url.trim());
771
+ var sanitizeOutgoingAttachments = ({
772
+ client,
773
+ message
774
+ }) => {
775
+ const attachments = message.attachments;
776
+ if (!attachments?.length) return message;
777
+ const unresolved = [];
778
+ let changed = false;
779
+ const sanitized = attachments.reduce((acc, attachment) => {
780
+ const { localMetadata, ...rest } = attachment;
781
+ if (!localMetadata) {
782
+ acc.push(attachment);
783
+ return acc;
784
+ }
785
+ changed = true;
786
+ const hasRemoteSource = isRemoteUrl(rest.asset_url) || isRemoteUrl(rest.image_url) || isRemoteUrl(rest.og_scrape_url) || isRemoteUrl(rest.title_link);
787
+ if (hasRemoteSource) {
788
+ acc.push(rest);
789
+ } else {
790
+ unresolved.push(rest);
791
+ }
792
+ return acc;
793
+ }, []);
794
+ if (unresolved.length) {
795
+ const text = `Dropped ${unresolved.length} attachment(s) whose upload never completed. Composing with pending uploads requires awaiting them before sending.`;
796
+ const extraData = { attachments: unresolved, message, tags: ["attachments"] };
797
+ client.logger("warn", text, extraData);
798
+ console.warn(text, extraData);
799
+ }
800
+ return changed ? { ...message, attachments: sanitized } : message;
801
+ };
762
802
  var localMessageToNewMessagePayload = (localMessage) => {
763
803
  const {
764
804
  // Remove all timestamp fields and client-specific fields.
@@ -2531,6 +2571,12 @@ var CooldownTimer = class extends WithSubscriptions {
2531
2571
  var isScrapedContent = (attachment) => !!attachment?.og_scrape_url || !!attachment?.title_link;
2532
2572
  var isLocalAttachment = (attachment) => !!attachment?.localMetadata?.id;
2533
2573
  var isLocalUploadAttachment = (attachment) => !!attachment?.localMetadata?.uploadState;
2574
+ var PENDING_UPLOAD_STATES = [
2575
+ "pending",
2576
+ "uploading"
2577
+ ];
2578
+ var isPendingUpload = (attachment) => isLocalUploadAttachment(attachment) && PENDING_UPLOAD_STATES.includes(attachment.localMetadata.uploadState);
2579
+ var isFinishedUpload = (attachment) => isLocalUploadAttachment(attachment) && attachment.localMetadata.uploadState === "finished";
2534
2580
  var isFileAttachment = (attachment, supportedVideoFormat = []) => attachment.type === "file" || !!(attachment.mime_type && supportedVideoFormat.indexOf(attachment.mime_type) === -1 && attachment.type !== "video");
2535
2581
  var isLocalFileAttachment = (attachment) => isFileAttachment(attachment) && isLocalAttachment(attachment);
2536
2582
  var isImageAttachment = (attachment) => attachment.type === "image" && !isScrapedContent(attachment);
@@ -2606,7 +2652,7 @@ var ensureIsLocalAttachment = (attachment) => {
2606
2652
  };
2607
2653
 
2608
2654
  // src/messageComposer/middleware/attachmentManager/postUpload/attachmentEnrichment.ts
2609
- var createPostUploadAttachmentEnrichmentMiddleware = () => ({
2655
+ var createPostUploadAttachmentEnrichmentMiddleware = (composer) => ({
2610
2656
  id: "stream-io/attachment-manager-middleware/post-upload-enrichment",
2611
2657
  handlers: {
2612
2658
  postProcess: ({
@@ -2619,9 +2665,10 @@ var createPostUploadAttachmentEnrichmentMiddleware = () => ({
2619
2665
  if (error) return forward();
2620
2666
  if (!attachment || !response) return discard();
2621
2667
  const enrichedAttachment = { ...attachment };
2622
- const previewUri = attachment.localMetadata.previewUri;
2623
- if (previewUri) {
2624
- if (previewUri.startsWith("blob:")) URL.revokeObjectURL(previewUri);
2668
+ const { id, previewUri } = attachment.localMetadata;
2669
+ const attachmentIsStillInComposer = !composer || !!composer.attachmentManager.attachmentsById[id];
2670
+ if (previewUri && attachmentIsStillInComposer) {
2671
+ if (previewUri.startsWith("blob:")) URL.revokeObjectURL?.(previewUri);
2625
2672
  delete enrichedAttachment.localMetadata.previewUri;
2626
2673
  }
2627
2674
  if (isLocalImageAttachment(attachment)) {
@@ -2644,6 +2691,12 @@ var createPostUploadAttachmentEnrichmentMiddleware = () => ({
2644
2691
  var withoutConcurrency = createRunner(wrapWithContinuationTracking);
2645
2692
  var withCancellation = createRunner(wrapWithCancellation);
2646
2693
  var pendingPromises = /* @__PURE__ */ new Map();
2694
+ function hasPending(tag) {
2695
+ return pendingPromises.has(tag);
2696
+ }
2697
+ async function settled(tag) {
2698
+ await pendingPromises.get(tag)?.promise;
2699
+ }
2647
2700
  function createRunner(wrapper) {
2648
2701
  return function run(tag, cb) {
2649
2702
  const { cb: wrapped, onContinued } = wrapper(tag, cb);
@@ -2686,6 +2739,16 @@ var MiddlewareExecutor = class {
2686
2739
  this.middleware = [];
2687
2740
  this.id = generateUUIDv4();
2688
2741
  }
2742
+ /**
2743
+ * The middleware currently installed, in execution order.
2744
+ *
2745
+ * Read-only: use `use` / `insert` / `replace` / `setOrder` / `remove` to change the chain. It
2746
+ * exists so components can ask what the chain is capable of - a middleware may declare
2747
+ * behaviour its host has to account for, and the host has no other way to find out.
2748
+ */
2749
+ get installedMiddleware() {
2750
+ return this.middleware;
2751
+ }
2689
2752
  use(middleware) {
2690
2753
  this.middleware = this.middleware.concat(middleware);
2691
2754
  return this;
@@ -2789,6 +2852,142 @@ var MiddlewareExecutor = class {
2789
2852
  }
2790
2853
  };
2791
2854
 
2855
+ // src/uploadManager.ts
2856
+ var import_axios = __toESM(require("axios"));
2857
+ var isUploadCancellation = (error) => import_axios.default.isCancel(error) || error instanceof Error && error.name === "AbortError";
2858
+ var initState = () => ({ uploads: {} });
2859
+ var upsertById = (uploads, record) => ({
2860
+ ...uploads,
2861
+ [record.id]: { ...uploads[record.id], ...record }
2862
+ });
2863
+ var updateById = (uploads, record) => {
2864
+ if (!(record.id in uploads)) return null;
2865
+ const current = uploads[record.id];
2866
+ return { ...uploads, [record.id]: { ...current, ...record } };
2867
+ };
2868
+ var UploadManager = class {
2869
+ constructor(client) {
2870
+ this.client = client;
2871
+ this.inFlightUploads = /* @__PURE__ */ new Map();
2872
+ this.getUpload = (id) => this.uploads[id];
2873
+ /**
2874
+ * Clears all upload records.
2875
+ * Invoked when the user disconnects so a later session does not inherit stale upload state.
2876
+ * Aborts every in-flight upload request via its `UploadRequestOptions.abortSignal`.
2877
+ */
2878
+ this.reset = () => {
2879
+ for (const { abortController } of this.inFlightUploads.values()) {
2880
+ abortController.abort();
2881
+ }
2882
+ this.inFlightUploads.clear();
2883
+ this.state.next(initState());
2884
+ };
2885
+ /**
2886
+ * Removes the upload record for `id` if present.
2887
+ * If an upload is still in progress, aborts its `UploadRequestOptions.abortSignal`.
2888
+ */
2889
+ this.deleteUploadRecord = (id) => {
2890
+ const flight = this.inFlightUploads.get(id);
2891
+ if (flight) {
2892
+ this.inFlightUploads.delete(id);
2893
+ flight.abortController.abort();
2894
+ }
2895
+ this.state.next((current) => {
2896
+ if (!(id in current.uploads)) return current;
2897
+ const uploads = { ...current.uploads };
2898
+ delete uploads[id];
2899
+ return { ...current, uploads };
2900
+ });
2901
+ };
2902
+ /**
2903
+ * Starts an upload for `id`, or returns the existing in-flight promise if one is already running.
2904
+ * Uses {@link StreamChat.channel}(`channelCid`) → `messageComposer.attachmentManager.doUploadRequest`.
2905
+ * Resolves with that result; rejects if the upload rejects (the record is removed from state either way).
2906
+ */
2907
+ this.upload = ({
2908
+ id,
2909
+ channelCid,
2910
+ file
2911
+ }) => {
2912
+ const existing = this.inFlightUploads.get(id);
2913
+ if (existing) return existing.promise;
2914
+ let resolvePromise;
2915
+ let rejectPromise;
2916
+ const promise = new Promise((resolve, reject) => {
2917
+ resolvePromise = resolve;
2918
+ rejectPromise = reject;
2919
+ });
2920
+ const abortController = new AbortController();
2921
+ this.inFlightUploads.set(id, { promise, abortController });
2922
+ void (async () => {
2923
+ const attachmentManager = this.resolveAttachmentManager(channelCid);
2924
+ const trackProgress = attachmentManager.config.trackUploadProgress;
2925
+ try {
2926
+ this.upsertUpload({
2927
+ uploadConfirmationPending: false,
2928
+ id,
2929
+ uploadProgress: trackProgress ? 0 : void 0
2930
+ });
2931
+ const onProgress = trackProgress ? (progress) => {
2932
+ this.updateUpload({
2933
+ id,
2934
+ uploadProgress: progress,
2935
+ // Only a number says anything about the flush, so a report without one leaves
2936
+ // the flag alone: `undefined` means the transport cannot measure this upload,
2937
+ // not that bytes were un-sent. Lowering it there would drop a UI that had
2938
+ // already gone indeterminate back to a determinate one with nothing to show.
2939
+ //
2940
+ // The record is removed in the `finally` below, so a record that still exists
2941
+ // while reporting 100% can only mean "flushed, awaiting the response".
2942
+ ...typeof progress === "number" ? { uploadConfirmationPending: progress >= 100 } : {}
2943
+ });
2944
+ } : void 0;
2945
+ const uploadRequestOptions = {
2946
+ abortSignal: abortController.signal,
2947
+ ...onProgress ? { onProgress } : {}
2948
+ };
2949
+ const response = await attachmentManager.doUploadRequest(
2950
+ file,
2951
+ uploadRequestOptions
2952
+ );
2953
+ resolvePromise(response);
2954
+ } catch (error) {
2955
+ rejectPromise(error);
2956
+ } finally {
2957
+ this.inFlightUploads.delete(id);
2958
+ this.deleteUploadRecord(id);
2959
+ }
2960
+ })();
2961
+ return promise;
2962
+ };
2963
+ this.upsertUpload = (record) => {
2964
+ this.state.partialNext({
2965
+ uploads: upsertById(this.uploads, record)
2966
+ });
2967
+ };
2968
+ this.updateUpload = (record) => {
2969
+ this.state.next((current) => {
2970
+ const nextUploads = updateById(current.uploads, record);
2971
+ if (!nextUploads) return current;
2972
+ return { ...current, uploads: nextUploads };
2973
+ });
2974
+ };
2975
+ this.state = new StateStore(initState());
2976
+ }
2977
+ resolveAttachmentManager(channelCid) {
2978
+ const colon = channelCid.indexOf(":");
2979
+ if (colon <= 0 || colon === channelCid.length - 1) {
2980
+ throw new Error(`Invalid channelCid: ${channelCid}`);
2981
+ }
2982
+ const channelType = channelCid.slice(0, colon);
2983
+ const channelId = channelCid.slice(colon + 1);
2984
+ return this.client.channel(channelType, channelId).messageComposer.attachmentManager;
2985
+ }
2986
+ get uploads() {
2987
+ return this.state.getLatestValue().uploads;
2988
+ }
2989
+ };
2990
+
2792
2991
  // src/messageComposer/middleware/attachmentManager/postUpload/uploadErrorHandler.ts
2793
2992
  var createUploadErrorHandlerMiddleware = (composer) => ({
2794
2993
  id: "stream-io/attachment-manager-middleware/upload-error",
@@ -2801,6 +3000,7 @@ var createUploadErrorHandlerMiddleware = (composer) => ({
2801
3000
  const { attachment, error } = state;
2802
3001
  if (!error) return forward();
2803
3002
  if (!attachment) return discard();
3003
+ if (isUploadCancellation(error)) return forward();
2804
3004
  const reason = error instanceof Error ? error.message : "unknown error";
2805
3005
  composer.client.notifications.addError({
2806
3006
  message: "Error uploading attachment",
@@ -2825,7 +3025,7 @@ var AttachmentPostUploadMiddlewareExecutor = class extends MiddlewareExecutor {
2825
3025
  super();
2826
3026
  this.use([
2827
3027
  createUploadErrorHandlerMiddleware(composer),
2828
- createPostUploadAttachmentEnrichmentMiddleware()
3028
+ createPostUploadAttachmentEnrichmentMiddleware(composer)
2829
3029
  ]);
2830
3030
  }
2831
3031
  };
@@ -3273,7 +3473,7 @@ function mergeWithDiff(target, source, customizer) {
3273
3473
  }
3274
3474
 
3275
3475
  // src/messageComposer/attachmentManager.ts
3276
- var initState = ({
3476
+ var initState2 = ({
3277
3477
  message
3278
3478
  }) => ({
3279
3479
  attachments: (message?.attachments ?? [])?.filter(({ og_scrape_url }) => !og_scrape_url).map((att) => {
@@ -3301,13 +3501,14 @@ var _AttachmentManager = class _AttachmentManager {
3301
3501
  ...attachment,
3302
3502
  localMetadata: {
3303
3503
  ...attachment.localMetadata,
3504
+ uploadConfirmationPending: false,
3304
3505
  uploadProgress: void 0,
3305
3506
  uploadState: "failed"
3306
3507
  }
3307
3508
  };
3308
3509
  };
3309
3510
  this.initState = ({ message } = {}) => {
3310
- this.state.next(initState({ message }));
3511
+ this.state.next(initState2({ message }));
3311
3512
  };
3312
3513
  this.getSnapshot = () => {
3313
3514
  const state = this.state.getLatestValue();
@@ -3384,8 +3585,22 @@ var _AttachmentManager = class _AttachmentManager {
3384
3585
  this.state.partialNext({ attachments });
3385
3586
  }
3386
3587
  };
3588
+ /**
3589
+ * Releases a local preview URL the SDK minted, if it minted one.
3590
+ *
3591
+ * Only `blob:` URLs are ours to revoke: `toLocalUploadAttachment` puts a
3592
+ * `URL.createObjectURL` result in `previewUri` for a `File`, but for a React Native
3593
+ * `FileReference` it stores the caller's own native file URI, which must be left alone.
3594
+ */
3595
+ this.revokePreviewUri = (attachment) => {
3596
+ const previewUri = attachment.localMetadata?.previewUri;
3597
+ if (previewUri?.startsWith("blob:")) URL.revokeObjectURL?.(previewUri);
3598
+ };
3387
3599
  this.removeAttachments = (localAttachmentIds) => {
3388
3600
  if (!localAttachmentIds.length) return;
3601
+ const removedAttachments = this.attachments.filter(
3602
+ (attachment) => localAttachmentIds.includes(attachment.localMetadata?.id)
3603
+ );
3389
3604
  this.state.partialNext({
3390
3605
  attachments: this.attachments.filter(
3391
3606
  (attachment) => !localAttachmentIds.includes(attachment.localMetadata?.id)
@@ -3394,6 +3609,7 @@ var _AttachmentManager = class _AttachmentManager {
3394
3609
  for (const id of localAttachmentIds) {
3395
3610
  this.client.uploadManager.deleteUploadRecord(id);
3396
3611
  }
3612
+ removedAttachments.forEach(this.revokePreviewUri);
3397
3613
  };
3398
3614
  this.getUploadConfigCheck = async (fileLike) => {
3399
3615
  const client = this.channel.getClient();
@@ -3545,21 +3761,27 @@ var _AttachmentManager = class _AttachmentManager {
3545
3761
  localMetadata: {
3546
3762
  ...attachment.localMetadata,
3547
3763
  uploadState: "failed",
3548
- uploadProgress: void 0
3764
+ uploadProgress: void 0,
3765
+ // `false`, not `undefined`: these updates go through `updateAttachment`, which merges
3766
+ // via `mergeWith` — and that skips `undefined` source values, so `undefined` would
3767
+ // leave a stale `true` in place.
3768
+ uploadConfirmationPending: false
3549
3769
  }
3550
3770
  };
3551
- this.client.notifications.addError({
3552
- message: "Error uploading attachment",
3553
- origin: {
3554
- emitter: "AttachmentManager",
3555
- context: { attachment, failedAttachment }
3556
- },
3557
- options: {
3558
- type: "api:attachment:upload:failed",
3559
- metadata: { reason },
3560
- originalError: error instanceof Error ? error : void 0
3561
- }
3562
- });
3771
+ if (!isUploadCancellation(error)) {
3772
+ this.client.notifications.addError({
3773
+ message: "Error uploading attachment",
3774
+ origin: {
3775
+ emitter: "AttachmentManager",
3776
+ context: { attachment, failedAttachment }
3777
+ },
3778
+ options: {
3779
+ type: "api:attachment:upload:failed",
3780
+ metadata: { reason },
3781
+ originalError: error instanceof Error ? error : void 0
3782
+ }
3783
+ });
3784
+ }
3563
3785
  this.updateAttachment(failedAttachment);
3564
3786
  return failedAttachment;
3565
3787
  }
@@ -3572,7 +3794,9 @@ var _AttachmentManager = class _AttachmentManager {
3572
3794
  localMetadata: {
3573
3795
  ...attachment.localMetadata,
3574
3796
  uploadState: "finished",
3575
- uploadProgress: void 0
3797
+ uploadProgress: void 0,
3798
+ // See the note in the failure branch: `undefined` cannot clear through `mergeWith`.
3799
+ uploadConfirmationPending: false
3576
3800
  }
3577
3801
  };
3578
3802
  const previewUri = uploadedAttachment.localMetadata.previewUri;
@@ -3621,7 +3845,9 @@ var _AttachmentManager = class _AttachmentManager {
3621
3845
  localMetadata: {
3622
3846
  ...attachment.localMetadata,
3623
3847
  uploadState: error ? "failed" : "finished",
3624
- uploadProgress: void 0
3848
+ uploadProgress: void 0,
3849
+ // See the note in `uploadAttachment`: `undefined` cannot clear through `mergeWith`.
3850
+ uploadConfirmationPending: false
3625
3851
  }
3626
3852
  },
3627
3853
  error,
@@ -3646,7 +3872,7 @@ var _AttachmentManager = class _AttachmentManager {
3646
3872
  );
3647
3873
  };
3648
3874
  this.composer = composer;
3649
- this.state = new StateStore(initState({ message }));
3875
+ this.state = new StateStore(initState2({ message }));
3650
3876
  this.attachmentsByIdGetterCache = { attachmentsById: {}, attachments: [] };
3651
3877
  this.preUploadMiddlewareExecutor = new AttachmentPreUploadMiddlewareExecutor({
3652
3878
  composer
@@ -3751,7 +3977,8 @@ var _AttachmentManager = class _AttachmentManager {
3751
3977
  localMetadata: {
3752
3978
  ...attachment.localMetadata,
3753
3979
  uploadState: "uploading",
3754
- uploadProgress: this.config.trackUploadProgress ? 0 : void 0
3980
+ uploadProgress: this.config.trackUploadProgress ? 0 : void 0,
3981
+ uploadConfirmationPending: false
3755
3982
  }
3756
3983
  }
3757
3984
  ]);
@@ -3764,7 +3991,8 @@ var _AttachmentManager = class _AttachmentManager {
3764
3991
  localMetadata: {
3765
3992
  ...attachment.localMetadata,
3766
3993
  uploadState: "uploading",
3767
- uploadProgress: nextUpload.uploadProgress
3994
+ uploadProgress: nextUpload.uploadProgress,
3995
+ uploadConfirmationPending: nextUpload.uploadConfirmationPending
3768
3996
  }
3769
3997
  });
3770
3998
  }
@@ -3818,6 +4046,50 @@ var localAttachmentToAttachment = (localAttachment) => {
3818
4046
  const { localMetadata, ...attachment } = localAttachment;
3819
4047
  return attachment;
3820
4048
  };
4049
+ var composeWithPendingUploads = ({
4050
+ composer,
4051
+ state
4052
+ }) => {
4053
+ const composerIsKeptAsDraft = !!composer.pollId;
4054
+ const relevantAttachments = composer.attachmentManager.attachments.filter(
4055
+ (attachment) => isFinishedUpload(attachment) || !composerIsKeptAsDraft && isPendingUpload(attachment)
4056
+ );
4057
+ const localAttachments = (state.localMessage.attachments ?? []).concat(
4058
+ relevantAttachments.map(
4059
+ (attachment) => isPendingUpload(attachment) ? attachment : localAttachmentToAttachment(attachment)
4060
+ )
4061
+ );
4062
+ const messageAttachments = (state.message.attachments ?? []).concat(
4063
+ relevantAttachments.filter(isFinishedUpload).map(localAttachmentToAttachment)
4064
+ );
4065
+ if (!localAttachments.length && !messageAttachments.length) return state;
4066
+ return {
4067
+ ...state,
4068
+ localMessage: localAttachments.length ? {
4069
+ ...state.localMessage,
4070
+ attachments: localAttachments
4071
+ } : state.localMessage,
4072
+ message: messageAttachments.length ? {
4073
+ ...state.message,
4074
+ attachments: messageAttachments
4075
+ } : state.message
4076
+ };
4077
+ };
4078
+ var createSendWithPendingUploadsAttachmentsMiddleware = (composer) => ({
4079
+ allowsPendingUploads: true,
4080
+ id: "stream-io/message-composer-middleware/attachments",
4081
+ handlers: {
4082
+ compose: ({
4083
+ state,
4084
+ next,
4085
+ forward
4086
+ }) => {
4087
+ const { attachmentManager } = composer;
4088
+ if (!attachmentManager) return forward();
4089
+ return next(composeWithPendingUploads({ composer, state }));
4090
+ }
4091
+ }
4092
+ });
3821
4093
  var createAttachmentsCompositionMiddleware = (composer) => ({
3822
4094
  id: "stream-io/message-composer-middleware/attachments",
3823
4095
  handlers: {
@@ -4034,7 +4306,7 @@ var getInitialMentions = (message) => {
4034
4306
  };
4035
4307
  var isSameMentionEntity = (first, second) => first.id === second.id && first.mentionType === second.mentionType;
4036
4308
  var getMentionsFromState = (state) => state.mentions ?? state.mentionedUsers.map(userResponseToMentionEntity);
4037
- var initState2 = ({
4309
+ var initState3 = ({
4038
4310
  composer,
4039
4311
  message
4040
4312
  }) => {
@@ -4063,7 +4335,7 @@ var initState2 = ({
4063
4335
  var TextComposer = class {
4064
4336
  constructor({ composer, message }) {
4065
4337
  this.initState = ({ message } = {}) => {
4066
- this.state.next(initState2({ composer: this.composer, message }));
4338
+ this.state.next(initState3({ composer: this.composer, message }));
4067
4339
  };
4068
4340
  this.getSnapshot = (state = this.state.getLatestValue()) => state;
4069
4341
  this.restoreSnapshot = (snapshot) => {
@@ -4256,7 +4528,7 @@ var TextComposer = class {
4256
4528
  this.commitState(output.state);
4257
4529
  };
4258
4530
  this.composer = composer;
4259
- this.state = new StateStore(initState2({ composer, message }));
4531
+ this.state = new StateStore(initState3({ composer, message }));
4260
4532
  this.middlewareExecutor = new TextComposerMiddlewareExecutor({ composer });
4261
4533
  }
4262
4534
  get channel() {
@@ -5958,6 +6230,13 @@ var MessageComposerMiddlewareExecutor = class extends MiddlewareExecutor {
5958
6230
  createCompositionDataCleanupMiddleware(composer)
5959
6231
  ]);
5960
6232
  }
6233
+ /**
6234
+ * Narrows the base accessor to the composition middleware type, so callers can read the
6235
+ * declarations it carries (`allowsPendingUploads`) without casting.
6236
+ */
6237
+ get installedMiddleware() {
6238
+ return super.installedMiddleware;
6239
+ }
5961
6240
  };
5962
6241
  var MessageDraftComposerMiddlewareExecutor = class extends MiddlewareExecutor {
5963
6242
  constructor({ composer }) {
@@ -7072,7 +7351,7 @@ var DEFAULT_COMPOSER_CONFIG = {
7072
7351
  };
7073
7352
 
7074
7353
  // src/messageComposer/CustomDataManager.ts
7075
- var initState3 = (options) => {
7354
+ var initState4 = (options) => {
7076
7355
  if (!options)
7077
7356
  return { message: {}, custom: {} };
7078
7357
  return { message: {}, custom: {} };
@@ -7081,14 +7360,14 @@ var CustomDataManager = class {
7081
7360
  constructor({ composer, message }) {
7082
7361
  this.isMessageDataEqual = (nextState, previousState) => JSON.stringify(nextState.message) === JSON.stringify(previousState?.message);
7083
7362
  this.initState = ({ message } = {}) => {
7084
- this.state.next(initState3({ composer: this.composer, message }));
7363
+ this.state.next(initState4({ composer: this.composer, message }));
7085
7364
  };
7086
7365
  this.getSnapshot = () => this.state.getLatestValue();
7087
7366
  this.restoreSnapshot = (snapshot) => {
7088
7367
  this.state.next(snapshot);
7089
7368
  };
7090
7369
  this.composer = composer;
7091
- this.state = new StateStore(initState3({ composer, message }));
7370
+ this.state = new StateStore(initState4({ composer, message }));
7092
7371
  }
7093
7372
  get customMessageData() {
7094
7373
  return this.state.getLatestValue().message;
@@ -7124,7 +7403,7 @@ var LinkPreviewStatus = /* @__PURE__ */ ((LinkPreviewStatus2) => {
7124
7403
  return LinkPreviewStatus2;
7125
7404
  })(LinkPreviewStatus || {});
7126
7405
  var linkPreviewArrayToMap = (linkPreviews) => new Map(linkPreviews.map((linkPreview) => [linkPreview.og_scrape_url, linkPreview]));
7127
- var initState4 = ({
7406
+ var initState5 = ({
7128
7407
  message
7129
7408
  }) => message ? {
7130
7409
  previews: message.attachments?.reduce((acc, attachment) => {
@@ -7143,7 +7422,7 @@ var _LinkPreviewsManager = class _LinkPreviewsManager {
7143
7422
  this.shouldDiscardEnrichQueries = false;
7144
7423
  this.initState = ({ message } = {}) => {
7145
7424
  this.cancelURLEnrichment();
7146
- this.state.next(initState4({ message: this.enabled ? message : void 0 }));
7425
+ this.state.next(initState5({ message: this.enabled ? message : void 0 }));
7147
7426
  };
7148
7427
  this.getSnapshot = () => this.state.getLatestValue();
7149
7428
  this.restoreSnapshot = (snapshot) => {
@@ -7246,7 +7525,7 @@ var _LinkPreviewsManager = class _LinkPreviewsManager {
7246
7525
  };
7247
7526
  this.composer = composer;
7248
7527
  this.state = new StateStore(
7249
- initState4({ message: this.enabled ? message : void 0 })
7528
+ initState5({ message: this.enabled ? message : void 0 })
7250
7529
  );
7251
7530
  this.findAndEnrichUrls = debounce(
7252
7531
  this._findAndEnrichUrls.bind(this),
@@ -7334,7 +7613,7 @@ var LinkPreviewsManager = _LinkPreviewsManager;
7334
7613
 
7335
7614
  // src/messageComposer/LocationComposer.ts
7336
7615
  var MIN_LIVE_LOCATION_SHARE_DURATION = 60 * 1e3;
7337
- var initState5 = ({
7616
+ var initState6 = ({
7338
7617
  message
7339
7618
  }) => ({
7340
7619
  location: message?.shared_location ?? null
@@ -7342,7 +7621,7 @@ var initState5 = ({
7342
7621
  var LocationComposer = class {
7343
7622
  constructor({ composer, message }) {
7344
7623
  this.initState = ({ message } = {}) => {
7345
- this.state.next(initState5({ message }));
7624
+ this.state.next(initState6({ message }));
7346
7625
  };
7347
7626
  this.getSnapshot = () => this.state.getLatestValue();
7348
7627
  this.restoreSnapshot = (snapshot) => {
@@ -7360,7 +7639,7 @@ var LocationComposer = class {
7360
7639
  });
7361
7640
  };
7362
7641
  this.composer = composer;
7363
- this.state = new StateStore(initState5({ message }));
7642
+ this.state = new StateStore(initState6({ message }));
7364
7643
  this._deviceId = this.config.getDeviceId();
7365
7644
  }
7366
7645
  get config() {
@@ -8081,7 +8360,7 @@ var initEditingAuditState = (composition) => {
8081
8360
  }
8082
8361
  };
8083
8362
  };
8084
- var initState6 = (composition) => {
8363
+ var initState7 = (composition) => {
8085
8364
  if (!composition) {
8086
8365
  return {
8087
8366
  draftId: null,
@@ -8168,7 +8447,7 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
8168
8447
  this.textComposer.initState({ message });
8169
8448
  this.pollComposer.initState();
8170
8449
  this.customDataManager.initState({ message });
8171
- this.state.next(initState6(composition));
8450
+ this.state.next(initState7(composition));
8172
8451
  };
8173
8452
  this.initStateFromChannelResponse = (channelApiResponse) => {
8174
8453
  if (this.channel.cid !== channelApiResponse.channel.cid) {
@@ -8634,7 +8913,7 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
8634
8913
  this.editingAuditState = new StateStore(
8635
8914
  this.initEditingAuditState(composition)
8636
8915
  );
8637
- this.state = new StateStore(initState6(composition));
8916
+ this.state = new StateStore(initState7(composition));
8638
8917
  this.compositionMiddlewareExecutor = new MessageComposerMiddlewareExecutor({
8639
8918
  composer: this
8640
8919
  });
@@ -8713,8 +8992,32 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
8713
8992
  const currentCommand = this.textComposer.command;
8714
8993
  return !currentCommand || this.validateCommandSendability(currentCommand).ready;
8715
8994
  }
8995
+ /**
8996
+ * Whether the installed composition middleware composes messages whose uploads are still in
8997
+ * flight — see `MessageCompositionMiddleware.allowsPendingUploads`.
8998
+ *
8999
+ * Derived from the chain rather than from config, so there is exactly one switch: the UI SDK
9000
+ * that implements the rest of the flow installs the middleware, and everything that has to
9001
+ * follow from that (starting with {@link hasSendableData}) follows on its own.
9002
+ *
9003
+ * **Temporary API.** In v10 sending with pending uploads becomes part of the composer
9004
+ * configuration (`MessageComposerConfig`, set through `updateConfig`), and this getter,
9005
+ * together with the middleware declaration it reads, is replaced by that config field.
9006
+ */
9007
+ get allowsPendingUploads() {
9008
+ return this.compositionMiddlewareExecutor.installedMiddleware.some(
9009
+ (middleware) => middleware.allowsPendingUploads
9010
+ );
9011
+ }
8716
9012
  get hasSendableData() {
8717
- return this.isCommandSendable && !!(!this.attachmentManager.uploadsInProgressCount && (!this.textComposer.textIsEmpty || this.attachmentManager.successfulUploadsCount > 0) || this.pollId || !!this.locationComposer.validLocation);
9013
+ if (!this.isCommandSendable) return false;
9014
+ if (this.allowsPendingUploads) {
9015
+ const hasSendableAttachment = this.attachmentManager.attachments.some(
9016
+ (attachment) => isFinishedUpload(attachment) || isPendingUpload(attachment)
9017
+ );
9018
+ return !!(!this.textComposer.textIsEmpty || hasSendableAttachment || this.pollId || this.locationComposer.validLocation);
9019
+ }
9020
+ return !!(!this.attachmentManager.uploadsInProgressCount && (!this.textComposer.textIsEmpty || this.attachmentManager.successfulUploadsCount > 0) || this.pollId || !!this.locationComposer.validLocation);
8718
9021
  }
8719
9022
  get compositionIsEmpty() {
8720
9023
  return !this.quotedMessage && this.contentIsEmpty;
@@ -9341,14 +9644,16 @@ var Channel = class {
9341
9644
  *
9342
9645
  * @return {Promise<SendMessageAPIResponse>} The Server Response
9343
9646
  */
9344
- async _sendMessage(message, options) {
9345
- return await this.getClient().post(
9346
- this._channelURL() + "/message",
9347
- {
9348
- message,
9349
- ...options
9350
- }
9351
- );
9647
+ async _sendMessage(rawMessage, options) {
9648
+ const client = this.getClient();
9649
+ const message = sanitizeOutgoingAttachments({
9650
+ client,
9651
+ message: rawMessage
9652
+ });
9653
+ return await client.post(this._channelURL() + "/message", {
9654
+ message,
9655
+ ...options
9656
+ });
9352
9657
  }
9353
9658
  async sendMessage(message, options) {
9354
9659
  try {
@@ -10799,6 +11104,7 @@ var Channel = class {
10799
11104
  // delivery-report network sync below is skipped for it.
10800
11105
  case "message.read_locally":
10801
11106
  case "message.read":
11107
+ if (event.thread) break;
10802
11108
  if (event.user?.id && event.created_at) {
10803
11109
  const previousReadState = channelState.read[event.user.id];
10804
11110
  channelState.read[event.user.id] = {
@@ -11248,7 +11554,7 @@ var ClientState = class {
11248
11554
  var import_isomorphic_ws = __toESM(require("isomorphic-ws"));
11249
11555
 
11250
11556
  // src/insights.ts
11251
- var import_axios = __toESM(require("axios"));
11557
+ var import_axios2 = __toESM(require("axios"));
11252
11558
  var InsightMetrics = class {
11253
11559
  constructor() {
11254
11560
  this.connectionStartTimestamp = null;
@@ -11261,7 +11567,7 @@ var postInsights = async (insightType, insights) => {
11261
11567
  const maxAttempts = 3;
11262
11568
  for (let i = 0; i < maxAttempts; i++) {
11263
11569
  try {
11264
- await import_axios.default.post(
11570
+ await import_axios2.default.post(
11265
11571
  `https://chat-insights.getstream.io/insights/${insightType}`,
11266
11572
  insights
11267
11573
  );
@@ -11769,131 +12075,6 @@ var StableWSConnection = class {
11769
12075
  }
11770
12076
  };
11771
12077
 
11772
- // src/uploadManager.ts
11773
- var initState7 = () => ({ uploads: {} });
11774
- var upsertById = (uploads, record) => ({
11775
- ...uploads,
11776
- [record.id]: { ...uploads[record.id], ...record }
11777
- });
11778
- var updateById = (uploads, record) => {
11779
- if (!(record.id in uploads)) return null;
11780
- const current = uploads[record.id];
11781
- return { ...uploads, [record.id]: { ...current, ...record } };
11782
- };
11783
- var UploadManager = class {
11784
- constructor(client) {
11785
- this.client = client;
11786
- this.inFlightUploads = /* @__PURE__ */ new Map();
11787
- this.getUpload = (id) => this.uploads[id];
11788
- /**
11789
- * Clears all upload records.
11790
- * Invoked when the user disconnects so a later session does not inherit stale upload state.
11791
- * Aborts every in-flight upload request via its `UploadRequestOptions.abortSignal`.
11792
- */
11793
- this.reset = () => {
11794
- for (const { abortController } of this.inFlightUploads.values()) {
11795
- abortController.abort();
11796
- }
11797
- this.inFlightUploads.clear();
11798
- this.state.next(initState7());
11799
- };
11800
- /**
11801
- * Removes the upload record for `id` if present.
11802
- * If an upload is still in progress, aborts its `UploadRequestOptions.abortSignal`.
11803
- */
11804
- this.deleteUploadRecord = (id) => {
11805
- const flight = this.inFlightUploads.get(id);
11806
- if (flight) {
11807
- this.inFlightUploads.delete(id);
11808
- flight.abortController.abort();
11809
- }
11810
- this.state.next((current) => {
11811
- if (!(id in current.uploads)) return current;
11812
- const uploads = { ...current.uploads };
11813
- delete uploads[id];
11814
- return { ...current, uploads };
11815
- });
11816
- };
11817
- /**
11818
- * Starts an upload for `id`, or returns the existing in-flight promise if one is already running.
11819
- * Uses {@link StreamChat.channel}(`channelCid`) → `messageComposer.attachmentManager.doUploadRequest`.
11820
- * Resolves with that result; rejects if the upload rejects (the record is removed from state either way).
11821
- */
11822
- this.upload = ({
11823
- id,
11824
- channelCid,
11825
- file
11826
- }) => {
11827
- const existing = this.inFlightUploads.get(id);
11828
- if (existing) return existing.promise;
11829
- let resolvePromise;
11830
- let rejectPromise;
11831
- const promise = new Promise((resolve, reject) => {
11832
- resolvePromise = resolve;
11833
- rejectPromise = reject;
11834
- });
11835
- const abortController = new AbortController();
11836
- this.inFlightUploads.set(id, { promise, abortController });
11837
- void (async () => {
11838
- const attachmentManager = this.resolveAttachmentManager(channelCid);
11839
- const trackProgress = attachmentManager.config.trackUploadProgress;
11840
- try {
11841
- this.upsertUpload({
11842
- id,
11843
- uploadProgress: trackProgress ? 0 : void 0
11844
- });
11845
- const onProgress = trackProgress ? (progress) => {
11846
- this.updateUpload({
11847
- id,
11848
- uploadProgress: progress
11849
- });
11850
- } : void 0;
11851
- const uploadRequestOptions = {
11852
- abortSignal: abortController.signal,
11853
- ...onProgress ? { onProgress } : {}
11854
- };
11855
- const response = await attachmentManager.doUploadRequest(
11856
- file,
11857
- uploadRequestOptions
11858
- );
11859
- resolvePromise(response);
11860
- } catch (error) {
11861
- rejectPromise(error);
11862
- } finally {
11863
- this.inFlightUploads.delete(id);
11864
- this.deleteUploadRecord(id);
11865
- }
11866
- })();
11867
- return promise;
11868
- };
11869
- this.upsertUpload = (record) => {
11870
- this.state.partialNext({
11871
- uploads: upsertById(this.uploads, record)
11872
- });
11873
- };
11874
- this.updateUpload = (record) => {
11875
- this.state.next((current) => {
11876
- const nextUploads = updateById(current.uploads, record);
11877
- if (!nextUploads) return current;
11878
- return { ...current, uploads: nextUploads };
11879
- });
11880
- };
11881
- this.state = new StateStore(initState7());
11882
- }
11883
- resolveAttachmentManager(channelCid) {
11884
- const colon = channelCid.indexOf(":");
11885
- if (colon <= 0 || colon === channelCid.length - 1) {
11886
- throw new Error(`Invalid channelCid: ${channelCid}`);
11887
- }
11888
- const channelType = channelCid.slice(0, colon);
11889
- const channelId = channelCid.slice(colon + 1);
11890
- return this.client.channel(channelType, channelId).messageComposer.attachmentManager;
11891
- }
11892
- get uploads() {
11893
- return this.state.getLatestValue().uploads;
11894
- }
11895
- };
11896
-
11897
12078
  // src/signing.ts
11898
12079
  var import_jsonwebtoken = __toESM(require("jsonwebtoken"));
11899
12080
  var import_crypto = __toESM(require("crypto"));
@@ -12152,7 +12333,7 @@ var TokenManager = class {
12152
12333
  };
12153
12334
 
12154
12335
  // src/connection_fallback.ts
12155
- var import_axios2 = __toESM(require("axios"));
12336
+ var import_axios3 = __toESM(require("axios"));
12156
12337
  var WSConnectionFallback = class {
12157
12338
  constructor({ client }) {
12158
12339
  /** @private */
@@ -12171,7 +12352,7 @@ var WSConnectionFallback = class {
12171
12352
  /** @private */
12172
12353
  this._req = async (params, config, retry) => {
12173
12354
  if (!this.cancelToken && !params.close) {
12174
- this.cancelToken = import_axios2.default.CancelToken.source();
12355
+ this.cancelToken = import_axios3.default.CancelToken.source();
12175
12356
  }
12176
12357
  try {
12177
12358
  const res = await this.client.doAxiosRequest(
@@ -12207,7 +12388,7 @@ var WSConnectionFallback = class {
12207
12388
  }
12208
12389
  }
12209
12390
  } catch (error) {
12210
- if (import_axios2.default.isCancel(error)) {
12391
+ if (import_axios3.default.isCancel(error)) {
12211
12392
  this._log(`_poll() - axios canceled request`);
12212
12393
  return;
12213
12394
  }
@@ -14630,7 +14811,7 @@ var StreamChat = class _StreamChat {
14630
14811
  return this.handleResponse(response);
14631
14812
  } catch (e) {
14632
14813
  e.client_request_id = requestConfig.headers?.["x-client-request-id"];
14633
- if (import_axios3.default.isCancel(e)) throw e;
14814
+ if (import_axios4.default.isCancel(e)) throw e;
14634
14815
  this._logApiError(type, url, e);
14635
14816
  this.consecutiveFailures += 1;
14636
14817
  if (e.response) {
@@ -14942,7 +15123,7 @@ var StreamChat = class _StreamChat {
14942
15123
  return this.userAgent;
14943
15124
  }
14944
15125
  if (!this.cachedUserAgent) {
14945
- const version = "9.51.0";
15126
+ const version = "9.52.0";
14946
15127
  const { name: sdkName, version: sdkVersion } = this.sdkIdentifier ?? {};
14947
15128
  const { name: appName, version: appVersion } = this.appIdentifier ?? {};
14948
15129
  const { os, model: deviceModel } = this.deviceIdentifier ?? {};
@@ -15010,7 +15191,7 @@ var StreamChat = class _StreamChat {
15010
15191
  keepAliveMsecs: 3e3
15011
15192
  });
15012
15193
  }
15013
- this.axiosInstance = import_axios3.default.create(this.options);
15194
+ this.axiosInstance = import_axios4.default.create(this.options);
15014
15195
  this.setBaseURL(this.options.baseURL || "https://chat.stream-io-api.com");
15015
15196
  if (typeof process !== "undefined" && "env" in process && process.env.STREAM_LOCAL_TEST_RUN) {
15016
15197
  this.setBaseURL("http://localhost:3030");
@@ -16362,6 +16543,11 @@ var StreamChat = class _StreamChat {
16362
16543
  }
16363
16544
  /**
16364
16545
  * unflagMessage - unflag a message
16546
+ *
16547
+ * @deprecated The `/moderation/unflag` endpoint is deprecated and is a no-op on
16548
+ * the backend - calling this resolves successfully but does not remove the flag.
16549
+ * Use `client.moderation.submitAction()` against the review queue item instead.
16550
+ *
16365
16551
  * @param {string} targetMessageID
16366
16552
  * @param {string} [options.user_id] currentUserID, only used with serverside auth
16367
16553
  * @returns {Promise<APIResponse>}
@@ -16374,6 +16560,11 @@ var StreamChat = class _StreamChat {
16374
16560
  }
16375
16561
  /**
16376
16562
  * unflagUser - unflag a user
16563
+ *
16564
+ * @deprecated The `/moderation/unflag` endpoint is deprecated and is a no-op on
16565
+ * the backend - calling this resolves successfully but does not remove the flag.
16566
+ * Use `client.moderation.submitAction()` against the review queue item instead.
16567
+ *
16377
16568
  * @param {string} targetID
16378
16569
  * @param {string} [options.user_id] currentUserID, only used with serverside auth
16379
16570
  * @returns {Promise<APIResponse>}
@@ -16658,7 +16849,10 @@ var StreamChat = class _StreamChat {
16658
16849
  if (!message.id) {
16659
16850
  throw Error("Please specify the message.id when calling updateMessage");
16660
16851
  }
16661
- const payload = toUpdatedMessagePayload(message);
16852
+ const payload = sanitizeOutgoingAttachments({
16853
+ client: this,
16854
+ message: toUpdatedMessagePayload(message)
16855
+ });
16662
16856
  if (typeof partialUserOrUserId === "string") {
16663
16857
  payload.user_id = partialUserOrUserId;
16664
16858
  } else if (typeof partialUserOrUserId?.id === "string") {
@@ -18474,7 +18668,7 @@ var OfflineError = class extends Error {
18474
18668
  };
18475
18669
 
18476
18670
  // src/offline-support/offline_sync_manager.ts
18477
- var import_axios4 = require("axios");
18671
+ var import_axios5 = require("axios");
18478
18672
  var OfflineDBSyncManager = class {
18479
18673
  constructor({
18480
18674
  client,
@@ -18622,7 +18816,7 @@ var OfflineDBSyncManager = class {
18622
18816
  });
18623
18817
  } catch (e) {
18624
18818
  console.log("An error has occurred while syncing the DB.", e);
18625
- if ((0, import_axios4.isAxiosError)(e) && e.code === "ECONNABORTED") {
18819
+ if ((0, import_axios5.isAxiosError)(e) && e.code === "ECONNABORTED") {
18626
18820
  return;
18627
18821
  }
18628
18822
  const error = e;
@@ -19131,6 +19325,7 @@ var AbstractOfflineDB = class {
19131
19325
  return this.handleMessageUpdatedEvent({ event, execute });
19132
19326
  }
19133
19327
  if (type === "message.read" || type === "notification.mark_read") {
19328
+ if (event.thread) return [];
19134
19329
  return this.handleRead({ event, unreadMessages: 0, execute });
19135
19330
  }
19136
19331
  if (type === "message.read_locally") {
@@ -19698,6 +19893,7 @@ var LiveLocationManager = _LiveLocationManager;
19698
19893
  createMessageComposerStateCompositionMiddleware,
19699
19894
  createPollComposerStateMiddleware,
19700
19895
  createPostUploadAttachmentEnrichmentMiddleware,
19896
+ createSendWithPendingUploadsAttachmentsMiddleware,
19701
19897
  createSharedLocationCompositionMiddleware,
19702
19898
  createTextComposerCompositionMiddleware,
19703
19899
  createTextComposerPreValidationMiddleware,
@@ -19726,6 +19922,7 @@ var LiveLocationManager = _LiveLocationManager;
19726
19922
  getTokenizedSuggestionDisplayName,
19727
19923
  getTriggerCharWithToken,
19728
19924
  gunzipPayload,
19925
+ hasPending,
19729
19926
  insertItemWithTrigger,
19730
19927
  isAudioAttachment,
19731
19928
  isBlobButNotFile,
@@ -19733,6 +19930,7 @@ var LiveLocationManager = _LiveLocationManager;
19733
19930
  isFileAttachment,
19734
19931
  isFileList,
19735
19932
  isFileReference,
19933
+ isFinishedUpload,
19736
19934
  isGiphyAttachment,
19737
19935
  isImageAttachment,
19738
19936
  isImageFile,
@@ -19745,9 +19943,11 @@ var LiveLocationManager = _LiveLocationManager;
19745
19943
  isLocalVoiceRecordingAttachment,
19746
19944
  isOwnUser,
19747
19945
  isPatch,
19946
+ isPendingUpload,
19748
19947
  isScrapedContent,
19749
19948
  isSharedLocationResponse,
19750
19949
  isTargetedOptionTextUpdate,
19950
+ isUploadCancellation,
19751
19951
  isUploadedAttachment,
19752
19952
  isVideoAttachment,
19753
19953
  isVoiceRecordingAttachment,
@@ -19767,11 +19967,14 @@ var LiveLocationManager = _LiveLocationManager;
19767
19967
  readFileAsArrayBuffer,
19768
19968
  removeDiacritics,
19769
19969
  replaceWordWithEntity,
19970
+ settled,
19770
19971
  stripCommandFromText,
19771
19972
  stripMentionTokens,
19772
19973
  textIsEmpty,
19773
19974
  timeLeftMs,
19774
19975
  verifyAndParseWebhook,
19775
- verifySignature
19976
+ verifySignature,
19977
+ withCancellation,
19978
+ withoutConcurrency
19776
19979
  });
19777
19980
  //# sourceMappingURL=index.node.js.map