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
@@ -316,7 +316,7 @@ var ChannelBatchUpdater = class {
316
316
 
317
317
  // src/client.ts
318
318
  var import_https = __toESM(require_https());
319
- import axios3 from "axios";
319
+ import axios4 from "axios";
320
320
 
321
321
  // src/utils.ts
322
322
  import FormData from "form-data";
@@ -575,6 +575,38 @@ function unformatMessage(message) {
575
575
  quoted_message: toMessageResponseBase(message.quoted_message)
576
576
  };
577
577
  }
578
+ var isRemoteUrl = (url) => !!url && /^https?:\/\//i.test(url.trim());
579
+ var sanitizeOutgoingAttachments = ({
580
+ client,
581
+ message
582
+ }) => {
583
+ const attachments = message.attachments;
584
+ if (!attachments?.length) return message;
585
+ const unresolved = [];
586
+ let changed = false;
587
+ const sanitized = attachments.reduce((acc, attachment) => {
588
+ const { localMetadata, ...rest } = attachment;
589
+ if (!localMetadata) {
590
+ acc.push(attachment);
591
+ return acc;
592
+ }
593
+ changed = true;
594
+ const hasRemoteSource = isRemoteUrl(rest.asset_url) || isRemoteUrl(rest.image_url) || isRemoteUrl(rest.og_scrape_url) || isRemoteUrl(rest.title_link);
595
+ if (hasRemoteSource) {
596
+ acc.push(rest);
597
+ } else {
598
+ unresolved.push(rest);
599
+ }
600
+ return acc;
601
+ }, []);
602
+ if (unresolved.length) {
603
+ const text = `Dropped ${unresolved.length} attachment(s) whose upload never completed. Composing with pending uploads requires awaiting them before sending.`;
604
+ const extraData = { attachments: unresolved, message, tags: ["attachments"] };
605
+ client.logger("warn", text, extraData);
606
+ console.warn(text, extraData);
607
+ }
608
+ return changed ? { ...message, attachments: sanitized } : message;
609
+ };
578
610
  var localMessageToNewMessagePayload = (localMessage) => {
579
611
  const {
580
612
  // Remove all timestamp fields and client-specific fields.
@@ -2347,6 +2379,12 @@ var CooldownTimer = class extends WithSubscriptions {
2347
2379
  var isScrapedContent = (attachment) => !!attachment?.og_scrape_url || !!attachment?.title_link;
2348
2380
  var isLocalAttachment = (attachment) => !!attachment?.localMetadata?.id;
2349
2381
  var isLocalUploadAttachment = (attachment) => !!attachment?.localMetadata?.uploadState;
2382
+ var PENDING_UPLOAD_STATES = [
2383
+ "pending",
2384
+ "uploading"
2385
+ ];
2386
+ var isPendingUpload = (attachment) => isLocalUploadAttachment(attachment) && PENDING_UPLOAD_STATES.includes(attachment.localMetadata.uploadState);
2387
+ var isFinishedUpload = (attachment) => isLocalUploadAttachment(attachment) && attachment.localMetadata.uploadState === "finished";
2350
2388
  var isFileAttachment = (attachment, supportedVideoFormat = []) => attachment.type === "file" || !!(attachment.mime_type && supportedVideoFormat.indexOf(attachment.mime_type) === -1 && attachment.type !== "video");
2351
2389
  var isLocalFileAttachment = (attachment) => isFileAttachment(attachment) && isLocalAttachment(attachment);
2352
2390
  var isImageAttachment = (attachment) => attachment.type === "image" && !isScrapedContent(attachment);
@@ -2422,7 +2460,7 @@ var ensureIsLocalAttachment = (attachment) => {
2422
2460
  };
2423
2461
 
2424
2462
  // src/messageComposer/middleware/attachmentManager/postUpload/attachmentEnrichment.ts
2425
- var createPostUploadAttachmentEnrichmentMiddleware = () => ({
2463
+ var createPostUploadAttachmentEnrichmentMiddleware = (composer) => ({
2426
2464
  id: "stream-io/attachment-manager-middleware/post-upload-enrichment",
2427
2465
  handlers: {
2428
2466
  postProcess: ({
@@ -2435,9 +2473,10 @@ var createPostUploadAttachmentEnrichmentMiddleware = () => ({
2435
2473
  if (error) return forward();
2436
2474
  if (!attachment || !response) return discard();
2437
2475
  const enrichedAttachment = { ...attachment };
2438
- const previewUri = attachment.localMetadata.previewUri;
2439
- if (previewUri) {
2440
- if (previewUri.startsWith("blob:")) URL.revokeObjectURL(previewUri);
2476
+ const { id, previewUri } = attachment.localMetadata;
2477
+ const attachmentIsStillInComposer = !composer || !!composer.attachmentManager.attachmentsById[id];
2478
+ if (previewUri && attachmentIsStillInComposer) {
2479
+ if (previewUri.startsWith("blob:")) URL.revokeObjectURL?.(previewUri);
2441
2480
  delete enrichedAttachment.localMetadata.previewUri;
2442
2481
  }
2443
2482
  if (isLocalImageAttachment(attachment)) {
@@ -2460,6 +2499,12 @@ var createPostUploadAttachmentEnrichmentMiddleware = () => ({
2460
2499
  var withoutConcurrency = createRunner(wrapWithContinuationTracking);
2461
2500
  var withCancellation = createRunner(wrapWithCancellation);
2462
2501
  var pendingPromises = /* @__PURE__ */ new Map();
2502
+ function hasPending(tag) {
2503
+ return pendingPromises.has(tag);
2504
+ }
2505
+ async function settled(tag) {
2506
+ await pendingPromises.get(tag)?.promise;
2507
+ }
2463
2508
  function createRunner(wrapper) {
2464
2509
  return function run(tag, cb) {
2465
2510
  const { cb: wrapped, onContinued } = wrapper(tag, cb);
@@ -2502,6 +2547,16 @@ var MiddlewareExecutor = class {
2502
2547
  this.middleware = [];
2503
2548
  this.id = generateUUIDv4();
2504
2549
  }
2550
+ /**
2551
+ * The middleware currently installed, in execution order.
2552
+ *
2553
+ * Read-only: use `use` / `insert` / `replace` / `setOrder` / `remove` to change the chain. It
2554
+ * exists so components can ask what the chain is capable of - a middleware may declare
2555
+ * behaviour its host has to account for, and the host has no other way to find out.
2556
+ */
2557
+ get installedMiddleware() {
2558
+ return this.middleware;
2559
+ }
2505
2560
  use(middleware) {
2506
2561
  this.middleware = this.middleware.concat(middleware);
2507
2562
  return this;
@@ -2605,6 +2660,142 @@ var MiddlewareExecutor = class {
2605
2660
  }
2606
2661
  };
2607
2662
 
2663
+ // src/uploadManager.ts
2664
+ import axios from "axios";
2665
+ var isUploadCancellation = (error) => axios.isCancel(error) || error instanceof Error && error.name === "AbortError";
2666
+ var initState = () => ({ uploads: {} });
2667
+ var upsertById = (uploads, record) => ({
2668
+ ...uploads,
2669
+ [record.id]: { ...uploads[record.id], ...record }
2670
+ });
2671
+ var updateById = (uploads, record) => {
2672
+ if (!(record.id in uploads)) return null;
2673
+ const current = uploads[record.id];
2674
+ return { ...uploads, [record.id]: { ...current, ...record } };
2675
+ };
2676
+ var UploadManager = class {
2677
+ constructor(client) {
2678
+ this.client = client;
2679
+ this.inFlightUploads = /* @__PURE__ */ new Map();
2680
+ this.getUpload = (id) => this.uploads[id];
2681
+ /**
2682
+ * Clears all upload records.
2683
+ * Invoked when the user disconnects so a later session does not inherit stale upload state.
2684
+ * Aborts every in-flight upload request via its `UploadRequestOptions.abortSignal`.
2685
+ */
2686
+ this.reset = () => {
2687
+ for (const { abortController } of this.inFlightUploads.values()) {
2688
+ abortController.abort();
2689
+ }
2690
+ this.inFlightUploads.clear();
2691
+ this.state.next(initState());
2692
+ };
2693
+ /**
2694
+ * Removes the upload record for `id` if present.
2695
+ * If an upload is still in progress, aborts its `UploadRequestOptions.abortSignal`.
2696
+ */
2697
+ this.deleteUploadRecord = (id) => {
2698
+ const flight = this.inFlightUploads.get(id);
2699
+ if (flight) {
2700
+ this.inFlightUploads.delete(id);
2701
+ flight.abortController.abort();
2702
+ }
2703
+ this.state.next((current) => {
2704
+ if (!(id in current.uploads)) return current;
2705
+ const uploads = { ...current.uploads };
2706
+ delete uploads[id];
2707
+ return { ...current, uploads };
2708
+ });
2709
+ };
2710
+ /**
2711
+ * Starts an upload for `id`, or returns the existing in-flight promise if one is already running.
2712
+ * Uses {@link StreamChat.channel}(`channelCid`) → `messageComposer.attachmentManager.doUploadRequest`.
2713
+ * Resolves with that result; rejects if the upload rejects (the record is removed from state either way).
2714
+ */
2715
+ this.upload = ({
2716
+ id,
2717
+ channelCid,
2718
+ file
2719
+ }) => {
2720
+ const existing = this.inFlightUploads.get(id);
2721
+ if (existing) return existing.promise;
2722
+ let resolvePromise;
2723
+ let rejectPromise;
2724
+ const promise = new Promise((resolve, reject) => {
2725
+ resolvePromise = resolve;
2726
+ rejectPromise = reject;
2727
+ });
2728
+ const abortController = new AbortController();
2729
+ this.inFlightUploads.set(id, { promise, abortController });
2730
+ void (async () => {
2731
+ const attachmentManager = this.resolveAttachmentManager(channelCid);
2732
+ const trackProgress = attachmentManager.config.trackUploadProgress;
2733
+ try {
2734
+ this.upsertUpload({
2735
+ uploadConfirmationPending: false,
2736
+ id,
2737
+ uploadProgress: trackProgress ? 0 : void 0
2738
+ });
2739
+ const onProgress = trackProgress ? (progress) => {
2740
+ this.updateUpload({
2741
+ id,
2742
+ uploadProgress: progress,
2743
+ // Only a number says anything about the flush, so a report without one leaves
2744
+ // the flag alone: `undefined` means the transport cannot measure this upload,
2745
+ // not that bytes were un-sent. Lowering it there would drop a UI that had
2746
+ // already gone indeterminate back to a determinate one with nothing to show.
2747
+ //
2748
+ // The record is removed in the `finally` below, so a record that still exists
2749
+ // while reporting 100% can only mean "flushed, awaiting the response".
2750
+ ...typeof progress === "number" ? { uploadConfirmationPending: progress >= 100 } : {}
2751
+ });
2752
+ } : void 0;
2753
+ const uploadRequestOptions = {
2754
+ abortSignal: abortController.signal,
2755
+ ...onProgress ? { onProgress } : {}
2756
+ };
2757
+ const response = await attachmentManager.doUploadRequest(
2758
+ file,
2759
+ uploadRequestOptions
2760
+ );
2761
+ resolvePromise(response);
2762
+ } catch (error) {
2763
+ rejectPromise(error);
2764
+ } finally {
2765
+ this.inFlightUploads.delete(id);
2766
+ this.deleteUploadRecord(id);
2767
+ }
2768
+ })();
2769
+ return promise;
2770
+ };
2771
+ this.upsertUpload = (record) => {
2772
+ this.state.partialNext({
2773
+ uploads: upsertById(this.uploads, record)
2774
+ });
2775
+ };
2776
+ this.updateUpload = (record) => {
2777
+ this.state.next((current) => {
2778
+ const nextUploads = updateById(current.uploads, record);
2779
+ if (!nextUploads) return current;
2780
+ return { ...current, uploads: nextUploads };
2781
+ });
2782
+ };
2783
+ this.state = new StateStore(initState());
2784
+ }
2785
+ resolveAttachmentManager(channelCid) {
2786
+ const colon = channelCid.indexOf(":");
2787
+ if (colon <= 0 || colon === channelCid.length - 1) {
2788
+ throw new Error(`Invalid channelCid: ${channelCid}`);
2789
+ }
2790
+ const channelType = channelCid.slice(0, colon);
2791
+ const channelId = channelCid.slice(colon + 1);
2792
+ return this.client.channel(channelType, channelId).messageComposer.attachmentManager;
2793
+ }
2794
+ get uploads() {
2795
+ return this.state.getLatestValue().uploads;
2796
+ }
2797
+ };
2798
+
2608
2799
  // src/messageComposer/middleware/attachmentManager/postUpload/uploadErrorHandler.ts
2609
2800
  var createUploadErrorHandlerMiddleware = (composer) => ({
2610
2801
  id: "stream-io/attachment-manager-middleware/upload-error",
@@ -2617,6 +2808,7 @@ var createUploadErrorHandlerMiddleware = (composer) => ({
2617
2808
  const { attachment, error } = state;
2618
2809
  if (!error) return forward();
2619
2810
  if (!attachment) return discard();
2811
+ if (isUploadCancellation(error)) return forward();
2620
2812
  const reason = error instanceof Error ? error.message : "unknown error";
2621
2813
  composer.client.notifications.addError({
2622
2814
  message: "Error uploading attachment",
@@ -2641,7 +2833,7 @@ var AttachmentPostUploadMiddlewareExecutor = class extends MiddlewareExecutor {
2641
2833
  super();
2642
2834
  this.use([
2643
2835
  createUploadErrorHandlerMiddleware(composer),
2644
- createPostUploadAttachmentEnrichmentMiddleware()
2836
+ createPostUploadAttachmentEnrichmentMiddleware(composer)
2645
2837
  ]);
2646
2838
  }
2647
2839
  };
@@ -3089,7 +3281,7 @@ function mergeWithDiff(target, source, customizer) {
3089
3281
  }
3090
3282
 
3091
3283
  // src/messageComposer/attachmentManager.ts
3092
- var initState = ({
3284
+ var initState2 = ({
3093
3285
  message
3094
3286
  }) => ({
3095
3287
  attachments: (message?.attachments ?? [])?.filter(({ og_scrape_url }) => !og_scrape_url).map((att) => {
@@ -3117,13 +3309,14 @@ var _AttachmentManager = class _AttachmentManager {
3117
3309
  ...attachment,
3118
3310
  localMetadata: {
3119
3311
  ...attachment.localMetadata,
3312
+ uploadConfirmationPending: false,
3120
3313
  uploadProgress: void 0,
3121
3314
  uploadState: "failed"
3122
3315
  }
3123
3316
  };
3124
3317
  };
3125
3318
  this.initState = ({ message } = {}) => {
3126
- this.state.next(initState({ message }));
3319
+ this.state.next(initState2({ message }));
3127
3320
  };
3128
3321
  this.getSnapshot = () => {
3129
3322
  const state = this.state.getLatestValue();
@@ -3200,8 +3393,22 @@ var _AttachmentManager = class _AttachmentManager {
3200
3393
  this.state.partialNext({ attachments });
3201
3394
  }
3202
3395
  };
3396
+ /**
3397
+ * Releases a local preview URL the SDK minted, if it minted one.
3398
+ *
3399
+ * Only `blob:` URLs are ours to revoke: `toLocalUploadAttachment` puts a
3400
+ * `URL.createObjectURL` result in `previewUri` for a `File`, but for a React Native
3401
+ * `FileReference` it stores the caller's own native file URI, which must be left alone.
3402
+ */
3403
+ this.revokePreviewUri = (attachment) => {
3404
+ const previewUri = attachment.localMetadata?.previewUri;
3405
+ if (previewUri?.startsWith("blob:")) URL.revokeObjectURL?.(previewUri);
3406
+ };
3203
3407
  this.removeAttachments = (localAttachmentIds) => {
3204
3408
  if (!localAttachmentIds.length) return;
3409
+ const removedAttachments = this.attachments.filter(
3410
+ (attachment) => localAttachmentIds.includes(attachment.localMetadata?.id)
3411
+ );
3205
3412
  this.state.partialNext({
3206
3413
  attachments: this.attachments.filter(
3207
3414
  (attachment) => !localAttachmentIds.includes(attachment.localMetadata?.id)
@@ -3210,6 +3417,7 @@ var _AttachmentManager = class _AttachmentManager {
3210
3417
  for (const id of localAttachmentIds) {
3211
3418
  this.client.uploadManager.deleteUploadRecord(id);
3212
3419
  }
3420
+ removedAttachments.forEach(this.revokePreviewUri);
3213
3421
  };
3214
3422
  this.getUploadConfigCheck = async (fileLike) => {
3215
3423
  const client = this.channel.getClient();
@@ -3361,21 +3569,27 @@ var _AttachmentManager = class _AttachmentManager {
3361
3569
  localMetadata: {
3362
3570
  ...attachment.localMetadata,
3363
3571
  uploadState: "failed",
3364
- uploadProgress: void 0
3572
+ uploadProgress: void 0,
3573
+ // `false`, not `undefined`: these updates go through `updateAttachment`, which merges
3574
+ // via `mergeWith` — and that skips `undefined` source values, so `undefined` would
3575
+ // leave a stale `true` in place.
3576
+ uploadConfirmationPending: false
3365
3577
  }
3366
3578
  };
3367
- this.client.notifications.addError({
3368
- message: "Error uploading attachment",
3369
- origin: {
3370
- emitter: "AttachmentManager",
3371
- context: { attachment, failedAttachment }
3372
- },
3373
- options: {
3374
- type: "api:attachment:upload:failed",
3375
- metadata: { reason },
3376
- originalError: error instanceof Error ? error : void 0
3377
- }
3378
- });
3579
+ if (!isUploadCancellation(error)) {
3580
+ this.client.notifications.addError({
3581
+ message: "Error uploading attachment",
3582
+ origin: {
3583
+ emitter: "AttachmentManager",
3584
+ context: { attachment, failedAttachment }
3585
+ },
3586
+ options: {
3587
+ type: "api:attachment:upload:failed",
3588
+ metadata: { reason },
3589
+ originalError: error instanceof Error ? error : void 0
3590
+ }
3591
+ });
3592
+ }
3379
3593
  this.updateAttachment(failedAttachment);
3380
3594
  return failedAttachment;
3381
3595
  }
@@ -3388,7 +3602,9 @@ var _AttachmentManager = class _AttachmentManager {
3388
3602
  localMetadata: {
3389
3603
  ...attachment.localMetadata,
3390
3604
  uploadState: "finished",
3391
- uploadProgress: void 0
3605
+ uploadProgress: void 0,
3606
+ // See the note in the failure branch: `undefined` cannot clear through `mergeWith`.
3607
+ uploadConfirmationPending: false
3392
3608
  }
3393
3609
  };
3394
3610
  const previewUri = uploadedAttachment.localMetadata.previewUri;
@@ -3437,7 +3653,9 @@ var _AttachmentManager = class _AttachmentManager {
3437
3653
  localMetadata: {
3438
3654
  ...attachment.localMetadata,
3439
3655
  uploadState: error ? "failed" : "finished",
3440
- uploadProgress: void 0
3656
+ uploadProgress: void 0,
3657
+ // See the note in `uploadAttachment`: `undefined` cannot clear through `mergeWith`.
3658
+ uploadConfirmationPending: false
3441
3659
  }
3442
3660
  },
3443
3661
  error,
@@ -3462,7 +3680,7 @@ var _AttachmentManager = class _AttachmentManager {
3462
3680
  );
3463
3681
  };
3464
3682
  this.composer = composer;
3465
- this.state = new StateStore(initState({ message }));
3683
+ this.state = new StateStore(initState2({ message }));
3466
3684
  this.attachmentsByIdGetterCache = { attachmentsById: {}, attachments: [] };
3467
3685
  this.preUploadMiddlewareExecutor = new AttachmentPreUploadMiddlewareExecutor({
3468
3686
  composer
@@ -3567,7 +3785,8 @@ var _AttachmentManager = class _AttachmentManager {
3567
3785
  localMetadata: {
3568
3786
  ...attachment.localMetadata,
3569
3787
  uploadState: "uploading",
3570
- uploadProgress: this.config.trackUploadProgress ? 0 : void 0
3788
+ uploadProgress: this.config.trackUploadProgress ? 0 : void 0,
3789
+ uploadConfirmationPending: false
3571
3790
  }
3572
3791
  }
3573
3792
  ]);
@@ -3580,7 +3799,8 @@ var _AttachmentManager = class _AttachmentManager {
3580
3799
  localMetadata: {
3581
3800
  ...attachment.localMetadata,
3582
3801
  uploadState: "uploading",
3583
- uploadProgress: nextUpload.uploadProgress
3802
+ uploadProgress: nextUpload.uploadProgress,
3803
+ uploadConfirmationPending: nextUpload.uploadConfirmationPending
3584
3804
  }
3585
3805
  });
3586
3806
  }
@@ -3634,6 +3854,50 @@ var localAttachmentToAttachment = (localAttachment) => {
3634
3854
  const { localMetadata, ...attachment } = localAttachment;
3635
3855
  return attachment;
3636
3856
  };
3857
+ var composeWithPendingUploads = ({
3858
+ composer,
3859
+ state
3860
+ }) => {
3861
+ const composerIsKeptAsDraft = !!composer.pollId;
3862
+ const relevantAttachments = composer.attachmentManager.attachments.filter(
3863
+ (attachment) => isFinishedUpload(attachment) || !composerIsKeptAsDraft && isPendingUpload(attachment)
3864
+ );
3865
+ const localAttachments = (state.localMessage.attachments ?? []).concat(
3866
+ relevantAttachments.map(
3867
+ (attachment) => isPendingUpload(attachment) ? attachment : localAttachmentToAttachment(attachment)
3868
+ )
3869
+ );
3870
+ const messageAttachments = (state.message.attachments ?? []).concat(
3871
+ relevantAttachments.filter(isFinishedUpload).map(localAttachmentToAttachment)
3872
+ );
3873
+ if (!localAttachments.length && !messageAttachments.length) return state;
3874
+ return {
3875
+ ...state,
3876
+ localMessage: localAttachments.length ? {
3877
+ ...state.localMessage,
3878
+ attachments: localAttachments
3879
+ } : state.localMessage,
3880
+ message: messageAttachments.length ? {
3881
+ ...state.message,
3882
+ attachments: messageAttachments
3883
+ } : state.message
3884
+ };
3885
+ };
3886
+ var createSendWithPendingUploadsAttachmentsMiddleware = (composer) => ({
3887
+ allowsPendingUploads: true,
3888
+ id: "stream-io/message-composer-middleware/attachments",
3889
+ handlers: {
3890
+ compose: ({
3891
+ state,
3892
+ next,
3893
+ forward
3894
+ }) => {
3895
+ const { attachmentManager } = composer;
3896
+ if (!attachmentManager) return forward();
3897
+ return next(composeWithPendingUploads({ composer, state }));
3898
+ }
3899
+ }
3900
+ });
3637
3901
  var createAttachmentsCompositionMiddleware = (composer) => ({
3638
3902
  id: "stream-io/message-composer-middleware/attachments",
3639
3903
  handlers: {
@@ -3850,7 +4114,7 @@ var getInitialMentions = (message) => {
3850
4114
  };
3851
4115
  var isSameMentionEntity = (first, second) => first.id === second.id && first.mentionType === second.mentionType;
3852
4116
  var getMentionsFromState = (state) => state.mentions ?? state.mentionedUsers.map(userResponseToMentionEntity);
3853
- var initState2 = ({
4117
+ var initState3 = ({
3854
4118
  composer,
3855
4119
  message
3856
4120
  }) => {
@@ -3879,7 +4143,7 @@ var initState2 = ({
3879
4143
  var TextComposer = class {
3880
4144
  constructor({ composer, message }) {
3881
4145
  this.initState = ({ message } = {}) => {
3882
- this.state.next(initState2({ composer: this.composer, message }));
4146
+ this.state.next(initState3({ composer: this.composer, message }));
3883
4147
  };
3884
4148
  this.getSnapshot = (state = this.state.getLatestValue()) => state;
3885
4149
  this.restoreSnapshot = (snapshot) => {
@@ -4072,7 +4336,7 @@ var TextComposer = class {
4072
4336
  this.commitState(output.state);
4073
4337
  };
4074
4338
  this.composer = composer;
4075
- this.state = new StateStore(initState2({ composer, message }));
4339
+ this.state = new StateStore(initState3({ composer, message }));
4076
4340
  this.middlewareExecutor = new TextComposerMiddlewareExecutor({ composer });
4077
4341
  }
4078
4342
  get channel() {
@@ -5774,6 +6038,13 @@ var MessageComposerMiddlewareExecutor = class extends MiddlewareExecutor {
5774
6038
  createCompositionDataCleanupMiddleware(composer)
5775
6039
  ]);
5776
6040
  }
6041
+ /**
6042
+ * Narrows the base accessor to the composition middleware type, so callers can read the
6043
+ * declarations it carries (`allowsPendingUploads`) without casting.
6044
+ */
6045
+ get installedMiddleware() {
6046
+ return super.installedMiddleware;
6047
+ }
5777
6048
  };
5778
6049
  var MessageDraftComposerMiddlewareExecutor = class extends MiddlewareExecutor {
5779
6050
  constructor({ composer }) {
@@ -6888,7 +7159,7 @@ var DEFAULT_COMPOSER_CONFIG = {
6888
7159
  };
6889
7160
 
6890
7161
  // src/messageComposer/CustomDataManager.ts
6891
- var initState3 = (options) => {
7162
+ var initState4 = (options) => {
6892
7163
  if (!options)
6893
7164
  return { message: {}, custom: {} };
6894
7165
  return { message: {}, custom: {} };
@@ -6897,14 +7168,14 @@ var CustomDataManager = class {
6897
7168
  constructor({ composer, message }) {
6898
7169
  this.isMessageDataEqual = (nextState, previousState) => JSON.stringify(nextState.message) === JSON.stringify(previousState?.message);
6899
7170
  this.initState = ({ message } = {}) => {
6900
- this.state.next(initState3({ composer: this.composer, message }));
7171
+ this.state.next(initState4({ composer: this.composer, message }));
6901
7172
  };
6902
7173
  this.getSnapshot = () => this.state.getLatestValue();
6903
7174
  this.restoreSnapshot = (snapshot) => {
6904
7175
  this.state.next(snapshot);
6905
7176
  };
6906
7177
  this.composer = composer;
6907
- this.state = new StateStore(initState3({ composer, message }));
7178
+ this.state = new StateStore(initState4({ composer, message }));
6908
7179
  }
6909
7180
  get customMessageData() {
6910
7181
  return this.state.getLatestValue().message;
@@ -6940,7 +7211,7 @@ var LinkPreviewStatus = /* @__PURE__ */ ((LinkPreviewStatus2) => {
6940
7211
  return LinkPreviewStatus2;
6941
7212
  })(LinkPreviewStatus || {});
6942
7213
  var linkPreviewArrayToMap = (linkPreviews) => new Map(linkPreviews.map((linkPreview) => [linkPreview.og_scrape_url, linkPreview]));
6943
- var initState4 = ({
7214
+ var initState5 = ({
6944
7215
  message
6945
7216
  }) => message ? {
6946
7217
  previews: message.attachments?.reduce((acc, attachment) => {
@@ -6959,7 +7230,7 @@ var _LinkPreviewsManager = class _LinkPreviewsManager {
6959
7230
  this.shouldDiscardEnrichQueries = false;
6960
7231
  this.initState = ({ message } = {}) => {
6961
7232
  this.cancelURLEnrichment();
6962
- this.state.next(initState4({ message: this.enabled ? message : void 0 }));
7233
+ this.state.next(initState5({ message: this.enabled ? message : void 0 }));
6963
7234
  };
6964
7235
  this.getSnapshot = () => this.state.getLatestValue();
6965
7236
  this.restoreSnapshot = (snapshot) => {
@@ -7062,7 +7333,7 @@ var _LinkPreviewsManager = class _LinkPreviewsManager {
7062
7333
  };
7063
7334
  this.composer = composer;
7064
7335
  this.state = new StateStore(
7065
- initState4({ message: this.enabled ? message : void 0 })
7336
+ initState5({ message: this.enabled ? message : void 0 })
7066
7337
  );
7067
7338
  this.findAndEnrichUrls = debounce(
7068
7339
  this._findAndEnrichUrls.bind(this),
@@ -7150,7 +7421,7 @@ var LinkPreviewsManager = _LinkPreviewsManager;
7150
7421
 
7151
7422
  // src/messageComposer/LocationComposer.ts
7152
7423
  var MIN_LIVE_LOCATION_SHARE_DURATION = 60 * 1e3;
7153
- var initState5 = ({
7424
+ var initState6 = ({
7154
7425
  message
7155
7426
  }) => ({
7156
7427
  location: message?.shared_location ?? null
@@ -7158,7 +7429,7 @@ var initState5 = ({
7158
7429
  var LocationComposer = class {
7159
7430
  constructor({ composer, message }) {
7160
7431
  this.initState = ({ message } = {}) => {
7161
- this.state.next(initState5({ message }));
7432
+ this.state.next(initState6({ message }));
7162
7433
  };
7163
7434
  this.getSnapshot = () => this.state.getLatestValue();
7164
7435
  this.restoreSnapshot = (snapshot) => {
@@ -7176,7 +7447,7 @@ var LocationComposer = class {
7176
7447
  });
7177
7448
  };
7178
7449
  this.composer = composer;
7179
- this.state = new StateStore(initState5({ message }));
7450
+ this.state = new StateStore(initState6({ message }));
7180
7451
  this._deviceId = this.config.getDeviceId();
7181
7452
  }
7182
7453
  get config() {
@@ -7897,7 +8168,7 @@ var initEditingAuditState = (composition) => {
7897
8168
  }
7898
8169
  };
7899
8170
  };
7900
- var initState6 = (composition) => {
8171
+ var initState7 = (composition) => {
7901
8172
  if (!composition) {
7902
8173
  return {
7903
8174
  draftId: null,
@@ -7984,7 +8255,7 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
7984
8255
  this.textComposer.initState({ message });
7985
8256
  this.pollComposer.initState();
7986
8257
  this.customDataManager.initState({ message });
7987
- this.state.next(initState6(composition));
8258
+ this.state.next(initState7(composition));
7988
8259
  };
7989
8260
  this.initStateFromChannelResponse = (channelApiResponse) => {
7990
8261
  if (this.channel.cid !== channelApiResponse.channel.cid) {
@@ -8450,7 +8721,7 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
8450
8721
  this.editingAuditState = new StateStore(
8451
8722
  this.initEditingAuditState(composition)
8452
8723
  );
8453
- this.state = new StateStore(initState6(composition));
8724
+ this.state = new StateStore(initState7(composition));
8454
8725
  this.compositionMiddlewareExecutor = new MessageComposerMiddlewareExecutor({
8455
8726
  composer: this
8456
8727
  });
@@ -8529,8 +8800,32 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
8529
8800
  const currentCommand = this.textComposer.command;
8530
8801
  return !currentCommand || this.validateCommandSendability(currentCommand).ready;
8531
8802
  }
8803
+ /**
8804
+ * Whether the installed composition middleware composes messages whose uploads are still in
8805
+ * flight — see `MessageCompositionMiddleware.allowsPendingUploads`.
8806
+ *
8807
+ * Derived from the chain rather than from config, so there is exactly one switch: the UI SDK
8808
+ * that implements the rest of the flow installs the middleware, and everything that has to
8809
+ * follow from that (starting with {@link hasSendableData}) follows on its own.
8810
+ *
8811
+ * **Temporary API.** In v10 sending with pending uploads becomes part of the composer
8812
+ * configuration (`MessageComposerConfig`, set through `updateConfig`), and this getter,
8813
+ * together with the middleware declaration it reads, is replaced by that config field.
8814
+ */
8815
+ get allowsPendingUploads() {
8816
+ return this.compositionMiddlewareExecutor.installedMiddleware.some(
8817
+ (middleware) => middleware.allowsPendingUploads
8818
+ );
8819
+ }
8532
8820
  get hasSendableData() {
8533
- return this.isCommandSendable && !!(!this.attachmentManager.uploadsInProgressCount && (!this.textComposer.textIsEmpty || this.attachmentManager.successfulUploadsCount > 0) || this.pollId || !!this.locationComposer.validLocation);
8821
+ if (!this.isCommandSendable) return false;
8822
+ if (this.allowsPendingUploads) {
8823
+ const hasSendableAttachment = this.attachmentManager.attachments.some(
8824
+ (attachment) => isFinishedUpload(attachment) || isPendingUpload(attachment)
8825
+ );
8826
+ return !!(!this.textComposer.textIsEmpty || hasSendableAttachment || this.pollId || this.locationComposer.validLocation);
8827
+ }
8828
+ return !!(!this.attachmentManager.uploadsInProgressCount && (!this.textComposer.textIsEmpty || this.attachmentManager.successfulUploadsCount > 0) || this.pollId || !!this.locationComposer.validLocation);
8534
8829
  }
8535
8830
  get compositionIsEmpty() {
8536
8831
  return !this.quotedMessage && this.contentIsEmpty;
@@ -9157,14 +9452,16 @@ var Channel = class {
9157
9452
  *
9158
9453
  * @return {Promise<SendMessageAPIResponse>} The Server Response
9159
9454
  */
9160
- async _sendMessage(message, options) {
9161
- return await this.getClient().post(
9162
- this._channelURL() + "/message",
9163
- {
9164
- message,
9165
- ...options
9166
- }
9167
- );
9455
+ async _sendMessage(rawMessage, options) {
9456
+ const client = this.getClient();
9457
+ const message = sanitizeOutgoingAttachments({
9458
+ client,
9459
+ message: rawMessage
9460
+ });
9461
+ return await client.post(this._channelURL() + "/message", {
9462
+ message,
9463
+ ...options
9464
+ });
9168
9465
  }
9169
9466
  async sendMessage(message, options) {
9170
9467
  try {
@@ -10615,6 +10912,7 @@ var Channel = class {
10615
10912
  // delivery-report network sync below is skipped for it.
10616
10913
  case "message.read_locally":
10617
10914
  case "message.read":
10915
+ if (event.thread) break;
10618
10916
  if (event.user?.id && event.created_at) {
10619
10917
  const previousReadState = channelState.read[event.user.id];
10620
10918
  channelState.read[event.user.id] = {
@@ -11064,7 +11362,7 @@ var ClientState = class {
11064
11362
  import WebSocket from "isomorphic-ws";
11065
11363
 
11066
11364
  // src/insights.ts
11067
- import axios from "axios";
11365
+ import axios2 from "axios";
11068
11366
  var InsightMetrics = class {
11069
11367
  constructor() {
11070
11368
  this.connectionStartTimestamp = null;
@@ -11077,7 +11375,7 @@ var postInsights = async (insightType, insights) => {
11077
11375
  const maxAttempts = 3;
11078
11376
  for (let i = 0; i < maxAttempts; i++) {
11079
11377
  try {
11080
- await axios.post(
11378
+ await axios2.post(
11081
11379
  `https://chat-insights.getstream.io/insights/${insightType}`,
11082
11380
  insights
11083
11381
  );
@@ -11585,131 +11883,6 @@ var StableWSConnection = class {
11585
11883
  }
11586
11884
  };
11587
11885
 
11588
- // src/uploadManager.ts
11589
- var initState7 = () => ({ uploads: {} });
11590
- var upsertById = (uploads, record) => ({
11591
- ...uploads,
11592
- [record.id]: { ...uploads[record.id], ...record }
11593
- });
11594
- var updateById = (uploads, record) => {
11595
- if (!(record.id in uploads)) return null;
11596
- const current = uploads[record.id];
11597
- return { ...uploads, [record.id]: { ...current, ...record } };
11598
- };
11599
- var UploadManager = class {
11600
- constructor(client) {
11601
- this.client = client;
11602
- this.inFlightUploads = /* @__PURE__ */ new Map();
11603
- this.getUpload = (id) => this.uploads[id];
11604
- /**
11605
- * Clears all upload records.
11606
- * Invoked when the user disconnects so a later session does not inherit stale upload state.
11607
- * Aborts every in-flight upload request via its `UploadRequestOptions.abortSignal`.
11608
- */
11609
- this.reset = () => {
11610
- for (const { abortController } of this.inFlightUploads.values()) {
11611
- abortController.abort();
11612
- }
11613
- this.inFlightUploads.clear();
11614
- this.state.next(initState7());
11615
- };
11616
- /**
11617
- * Removes the upload record for `id` if present.
11618
- * If an upload is still in progress, aborts its `UploadRequestOptions.abortSignal`.
11619
- */
11620
- this.deleteUploadRecord = (id) => {
11621
- const flight = this.inFlightUploads.get(id);
11622
- if (flight) {
11623
- this.inFlightUploads.delete(id);
11624
- flight.abortController.abort();
11625
- }
11626
- this.state.next((current) => {
11627
- if (!(id in current.uploads)) return current;
11628
- const uploads = { ...current.uploads };
11629
- delete uploads[id];
11630
- return { ...current, uploads };
11631
- });
11632
- };
11633
- /**
11634
- * Starts an upload for `id`, or returns the existing in-flight promise if one is already running.
11635
- * Uses {@link StreamChat.channel}(`channelCid`) → `messageComposer.attachmentManager.doUploadRequest`.
11636
- * Resolves with that result; rejects if the upload rejects (the record is removed from state either way).
11637
- */
11638
- this.upload = ({
11639
- id,
11640
- channelCid,
11641
- file
11642
- }) => {
11643
- const existing = this.inFlightUploads.get(id);
11644
- if (existing) return existing.promise;
11645
- let resolvePromise;
11646
- let rejectPromise;
11647
- const promise = new Promise((resolve, reject) => {
11648
- resolvePromise = resolve;
11649
- rejectPromise = reject;
11650
- });
11651
- const abortController = new AbortController();
11652
- this.inFlightUploads.set(id, { promise, abortController });
11653
- void (async () => {
11654
- const attachmentManager = this.resolveAttachmentManager(channelCid);
11655
- const trackProgress = attachmentManager.config.trackUploadProgress;
11656
- try {
11657
- this.upsertUpload({
11658
- id,
11659
- uploadProgress: trackProgress ? 0 : void 0
11660
- });
11661
- const onProgress = trackProgress ? (progress) => {
11662
- this.updateUpload({
11663
- id,
11664
- uploadProgress: progress
11665
- });
11666
- } : void 0;
11667
- const uploadRequestOptions = {
11668
- abortSignal: abortController.signal,
11669
- ...onProgress ? { onProgress } : {}
11670
- };
11671
- const response = await attachmentManager.doUploadRequest(
11672
- file,
11673
- uploadRequestOptions
11674
- );
11675
- resolvePromise(response);
11676
- } catch (error) {
11677
- rejectPromise(error);
11678
- } finally {
11679
- this.inFlightUploads.delete(id);
11680
- this.deleteUploadRecord(id);
11681
- }
11682
- })();
11683
- return promise;
11684
- };
11685
- this.upsertUpload = (record) => {
11686
- this.state.partialNext({
11687
- uploads: upsertById(this.uploads, record)
11688
- });
11689
- };
11690
- this.updateUpload = (record) => {
11691
- this.state.next((current) => {
11692
- const nextUploads = updateById(current.uploads, record);
11693
- if (!nextUploads) return current;
11694
- return { ...current, uploads: nextUploads };
11695
- });
11696
- };
11697
- this.state = new StateStore(initState7());
11698
- }
11699
- resolveAttachmentManager(channelCid) {
11700
- const colon = channelCid.indexOf(":");
11701
- if (colon <= 0 || colon === channelCid.length - 1) {
11702
- throw new Error(`Invalid channelCid: ${channelCid}`);
11703
- }
11704
- const channelType = channelCid.slice(0, colon);
11705
- const channelId = channelCid.slice(colon + 1);
11706
- return this.client.channel(channelType, channelId).messageComposer.attachmentManager;
11707
- }
11708
- get uploads() {
11709
- return this.state.getLatestValue().uploads;
11710
- }
11711
- };
11712
-
11713
11886
  // src/signing.ts
11714
11887
  var import_jsonwebtoken = __toESM(require_jsonwebtoken());
11715
11888
  var import_crypto = __toESM(require_crypto());
@@ -11968,7 +12141,7 @@ var TokenManager = class {
11968
12141
  };
11969
12142
 
11970
12143
  // src/connection_fallback.ts
11971
- import axios2 from "axios";
12144
+ import axios3 from "axios";
11972
12145
  var WSConnectionFallback = class {
11973
12146
  constructor({ client }) {
11974
12147
  /** @private */
@@ -11987,7 +12160,7 @@ var WSConnectionFallback = class {
11987
12160
  /** @private */
11988
12161
  this._req = async (params, config, retry) => {
11989
12162
  if (!this.cancelToken && !params.close) {
11990
- this.cancelToken = axios2.CancelToken.source();
12163
+ this.cancelToken = axios3.CancelToken.source();
11991
12164
  }
11992
12165
  try {
11993
12166
  const res = await this.client.doAxiosRequest(
@@ -12023,7 +12196,7 @@ var WSConnectionFallback = class {
12023
12196
  }
12024
12197
  }
12025
12198
  } catch (error) {
12026
- if (axios2.isCancel(error)) {
12199
+ if (axios3.isCancel(error)) {
12027
12200
  this._log(`_poll() - axios canceled request`);
12028
12201
  return;
12029
12202
  }
@@ -14446,7 +14619,7 @@ var StreamChat = class _StreamChat {
14446
14619
  return this.handleResponse(response);
14447
14620
  } catch (e) {
14448
14621
  e.client_request_id = requestConfig.headers?.["x-client-request-id"];
14449
- if (axios3.isCancel(e)) throw e;
14622
+ if (axios4.isCancel(e)) throw e;
14450
14623
  this._logApiError(type, url, e);
14451
14624
  this.consecutiveFailures += 1;
14452
14625
  if (e.response) {
@@ -14758,7 +14931,7 @@ var StreamChat = class _StreamChat {
14758
14931
  return this.userAgent;
14759
14932
  }
14760
14933
  if (!this.cachedUserAgent) {
14761
- const version = "9.51.0";
14934
+ const version = "9.52.0";
14762
14935
  const { name: sdkName, version: sdkVersion } = this.sdkIdentifier ?? {};
14763
14936
  const { name: appName, version: appVersion } = this.appIdentifier ?? {};
14764
14937
  const { os, model: deviceModel } = this.deviceIdentifier ?? {};
@@ -14826,7 +14999,7 @@ var StreamChat = class _StreamChat {
14826
14999
  keepAliveMsecs: 3e3
14827
15000
  });
14828
15001
  }
14829
- this.axiosInstance = axios3.create(this.options);
15002
+ this.axiosInstance = axios4.create(this.options);
14830
15003
  this.setBaseURL(this.options.baseURL || "https://chat.stream-io-api.com");
14831
15004
  if (typeof process !== "undefined" && "env" in process && process.env.STREAM_LOCAL_TEST_RUN) {
14832
15005
  this.setBaseURL("http://localhost:3030");
@@ -16178,6 +16351,11 @@ var StreamChat = class _StreamChat {
16178
16351
  }
16179
16352
  /**
16180
16353
  * unflagMessage - unflag a message
16354
+ *
16355
+ * @deprecated The `/moderation/unflag` endpoint is deprecated and is a no-op on
16356
+ * the backend - calling this resolves successfully but does not remove the flag.
16357
+ * Use `client.moderation.submitAction()` against the review queue item instead.
16358
+ *
16181
16359
  * @param {string} targetMessageID
16182
16360
  * @param {string} [options.user_id] currentUserID, only used with serverside auth
16183
16361
  * @returns {Promise<APIResponse>}
@@ -16190,6 +16368,11 @@ var StreamChat = class _StreamChat {
16190
16368
  }
16191
16369
  /**
16192
16370
  * unflagUser - unflag a user
16371
+ *
16372
+ * @deprecated The `/moderation/unflag` endpoint is deprecated and is a no-op on
16373
+ * the backend - calling this resolves successfully but does not remove the flag.
16374
+ * Use `client.moderation.submitAction()` against the review queue item instead.
16375
+ *
16193
16376
  * @param {string} targetID
16194
16377
  * @param {string} [options.user_id] currentUserID, only used with serverside auth
16195
16378
  * @returns {Promise<APIResponse>}
@@ -16474,7 +16657,10 @@ var StreamChat = class _StreamChat {
16474
16657
  if (!message.id) {
16475
16658
  throw Error("Please specify the message.id when calling updateMessage");
16476
16659
  }
16477
- const payload = toUpdatedMessagePayload(message);
16660
+ const payload = sanitizeOutgoingAttachments({
16661
+ client: this,
16662
+ message: toUpdatedMessagePayload(message)
16663
+ });
16478
16664
  if (typeof partialUserOrUserId === "string") {
16479
16665
  payload.user_id = partialUserOrUserId;
16480
16666
  } else if (typeof partialUserOrUserId?.id === "string") {
@@ -18947,6 +19133,7 @@ var AbstractOfflineDB = class {
18947
19133
  return this.handleMessageUpdatedEvent({ event, execute });
18948
19134
  }
18949
19135
  if (type === "message.read" || type === "notification.mark_read") {
19136
+ if (event.thread) return [];
18950
19137
  return this.handleRead({ event, unreadMessages: 0, execute });
18951
19138
  }
18952
19139
  if (type === "message.read_locally") {
@@ -19513,6 +19700,7 @@ export {
19513
19700
  createMessageComposerStateCompositionMiddleware,
19514
19701
  createPollComposerStateMiddleware,
19515
19702
  createPostUploadAttachmentEnrichmentMiddleware,
19703
+ createSendWithPendingUploadsAttachmentsMiddleware,
19516
19704
  createSharedLocationCompositionMiddleware,
19517
19705
  createTextComposerCompositionMiddleware,
19518
19706
  createTextComposerPreValidationMiddleware,
@@ -19541,6 +19729,7 @@ export {
19541
19729
  getTokenizedSuggestionDisplayName,
19542
19730
  getTriggerCharWithToken,
19543
19731
  gunzipPayload,
19732
+ hasPending,
19544
19733
  insertItemWithTrigger,
19545
19734
  isAudioAttachment,
19546
19735
  isBlobButNotFile,
@@ -19548,6 +19737,7 @@ export {
19548
19737
  isFileAttachment,
19549
19738
  isFileList,
19550
19739
  isFileReference,
19740
+ isFinishedUpload,
19551
19741
  isGiphyAttachment,
19552
19742
  isImageAttachment,
19553
19743
  isImageFile,
@@ -19560,9 +19750,11 @@ export {
19560
19750
  isLocalVoiceRecordingAttachment,
19561
19751
  isOwnUser,
19562
19752
  isPatch,
19753
+ isPendingUpload,
19563
19754
  isScrapedContent,
19564
19755
  isSharedLocationResponse,
19565
19756
  isTargetedOptionTextUpdate,
19757
+ isUploadCancellation,
19566
19758
  isUploadedAttachment,
19567
19759
  isVideoAttachment,
19568
19760
  isVoiceRecordingAttachment,
@@ -19582,11 +19774,14 @@ export {
19582
19774
  readFileAsArrayBuffer,
19583
19775
  removeDiacritics,
19584
19776
  replaceWordWithEntity,
19777
+ settled,
19585
19778
  stripCommandFromText,
19586
19779
  stripMentionTokens,
19587
19780
  textIsEmpty,
19588
19781
  timeLeftMs,
19589
19782
  verifyAndParseWebhook,
19590
- verifySignature
19783
+ verifySignature,
19784
+ withCancellation,
19785
+ withoutConcurrency
19591
19786
  };
19592
19787
  //# sourceMappingURL=index.mjs.map